From 3d100923fc688519a9ee264f1cee5280f6c3dc2a Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Tue, 22 Oct 2019 19:26:08 +0300 Subject: [PATCH 001/663] add some configurable build output to xamlil build tasks --- packages/Avalonia/AvaloniaBuildTasks.targets | 5 ++++- .../CompileAvaloniaXamlTask.cs | 11 ++++++++-- src/Avalonia.Build.Tasks/Extensions.cs | 9 ++++++-- .../GenerateAvaloniaResourcesTask.cs | 21 ++++++++++++++++--- .../XamlCompilerTaskExecutor.cs | 4 +++- 5 files changed, 41 insertions(+), 9 deletions(-) diff --git a/packages/Avalonia/AvaloniaBuildTasks.targets b/packages/Avalonia/AvaloniaBuildTasks.targets index 552713f94b..578fadff97 100644 --- a/packages/Avalonia/AvaloniaBuildTasks.targets +++ b/packages/Avalonia/AvaloniaBuildTasks.targets @@ -38,7 +38,8 @@ Output="$(AvaloniaResourcesTemporaryFilePath)" Root="$(MSBuildProjectDirectory)" Resources="@(AvaloniaResource)" - EmbeddedResources="@(EmbeddedResources)"/> + EmbeddedResources="@(EmbeddedResources)" + ReportImportance="$(AvaloniaXamlReportImportance)"/> @@ -54,6 +55,7 @@ $(IntermediateOutputPath)/Avalonia/references $(IntermediateOutputPath)/Avalonia/original.dll false + low AssemblyFile:{AssemblyFile}, ProjectDirectory:{ProjectDirectory}, OutputPath:{OutputPath}"; + BuildEngine.LogMessage(msg, outputImportance < MessageImportance.Low ? MessageImportance.High : outputImportance); + var res = XamlCompilerTaskExecutor.Compile(BuildEngine, input, File.ReadAllLines(ReferencesFilePath).Where(l => !string.IsNullOrWhiteSpace(l)).ToArray(), - ProjectDirectory, OutputPath, VerifyIl); + ProjectDirectory, OutputPath, VerifyIl, outputImportance); if (!res.Success) return false; if (!res.WrittenFile) @@ -68,7 +73,9 @@ namespace Avalonia.Build.Tasks public string OutputPath { get; set; } public bool VerifyIl { get; set; } - + + public string ReportImportance { get; set; } + public IBuildEngine BuildEngine { get; set; } public ITaskHost HostObject { get; set; } } diff --git a/src/Avalonia.Build.Tasks/Extensions.cs b/src/Avalonia.Build.Tasks/Extensions.cs index 440c6d7489..46c12eaf3d 100644 --- a/src/Avalonia.Build.Tasks/Extensions.cs +++ b/src/Avalonia.Build.Tasks/Extensions.cs @@ -9,14 +9,19 @@ namespace Avalonia.Build.Tasks public static void LogError(this IBuildEngine engine, BuildEngineErrorCode code, string file, string message) { - engine.LogErrorEvent(new BuildErrorEventArgs("Avalonia", FormatErrorCode(code), file ?? "", 0, 0, 0, 0, message, + engine.LogErrorEvent(new BuildErrorEventArgs("Avalonia", FormatErrorCode(code), file ?? "", 0, 0, 0, 0, message, "", "Avalonia")); } - + public static void LogWarning(this IBuildEngine engine, BuildEngineErrorCode code, string file, string message) { engine.LogWarningEvent(new BuildWarningEventArgs("Avalonia", FormatErrorCode(code), file ?? "", 0, 0, 0, 0, message, "", "Avalonia")); } + + public static void LogMessage(this IBuildEngine engine, string message, MessageImportance imp) + { + engine.LogMessageEvent(new BuildMessageEventArgs(message, "", "Avalonia", imp)); + } } } diff --git a/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs b/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs index 98ebb3e7d1..493d5ecc1b 100644 --- a/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs +++ b/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs @@ -22,6 +22,10 @@ namespace Avalonia.Build.Tasks [Required] public ITaskItem[] EmbeddedResources { get; set; } + public string ReportImportance { get; set; } + + private MessageImportance _reportImportance; + class Source { public string Path { get; set; } @@ -65,7 +69,14 @@ namespace Avalonia.Build.Tasks } } - List BuildResourceSources() => Resources.Select(r => new Source(r.ItemSpec, Root)).ToList(); + List BuildResourceSources() + => Resources.Select(r => + { + + var src = new Source(r.ItemSpec, Root); + BuildEngine.LogMessage($"avares -> name:{src.Path}, path: {src.SystemPath}, size:{src.Size}, ItemSpec:{r.ItemSpec}", _reportImportance); + return src; + }).ToList(); private void Pack(Stream output, List sources) { @@ -136,10 +147,14 @@ namespace Avalonia.Build.Tasks sources.Add(new Source("/!AvaloniaResourceXamlInfo", ms.ToArray())); return true; } - + public bool Execute() { - foreach(var r in EmbeddedResources.Where(r=>r.ItemSpec.EndsWith(".xaml")||r.ItemSpec.EndsWith(".paml"))) + Enum.TryParse(ReportImportance, out _reportImportance); + + BuildEngine.LogMessage($"GenerateAvaloniaResourcesTask -> Root: {Root}, {Resources?.Count()} resources, Output:{Output}", _reportImportance < MessageImportance.Low ? MessageImportance.High : _reportImportance); + + foreach (var r in EmbeddedResources.Where(r => r.ItemSpec.EndsWith(".xaml") || r.ItemSpec.EndsWith(".paml"))) BuildEngine.LogWarning(BuildEngineErrorCode.LegacyResmScheme, r.ItemSpec, "XAML file is packed using legacy EmbeddedResource/resm scheme, relative URIs won't work"); var resources = BuildResourceSources(); diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs index e348eb0fbc..3b69109e68 100644 --- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs +++ b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs @@ -40,7 +40,7 @@ namespace Avalonia.Build.Tasks } public static CompileResult Compile(IBuildEngine engine, string input, string[] references, string projectDirectory, - string output, bool verifyIl) + string output, bool verifyIl, MessageImportance logImportance) { var typeSystem = new CecilTypeSystem(references.Concat(new[] {input}), input); var asm = typeSystem.TargetAssemblyDefinition; @@ -121,6 +121,8 @@ namespace Avalonia.Build.Tasks { try { + engine.LogMessage($"XAMLIL: {res.Name} -> {res.Uri}", logImportance); + // StreamReader is needed here to handle BOM var xaml = new StreamReader(new MemoryStream(res.FileContents)).ReadToEnd(); var parsed = XDocumentXamlIlParser.Parse(xaml); From 22ae93ed952fead90b50cd92f8782ab5b43809a3 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Wed, 23 Oct 2019 16:26:09 +0300 Subject: [PATCH 002/663] fix a problem in xamlil resource path generation, sometimes resource path look like avares://AssemblyName.dll/../../../ResourceName.xaml --- .../GenerateAvaloniaResourcesTask.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs b/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs index 493d5ecc1b..406abe6f99 100644 --- a/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs +++ b/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs @@ -33,15 +33,11 @@ namespace Avalonia.Build.Tasks private byte[] _data; private string _sourcePath; - public Source(string file, string root) + public Source(string relativePath, string root) { - file = SPath.GetFullPath(file); root = SPath.GetFullPath(root); - var fileUri = new Uri(file, UriKind.Absolute); - var rootUri = new Uri(root, UriKind.Absolute); - rootUri = new Uri(rootUri.ToString().TrimEnd('/') + '/'); - Path = '/' + rootUri.MakeRelativeUri(fileUri).ToString().TrimStart('/'); - _sourcePath = file; + Path = "/" + relativePath.Replace('\\', '/'); + _sourcePath = SPath.Combine(root, relativePath); Size = (int)new FileInfo(_sourcePath).Length; } From 89e7665e8ec617743a23421548461aa260e813a5 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Wed, 23 Oct 2019 16:52:09 +0300 Subject: [PATCH 003/663] move AvaloniaXamlReportImportance to more visible place --- packages/Avalonia/AvaloniaBuildTasks.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/Avalonia/AvaloniaBuildTasks.targets b/packages/Avalonia/AvaloniaBuildTasks.targets index 578fadff97..537495fcad 100644 --- a/packages/Avalonia/AvaloniaBuildTasks.targets +++ b/packages/Avalonia/AvaloniaBuildTasks.targets @@ -2,6 +2,7 @@ <_AvaloniaUseExternalMSBuild>$(AvaloniaUseExternalMSBuild) <_AvaloniaUseExternalMSBuild Condition="'$(_AvaloniaForceInternalMSBuild)' == 'true'">false + low $(IntermediateOutputPath)/Avalonia/references $(IntermediateOutputPath)/Avalonia/original.dll false - low Date: Mon, 18 Nov 2019 21:54:18 +0300 Subject: [PATCH 004/663] HTML/WebSocket previewer "transport" + file watcher "transport" --- nukebuild/Build.cs | 14 + .../Remote/Server/RemoteServerTopLevelImpl.cs | 4 +- .../Avalonia.DesignerSupport.csproj | 3 + .../Remote/DetachableTransportConnection.cs | 4 +- .../Remote/FileWatcherTransport.cs | 90 + .../Remote/HtmlTransport/HtmlTransport.cs | 268 + .../SimpleWebSocketHttpServer.cs | 472 + .../Remote/HtmlTransport/webapp/.gitignore | 2 + .../HtmlTransport/webapp/package-lock.json | 8878 +++++++++++++++++ .../Remote/HtmlTransport/webapp/package.json | 41 + .../webapp/src/FramePresenter.tsx | 57 + .../webapp/src/PreviewerServerConnection.ts | 78 + .../HtmlTransport/webapp/src/index.html | 14 + .../Remote/HtmlTransport/webapp/src/index.tsx | 15 + .../Remote/HtmlTransport/webapp/tsconfig.json | 35 + .../HtmlTransport/webapp/webpack.config.js | 117 + .../Remote/RemoteDesignerEntryPoint.cs | 53 +- .../BsonStreamTransport.cs | 4 + src/Avalonia.Remote.Protocol/ITransport.cs | 1 + .../TransportConnectionWrapper.cs | 2 + .../TransportMessages.cs | 9 + .../ViewportMessages.cs | 2 + 22 files changed, 10153 insertions(+), 10 deletions(-) create mode 100644 src/Avalonia.DesignerSupport/Remote/FileWatcherTransport.cs create mode 100644 src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs create mode 100644 src/Avalonia.DesignerSupport/Remote/HtmlTransport/SimpleWebSocketHttpServer.cs create mode 100644 src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/.gitignore create mode 100644 src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/package-lock.json create mode 100644 src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/package.json create mode 100644 src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/FramePresenter.tsx create mode 100644 src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/PreviewerServerConnection.ts create mode 100644 src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/index.html create mode 100644 src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/index.tsx create mode 100644 src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/tsconfig.json create mode 100644 src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/webpack.config.js create mode 100644 src/Avalonia.Remote.Protocol/TransportMessages.cs diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs index dd2f27116d..a814c24de2 100644 --- a/nukebuild/Build.cs +++ b/nukebuild/Build.cs @@ -12,6 +12,7 @@ using Nuke.Common.ProjectModel; using Nuke.Common.Tooling; using Nuke.Common.Tools.DotNet; using Nuke.Common.Tools.MSBuild; +using Nuke.Common.Tools.Npm; using Nuke.Common.Utilities; using static Nuke.Common.EnvironmentInfo; using static Nuke.Common.IO.FileSystemTasks; @@ -123,8 +124,21 @@ partial class Build : NukeBuild EnsureCleanDirectory(Parameters.TestResultsRoot); }); + Target CompileHtmlPreviewer => _ => _ + .DependsOn("Clean") + .Executes(() => + { + var webappDir = RootDirectory / "src" / "Avalonia.DesignerSupport" / "Remote" / "HtmlTransport" / "webapp"; + + NpmTasks.NpmInstall(c => c.SetWorkingDirectory(webappDir)); + NpmTasks.NpmRun(c => c + .SetWorkingDirectory(webappDir) + .SetCommand("dist")); + }); + Target Compile => _ => _ .DependsOn(Clean) + .DependsOn(CompileHtmlPreviewer) .Executes(() => { if (Parameters.IsRunningOnWindows) diff --git a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs index 9d70fbcf31..f5c673d5f9 100644 --- a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs +++ b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs @@ -115,7 +115,7 @@ namespace Avalonia.Controls.Remote.Server { lock (_lock) { - _lastReceivedFrame = lastFrame.SequenceId; + _lastReceivedFrame = Math.Max(lastFrame.SequenceId, _lastReceivedFrame); } Dispatcher.UIThread.Post(RenderIfNeeded); } @@ -298,6 +298,8 @@ namespace Avalonia.Controls.Remote.Server Width = width, Height = height, Stride = width * bpp, + DpiX = _dpi.X, + DpiY = _dpi.Y }; } diff --git a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj index 2c5aaf1a70..76ea8a3445 100644 --- a/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj +++ b/src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj @@ -8,8 +8,11 @@ --> 0.7.0 CS1591 + true + + diff --git a/src/Avalonia.DesignerSupport/Remote/DetachableTransportConnection.cs b/src/Avalonia.DesignerSupport/Remote/DetachableTransportConnection.cs index 9c125448a0..eb91622543 100644 --- a/src/Avalonia.DesignerSupport/Remote/DetachableTransportConnection.cs +++ b/src/Avalonia.DesignerSupport/Remote/DetachableTransportConnection.cs @@ -35,5 +35,7 @@ namespace Avalonia.DesignerSupport.Remote add {} remove {} } + + public void Start() => _inner?.Start(); } -} \ No newline at end of file +} diff --git a/src/Avalonia.DesignerSupport/Remote/FileWatcherTransport.cs b/src/Avalonia.DesignerSupport/Remote/FileWatcherTransport.cs new file mode 100644 index 0000000000..0cb71dd217 --- /dev/null +++ b/src/Avalonia.DesignerSupport/Remote/FileWatcherTransport.cs @@ -0,0 +1,90 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Avalonia.Remote.Protocol; +using Avalonia.Remote.Protocol.Designer; +using Avalonia.Threading; + +namespace Avalonia.DesignerSupport.Remote +{ + class FileWatcherTransport : IAvaloniaRemoteTransportConnection, ITransportWithEnforcedMethod + { + private string _path; + private string _lastContents; + private bool _disposed; + + public FileWatcherTransport(Uri file) + { + _path = file.LocalPath; + } + + public void Dispose() + { + _disposed = true; + } + + void Dump(object o, string pad) + { + foreach (var p in o.GetType().GetProperties()) + { + Console.Write($"{pad}{p.Name}: "); + var v = p.GetValue(o); + if (v == null || v.GetType().IsPrimitive || v is string || v is Guid) + Console.WriteLine(v); + else + { + Console.WriteLine(); + Dump(v, pad + " "); + } + } + } + + + public Task Send(object data) + { + Console.WriteLine(data.GetType().Name); + Dump(data, " "); + return Task.CompletedTask; + } + + private Action _onMessage; + public event Action OnMessage + { + add + { + _onMessage+=value; + } + remove { _onMessage -= value; } + } + + public event Action OnException; + public void Start() + { + UpdaterThread(); + } + + // I couldn't get FileSystemWatcher working on Linux, so I came up with this abomination + async void UpdaterThread() + { + while (!_disposed) + { + var data = File.ReadAllText(_path); + if (data != _lastContents) + { + Console.WriteLine("Triggering XAML update"); + _lastContents = data; + _onMessage?.Invoke(this, new UpdateXamlMessage { Xaml = data }); + } + + await Task.Delay(100); + } + } + + public string PreviewerMethod => RemoteDesignerEntryPoint.Methods.Html; + } + + interface ITransportWithEnforcedMethod + { + string PreviewerMethod { get; } + } +} diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs new file mode 100644 index 0000000000..2ca609bba9 --- /dev/null +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs @@ -0,0 +1,268 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Avalonia.Remote.Protocol; +using Avalonia.Remote.Protocol.Viewport; + +namespace Avalonia.DesignerSupport.Remote.HtmlTransport +{ + public class HtmlWebSocketTransport : IAvaloniaRemoteTransportConnection + { + private readonly IAvaloniaRemoteTransportConnection _signalTransport; + private readonly SimpleWebSocketHttpServer _simpleServer; + private readonly Dictionary _resources; + private SimpleWebSocket _pendingSocket; + private bool _disposed; + private object _lock = new object(); + private AutoResetEvent _wakeup = new AutoResetEvent(false); + private FrameMessage _lastFrameMessage = null; + private FrameMessage _lastSentFrameMessage = null; + private RequestViewportResizeMessage _lastViewportRequest; + private Action _onMessage; + private Action _onException; + + private static readonly Dictionary Mime = new Dictionary + { + ["html"] = "text/html", ["htm"] = "text/html", ["js"] = "text/javascript", ["css"] = "text/css" + }; + + private static readonly byte[] NotFound = Encoding.UTF8.GetBytes("404 - Not Found"); + + + public HtmlWebSocketTransport(IAvaloniaRemoteTransportConnection signalTransport, Uri listenUri) + { + if (listenUri.Scheme != "http") + throw new ArgumentException("listenUri"); + + var resourcePrefix = "Avalonia.DesignerSupport.Remote.HtmlTransport.webapp.build."; + _resources = typeof(HtmlWebSocketTransport).Assembly.GetManifestResourceNames() + .Where(r => r.StartsWith(resourcePrefix) && r.EndsWith(".gz")).ToDictionary( + r => r.Substring(resourcePrefix.Length).Substring(0,r.Length-resourcePrefix.Length-3), + r => + { + + using (var s = + new GZipStream(typeof(HtmlWebSocketTransport).Assembly.GetManifestResourceStream(r), + CompressionMode.Decompress)) + { + var ms = new MemoryStream(); + s.CopyTo(ms); + return ms.ToArray(); + } + }); + + _signalTransport = signalTransport; + var host = listenUri.Host.Split(':'); + var address = IPAddress.Parse(host[0]); + var port = host.Length > 1 ? int.Parse(host[1]) : 5000; + + _simpleServer = new SimpleWebSocketHttpServer(address, port); + _simpleServer.Listen(); + Task.Run(AcceptWorker); + Task.Run(SocketWorker); + _signalTransport.Send(new HtmlTransportStartedMessage { Uri = "http://" + address + ":" + port + "/" }); + } + + async void AcceptWorker() + { + while (true) + { + + using (var req = await _simpleServer.AcceptAsync()) + { + + if (!req.IsWebsocketRequest) + { + + var key = req.Path == "/" ? "index.html" : req.Path.TrimStart('/').Replace('/', '.'); + if (_resources.TryGetValue(key, out var data)) + { + var ext = Path.GetExtension(key).Substring(1); + string mime = null; + if (ext == null || !Mime.TryGetValue(ext, out mime)) + mime = "application/octet-stream"; + await req.RespondAsync(200, data, mime); + } + else + { + await req.RespondAsync(404, NotFound, "text/plain"); + } + } + else + { + var socket = await req.AcceptWebSocket(); + SocketReceiveWorker(socket); + lock (_lock) + { + _pendingSocket?.Dispose(); + _pendingSocket = socket; + } + } + } + } + } + + async void SocketReceiveWorker(SimpleWebSocket socket) + { + try + { + while (true) + { + var msg = await socket.ReceiveMessage().ConfigureAwait(false); + if(msg == null) + return; + if (msg.IsText) + { + var s = Encoding.UTF8.GetString(msg.Data); + var parts = s.Split(':'); + if (parts[0] == "frame-received") + _onMessage?.Invoke(this, new FrameReceivedMessage { SequenceId = long.Parse(parts[1]) }); + } + } + } + catch(Exception e) + { + Console.Error.WriteLine(e.ToString()); + } + } + + async void SocketWorker() + { + try + { + SimpleWebSocket socket = null; + while (true) + { + if (_disposed) + { + socket?.Dispose(); + return; + } + + FrameMessage sendNow = null; + lock (_lock) + { + if (_pendingSocket != null) + { + socket?.Dispose(); + socket = _pendingSocket; + _pendingSocket = null; + _lastSentFrameMessage = null; + } + + if (_lastFrameMessage != _lastSentFrameMessage) + _lastSentFrameMessage = sendNow = _lastFrameMessage; + } + + if (sendNow != null && socket != null) + { + await socket.SendMessage( + $"frame:{sendNow.SequenceId}:{sendNow.Width}:{sendNow.Height}:{sendNow.Stride}:{sendNow.DpiX}:{sendNow.DpiY}"); + await socket.SendMessage(false, sendNow.Data); + } + + _wakeup.WaitOne(TimeSpan.FromSeconds(1)); + } + } + catch(Exception e) + { + Console.Error.WriteLine(e.ToString()); + } + } + + public void Dispose() + { + _pendingSocket?.Dispose(); + _simpleServer.Dispose(); + } + + + public Task Send(object data) + { + if (data is FrameMessage frame) + { + _lastFrameMessage = frame; + _wakeup.Set(); + return Task.CompletedTask; + } + if (data is RequestViewportResizeMessage req) + { + return Task.CompletedTask; + } + return _signalTransport.Send(data); + } + + public void Start() + { + _onMessage?.Invoke(this, new Avalonia.Remote.Protocol.Viewport.ClientSupportedPixelFormatsMessage + { + Formats = new []{PixelFormat.Rgba8888} + }); + _signalTransport.Start(); + } + + #region Forward + public event Action OnMessage + { + add + { + bool subscribeToInner; + lock (_lock) + { + subscribeToInner = _onMessage == null; + _onMessage += value; + } + + if (subscribeToInner) + _signalTransport.OnMessage += OnSignalTransportMessage; + } + remove + { + lock (_lock) + { + _onMessage -= value; + if (_onMessage == null) + _signalTransport.OnMessage -= OnSignalTransportMessage; + } + } + } + + private void OnSignalTransportMessage(IAvaloniaRemoteTransportConnection signal, object message) => _onMessage?.Invoke(this, message); + + public event Action OnException + { + add + { + lock (_lock) + { + var subscribeToInner = _onException == null; + _onException += value; + if (subscribeToInner) + _signalTransport.OnException += OnSignalTransportException; + } + } + remove + { + lock (_lock) + { + _onException -= value; + if(_onException==null) + _signalTransport.OnException -= OnSignalTransportException; + } + + } + } + + private void OnSignalTransportException(IAvaloniaRemoteTransportConnection arg1, Exception ex) + { + _onException?.Invoke(this, ex); + } + #endregion + } +} diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/SimpleWebSocketHttpServer.cs b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/SimpleWebSocketHttpServer.cs new file mode 100644 index 0000000000..6af21bbcf5 --- /dev/null +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/SimpleWebSocketHttpServer.cs @@ -0,0 +1,472 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; + +namespace Avalonia.DesignerSupport.Remote.HtmlTransport +{ + public class SimpleWebSocketHttpServer : IDisposable + { + private readonly IPAddress _address; + private readonly int _port; + private TcpListener _listener; + + public async Task AcceptAsync() + { + while (true) + { + var res = await AcceptAsyncImpl(); + if (res != null) + return res; + } + } + async Task AcceptAsyncImpl() + { + if (_listener == null) + throw new InvalidOperationException("Currently not listening"); + var socket = await _listener.AcceptSocketAsync(); + var stream = new NetworkStream(socket); + bool error = true; + async Task ReadLineAsync() + { + var readBuffer = new byte[1]; + var lineBuffer = new byte[1024]; + for (var c = 0; c < 1024; c++) + { + if (await stream.ReadAsync(readBuffer, 0, 1) == 0) + throw new EndOfStreamException(); + if (readBuffer[0] == 10) + { + if (c == 0) + return ""; + if (lineBuffer[c - 1] == 13) + c--; + if (c == 0) + return ""; + + return Encoding.UTF8.GetString(lineBuffer, 0, c); + } + lineBuffer[c] = readBuffer[0]; + } + + throw new InvalidDataException("Header is too large"); + } + + var headers = new Dictionary(); + string line = null; + try + { + + line = await ReadLineAsync(); + var sp = line.Split(' '); + if (sp.Length != 3 || !sp[2].StartsWith("HTTP") || sp[0] != "GET") + return null; + var path = sp[1]; + + while (true) + { + line = await ReadLineAsync(); + if (line == "") + break; + sp = line.Split(new[] {':'}, 2); + headers[sp[0]] = sp[1].TrimStart(); + } + + error = false; + + return new SimpleWebSocketHttpRequest(stream, path, headers); + } + catch + { + error = true; + return null; + } + finally + { + if (error) + stream.Dispose(); + } + + } + + public void Listen() + { + var listener = new TcpListener(_address, _port); + listener.Start(); + _listener = listener; + } + + public SimpleWebSocketHttpServer(IPAddress address, int port) + { + _address = address; + _port = port; + } + + public void Dispose() + { + _listener?.Stop(); + _listener = null; + } + } + + + public class SimpleWebSocketHttpRequest : IDisposable + { + public Dictionary Headers { get; } + public string Path { get; } + private NetworkStream _stream; + public bool IsWebsocketRequest { get; } + public IReadOnlyList WebSocketProtocols { get; } + private string _websocketKey; + + public SimpleWebSocketHttpRequest(NetworkStream stream, string path, Dictionary headers) + { + Path = path; + Headers = headers; + + _stream = stream; + if (headers.TryGetValue("Connection", out var h) + && h.Contains("Upgrade") + && headers.TryGetValue("Upgrade", out h) && + h == "websocket" + && headers.TryGetValue("Sec-WebSocket-Key", out _websocketKey)) + { + IsWebsocketRequest = true; + if (headers.TryGetValue("Sec-WebSocket-Protocol", out h)) + WebSocketProtocols = h.Split(',').Select(x => x.Trim()).ToArray(); + else WebSocketProtocols = new string[0]; + } + } + + public async Task RespondAsync(int code, byte[] data, string contentType) + { + var headers = Encoding.UTF8.GetBytes($"HTTP/1.1 {code} {(HttpStatusCode)code}\r\nConnection: close\r\nContent-Type: {contentType}\r\nContent-Length: {data.Length}\r\n\r\n"); + await _stream.WriteAsync(headers, 0, headers.Length); + await _stream.WriteAsync(data, 0, data.Length); + _stream.Dispose(); + _stream = null; + + } + + + public async Task AcceptWebSocket(string protocol = null) + { + + var handshakeSource = _websocketKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + string handshake; + using (var sha1 = SHA1.Create()) + handshake = Convert.ToBase64String(sha1.ComputeHash(Encoding.UTF8.GetBytes(handshakeSource))); + var headers = + "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: " + + handshake + "\r\n"; + if (protocol != null) + headers += protocol + "\r\n"; + headers += "\r\n"; + var bheaders = Encoding.UTF8.GetBytes(headers); + await _stream.WriteAsync(bheaders, 0, bheaders.Length); + + var s = _stream; + _stream = null; + return new SimpleWebSocket(s); + } + + public void Dispose() => _stream?.Dispose(); + } + + + + public class SimpleWebSocket : IDisposable + { + class AsyncLock + { + private object _syncRoot = new object(); + private Queue> _queue = new Queue>(); + private bool _locked; + public Task LockAsync() + { + lock (_syncRoot) + { + if (!_locked) + { + _locked = true; + return Task.FromResult(new Lock(this)); + } + else + { + var tcs = new TaskCompletionSource(); + _queue.Enqueue(tcs); + return tcs.Task; + } + } + } + + private void Unlock() + { + lock (_syncRoot) + { + if (_queue.Count != 0) + _queue.Dequeue().SetResult(new Lock(this)); + else + _locked = false; + } + } + + class Lock : IDisposable + { + private AsyncLock _parent; + private object _syncRoot = new object(); + + public Lock(AsyncLock parent) + { + _parent = parent; + } + + public void Dispose() + { + lock (_syncRoot) + { + if (_parent == null) + return; + var p = _parent; + _parent = null; + p.Unlock(); + } + } + } + } + + private Stream _stream; + private AsyncLock _sendLock = new AsyncLock(); + private AsyncLock _recvLock = new AsyncLock(); + private const int WebsocketInitialHeaderLength = 2; + private const int WebsocketLen16Length = 4; + private const int WebsocketLen64Length = 10; + + private const int WebsocketLen16Code = 126; + private const int WebsocketLen64Code = 127; + + [StructLayout(LayoutKind.Explicit)] + struct WebSocketHeader + { + [FieldOffset(0)] public byte Mask; + [FieldOffset(1)] public byte Length8; + [FieldOffset(2)] public ushort Length16; + [FieldOffset(2)] public ulong Length64; + } + + readonly byte[] _sendHeaderBuffer = new byte[10]; + readonly MemoryStream _receiveFrameStream = new MemoryStream(); + readonly MemoryStream _receiveMessageStream = new MemoryStream(); + private FrameType _currentMessageFrameType; + + enum FrameType + { + Continue = 0x0, + Text = 0x1, + Binary = 0x2, + Close = 0x8, + Ping = 0x9, + Pong = 0xA + } + + internal SimpleWebSocket(Stream stream) + { + _stream = stream; + } + + public void Dispose() + { + _stream?.Dispose(); + _stream = null; + } + + public Task SendMessage(string text) + { + var data = Encoding.UTF8.GetBytes(text); + return SendMessage(true, data); + } + public Task SendMessage(bool isText, byte[] data) => SendMessage(isText, data, 0, data.Length); + + + public Task SendMessage(bool isText, byte[] data, int offset, int length) + => SendFrame(isText ? FrameType.Text : FrameType.Binary, data, offset, length); + + async Task SendFrame(FrameType type, byte[] data, int offset, int length) + { + using (var l = await _sendLock.LockAsync()) + { + var header = new WebSocketHeader(); + + int headerLength; + if (data.Length <= 125) + { + headerLength = WebsocketInitialHeaderLength; + header.Length8 = (byte) length; + } + else if (length <= 0xffff) + { + headerLength = WebsocketLen16Length; + header.Length8 = WebsocketLen16Code; + header.Length16 = (ushort) IPAddress.HostToNetworkOrder((short) (ushort) length); + + } + else + { + headerLength = WebsocketLen64Length; + header.Length8 = WebsocketLen64Code; + header.Length64 = (ulong) IPAddress.HostToNetworkOrder((long) length); + } + + var endOfMessage = true; + header.Mask = (byte) (((endOfMessage ? 1u : 0u) << 7) | ((byte) (type) & 0xf)); + unsafe + { + Marshal.Copy(new IntPtr(&header), _sendHeaderBuffer, 0, headerLength); + } + + await _stream.WriteAsync(_sendHeaderBuffer, 0, headerLength); + await _stream.WriteAsync(data, offset, length); + } + } + + struct Frame + { + public byte[] Data; + public bool EndOfMessage; + public FrameType FrameType; + } + + byte[] _recvHeaderBuffer = new byte[8]; + byte[] _maskBuffer = new byte[4]; + async Task ReadFrame() + { + _receiveFrameStream.Position = 0; + _receiveFrameStream.SetLength(0); + await ReadExact(_stream, _recvHeaderBuffer, 0, 2); + var masked = (_recvHeaderBuffer[1] & 0x80) != 0; + var len0 = (_recvHeaderBuffer[1] & 0x7F); + var endOfMessage = (_recvHeaderBuffer[0] & 0x80) != 0; + var frameType = (FrameType) (_recvHeaderBuffer[0] & 0xf); + int length; + if (len0 <= 125) + length = len0; + else if (len0 == WebsocketLen16Code) + { + await ReadExact(_stream, _recvHeaderBuffer, 0, 2); + length = (ushort) IPAddress.NetworkToHostOrder(BitConverter.ToInt16(_recvHeaderBuffer, 0)); + } + + else + { + await ReadExact(_stream, _recvHeaderBuffer, 0, 8); + length = (int) (ulong) IPAddress.NetworkToHostOrder((long) BitConverter.ToUInt64(_recvHeaderBuffer, 0)); + } + + if (masked) + await ReadExact(_stream, _maskBuffer, 0, 4); + await ReadExact(_stream, _receiveFrameStream, length); + var data = _receiveFrameStream.ToArray(); + if(masked) + for (var c = 0; c < data.Length; c++) + data[c] = (byte) (data[c] ^ _maskBuffer[c % 4]); + + return new Frame + { + Data = data, + EndOfMessage = endOfMessage, + FrameType = frameType + }; + } + + + public async Task ReceiveMessage() + { + using (await _recvLock.LockAsync()) + { + while (true) + { + var frame = await ReadFrame(); + + if (frame.FrameType == FrameType.Close) + return null; + if (frame.FrameType == FrameType.Ping) + await SendFrame(FrameType.Pong, frame.Data, 0, frame.Data.Length); + if (frame.FrameType == FrameType.Text || frame.FrameType == FrameType.Binary) + { + var isText = frame.FrameType == FrameType.Text; + if (_receiveMessageStream.Length == 0 && frame.EndOfMessage) + return new SimpleWebSocketMessage + { + IsText = isText, + Data = frame.Data + }; + + _receiveMessageStream.Write(frame.Data, 0, frame.Data.Length); + _currentMessageFrameType = frame.FrameType; + } + if (frame.FrameType == FrameType.Continue) + { + frame.FrameType = _currentMessageFrameType; + _receiveMessageStream.Write(frame.Data, 0, frame.Data.Length); + if (frame.EndOfMessage) + { + var isText = frame.FrameType == FrameType.Text; + var data = _receiveMessageStream.ToArray(); + _receiveMessageStream.Position = 0; + _receiveMessageStream.SetLength(0); + return new SimpleWebSocketMessage + { + IsText = isText, + Data = data + }; + } + } + } + } + } + + + byte[] _readExactBuffer = new byte[4096]; + async Task ReadExact(Stream from, MemoryStream to, int length) + { + while (length>0) + { + var toRead = Math.Min(length, _readExactBuffer.Length); + var read = await from.ReadAsync(_readExactBuffer, 0, toRead); + to.Write(_readExactBuffer, 0, read); + if (read <= 0) + throw new EndOfStreamException(); + length -= read; + } + } + + async Task ReadExact(Stream from, byte[] to, int offset, int length) + { + while (length > 0) + { + var read = await from.ReadAsync(to, offset, length); + if (read <= 0) + throw new EndOfStreamException(); + length -= read; + offset += read; + } + } + } + + public class SimpleWebSocketMessage + { + public bool IsText { get; set; } + public byte[] Data { get; set; } + + public string AsString() + { + return Encoding.UTF8.GetString(Data); + } + } +} diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/.gitignore b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/.gitignore new file mode 100644 index 0000000000..e3fbd98336 --- /dev/null +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/.gitignore @@ -0,0 +1,2 @@ +build +node_modules diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/package-lock.json b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/package-lock.json new file mode 100644 index 0000000000..87536c670f --- /dev/null +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/package-lock.json @@ -0,0 +1,8878 @@ +{ + "name": "simple", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@mrmlnc/readdir-enhanced": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", + "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", + "dev": true, + "requires": { + "call-me-maybe": "^1.0.1", + "glob-to-regexp": "^0.3.0" + } + }, + "@nodelib/fs.stat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", + "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", + "dev": true + }, + "@samverschueren/stream-to-observable": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz", + "integrity": "sha512-MI4Xx6LHs4Webyvi6EbspgyAb4D2Q2VtnCQ1blOJcoLS6mVa8lNN2rkIy1CVxfTUpoyIbCTkXES1rLXztFD1lg==", + "dev": true, + "requires": { + "any-observable": "^0.3.0" + } + }, + "@sindresorhus/is": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz", + "integrity": "sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==", + "dev": true + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, + "@types/prop-types": { + "version": "15.7.3", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" + }, + "@types/react": { + "version": "16.9.11", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.11.tgz", + "integrity": "sha512-UBT4GZ3PokTXSWmdgC/GeCGEJXE5ofWyibCcecRLUVN2ZBpXQGVgQGtG2foS7CrTKFKlQVVswLvf7Js6XA/CVQ==", + "requires": { + "@types/prop-types": "*", + "csstype": "^2.2.0" + } + }, + "@types/react-dom": { + "version": "16.9.4", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.4.tgz", + "integrity": "sha512-fya9xteU/n90tda0s+FtN5Ym4tbgxpq/hb/Af24dvs6uYnYn+fspaxw5USlw0R8apDNwxsqumdRoCoKitckQqw==", + "requires": { + "@types/react": "*" + } + }, + "@webassemblyjs/ast": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.5.13.tgz", + "integrity": "sha512-49nwvW/Hx9i+OYHg+mRhKZfAlqThr11Dqz8TsrvqGKMhdI2ijy3KBJOun2Z4770TPjrIJhR6KxChQIDaz8clDA==", + "dev": true, + "requires": { + "@webassemblyjs/helper-module-context": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/wast-parser": "1.5.13", + "debug": "^3.1.0", + "mamacro": "^0.0.3" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.5.13.tgz", + "integrity": "sha512-vrvvB18Kh4uyghSKb0NTv+2WZx871WL2NzwMj61jcq2bXkyhRC+8Q0oD7JGVf0+5i/fKQYQSBCNMMsDMRVAMqA==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.5.13.tgz", + "integrity": "sha512-dBh2CWYqjaDlvMmRP/kudxpdh30uXjIbpkLj9HQe+qtYlwvYjPRjdQXrq1cTAAOUSMTtzqbXIxEdEZmyKfcwsg==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.5.13.tgz", + "integrity": "sha512-v7igWf1mHcpJNbn4m7e77XOAWXCDT76Xe7Is1VQFXc4K5jRcFrl9D0NrqM4XifQ0bXiuTSkTKMYqDxu5MhNljA==", + "dev": true, + "requires": { + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.5.13.tgz", + "integrity": "sha512-yN6ScQQDFCiAXnVctdVO/J5NQRbwyTbQzsGzEgXsAnrxhjp0xihh+nNHQTMrq5UhOqTb5LykpJAvEv9AT0jnAQ==", + "dev": true, + "requires": { + "@webassemblyjs/wast-printer": "1.5.13" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.5.13.tgz", + "integrity": "sha512-hSIKzbXjVMRvy3Jzhgu+vDd/aswJ+UMEnLRCkZDdknZO3Z9e6rp1DAs0tdLItjCFqkz9+0BeOPK/mk3eYvVzZg==", + "dev": true + }, + "@webassemblyjs/helper-module-context": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.5.13.tgz", + "integrity": "sha512-zxJXULGPLB7r+k+wIlvGlXpT4CYppRz8fLUM/xobGHc9Z3T6qlmJD9ySJ2jknuktuuiR9AjnNpKYDECyaiX+QQ==", + "dev": true, + "requires": { + "debug": "^3.1.0", + "mamacro": "^0.0.3" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.5.13.tgz", + "integrity": "sha512-0n3SoNGLvbJIZPhtMFq0XmmnA/YmQBXaZKQZcW8maGKwLpVcgjNrxpFZHEOLKjXJYVN5Il8vSfG7nRX50Zn+aw==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.5.13.tgz", + "integrity": "sha512-IJ/goicOZ5TT1axZFSnlAtz4m8KEjYr12BNOANAwGFPKXM4byEDaMNXYowHMG0yKV9a397eU/NlibFaLwr1fbw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-buffer": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/wasm-gen": "1.5.13", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@webassemblyjs/ieee754": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.5.13.tgz", + "integrity": "sha512-TseswvXEPpG5TCBKoLx9tT7+/GMACjC1ruo09j46ULRZWYm8XHpDWaosOjTnI7kr4SRJFzA6MWoUkAB+YCGKKg==", + "dev": true, + "requires": { + "ieee754": "^1.1.11" + } + }, + "@webassemblyjs/leb128": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.5.13.tgz", + "integrity": "sha512-0NRMxrL+GG3eISGZBmLBLAVjphbN8Si15s7jzThaw1UE9e5BY1oH49/+MA1xBzxpf1OW5sf9OrPDOclk9wj2yg==", + "dev": true, + "requires": { + "long": "4.0.0" + }, + "dependencies": { + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", + "dev": true + } + } + }, + "@webassemblyjs/utf8": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.5.13.tgz", + "integrity": "sha512-Ve1ilU2N48Ew0lVGB8FqY7V7hXjaC4+PeZM+vDYxEd+R2iQ0q+Wb3Rw8v0Ri0+rxhoz6gVGsnQNb4FjRiEH/Ng==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.5.13.tgz", + "integrity": "sha512-X7ZNW4+Hga4f2NmqENnHke2V/mGYK/xnybJSIXImt1ulxbCOEs/A+ZK/Km2jgihjyVxp/0z0hwIcxC6PrkWtgw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-buffer": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/helper-wasm-section": "1.5.13", + "@webassemblyjs/wasm-gen": "1.5.13", + "@webassemblyjs/wasm-opt": "1.5.13", + "@webassemblyjs/wasm-parser": "1.5.13", + "@webassemblyjs/wast-printer": "1.5.13", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.5.13.tgz", + "integrity": "sha512-yfv94Se8R73zmr8GAYzezFHc3lDwE/lBXQddSiIZEKZFuqy7yWtm3KMwA1uGbv5G1WphimJxboXHR80IgX1hQA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/ieee754": "1.5.13", + "@webassemblyjs/leb128": "1.5.13", + "@webassemblyjs/utf8": "1.5.13" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.5.13.tgz", + "integrity": "sha512-IkXSkgzVhQ0QYAdIayuCWMmXSYx0dHGU8Ah/AxJf1gBvstMWVnzJnBwLsXLyD87VSBIcsqkmZ28dVb0mOC3oBg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-buffer": "1.5.13", + "@webassemblyjs/wasm-gen": "1.5.13", + "@webassemblyjs/wasm-parser": "1.5.13", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.5.13.tgz", + "integrity": "sha512-XnYoIcu2iqq8/LrtmdnN3T+bRjqYFjRHqWbqK3osD/0r/Fcv4d9ecRzjVtC29ENEuNTK4mQ9yyxCBCbK8S/cpg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-api-error": "1.5.13", + "@webassemblyjs/helper-wasm-bytecode": "1.5.13", + "@webassemblyjs/ieee754": "1.5.13", + "@webassemblyjs/leb128": "1.5.13", + "@webassemblyjs/utf8": "1.5.13" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.5.13.tgz", + "integrity": "sha512-Lbz65T0LQ1LgzKiUytl34CwuhMNhaCLgrh0JW4rJBN6INnBB8NMwUfQM+FxTnLY9qJ+lHJL/gCM5xYhB9oWi4A==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/floating-point-hex-parser": "1.5.13", + "@webassemblyjs/helper-api-error": "1.5.13", + "@webassemblyjs/helper-code-frame": "1.5.13", + "@webassemblyjs/helper-fsm": "1.5.13", + "long": "^3.2.0", + "mamacro": "^0.0.3" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.5.13.tgz", + "integrity": "sha512-QcwogrdqcBh8Z+eUF8SG+ag5iwQSXxQJELBEHmLkk790wgQgnIMmntT2sMAMw53GiFNckArf5X0bsCA44j3lWQ==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/wast-parser": "1.5.13", + "long": "^3.2.0" + } + }, + "acorn": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", + "dev": true + }, + "acorn-dynamic-import": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz", + "integrity": "sha512-zVWV8Z8lislJoOKKqdNMOB+s6+XV5WERty8MnKBeFgwA+19XJjJHs2RP5dzM57FftIs+jQnRToLiWazKr6sSWg==", + "dev": true, + "requires": { + "acorn": "^5.0.0" + } + }, + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", + "dev": true + }, + "ajv-keywords": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", + "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", + "dev": true + }, + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "any-observable": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz", + "integrity": "sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==", + "dev": true + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-differ": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", + "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=", + "dev": true + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "dev": true, + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "ast-types": { + "version": "0.11.5", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.11.5.tgz", + "integrity": "sha512-oJjo+5e7/vEc2FBK8gUalV0pba4L3VdBIs2EKhOLHLcOd2FgQIVQN9xb0eZ9IjEWyAL7vq6fGJxOvVvdCHNyMw==", + "dev": true + }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "awesome-typescript-loader": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/awesome-typescript-loader/-/awesome-typescript-loader-5.2.1.tgz", + "integrity": "sha512-slv66OAJB8orL+UUaTI3pKlLorwIvS4ARZzYR9iJJyGsEgOqueMfOMdKySWzZ73vIkEe3fcwFgsKMg4d8zyb1g==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "enhanced-resolve": "^4.0.0", + "loader-utils": "^1.1.0", + "lodash": "^4.17.5", + "micromatch": "^3.1.9", + "mkdirp": "^0.5.1", + "source-map-support": "^0.5.3", + "webpack-log": "^1.2.0" + } + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "babel-core": { + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.1", + "debug": "^2.6.9", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.8", + "slash": "^1.0.0", + "source-map": "^0.5.7" + }, + "dependencies": { + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + } + } + }, + "babel-generator": { + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", + "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", + "dev": true, + "requires": { + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.17.4", + "source-map": "^0.5.7", + "trim-right": "^1.0.1" + }, + "dependencies": { + "jsesc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", + "dev": true + } + } + }, + "babel-helper-bindify-decorators": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-bindify-decorators/-/babel-helper-bindify-decorators-6.24.1.tgz", + "integrity": "sha1-FMGeXxQte0fxmlJDHlKxzLxAozA=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-builder-binary-assignment-operator-visitor": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", + "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", + "dev": true, + "requires": { + "babel-helper-explode-assignable-expression": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-call-delegate": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", + "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", + "dev": true, + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-define-map": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", + "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-helper-explode-assignable-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", + "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-explode-class": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-explode-class/-/babel-helper-explode-class-6.24.1.tgz", + "integrity": "sha1-fcKjkQ3uAHBW4eMdZAztPVTqqes=", + "dev": true, + "requires": { + "babel-helper-bindify-decorators": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", + "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", + "dev": true, + "requires": { + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-get-function-arity": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", + "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-hoist-variables": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", + "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-optimise-call-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", + "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-regex": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", + "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-helper-remap-async-to-generator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", + "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-replace-supers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", + "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", + "dev": true, + "requires": { + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helpers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", + "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-check-es2015-constants": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", + "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-syntax-async-functions": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", + "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=", + "dev": true + }, + "babel-plugin-syntax-async-generators": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz", + "integrity": "sha1-a8lj67FuzLrmuStZbrfzXDQqi5o=", + "dev": true + }, + "babel-plugin-syntax-class-constructor-call": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-constructor-call/-/babel-plugin-syntax-class-constructor-call-6.18.0.tgz", + "integrity": "sha1-nLnTn+Q8hgC+yBRkVt3L1OGnZBY=", + "dev": true + }, + "babel-plugin-syntax-class-properties": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz", + "integrity": "sha1-1+sjt5oxf4VDlixQW4J8fWysJ94=", + "dev": true + }, + "babel-plugin-syntax-decorators": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz", + "integrity": "sha1-MSVjtNvePMgGzuPkFszurd0RrAs=", + "dev": true + }, + "babel-plugin-syntax-dynamic-import": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz", + "integrity": "sha1-jWomIpyDdFqZgqRBBRVyyqF5sdo=", + "dev": true + }, + "babel-plugin-syntax-exponentiation-operator": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", + "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=", + "dev": true + }, + "babel-plugin-syntax-export-extensions": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-export-extensions/-/babel-plugin-syntax-export-extensions-6.13.0.tgz", + "integrity": "sha1-cKFITw+QiaToStRLrDU8lbmxJyE=", + "dev": true + }, + "babel-plugin-syntax-flow": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz", + "integrity": "sha1-TDqyCiryaqIM0lmVw5jE63AxDI0=", + "dev": true + }, + "babel-plugin-syntax-object-rest-spread": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", + "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=", + "dev": true + }, + "babel-plugin-syntax-trailing-function-commas": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", + "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=", + "dev": true + }, + "babel-plugin-transform-async-generator-functions": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.24.1.tgz", + "integrity": "sha1-8FiQAUX9PpkHpt3yjaWfIVJYpds=", + "dev": true, + "requires": { + "babel-helper-remap-async-to-generator": "^6.24.1", + "babel-plugin-syntax-async-generators": "^6.5.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-async-to-generator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", + "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", + "dev": true, + "requires": { + "babel-helper-remap-async-to-generator": "^6.24.1", + "babel-plugin-syntax-async-functions": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-class-constructor-call": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-constructor-call/-/babel-plugin-transform-class-constructor-call-6.24.1.tgz", + "integrity": "sha1-gNwoVQWsBn3LjWxl4vbxGrd2Xvk=", + "dev": true, + "requires": { + "babel-plugin-syntax-class-constructor-call": "^6.18.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-class-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz", + "integrity": "sha1-anl2PqYdM9NvN7YRqp3vgagbRqw=", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-plugin-syntax-class-properties": "^6.8.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-decorators": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-decorators/-/babel-plugin-transform-decorators-6.24.1.tgz", + "integrity": "sha1-eIAT2PjGtSIr33s0Q5Df13Vp4k0=", + "dev": true, + "requires": { + "babel-helper-explode-class": "^6.24.1", + "babel-plugin-syntax-decorators": "^6.13.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-arrow-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", + "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-block-scoped-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", + "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-block-scoping": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", + "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-plugin-transform-es2015-classes": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", + "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", + "dev": true, + "requires": { + "babel-helper-define-map": "^6.24.1", + "babel-helper-function-name": "^6.24.1", + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-helper-replace-supers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-computed-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", + "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-destructuring": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", + "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-duplicate-keys": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", + "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-for-of": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", + "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", + "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", + "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-modules-amd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", + "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", + "dev": true, + "requires": { + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-commonjs": { + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", + "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", + "dev": true, + "requires": { + "babel-plugin-transform-strict-mode": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-types": "^6.26.0" + } + }, + "babel-plugin-transform-es2015-modules-systemjs": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", + "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", + "dev": true, + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-umd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", + "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", + "dev": true, + "requires": { + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-object-super": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", + "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", + "dev": true, + "requires": { + "babel-helper-replace-supers": "^6.24.1", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-parameters": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", + "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", + "dev": true, + "requires": { + "babel-helper-call-delegate": "^6.24.1", + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-shorthand-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", + "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-spread": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", + "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-sticky-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", + "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", + "dev": true, + "requires": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-template-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", + "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-typeof-symbol": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", + "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-unicode-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", + "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", + "dev": true, + "requires": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "regexpu-core": "^2.0.0" + }, + "dependencies": { + "regexpu-core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", + "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", + "dev": true, + "requires": { + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + } + } + } + }, + "babel-plugin-transform-exponentiation-operator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", + "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", + "dev": true, + "requires": { + "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1", + "babel-plugin-syntax-exponentiation-operator": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-export-extensions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-export-extensions/-/babel-plugin-transform-export-extensions-6.22.0.tgz", + "integrity": "sha1-U3OLR+deghhYnuqUbLvTkQm75lM=", + "dev": true, + "requires": { + "babel-plugin-syntax-export-extensions": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-flow-strip-types": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz", + "integrity": "sha1-hMtnKTXUNxT9wyvOhFaNh0Qc988=", + "dev": true, + "requires": { + "babel-plugin-syntax-flow": "^6.18.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-object-rest-spread": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz", + "integrity": "sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY=", + "dev": true, + "requires": { + "babel-plugin-syntax-object-rest-spread": "^6.8.0", + "babel-runtime": "^6.26.0" + } + }, + "babel-plugin-transform-regenerator": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", + "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", + "dev": true, + "requires": { + "regenerator-transform": "^0.10.0" + } + }, + "babel-plugin-transform-strict-mode": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", + "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-preset-es2015": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz", + "integrity": "sha1-1EBQ1rwsn+6nAqrzjXJ6AhBTiTk=", + "dev": true, + "requires": { + "babel-plugin-check-es2015-constants": "^6.22.0", + "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoping": "^6.24.1", + "babel-plugin-transform-es2015-classes": "^6.24.1", + "babel-plugin-transform-es2015-computed-properties": "^6.24.1", + "babel-plugin-transform-es2015-destructuring": "^6.22.0", + "babel-plugin-transform-es2015-duplicate-keys": "^6.24.1", + "babel-plugin-transform-es2015-for-of": "^6.22.0", + "babel-plugin-transform-es2015-function-name": "^6.24.1", + "babel-plugin-transform-es2015-literals": "^6.22.0", + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-plugin-transform-es2015-modules-systemjs": "^6.24.1", + "babel-plugin-transform-es2015-modules-umd": "^6.24.1", + "babel-plugin-transform-es2015-object-super": "^6.24.1", + "babel-plugin-transform-es2015-parameters": "^6.24.1", + "babel-plugin-transform-es2015-shorthand-properties": "^6.24.1", + "babel-plugin-transform-es2015-spread": "^6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "^6.24.1", + "babel-plugin-transform-es2015-template-literals": "^6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "^6.22.0", + "babel-plugin-transform-es2015-unicode-regex": "^6.24.1", + "babel-plugin-transform-regenerator": "^6.24.1" + } + }, + "babel-preset-stage-1": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-stage-1/-/babel-preset-stage-1-6.24.1.tgz", + "integrity": "sha1-dpLNfc1oSZB+auSgqFWJz7niv7A=", + "dev": true, + "requires": { + "babel-plugin-transform-class-constructor-call": "^6.24.1", + "babel-plugin-transform-export-extensions": "^6.22.0", + "babel-preset-stage-2": "^6.24.1" + } + }, + "babel-preset-stage-2": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-stage-2/-/babel-preset-stage-2-6.24.1.tgz", + "integrity": "sha1-2eKWD7PXEYfw5k7sYrwHdnIZvcE=", + "dev": true, + "requires": { + "babel-plugin-syntax-dynamic-import": "^6.18.0", + "babel-plugin-transform-class-properties": "^6.24.1", + "babel-plugin-transform-decorators": "^6.24.1", + "babel-preset-stage-3": "^6.24.1" + } + }, + "babel-preset-stage-3": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-stage-3/-/babel-preset-stage-3-6.24.1.tgz", + "integrity": "sha1-g2raCp56f6N8sTj7kyb4eTSkg5U=", + "dev": true, + "requires": { + "babel-plugin-syntax-trailing-function-commas": "^6.22.0", + "babel-plugin-transform-async-generator-functions": "^6.24.1", + "babel-plugin-transform-async-to-generator": "^6.24.1", + "babel-plugin-transform-exponentiation-operator": "^6.24.1", + "babel-plugin-transform-object-rest-spread": "^6.22.0" + } + }, + "babel-register": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", + "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", + "dev": true, + "requires": { + "babel-core": "^6.26.0", + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "home-or-tmp": "^2.0.0", + "lodash": "^4.17.4", + "mkdirp": "^0.5.1", + "source-map-support": "^0.4.15" + }, + "dependencies": { + "source-map-support": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "dev": true, + "requires": { + "source-map": "^0.5.6" + } + } + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dev": true, + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "babel-template": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" + }, + "dependencies": { + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + } + } + }, + "babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" + }, + "dependencies": { + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + } + } + }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + } + }, + "babylon": { + "version": "7.0.0-beta.47", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.47.tgz", + "integrity": "sha512-+rq2cr4GDhtToEzKFD6KZZMDBXhjFAr9JjPw9pAppZACeEWqNM294j+NdBzkSHYXwzzBmVjZ3nEVJlOhbR2gOQ==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", + "dev": true + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true + }, + "binaryextensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-2.2.0.tgz", + "integrity": "sha512-bHhs98rj/7i/RZpCSJ3uk55pLXOItjIrh2sRQZSM6OoktScX+LxJzvlU+FELp9j3TdcddTmmYArLSGptCTwjuw==", + "dev": true + }, + "bluebird": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz", + "integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==", + "dev": true + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + }, + "body": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/body/-/body-5.1.0.tgz", + "integrity": "sha1-5LoM5BCkaTYyM2dgnstOZVMSUGk=", + "dev": true, + "requires": { + "continuable-cache": "^0.3.1", + "error": "^7.0.0", + "raw-body": "~1.1.0", + "safe-json-parse": "~1.0.1" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "dev": true, + "requires": { + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "dev": true, + "requires": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "dev": true + }, + "buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", + "dev": true + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", + "integrity": "sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g=", + "dev": true + }, + "cacache": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", + "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", + "dev": true, + "requires": { + "bluebird": "^3.5.1", + "chownr": "^1.0.1", + "glob": "^7.1.2", + "graceful-fs": "^4.1.11", + "lru-cache": "^4.1.1", + "mississippi": "^2.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^5.2.4", + "unique-filename": "^1.1.0", + "y18n": "^4.0.0" + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "cacheable-request": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-2.1.4.tgz", + "integrity": "sha1-DYCIAbY0KtM8kd+dC0TcCbkeXD0=", + "dev": true, + "requires": { + "clone-response": "1.0.2", + "get-stream": "3.0.0", + "http-cache-semantics": "3.8.1", + "keyv": "3.0.0", + "lowercase-keys": "1.0.0", + "normalize-url": "2.0.1", + "responselike": "1.0.2" + }, + "dependencies": { + "lowercase-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz", + "integrity": "sha1-TjNms55/VFfjXxMkvfb4jQv8cwY=", + "dev": true + } + } + }, + "call-me-maybe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", + "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=", + "dev": true + }, + "camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", + "dev": true, + "requires": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", + "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "dev": true + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "chownr": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", + "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==", + "dev": true + }, + "chrome-trace-event": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", + "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "clean-css": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", + "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", + "dev": true, + "requires": { + "source-map": "~0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "clean-webpack-plugin": { + "version": "0.1.19", + "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-0.1.19.tgz", + "integrity": "sha512-M1Li5yLHECcN2MahoreuODul5LkjohJGFxLPTjl3j1ttKrF5rgjZET1SJduuqxLAuT1gAPOdkhg03qcaaU1KeA==", + "dev": true, + "requires": { + "rimraf": "^2.6.1" + } + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-table": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.1.tgz", + "integrity": "sha1-9TsFJmqLGguTSz0IIebi3FkUriM=", + "dev": true, + "requires": { + "colors": "1.0.3" + }, + "dependencies": { + "colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", + "dev": true + } + } + }, + "cli-truncate": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz", + "integrity": "sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=", + "dev": true, + "requires": { + "slice-ansi": "0.0.4", + "string-width": "^1.0.1" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + } + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true + }, + "clone-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", + "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", + "dev": true + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", + "dev": true + }, + "cloneable-readable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", + "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true + }, + "commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "compression-webpack-plugin": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compression-webpack-plugin/-/compression-webpack-plugin-2.0.0.tgz", + "integrity": "sha512-bDgd7oTUZC8EkRx8j0sjyCfeiO+e5sFcfgaFcjVhfQf5lLya7oY2BczxcJ7IUuVjz5m6fy8IECFmVFew3xLk8Q==", + "dev": true, + "requires": { + "cacache": "^11.2.0", + "find-cache-dir": "^2.0.0", + "neo-async": "^2.5.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^1.4.0", + "webpack-sources": "^1.0.1" + }, + "dependencies": { + "cacache": { + "version": "11.3.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.3.tgz", + "integrity": "sha512-p8WcneCytvzPxhDvYp31PD039vi77I12W+/KfR9S8AZbaiARFBCpsPJS+9uhWfeBfeAtW7o/4vt3MUqLkbY6nA==", + "dev": true, + "requires": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + } + }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "p-limit": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "dev": true + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "continuable-cache": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/continuable-cache/-/continuable-cache-0.3.1.tgz", + "integrity": "sha1-vXJ6f67XfnH/OYWskzUakSczrQ8=", + "dev": true + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "copy-webpack-plugin": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.6.0.tgz", + "integrity": "sha512-Y+SQCF+0NoWQryez2zXn5J5knmr9z/9qSQt7fbL78u83rxmigOy8X5+BFn8CFSuX+nKT8gpYwJX68ekqtQt6ZA==", + "dev": true, + "requires": { + "cacache": "^10.0.4", + "find-cache-dir": "^1.0.0", + "globby": "^7.1.1", + "is-glob": "^4.0.0", + "loader-utils": "^1.1.0", + "minimatch": "^3.0.4", + "p-limit": "^1.0.0", + "serialize-javascript": "^1.4.0" + } + }, + "core-js": { + "version": "2.6.10", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.10.tgz", + "integrity": "sha512-I39t74+4t+zau64EN1fE5v2W31Adtc/REhzWN+gWRRXg6WH5qAsZm62DHpQ1+Yhe4047T55jvzz7MUqF/dBBlA==", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-env": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-5.2.1.tgz", + "integrity": "sha512-1yHhtcfAd1r4nwQgknowuUNfIT9E8dOMMspC36g45dN+iD1blloi7xp8X/xAIDnjHWyt1uQ8PHk2fkNaym7soQ==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.5" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "css-loader": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-1.0.1.tgz", + "integrity": "sha512-+ZHAZm/yqvJ2kDtPne3uX0C+Vr3Zn5jFn2N4HywtS5ujwvsVkyg0VArEXpl3BgczDA8anieki1FIzhchX4yrDw==", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "css-selector-tokenizer": "^0.7.0", + "icss-utils": "^2.1.0", + "loader-utils": "^1.0.2", + "lodash": "^4.17.11", + "postcss": "^6.0.23", + "postcss-modules-extract-imports": "^1.2.0", + "postcss-modules-local-by-default": "^1.2.0", + "postcss-modules-scope": "^1.1.0", + "postcss-modules-values": "^1.3.0", + "postcss-value-parser": "^3.3.0", + "source-list-map": "^2.0.0" + } + }, + "css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "dev": true, + "requires": { + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" + } + }, + "css-selector-tokenizer": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz", + "integrity": "sha512-xYL0AMZJ4gFzJQsHUKa5jiWWi2vH77WVNg7JYRyewwj6oPh4yb/y6Y9ZCw9dsj/9UauMhtuxR+ogQd//EdEVNA==", + "dev": true, + "requires": { + "cssesc": "^0.1.0", + "fastparse": "^1.1.1", + "regexpu-core": "^1.0.0" + } + }, + "css-what": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", + "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", + "dev": true + }, + "cssesc": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", + "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=", + "dev": true + }, + "csstype": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.7.tgz", + "integrity": "sha512-9Mcn9sFbGBAdmimWb2gLVDtFJzeKtDGIr76TUqmjZrw9LFXBMSU70lcs+C0/7fyCd6iBDqmksUcCOUIkisPHsQ==" + }, + "cyclist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", + "dev": true + }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dev": true, + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "dargs": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-5.1.0.tgz", + "integrity": "sha1-7H6lDHhWTNNsnV7Bj2Yyn63ieCk=", + "dev": true + }, + "date-fns": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", + "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==", + "dev": true + }, + "dateformat": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", + "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "detect-conflict": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/detect-conflict/-/detect-conflict-1.0.1.tgz", + "integrity": "sha1-CIZXpmqWHAUBnbfEIwiDsca0F24=", + "dev": true + }, + "detect-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "dir-glob": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", + "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", + "dev": true, + "requires": { + "path-type": "^3.0.0" + } + }, + "dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dev": true, + "requires": { + "utila": "~0.4" + } + }, + "dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", + "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==", + "dev": true + } + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "dev": true + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dev": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "editions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/editions/-/editions-2.2.0.tgz", + "integrity": "sha512-RYg3iEA2BDLCNVe8PUkD+ox5vAKxB9XS/mAhx1bdxGCF0CpX077C0pyTA9t5D6idCYA3avl5/XDHKPsHFrygfw==", + "dev": true, + "requires": { + "errlop": "^1.1.2", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "ejs": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.7.2.tgz", + "integrity": "sha512-rHGwtpl67oih3xAHbZlpw5rQAt+YV1mSCu2fUZ9XNrfaGEhom7E+AUiMci+ByP4aSfuAWx7hE0BPuJLMrpXwOw==", + "dev": true + }, + "elegant-spinner": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", + "integrity": "sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=", + "dev": true + }, + "elliptic": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.1.tgz", + "integrity": "sha512-xvJINNLbTeWQjrl6X+7eQCrIy/YPv5XCpKW6kB5mKvtnGILoLDcySuwomfdzt0BMdLNVnuRNTuzKNHj0bva1Cg==", + "dev": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz", + "integrity": "sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + } + }, + "entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz", + "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==", + "dev": true + }, + "envinfo": { + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-5.12.1.tgz", + "integrity": "sha512-pwdo0/G3CIkQ0y6PCXq4RdkvId2elvtPCJMG0konqlrfkWQbf1DWeH9K2b/cvu2YgGvPPTOnonZxXM1gikFu1w==", + "dev": true + }, + "errlop": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/errlop/-/errlop-1.3.0.tgz", + "integrity": "sha512-qbb3CRis7c/XouHw41umhSIt4e0KFcESXDrWvgFwIuD/neT1WBO9lKFj7sriqPgF8xEY0Vz6Ws5hMbjN4q/hsQ==", + "dev": true, + "requires": { + "editions": "^2.2.0" + } + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, + "error": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/error/-/error-7.2.1.tgz", + "integrity": "sha512-fo9HBvWnx3NGUKMvMwB/CBCMMrfEJgbDTVDEkPygA3Bdd3lM1OyCd+rbQ8BwnpF6GdVeOLDNmyL4N5Bg80ZvdA==", + "dev": true, + "requires": { + "string-template": "~0.2.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.16.0.tgz", + "integrity": "sha512-xdQnfykZ9JMEiasTAJZJdMWCQ1Vm00NBw79/AWi7ELfZuuPCSOMDZbT9mkOfSctVtfhb+sAAzrm+j//GjjLHLg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.0", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-inspect": "^1.6.0", + "object-keys": "^1.1.1", + "string.prototype.trimleft": "^2.1.0", + "string.prototype.trimright": "^2.1.0" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es5-ext": { + "version": "0.10.52", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.52.tgz", + "integrity": "sha512-bWCbE9fbpYQY4CU6hJbJ1vSz70EClMlDgJ7BmwI+zEJhxrwjesZRPglGJlsZhu0334U3hI+gaspwksH9IGD6ag==", + "dev": true, + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.2", + "next-tick": "~1.0.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dev": true, + "requires": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "events": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz", + "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==", + "dev": true + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "dependencies": { + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "dev": true, + "requires": { + "fill-range": "^2.1.0" + }, + "dependencies": { + "fill-range": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "dev": true, + "requires": { + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^3.0.0", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" + } + }, + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "ext": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.2.0.tgz", + "integrity": "sha512-0ccUQK/9e3NreLFg6K6np8aPyRgwycx+oFGtfx1dSp7Wj00Ozw9r05FgBRlzjf2XBM7LAzwgLyDscRrtSU91hA==", + "dev": true, + "requires": { + "type": "^2.0.0" + }, + "dependencies": { + "type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.0.0.tgz", + "integrity": "sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow==", + "dev": true + } + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "external-editor": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", + "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "dev": true, + "requires": { + "chardet": "^0.4.0", + "iconv-lite": "^0.4.17", + "tmp": "^0.0.33" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, + "fast-glob": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", + "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==", + "dev": true, + "requires": { + "@mrmlnc/readdir-enhanced": "^2.2.1", + "@nodelib/fs.stat": "^1.1.2", + "glob-parent": "^3.1.0", + "is-glob": "^4.0.0", + "merge2": "^1.2.3", + "micromatch": "^3.1.10" + } + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", + "dev": true + }, + "faye-websocket": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "figgy-pudding": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", + "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==", + "dev": true + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-loader": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", + "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==", + "dev": true, + "requires": { + "loader-utils": "^1.0.2", + "schema-utils": "^0.4.5" + } + }, + "filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", + "dev": true + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "find-cache-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", + "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^1.0.0", + "pkg-dir": "^2.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "first-chunk-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz", + "integrity": "sha1-G97NuOCDwGZLkZRVgVd6Q6nzHXA=", + "dev": true, + "requires": { + "readable-stream": "^2.0.2" + } + }, + "flow-parser": { + "version": "0.112.0", + "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.112.0.tgz", + "integrity": "sha512-sxjnwhR76B/fUN6n/XerYzn8R1HvtVo3SM8Il3WiZ4nkAlb2BBzKe1TSVKGSyZgD6FW9Bsxom/57ktkqrqmXGA==", + "dev": true + }, + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "dev": true, + "requires": { + "for-in": "^1.0.1" + } + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", + "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", + "dev": true, + "optional": true, + "requires": { + "nan": "^2.12.1", + "node-pre-gyp": "^0.12.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "minipass": { + "version": "2.3.5", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.3.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "^4.1.0", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.12.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.4.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.6.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.7.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "yallist": { + "version": "3.0.3", + "bundled": true, + "dev": true + } + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "gh-got": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gh-got/-/gh-got-6.0.0.tgz", + "integrity": "sha512-F/mS+fsWQMo1zfgG9MD8KWvTWPPzzhuVwY++fhQ5Ggd+0P+CAMHtzMZhNxG+TqGfHDChJKsbh6otfMGqO2AKBw==", + "dev": true, + "requires": { + "got": "^7.0.0", + "is-plain-obj": "^1.1.0" + }, + "dependencies": { + "got": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", + "integrity": "sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==", + "dev": true, + "requires": { + "decompress-response": "^3.2.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-plain-obj": "^1.1.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "isurl": "^1.0.0-alpha5", + "lowercase-keys": "^1.0.0", + "p-cancelable": "^0.3.0", + "p-timeout": "^1.1.1", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "url-parse-lax": "^1.0.0", + "url-to-options": "^1.0.1" + } + }, + "p-cancelable": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", + "integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==", + "dev": true + }, + "p-timeout": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", + "integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=", + "dev": true, + "requires": { + "p-finally": "^1.0.0" + } + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "dev": true + }, + "url-parse-lax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "dev": true, + "requires": { + "prepend-http": "^1.0.1" + } + } + } + }, + "github-username": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/github-username/-/github-username-4.1.0.tgz", + "integrity": "sha1-y+KABBiDIG2kISrp5LXxacML9Bc=", + "dev": true, + "requires": { + "gh-got": "^6.0.0" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-all": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-all/-/glob-all-3.1.0.tgz", + "integrity": "sha1-iRPd+17hrHgSZWJBsD1SF8ZLAqs=", + "dev": true, + "requires": { + "glob": "^7.0.5", + "yargs": "~1.2.6" + }, + "dependencies": { + "minimist": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.1.0.tgz", + "integrity": "sha1-md9lelJXTCHJBXSX33QnkLK0wN4=", + "dev": true + }, + "yargs": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-1.2.6.tgz", + "integrity": "sha1-nHtKgv1dWVsr8Xq23MQxNUMv40s=", + "dev": true, + "requires": { + "minimist": "^0.1.0" + } + } + } + }, + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "dev": true, + "requires": { + "glob-parent": "^2.0.0", + "is-glob": "^2.0.0" + }, + "dependencies": { + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "requires": { + "is-glob": "^2.0.0" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + } + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "glob-to-regexp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", + "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=", + "dev": true + }, + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + } + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true + }, + "globby": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", + "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "dir-glob": "^2.0.0", + "glob": "^7.1.2", + "ignore": "^3.3.5", + "pify": "^3.0.0", + "slash": "^1.0.0" + } + }, + "got": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/got/-/got-8.3.2.tgz", + "integrity": "sha512-qjUJ5U/hawxosMryILofZCkm3C84PLJS/0grRIpjAwu+Lkxxj5cxeCU25BG0/3mDSpXKTyZr8oh8wIgLaH0QCw==", + "dev": true, + "requires": { + "@sindresorhus/is": "^0.7.0", + "cacheable-request": "^2.1.1", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "into-stream": "^3.1.0", + "is-retry-allowed": "^1.1.0", + "isurl": "^1.0.0-alpha5", + "lowercase-keys": "^1.0.0", + "mimic-response": "^1.0.0", + "p-cancelable": "^0.4.0", + "p-timeout": "^2.0.1", + "pify": "^3.0.0", + "safe-buffer": "^5.1.1", + "timed-out": "^4.0.1", + "url-parse-lax": "^3.0.0", + "url-to-options": "^1.0.1" + } + }, + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + }, + "grouped-queue": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/grouped-queue/-/grouped-queue-0.3.3.tgz", + "integrity": "sha1-wWfSpTGcWg4JZO9qJbfC34mWyFw=", + "dev": true, + "requires": { + "lodash": "^4.17.2" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-color": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz", + "integrity": "sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbol-support-x": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", + "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==", + "dev": true + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "has-to-string-tag-x": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", + "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", + "dev": true, + "requires": { + "has-symbol-support-x": "^1.4.1" + } + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-wbg3bpgA/ZqWrZuMOeJi8+SKMhr7X9TesL/rXMjTzh0p0JUBo3II8DHboYbuIXWRlttrUFxwcu/5kygrCw8fJw==", + "requires": { + "react-is": "^16.7.0" + } + }, + "home-or-tmp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", + "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", + "dev": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.1" + } + }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "hosted-git-info": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz", + "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==", + "dev": true + }, + "html-minifier": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz", + "integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==", + "dev": true, + "requires": { + "camel-case": "3.0.x", + "clean-css": "4.2.x", + "commander": "2.17.x", + "he": "1.2.x", + "param-case": "2.1.x", + "relateurl": "0.2.x", + "uglify-js": "3.4.x" + } + }, + "html-webpack-plugin": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", + "integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=", + "dev": true, + "requires": { + "html-minifier": "^3.2.3", + "loader-utils": "^0.2.16", + "lodash": "^4.17.3", + "pretty-error": "^2.0.2", + "tapable": "^1.0.0", + "toposort": "^1.0.0", + "util.promisify": "1.0.0" + }, + "dependencies": { + "big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, + "loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "dev": true, + "requires": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" + } + } + } + }, + "htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dev": true, + "requires": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + }, + "readable-stream": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", + "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "http-cache-semantics": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", + "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==", + "dev": true + }, + "http-parser-js": { + "version": "0.4.10", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.10.tgz", + "integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q=", + "dev": true + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-replace-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", + "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", + "dev": true + }, + "icss-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-2.1.0.tgz", + "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=", + "dev": true, + "requires": { + "postcss": "^6.0.1" + } + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "dev": true + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "dev": true + }, + "ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", + "dev": true + }, + "import-local": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-1.0.0.tgz", + "integrity": "sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ==", + "dev": true, + "requires": { + "pkg-dir": "^2.0.0", + "resolve-cwd": "^2.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "inquirer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", + "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", + "dev": true, + "requires": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^2.1.0", + "figures": "^2.0.0", + "lodash": "^4.3.0", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^5.5.2", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "interpret": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", + "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", + "dev": true + }, + "into-stream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz", + "integrity": "sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY=", + "dev": true, + "requires": { + "from2": "^2.1.1", + "p-is-promise": "^1.1.0" + } + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "requires": { + "loose-envify": "^1.0.0" + } + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "dev": true + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", + "dev": true + }, + "is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "dev": true, + "requires": { + "is-primitive": "^2.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", + "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=", + "dev": true + }, + "is-observable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-observable/-/is-observable-1.1.0.tgz", + "integrity": "sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA==", + "dev": true, + "requires": { + "symbol-observable": "^1.1.0" + }, + "dependencies": { + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", + "dev": true + } + } + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", + "dev": true + }, + "is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", + "dev": true + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "^1.0.1" + } + }, + "is-retry-allowed": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", + "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", + "dev": true + }, + "is-scoped": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-scoped/-/is-scoped-1.0.0.tgz", + "integrity": "sha1-RJypgpnnEwOCViieyytUDcQ3yzA=", + "dev": true, + "requires": { + "scoped-regex": "^1.0.0" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isbinaryfile": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", + "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", + "dev": true, + "requires": { + "buffer-alloc": "^1.2.0" + } + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "istextorbinary": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-2.6.0.tgz", + "integrity": "sha512-+XRlFseT8B3L9KyjxxLjfXSLMuErKDsd8DBNrsaxoViABMEZlOSCstwmw0qpoFX3+U6yWU1yhLudAe6/lETGGA==", + "dev": true, + "requires": { + "binaryextensions": "^2.1.2", + "editions": "^2.2.0", + "textextensions": "^2.5.0" + } + }, + "isurl": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", + "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", + "dev": true, + "requires": { + "has-to-string-tag-x": "^1.2.0", + "is-object": "^1.0.1" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "jscodeshift": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/jscodeshift/-/jscodeshift-0.5.1.tgz", + "integrity": "sha512-sRMollbhbmSDrR79JMAnhEjyZJlQQVozeeY9A6/KNuV26DNcuB3mGSCWXp0hks9dcwRNOELbNOiwraZaXXRk5Q==", + "dev": true, + "requires": { + "babel-plugin-transform-flow-strip-types": "^6.8.0", + "babel-preset-es2015": "^6.9.0", + "babel-preset-stage-1": "^6.5.0", + "babel-register": "^6.9.0", + "babylon": "^7.0.0-beta.47", + "colors": "^1.1.2", + "flow-parser": "^0.*", + "lodash": "^4.13.1", + "micromatch": "^2.3.7", + "neo-async": "^2.5.0", + "node-dir": "0.1.8", + "nomnom": "^1.8.1", + "recast": "^0.15.0", + "temp": "^0.8.1", + "write-file-atomic": "^1.2.0" + }, + "dependencies": { + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1" + } + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true, + "requires": { + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" + } + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true, + "requires": { + "is-posix-bracket": "^0.1.0" + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "requires": { + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "keyv": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.0.0.tgz", + "integrity": "sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA==", + "dev": true, + "requires": { + "json-buffer": "3.0.0" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "listr": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/listr/-/listr-0.14.3.tgz", + "integrity": "sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA==", + "dev": true, + "requires": { + "@samverschueren/stream-to-observable": "^0.3.0", + "is-observable": "^1.1.0", + "is-promise": "^2.1.0", + "is-stream": "^1.1.0", + "listr-silent-renderer": "^1.1.1", + "listr-update-renderer": "^0.5.0", + "listr-verbose-renderer": "^0.5.0", + "p-map": "^2.0.0", + "rxjs": "^6.3.3" + }, + "dependencies": { + "rxjs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.3.tgz", + "integrity": "sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + } + } + }, + "listr-silent-renderer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz", + "integrity": "sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=", + "dev": true + }, + "listr-update-renderer": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz", + "integrity": "sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA==", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "cli-truncate": "^0.2.1", + "elegant-spinner": "^1.0.1", + "figures": "^1.7.0", + "indent-string": "^3.0.0", + "log-symbols": "^1.0.2", + "log-update": "^2.3.0", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + } + }, + "log-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", + "integrity": "sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=", + "dev": true, + "requires": { + "chalk": "^1.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "listr-verbose-renderer": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz", + "integrity": "sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "cli-cursor": "^2.1.0", + "date-fns": "^1.27.2", + "figures": "^2.0.0" + } + }, + "livereload-js": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-2.4.0.tgz", + "integrity": "sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw==", + "dev": true + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", + "dev": true + }, + "loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "dev": true, + "requires": { + "chalk": "^2.0.1" + } + }, + "log-update": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-2.3.0.tgz", + "integrity": "sha1-iDKP19HOeTiykoN0bwsbwSayRwg=", + "dev": true, + "requires": { + "ansi-escapes": "^3.0.0", + "cli-cursor": "^2.0.0", + "wrap-ansi": "^3.0.1" + } + }, + "loglevelnext": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/loglevelnext/-/loglevelnext-1.0.5.tgz", + "integrity": "sha512-V/73qkPuJmx4BcBF19xPBr+0ZRVBhc4POxvZTZdMeXpJ4NItXSJ/MSwuFT0kQJlCbXvdlZoQQ/418bS1y9Jh6A==", + "dev": true, + "requires": { + "es6-symbol": "^3.1.1", + "object.assign": "^4.1.0" + } + }, + "long": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", + "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", + "dev": true + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "mamacro": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", + "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==", + "dev": true + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "math-random": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", + "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==", + "dev": true + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "mem": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + }, + "dependencies": { + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", + "dev": true + } + } + }, + "mem-fs": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/mem-fs/-/mem-fs-1.1.3.tgz", + "integrity": "sha1-uK6NLj/Lb10/kWXBLUVRoGXZicw=", + "dev": true, + "requires": { + "through2": "^2.0.0", + "vinyl": "^1.1.0", + "vinyl-file": "^2.0.0" + } + }, + "mem-fs-editor": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/mem-fs-editor/-/mem-fs-editor-4.0.3.tgz", + "integrity": "sha512-tgWmwI/+6vwu6POan82dTjxEpwAoaj0NAFnghtVo/FcLK2/7IhPUtFUUYlwou4MOY6OtjTUJtwpfH1h+eSUziw==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "deep-extend": "^0.6.0", + "ejs": "^2.5.9", + "glob": "^7.0.3", + "globby": "^7.1.1", + "isbinaryfile": "^3.0.2", + "mkdirp": "^0.5.0", + "multimatch": "^2.0.0", + "rimraf": "^2.2.8", + "through2": "^2.0.0", + "vinyl": "^2.0.1" + }, + "dependencies": { + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", + "dev": true + }, + "clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", + "dev": true + }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true + }, + "vinyl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", + "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", + "dev": true, + "requires": { + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" + } + } + } + }, + "memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "merge2": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.3.0.tgz", + "integrity": "sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + } + }, + "mime": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", + "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", + "dev": true + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true + }, + "mini-css-extract-plugin": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.4.5.tgz", + "integrity": "sha512-dqBanNfktnp2hwL2YguV9Jh91PFX7gu7nRLs4TGsbAfAG6WOtlynFRYzwDwmmeSb5uIwHo9nx1ta0f7vAZVp2w==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "schema-utils": "^1.0.0", + "webpack-sources": "^1.1.0" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "mississippi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz", + "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^2.0.1", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + } + } + }, + "mobx": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mobx/-/mobx-4.3.0.tgz", + "integrity": "sha1-UKXmP9AZeoPGgWb/g62kW2XIWfg=" + }, + "mobx-react": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-5.4.4.tgz", + "integrity": "sha512-2mTzpyEjVB/RGk2i6KbcmP4HWcAUFox5ZRCrGvSyz49w20I4C4qql63grPpYrS9E9GKwgydBHQlA4y665LuRCQ==", + "requires": { + "hoist-non-react-statics": "^3.0.0", + "react-lifecycles-compat": "^3.0.2" + } + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "multimatch": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-2.1.0.tgz", + "integrity": "sha1-nHkGoi+0wCkZ4vX3UWG0zb1LKis=", + "dev": true, + "requires": { + "array-differ": "^1.0.0", + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "minimatch": "^3.0.0" + } + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", + "dev": true, + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "neo-async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", + "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", + "dev": true + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "dev": true, + "requires": { + "lower-case": "^1.1.1" + } + }, + "node-dir": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/node-dir/-/node-dir-0.1.8.tgz", + "integrity": "sha1-VfuN62mQcHB/tn+RpGDwRIKUx30=", + "dev": true + }, + "node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "dev": true, + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "nomnom": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.8.1.tgz", + "integrity": "sha1-IVH3Ikcrp55Qp2/BJbuMjy5Nwqc=", + "dev": true, + "requires": { + "chalk": "~0.4.0", + "underscore": "~1.6.0" + }, + "dependencies": { + "ansi-styles": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.0.0.tgz", + "integrity": "sha1-yxAt8cVvUSPquLZ817mAJ6AnkXg=", + "dev": true + }, + "chalk": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz", + "integrity": "sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8=", + "dev": true, + "requires": { + "ansi-styles": "~1.0.0", + "has-color": "~0.1.0", + "strip-ansi": "~0.1.0" + } + }, + "strip-ansi": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz", + "integrity": "sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE=", + "dev": true + } + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-url": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz", + "integrity": "sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==", + "dev": true, + "requires": { + "prepend-http": "^2.0.0", + "query-string": "^5.0.1", + "sort-keys": "^2.0.0" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "dev": true, + "requires": { + "boolbase": "~1.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + } + }, + "object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", + "dev": true, + "requires": { + "for-own": "^0.1.4", + "is-extendable": "^0.1.1" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-cancelable": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz", + "integrity": "sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ==", + "dev": true + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true + }, + "p-each-series": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", + "integrity": "sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=", + "dev": true, + "requires": { + "p-reduce": "^1.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-is-promise": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", + "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=", + "dev": true + }, + "p-lazy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-lazy/-/p-lazy-1.0.0.tgz", + "integrity": "sha1-7FPIAvLuOsKPFmzILQsrAt4nqDU=", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true + }, + "p-reduce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", + "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=", + "dev": true + }, + "p-timeout": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz", + "integrity": "sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==", + "dev": true, + "requires": { + "p-finally": "^1.0.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "pako": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", + "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==", + "dev": true + }, + "parallel-transform": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", + "dev": true, + "requires": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", + "dev": true, + "requires": { + "no-case": "^2.2.0" + } + }, + "parse-asn1": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", + "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", + "dev": true, + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "dev": true, + "requires": { + "glob-base": "^0.3.0", + "is-dotfile": "^1.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.0" + }, + "dependencies": { + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + } + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pbkdf2": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-modules-extract-imports": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.1.tgz", + "integrity": "sha512-6jt9XZwUhwmRUhb/CkyJY020PYaPJsCyt3UjbaWo6XEbH/94Hmv6MP7fG2C5NDU/BcHzyGYxNtHvM+LTf9HrYw==", + "dev": true, + "requires": { + "postcss": "^6.0.1" + } + }, + "postcss-modules-local-by-default": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", + "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", + "dev": true, + "requires": { + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" + } + }, + "postcss-modules-scope": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", + "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", + "dev": true, + "requires": { + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" + } + }, + "postcss-modules-values": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", + "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", + "dev": true, + "requires": { + "icss-replace-symbols": "^1.1.0", + "postcss": "^6.0.1" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "dev": true + }, + "preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", + "dev": true + }, + "prettier": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", + "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", + "dev": true + }, + "pretty-bytes": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-4.0.2.tgz", + "integrity": "sha1-sr+C5zUNZcbDOqlaqlpPYyf2HNk=", + "dev": true + }, + "pretty-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", + "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=", + "dev": true, + "requires": { + "renderkid": "^2.0.1", + "utila": "~0.4" + } + }, + "private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", + "dev": true + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "qs": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.1.tgz", + "integrity": "sha512-Cxm7/SS/y/Z3MHWSxXb8lIFqgqBowP5JMlTUFyJN88y0SGQhVmZnqFK/PeuMX9LzUyWsqqhNxIyg0jlzq946yA==", + "dev": true + }, + "query-string": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", + "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", + "dev": true, + "requires": { + "decode-uri-component": "^0.2.0", + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + } + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "randomatic": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", + "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", + "dev": true, + "requires": { + "is-number": "^4.0.0", + "kind-of": "^6.0.0", + "math-random": "^1.0.1" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true + } + } + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "raw-body": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-1.1.7.tgz", + "integrity": "sha1-HQJ8K/oRasxmI7yo8AAWVyqH1CU=", + "dev": true, + "requires": { + "bytes": "1", + "string_decoder": "0.10" + }, + "dependencies": { + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "react": { + "version": "16.12.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.12.0.tgz", + "integrity": "sha512-fglqy3k5E+81pA8s+7K0/T3DBCF0ZDOher1elBFzF7O6arXJgzyu/FW+COxFvAWXJoJN9KIZbT2LXlukwphYTA==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2" + } + }, + "react-dom": { + "version": "16.12.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.12.0.tgz", + "integrity": "sha512-LMxFfAGrcS3kETtQaCkTKjMiifahaMySFDn71fZUNpPHZQEzmk/GiAeIT8JSOrHB23fnuCOMruL2a8NYlw+8Gw==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.18.0" + } + }, + "react-is": { + "version": "16.12.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz", + "integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==" + }, + "react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "read-chunk": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/read-chunk/-/read-chunk-2.1.0.tgz", + "integrity": "sha1-agTAkoAF7Z1C4aasVgDhnLx/9lU=", + "dev": true, + "requires": { + "pify": "^3.0.0", + "safe-buffer": "^5.1.1" + } + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "recast": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.15.5.tgz", + "integrity": "sha512-nkAYNqarh73cMWRKFiPQ8I9dOLFvFk6SnG8u/LUlOYfArDOD/EjsVRAs860TlBLrpxqAXHGET/AUAVjdEymL5w==", + "dev": true, + "requires": { + "ast-types": "0.11.5", + "esprima": "~4.0.0", + "private": "~0.1.5", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "requires": { + "resolve": "^1.1.6" + } + }, + "regenerate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", + "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", + "dev": true + }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", + "dev": true + }, + "regenerator-transform": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", + "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", + "dev": true, + "requires": { + "babel-runtime": "^6.18.0", + "babel-types": "^6.19.0", + "private": "^0.1.6" + } + }, + "regex-cache": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "dev": true, + "requires": { + "is-equal-shallow": "^0.1.3" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regexpu-core": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", + "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", + "dev": true, + "requires": { + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + } + }, + "regjsgen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", + "dev": true + }, + "regjsparser": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "dev": true + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "renderkid": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.3.tgz", + "integrity": "sha512-z8CLQp7EZBPCwCnncgf9C4XAi3WR0dv+uWu/PjIyhhAb5d6IJ/QZqlHFprHeKT+59//V6BNUsLbvN8+2LarxGA==", + "dev": true, + "requires": { + "css-select": "^1.1.0", + "dom-converter": "^0.2", + "htmlparser2": "^3.3.0", + "strip-ansi": "^3.0.0", + "utila": "^0.4.0" + } + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "^1.0.0" + } + }, + "replace-ext": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", + "dev": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "resolve": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dev": true, + "requires": { + "lowercase-keys": "^1.0.0" + } + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "^2.1.0" + } + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "dev": true, + "requires": { + "aproba": "^1.1.1" + } + }, + "rxjs": { + "version": "5.5.12", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz", + "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==", + "dev": true, + "requires": { + "symbol-observable": "1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safe-json-parse": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/safe-json-parse/-/safe-json-parse-1.0.1.tgz", + "integrity": "sha1-PnZyPjjf3aE8mx0poeB//uSzC1c=", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "scheduler": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.18.0.tgz", + "integrity": "sha512-agTSHR1Nbfi6ulI0kYNK0203joW2Y5W4po4l+v03tOoiJKpTBbxpNhWDvqc/4IcOw+KLmSiQLTasZ4cab2/UWQ==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "schema-utils": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", + "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0" + } + }, + "scoped-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/scoped-regex/-/scoped-regex-1.0.0.tgz", + "integrity": "sha1-o0a7Gs1CB65wvXwMfKnlZra63bg=", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "serialize-javascript": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz", + "integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "shelljs": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.3.tgz", + "integrity": "sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A==", + "dev": true, + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + } + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + }, + "slice-ansi": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", + "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", + "dev": true + }, + "slide": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", + "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=", + "dev": true + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "sort-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz", + "integrity": "sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=", + "dev": true, + "requires": { + "is-plain-obj": "^1.0.0" + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-loader": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-0.2.4.tgz", + "integrity": "sha512-OU6UJUty+i2JDpTItnizPrlpOIBLmQbWMuBg9q5bVtnHACqw1tn9nNwqJLbv0/00JjnJb/Ee5g5WS5vrRv7zIQ==", + "dev": true, + "requires": { + "async": "^2.5.0", + "loader-utils": "^1.1.0" + } + }, + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "dev": true, + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", + "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "dev": true + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "ssri": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz", + "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.1" + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", + "dev": true + }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", + "dev": true + }, + "string-template": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz", + "integrity": "sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "string.prototype.trimleft": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", + "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string.prototype.trimright": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz", + "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-bom-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-2.0.0.tgz", + "integrity": "sha1-+H217yYT9paKpUWr/h7HKLaoKco=", + "dev": true, + "requires": { + "first-chunk-stream": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "dependencies": { + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + } + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "style-loader": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.21.0.tgz", + "integrity": "sha512-T+UNsAcl3Yg+BsPKs1vd22Fr8sVT+CJMtzqc6LEw9bbJZb43lm9GoeIfUcDEefBSWC0BhYbcdupV1GtI4DGzxg==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "schema-utils": "^0.4.5" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "symbol-observable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", + "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", + "dev": true + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true + }, + "temp": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.4.tgz", + "integrity": "sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg==", + "dev": true, + "requires": { + "rimraf": "~2.6.2" + }, + "dependencies": { + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "textextensions": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-2.6.0.tgz", + "integrity": "sha512-49WtAWS+tcsy93dRt6P0P3AMD2m5PvXRhuEA0kaXos5ZLlujtYmpmFsB+QvWUSxE1ZsstmYXfQ7L40+EcQgpAQ==", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", + "dev": true + }, + "timers-browserify": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz", + "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==", + "dev": true, + "requires": { + "setimmediate": "^1.0.4" + } + }, + "tiny-lr": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-1.1.1.tgz", + "integrity": "sha512-44yhA3tsaRoMOjQQ+5v5mVdqef+kH6Qze9jTpqtVufgYjYt08zyZAwNwwVBj3i1rJMnR52IxOW0LK0vBzgAkuA==", + "dev": true, + "requires": { + "body": "^5.1.0", + "debug": "^3.1.0", + "faye-websocket": "~0.10.0", + "livereload-js": "^2.3.0", + "object-assign": "^4.1.0", + "qs": "^6.4.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "to-string-loader": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/to-string-loader/-/to-string-loader-1.1.6.tgz", + "integrity": "sha512-VNg62//PS1WfNwrK3n7t6wtK5Vdtx/qeYLLEioW46VMlYUwAYT6wnfB+OwS2FMTCalIHu0tk79D3RXX8ttmZTQ==", + "dev": true, + "requires": { + "loader-utils": "^1.0.0" + } + }, + "toposort": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz", + "integrity": "sha1-LmhELZ9k7HILjMieZEOsbKqVACk=", + "dev": true + }, + "trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", + "dev": true + }, + "tsconfig-paths": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", + "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + } + }, + "tsconfig-paths-webpack-plugin": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-3.2.0.tgz", + "integrity": "sha512-S/gOOPOkV8rIL4LurZ1vUdYCVgo15iX9ZMJ6wx6w2OgcpT/G4wMyHB6WM+xheSqGMrWKuxFul+aXpCju3wmj/g==", + "dev": true, + "requires": { + "chalk": "^2.3.0", + "enhanced-resolve": "^4.0.0", + "tsconfig-paths": "^3.4.0" + } + }, + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", + "dev": true + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "dev": true + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "typescript": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", + "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", + "dev": true + }, + "uglify-js": { + "version": "3.4.10", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz", + "integrity": "sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==", + "dev": true, + "requires": { + "commander": "~2.19.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "uglifyjs-webpack-plugin": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.3.0.tgz", + "integrity": "sha512-ovHIch0AMlxjD/97j9AYovZxG5wnHOPkL7T1GKochBADp/Zwc44pEWNqpKl1Loupp1WhFg7SlYmHZRUfdAacgw==", + "dev": true, + "requires": { + "cacache": "^10.0.4", + "find-cache-dir": "^1.0.0", + "schema-utils": "^0.4.5", + "serialize-javascript": "^1.4.0", + "source-map": "^0.6.1", + "uglify-es": "^3.3.4", + "webpack-sources": "^1.1.0", + "worker-farm": "^1.5.2" + }, + "dependencies": { + "commander": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", + "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "uglify-es": { + "version": "3.3.9", + "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", + "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", + "dev": true, + "requires": { + "commander": "~2.13.0", + "source-map": "~0.6.1" + } + } + } + }, + "underscore": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", + "dev": true + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "untildify": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-3.0.3.tgz", + "integrity": "sha512-iSk/J8efr8uPT/Z4eSUywnqyrQU7DSdMfdqK4iWEaUVVmcP5JcnpRqmVMwcwcnmI1ATFNgC5V90u09tBynNFKA==", + "dev": true + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true + }, + "upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=", + "dev": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "url-loader": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-1.1.2.tgz", + "integrity": "sha512-dXHkKmw8FhPqu8asTc1puBfe3TehOCo2+RmOOev5suNCIYBcT626kxiWg1NBVkwc4rO8BGa7gP70W7VXuqHrjg==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "mime": "^2.0.3", + "schema-utils": "^1.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "dev": true, + "requires": { + "prepend-http": "^2.0.0" + } + }, + "url-to-options": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", + "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=", + "dev": true + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dev": true, + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", + "dev": true + }, + "uuid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", + "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==", + "dev": true + }, + "v8-compile-cache": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", + "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "vinyl": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-1.2.0.tgz", + "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", + "dev": true, + "requires": { + "clone": "^1.0.0", + "clone-stats": "^0.0.1", + "replace-ext": "0.0.1" + } + }, + "vinyl-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/vinyl-file/-/vinyl-file-2.0.0.tgz", + "integrity": "sha1-p+v1/779obfRjRQPyweyI++2dRo=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.3.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0", + "strip-bom-stream": "^2.0.0", + "vinyl": "^1.1.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + } + } + }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true + }, + "watchpack": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", + "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", + "dev": true, + "requires": { + "chokidar": "^2.0.2", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" + } + }, + "webpack": { + "version": "4.16.5", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.16.5.tgz", + "integrity": "sha512-i5cHYHonzSc1zBuwB5MSzW4v9cScZFbprkHK8ZgzPDCRkQXGGpYzPmJhbus5bOrZ0tXTcQp+xyImRSvKb0b+Kw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.5.13", + "@webassemblyjs/helper-module-context": "1.5.13", + "@webassemblyjs/wasm-edit": "1.5.13", + "@webassemblyjs/wasm-opt": "1.5.13", + "@webassemblyjs/wasm-parser": "1.5.13", + "acorn": "^5.6.2", + "acorn-dynamic-import": "^3.0.0", + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0", + "chrome-trace-event": "^1.0.0", + "enhanced-resolve": "^4.1.0", + "eslint-scope": "^4.0.0", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.3.0", + "loader-utils": "^1.1.0", + "memory-fs": "~0.4.1", + "micromatch": "^3.1.8", + "mkdirp": "~0.5.0", + "neo-async": "^2.5.0", + "node-libs-browser": "^2.0.0", + "schema-utils": "^0.4.4", + "tapable": "^1.0.0", + "uglifyjs-webpack-plugin": "^1.2.4", + "watchpack": "^1.5.0", + "webpack-sources": "^1.0.1" + }, + "dependencies": { + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + } + } + }, + "webpack-addons": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/webpack-addons/-/webpack-addons-1.1.5.tgz", + "integrity": "sha512-MGO0nVniCLFAQz1qv22zM02QPjcpAoJdy7ED0i3Zy7SY1IecgXCm460ib7H/Wq7e9oL5VL6S2BxaObxwIcag0g==", + "dev": true, + "requires": { + "jscodeshift": "^0.4.0" + }, + "dependencies": { + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1" + } + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "ast-types": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.10.1.tgz", + "integrity": "sha512-UY7+9DPzlJ9VM8eY0b2TUZcZvF+1pO0hzMtAyjBYKhOmnvRlqYNYnWdtsMj0V16CGaMlpL0G1jnLbLo4AyotuQ==", + "dev": true + }, + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true, + "requires": { + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" + } + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true, + "requires": { + "is-posix-bracket": "^0.1.0" + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "jscodeshift": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/jscodeshift/-/jscodeshift-0.4.1.tgz", + "integrity": "sha512-iOX6If+hsw0q99V3n31t4f5VlD1TQZddH08xbT65ZqA7T4Vkx68emrDZMUOLVvCEAJ6NpAk7DECe3fjC/t52AQ==", + "dev": true, + "requires": { + "async": "^1.5.0", + "babel-plugin-transform-flow-strip-types": "^6.8.0", + "babel-preset-es2015": "^6.9.0", + "babel-preset-stage-1": "^6.5.0", + "babel-register": "^6.9.0", + "babylon": "^6.17.3", + "colors": "^1.1.2", + "flow-parser": "^0.*", + "lodash": "^4.13.1", + "micromatch": "^2.3.7", + "node-dir": "0.1.8", + "nomnom": "^1.8.1", + "recast": "^0.12.5", + "temp": "^0.8.1", + "write-file-atomic": "^1.2.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "requires": { + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "recast": { + "version": "0.12.9", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.12.9.tgz", + "integrity": "sha512-y7ANxCWmMW8xLOaiopiRDlyjQ9ajKRENBH+2wjntIbk3A6ZR1+BLQttkmSHMY7Arl+AAZFwJ10grg2T6f1WI8A==", + "dev": true, + "requires": { + "ast-types": "0.10.1", + "core-js": "^2.4.1", + "esprima": "~4.0.0", + "private": "~0.1.5", + "source-map": "~0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "webpack-cli": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-2.1.5.tgz", + "integrity": "sha512-CiWQR+1JS77rmyiO6y1q8Kt/O+e8nUUC9YfJ25JtSmzDwbqJV7vIsh3+QKRHVTbTCa0DaVh8iY1LBiagUIDB3g==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "diff": "^3.5.0", + "enhanced-resolve": "^4.0.0", + "envinfo": "^5.7.0", + "glob-all": "^3.1.0", + "global-modules": "^1.0.0", + "got": "^8.3.1", + "import-local": "^1.0.0", + "inquirer": "^5.2.0", + "interpret": "^1.1.0", + "jscodeshift": "^0.5.0", + "listr": "^0.14.1", + "loader-utils": "^1.1.0", + "lodash": "^4.17.10", + "log-symbols": "^2.2.0", + "mkdirp": "^0.5.1", + "p-each-series": "^1.0.0", + "p-lazy": "^1.0.0", + "prettier": "^1.12.1", + "supports-color": "^5.4.0", + "v8-compile-cache": "^2.0.0", + "webpack-addons": "^1.1.5", + "yargs": "^11.1.0", + "yeoman-environment": "^2.1.1", + "yeoman-generator": "^2.0.5" + } + }, + "webpack-livereload-plugin": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/webpack-livereload-plugin/-/webpack-livereload-plugin-2.1.1.tgz", + "integrity": "sha512-W7Q55QbPvVJotpIZSjjwzmqQ22333ExYxWM3WFlHKkbPStQqVRSmJkjntUqXF9jtpdeXi8r8HLkA1RVnAP0SQA==", + "dev": true, + "requires": { + "tiny-lr": "^1.1.1" + } + }, + "webpack-log": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-1.2.0.tgz", + "integrity": "sha512-U9AnICnu50HXtiqiDxuli5gLB5PGBo7VvcHx36jRZHwK4vzOYLbImqT4lwWwoMHdQWwEKw736fCHEekokTEKHA==", + "dev": true, + "requires": { + "chalk": "^2.1.0", + "log-symbols": "^2.1.0", + "loglevelnext": "^1.0.1", + "uuid": "^3.1.0" + } + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "websocket-driver": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.3.tgz", + "integrity": "sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg==", + "dev": true, + "requires": { + "http-parser-js": ">=0.4.0 <0.4.11", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", + "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "dev": true, + "requires": { + "errno": "~0.1.7" + } + }, + "wrap-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-3.0.1.tgz", + "integrity": "sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo=", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write-file-atomic": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz", + "integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "slide": "^1.1.5" + } + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, + "yargs": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.1.tgz", + "integrity": "sha512-PRU7gJrJaXv3q3yQZ/+/X6KBswZiaQ+zOmdprZcouPYtQgvNU35i+68M4b1ZHLZtYFT5QObFLV+ZkmJYcwKdiw==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.1.1", + "find-up": "^2.1.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.1.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^9.0.2" + }, + "dependencies": { + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + } + } + }, + "yargs-parser": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz", + "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } + }, + "yeoman-environment": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/yeoman-environment/-/yeoman-environment-2.6.0.tgz", + "integrity": "sha512-Hl0LBs9/mKun8XyJ6gFiUNhZwjN/eezn+E9IFWz6KtXg/3wsnztF2lgtE8eIjfhWYtvY4yMq9iizi1Ei5JJ+7A==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "debug": "^3.1.0", + "diff": "^3.5.0", + "escape-string-regexp": "^1.0.2", + "globby": "^8.0.1", + "grouped-queue": "^0.3.3", + "inquirer": "^6.0.0", + "is-scoped": "^1.0.0", + "lodash": "^4.17.10", + "log-symbols": "^2.2.0", + "mem-fs": "^1.1.0", + "strip-ansi": "^4.0.0", + "text-table": "^0.2.0", + "untildify": "^3.0.3" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "dir-glob": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz", + "integrity": "sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "path-type": "^3.0.0" + } + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "globby": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-8.0.2.tgz", + "integrity": "sha512-yTzMmKygLp8RUpG1Ymu2VXPSJQZjNAZPD4ywgYEaG7e4tBJeUQBO8OpXrf1RCNcEs5alsoJYPAMiIHP0cmeC7w==", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "dir-glob": "2.0.0", + "fast-glob": "^2.0.2", + "glob": "^7.1.2", + "ignore": "^3.3.5", + "pify": "^3.0.0", + "slash": "^1.0.0" + } + }, + "inquirer": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", + "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", + "dev": true, + "requires": { + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^2.0.0", + "lodash": "^4.17.12", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.4.0", + "string-width": "^2.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" + }, + "dependencies": { + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "rxjs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.3.tgz", + "integrity": "sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + } + } + } + } + }, + "yeoman-generator": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/yeoman-generator/-/yeoman-generator-2.0.5.tgz", + "integrity": "sha512-rV6tJ8oYzm4mmdF2T3wjY+Q42jKF2YiiD0VKfJ8/0ZYwmhCKC9Xs2346HVLPj/xE13i68psnFJv7iS6gWRkeAg==", + "dev": true, + "requires": { + "async": "^2.6.0", + "chalk": "^2.3.0", + "cli-table": "^0.3.1", + "cross-spawn": "^6.0.5", + "dargs": "^5.1.0", + "dateformat": "^3.0.3", + "debug": "^3.1.0", + "detect-conflict": "^1.0.0", + "error": "^7.0.2", + "find-up": "^2.1.0", + "github-username": "^4.0.0", + "istextorbinary": "^2.2.1", + "lodash": "^4.17.10", + "make-dir": "^1.1.0", + "mem-fs-editor": "^4.0.0", + "minimist": "^1.2.0", + "pretty-bytes": "^4.0.2", + "read-chunk": "^2.1.0", + "read-pkg-up": "^3.0.0", + "rimraf": "^2.6.2", + "run-async": "^2.0.0", + "shelljs": "^0.8.0", + "text-table": "^0.2.0", + "through2": "^2.0.0", + "yeoman-environment": "^2.0.5" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + } + } +} diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/package.json b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/package.json new file mode 100644 index 0000000000..88a2bf4881 --- /dev/null +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/package.json @@ -0,0 +1,41 @@ +{ + "name": "simple", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "webpack-ver": "cross-env NODE_ENV=production webpack --version", + "dist": "cross-env NODE_ENV=production webpack --display-error-details", + "watch": "cross-env NODE_ENV=development webpack --watch --display-error-details" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "awesome-typescript-loader": "^5.0.0", + "clean-webpack-plugin": "^0.1.19", + "compression-webpack-plugin": "^2.0.0", + "copy-webpack-plugin": "^4.6.0", + "cross-env": "^5.1.6", + "css-loader": "^1.0.0", + "file-loader": "^1.1.11", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.1", + "source-map-loader": "^0.2.3", + "style-loader": "^0.21.0", + "to-string-loader": "^1.1.5", + "tsconfig-paths-webpack-plugin": "^3.2.0", + "typescript": "^2.9.2", + "url-loader": "^1.0.1", + "webpack": "~4.16.3", + "webpack-cli": "~2.1.3", + "webpack-livereload-plugin": "~2.1.1" + }, + "dependencies": { + "@types/react": "^16.3.14", + "@types/react-dom": "^16.0.5", + "mobx": "4.3.0", + "mobx-react": "^5.1.2", + "react": "^16.3.2", + "react-dom": "^16.3.2" + } +} diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/FramePresenter.tsx b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/FramePresenter.tsx new file mode 100644 index 0000000000..0059cfe683 --- /dev/null +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/FramePresenter.tsx @@ -0,0 +1,57 @@ +import {PreviewerFrame, PreviewerServerConnection} from "src/PreviewerServerConnection"; +import * as React from "react"; + +interface PreviewerPresenterProps { + conn: PreviewerServerConnection; +} + +export class PreviewerPresenter extends React.Component { + private canvasRef: React.RefObject; + + constructor(props: PreviewerPresenterProps) { + super(props); + this.state = {width: 1, height: 1}; + this.canvasRef = React.createRef() + this.componentDidUpdate({ + conn: null! + }, this.state); + } + + componentDidMount(): void { + this.updateCanvas(this.canvasRef.current, this.props.conn.currentFrame); + } + + componentDidUpdate(prevProps: Readonly, prevState: Readonly<{}>, snapshot?: any): void { + if(prevProps.conn != this.props.conn) + { + if(prevProps.conn) + prevProps.conn.removeFrameListener(this.frameHandler); + if(this.props.conn) + this.props.conn.addFrameListener(this.frameHandler); + } + } + + private frameHandler = (frame: PreviewerFrame)=>{ + this.updateCanvas(this.canvasRef.current, frame); + }; + + + updateCanvas(canvas: HTMLCanvasElement | null, frame: PreviewerFrame | null) { + if (!canvas) + return; + if (frame == null){ + canvas.width = canvas.height = 1; + canvas.getContext('2d')!.clearRect(0,0,1,1); + } + else { + canvas.width = frame.data.width; + canvas.height = frame.data.height; + const ctx = canvas.getContext('2d')!; + ctx.putImageData(frame.data, 0,0); + } + } + + render() { + return + } +} diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/PreviewerServerConnection.ts b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/PreviewerServerConnection.ts new file mode 100644 index 0000000000..891c5094e7 --- /dev/null +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/PreviewerServerConnection.ts @@ -0,0 +1,78 @@ +export interface PreviewerFrame { + data: ImageData; + dpiX: number; + dpiY: number; +} + +export class PreviewerServerConnection { + private nextFrame = { + width: 0, + height: 0, + stride: 0, + dpiX: 0, + dpiY: 0, + sequenceId: "0" + }; + + public currentFrame: PreviewerFrame | null; + private handlers = new Set<(frame: PreviewerFrame | null) => void>(); + private conn: WebSocket; + + public addFrameListener(listener: (frame: PreviewerFrame | null) => void) { + this.handlers.add(listener); + if (this.currentFrame) + listener(this.currentFrame); + } + + public removeFrameListener(listener: (frame: PreviewerFrame | null) => void) { + this.handlers.delete(listener); + } + + constructor(uri: string) { + this.currentFrame = null; + var conn = this.conn = new WebSocket(uri); + conn.binaryType = 'arraybuffer'; + + const onMessage = this.onMessage; + conn.onmessage = msg => onMessage(msg); + + const onClose = () => this.setFrame(null); + conn.onclose = () => onClose(); + conn.onerror = (err: Event) => { + onClose(); + console.log("Connection error: " + err); + } + } + + private onMessage = (msg: MessageEvent) => { + if (typeof msg.data == 'string' || msg.data instanceof String) { + const parts = msg.data.split(':'); + if (parts[0] == 'frame') { + this.nextFrame = { + sequenceId: parts[1], + width: parseInt(parts[2]), + height: parseInt(parts[3]), + stride: parseInt(parts[4]), + dpiX: parseInt(parts[5]), + dpiY: parseInt(parts[6]) + } + } + } else if (msg.data instanceof ArrayBuffer) { + const arr = new Uint8ClampedArray(msg.data, 0); + const imageData = new ImageData(arr, this.nextFrame.width, this.nextFrame.height); + this.conn.send('frame-received:' + this.nextFrame.sequenceId); + this.setFrame({ + data: imageData, + dpiX: this.nextFrame.dpiX, + dpiY: this.nextFrame.dpiY + }); + + + } + }; + + private setFrame(frame: PreviewerFrame | null) { + this.currentFrame = frame; + this.handlers.forEach(h => h(this.currentFrame)); + } +} diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/index.html b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/index.html new file mode 100644 index 0000000000..f5847cdd58 --- /dev/null +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/index.html @@ -0,0 +1,14 @@ + + + + + Avalonia XAML previewer web edition + + + +
+
Loading...
+
+ + + diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/index.tsx b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/index.tsx new file mode 100644 index 0000000000..6da4d1fc56 --- /dev/null +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/index.tsx @@ -0,0 +1,15 @@ +import * as React from "react"; +import {PreviewerPresenter} from './FramePresenter' +import {PreviewerServerConnection} from "src/PreviewerServerConnection"; +import * as ReactDOM from "react-dom"; + +const loc = window.location; +const conn = new PreviewerServerConnection((loc.protocol === "https:" ? "wss" : "ws") + "://" + loc.host + "/ws"); + +const App = function(){ + return
+ +
+}; + +ReactDOM.render(, document.getElementById("app")); diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/tsconfig.json b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/tsconfig.json new file mode 100644 index 0000000000..f933cde76a --- /dev/null +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/tsconfig.json @@ -0,0 +1,35 @@ +{ + "compilerOptions": { + "outDir": "build/dist", + "module": "esnext", + "target": "es5", + "lib": ["es6", "dom"], + "sourceMap": true, + "allowJs": true, + "jsx": "react", + "moduleResolution": "node", + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": true, + "strictNullChecks": true, + "suppressImplicitAnyIndexErrors": true, + "noUnusedLocals": false, + "baseUrl": ".", + "experimentalDecorators": true, + "paths": { + "*": ["./node_modules/@types/*", "./node_modules/*"], + "src/*": ["./src/*"] + } + }, + "include": ["./src/**/*"], + "exclude": [ + "node_modules", + "build", + "scripts", + "acceptance-tests", + "webpack", + "jest", + "src/setupTests.ts" + ] +} diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/webpack.config.js b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/webpack.config.js new file mode 100644 index 0000000000..3057f269a5 --- /dev/null +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/webpack.config.js @@ -0,0 +1,117 @@ +const webpack = require('webpack'); +const path = require('path'); +const LiveReloadPlugin = require('webpack-livereload-plugin'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const CompressionPlugin = require('compression-webpack-plugin'); +const CleanWebpackPlugin = require('clean-webpack-plugin'); +const MiniCssExtractPlugin = require("mini-css-extract-plugin"); +const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin'); +const CopyWebpackPlugin = require('copy-webpack-plugin'); +const prod = process.env.NODE_ENV == 'production'; + +class Printer { + apply(compiler) { + compiler.hooks.afterEmit.tap("Printer", ()=> console.log("Build completed at " + new Date().toString())); + compiler.hooks.watchRun.tap("Printer", ()=> console.log("Watch triggered at " + new Date().toString())); + } +} + +const config = { + entry: { + bundle: './src/index.tsx' + }, + output: { + path: path.resolve(__dirname, 'build'), + publicPath: '/', + filename: '[name].[chunkhash].js' + }, + performance: { hints: false }, + mode: prod ? "production" : "development", + module: { + rules: [ + { + enforce: "pre", + test: /\.js$/, + loader: "source-map-loader", + exclude: [ + path.resolve(__dirname, 'node_modules/mobx-state-router') + ] + }, + { + "oneOf": [ + { + test: /\.(ts|tsx)$/, + exclude: /node_modules/, + use: 'awesome-typescript-loader' + }, + { + test: /\.css$/, + use: [ + MiniCssExtractPlugin.loader, + 'css-loader' + ] + }, + { + test: /\.(jpg|png)$/, + use: { + loader: "url-loader", + options: { + limit: 25000, + }, + }, + }, + { + test: /.(ttf|otf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/, + use: [{ + loader: 'file-loader', + options: { + name: '[name].[ext]', + outputPath: 'fonts/', // where the fonts will go + } + }] + }, + { + loader: require.resolve('file-loader'), + exclude: [/\.(js|jsx|mjs|tsx|ts)$/, /\.html$/, /\.json$/], + options: { + name: 'assets/[name].[hash:8].[ext]', + }, + }] + }, + + ] + }, + devtool: "source-map", + resolve: { + modules: [path.resolve(__dirname, 'node_modules')], + plugins: [new TsconfigPathsPlugin({ configFile: "./tsconfig.json", logLevel: 'info' })], + extensions: ['.ts', '.tsx', '.js', '.json'], + alias: { + 'src': path.resolve(__dirname, 'src') + } + }, + plugins: + [ + new Printer(), + new CleanWebpackPlugin([path.resolve(__dirname, 'build')]), + new MiniCssExtractPlugin({ + filename: "[name].[chunkhash]h" + + ".css", + chunkFilename: "[id].[chunkhash].css" + }), + new LiveReloadPlugin({appendScriptTag: !prod}), + new HtmlWebpackPlugin({ + template: path.resolve(__dirname, './src/index.html'), + filename: 'index.html' //relative to root of the application + }), + new CopyWebpackPlugin([ + // relative path from src + //{ from: './src/favicon.ico' }, + //{ from: './src/assets' } + ]), + new CompressionPlugin({ + test: /(\?.*)?$/i + }) + ] +}; +module.exports = config; diff --git a/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs b/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs index 617fbc8005..44d9a94f5a 100644 --- a/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs +++ b/src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs @@ -5,6 +5,7 @@ using System.Reflection; using System.Threading; using System.Xml; using Avalonia.Controls; +using Avalonia.DesignerSupport.Remote.HtmlTransport; using Avalonia.Input; using Avalonia.Remote.Protocol; using Avalonia.Remote.Protocol.Designer; @@ -24,15 +25,16 @@ namespace Avalonia.DesignerSupport.Remote { public string AppPath { get; set; } public Uri Transport { get; set; } + public Uri HtmlMethodListenUri { get; set; } public string Method { get; set; } = Methods.AvaloniaRemote; public string SessionId { get; set; } = Guid.NewGuid().ToString(); } - static class Methods + internal static class Methods { public const string AvaloniaRemote = "avalonia-remote"; public const string Win32 = "win32"; - + public const string Html = "html"; } static Exception Die(string error) @@ -52,6 +54,19 @@ namespace Avalonia.DesignerSupport.Remote { Console.Error.WriteLine("Usage: --transport transport_spec --session-id sid --method method app"); Console.Error.WriteLine(); + Console.Error.WriteLine("--transport: transport used for communication with the IDE"); + Console.Error.WriteLine(" 'tcp-bson' (e. g. 'tcp-bson://127.0.0.1:30243/') - TCP-based transport with BSON serialization of messages defined in Avalonia.Remote.Protocol"); + Console.Error.WriteLine(" 'file' (e. g. 'file://C://my/file.xaml' - pseudo-transport that triggers XAML updates on file changes, useful as a standalone previewer tool, always uses http preview method"); + Console.Error.WriteLine(); + Console.Error.WriteLine("--session-id: session id to be sent to IDE process"); + Console.Error.WriteLine(); + Console.Error.WriteLine("--method: the way the XAML is displayed"); + Console.Error.WriteLine(" 'avalonia-remote' - binary image is sent via transport connection in FrameMessage"); + Console.Error.WriteLine(" 'win32' - XAML is displayed in win32 window (handle could be obtained from UpdateXamlResultMessage), IDE is responsible to use user32!SetParent"); + Console.Error.WriteLine(" 'html' - Previewer starts an HTML server and displays XAML previewer as a web page"); + Console.Error.WriteLine(); + Console.Error.WriteLine("--html-url - endpoint for HTML method to listen on, e. g. http://127.0.0.1:8081"); + Console.Error.WriteLine(); Console.Error.WriteLine("Example: --transport tcp-bson://127.0.0.1:30243/ --session-id 123 --method avalonia-remote MyApp.exe"); Console.Error.Flush(); return Die(null); @@ -74,6 +89,8 @@ namespace Avalonia.DesignerSupport.Remote next = a => rv.Transport = new Uri(a, UriKind.Absolute); else if (arg == "--method") next = a => rv.Method = a; + else if (arg == "--html-url") + next = a => rv.HtmlMethodListenUri = new Uri(a, UriKind.Absolute); else if (arg == "--session-id") next = a => rv.SessionId = a; else if (rv.AppPath == null) @@ -89,6 +106,9 @@ namespace Avalonia.DesignerSupport.Remote { PrintUsage(); } + + if (next != null) + PrintUsage(); return rv; } @@ -98,27 +118,40 @@ namespace Avalonia.DesignerSupport.Remote { return new BsonTcpTransport().Connect(IPAddress.Parse(transport.Host), transport.Port).Result; } + + if (transport.Scheme == "file") + { + return new FileWatcherTransport(transport); + } PrintUsage(); return null; } interface IAppInitializer { - Application GetConfiguredApp(IAvaloniaRemoteTransportConnection transport, CommandLineArgs args, object obj); + IAvaloniaRemoteTransportConnection ConfigureApp(IAvaloniaRemoteTransportConnection transport, CommandLineArgs args, object obj); } - + class AppInitializer : IAppInitializer where T : AppBuilderBase, new() { - public Application GetConfiguredApp(IAvaloniaRemoteTransportConnection transport, + public IAvaloniaRemoteTransportConnection ConfigureApp(IAvaloniaRemoteTransportConnection transport, CommandLineArgs args, object obj) { - var builder = (AppBuilderBase) obj; + var builder = (AppBuilderBase)obj; if (args.Method == Methods.AvaloniaRemote) builder.UseWindowingSubsystem(() => PreviewerWindowingPlatform.Initialize(transport)); + if (args.Method == Methods.Html) + { + transport = new HtmlWebSocketTransport(transport, + args.HtmlMethodListenUri ?? new Uri("http://localhost:5000")); + builder.UseWindowingSubsystem(() => + PreviewerWindowingPlatform.Initialize(transport)); + } + if (args.Method == Methods.Win32) builder.UseWindowingSubsystem("Avalonia.Win32"); builder.SetupWithoutStarting(); - return builder.Instance; + return transport; } } @@ -128,6 +161,8 @@ namespace Avalonia.DesignerSupport.Remote { var args = ParseCommandLineArgs(cmdline); var transport = CreateTransport(args.Transport); + if (transport is ITransportWithEnforcedMethod enforcedMethod) + args.Method = enforcedMethod.PreviewerMethod; var asm = Assembly.LoadFile(System.IO.Path.GetFullPath(args.AppPath)); var entryPoint = asm.EntryPoint; if (entryPoint == null) @@ -141,12 +176,14 @@ namespace Avalonia.DesignerSupport.Remote var appBuilder = builderMethod.Invoke(null, null); Log($"Initializing application in design mode"); var initializer =(IAppInitializer)Activator.CreateInstance(typeof(AppInitializer<>).MakeGenericType(appBuilder.GetType())); - var app = initializer.GetConfiguredApp(transport, args, appBuilder); + transport = initializer.ConfigureApp(transport, args, appBuilder); s_transport = transport; transport.OnMessage += OnTransportMessage; transport.OnException += (t, e) => Die(e.ToString()); + transport.Start(); Log("Sending StartDesignerSessionMessage"); transport.Send(new StartDesignerSessionMessage {SessionId = args.SessionId}); + Dispatcher.UIThread.MainLoop(CancellationToken.None); } diff --git a/src/Avalonia.Remote.Protocol/BsonStreamTransport.cs b/src/Avalonia.Remote.Protocol/BsonStreamTransport.cs index 78712985d9..c58aafa4e0 100644 --- a/src/Avalonia.Remote.Protocol/BsonStreamTransport.cs +++ b/src/Avalonia.Remote.Protocol/BsonStreamTransport.cs @@ -144,5 +144,9 @@ namespace Avalonia.Remote.Protocol public event Action OnMessage; public event Action OnException; + public void Start() + { + + } } } diff --git a/src/Avalonia.Remote.Protocol/ITransport.cs b/src/Avalonia.Remote.Protocol/ITransport.cs index 0f419b95ae..6772fa75b2 100644 --- a/src/Avalonia.Remote.Protocol/ITransport.cs +++ b/src/Avalonia.Remote.Protocol/ITransport.cs @@ -8,5 +8,6 @@ namespace Avalonia.Remote.Protocol Task Send(object data); event Action OnMessage; event Action OnException; + void Start(); } } diff --git a/src/Avalonia.Remote.Protocol/TransportConnectionWrapper.cs b/src/Avalonia.Remote.Protocol/TransportConnectionWrapper.cs index d7919af9d9..77c77a70ad 100644 --- a/src/Avalonia.Remote.Protocol/TransportConnectionWrapper.cs +++ b/src/Avalonia.Remote.Protocol/TransportConnectionWrapper.cs @@ -98,5 +98,7 @@ namespace Avalonia.Remote.Protocol add => _onException.Add(value); remove => _onException.Remove(value); } + + public void Start() => _conn.Start(); } } diff --git a/src/Avalonia.Remote.Protocol/TransportMessages.cs b/src/Avalonia.Remote.Protocol/TransportMessages.cs new file mode 100644 index 0000000000..08486806c7 --- /dev/null +++ b/src/Avalonia.Remote.Protocol/TransportMessages.cs @@ -0,0 +1,9 @@ +namespace Avalonia.Remote.Protocol +{ + [AvaloniaRemoteMessageGuid("53778004-78fa-4381-8ec3-176a6f2328b6")] + public class HtmlTransportStartedMessage + { + public string Uri { get; set; } + + } +} diff --git a/src/Avalonia.Remote.Protocol/ViewportMessages.cs b/src/Avalonia.Remote.Protocol/ViewportMessages.cs index dc4550b846..06881f2c7f 100644 --- a/src/Avalonia.Remote.Protocol/ViewportMessages.cs +++ b/src/Avalonia.Remote.Protocol/ViewportMessages.cs @@ -60,6 +60,8 @@ public int Width { get; set; } public int Height { get; set; } public int Stride { get; set; } + public double DpiX { get; set; } + public double DpiY { get; set; } } } From 3c5126fdf0b8cae15bc912d4a536c5eb8738e11c Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Mon, 18 Nov 2019 22:59:17 +0300 Subject: [PATCH 005/663] Fixed host/port parsing --- .../Remote/HtmlTransport/HtmlTransport.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs index 2ca609bba9..55e2df8890 100644 --- a/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs +++ b/src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs @@ -58,15 +58,13 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport }); _signalTransport = signalTransport; - var host = listenUri.Host.Split(':'); - var address = IPAddress.Parse(host[0]); - var port = host.Length > 1 ? int.Parse(host[1]) : 5000; - - _simpleServer = new SimpleWebSocketHttpServer(address, port); + var address = IPAddress.Parse(listenUri.Host); + + _simpleServer = new SimpleWebSocketHttpServer(address, listenUri.Port); _simpleServer.Listen(); Task.Run(AcceptWorker); Task.Run(SocketWorker); - _signalTransport.Send(new HtmlTransportStartedMessage { Uri = "http://" + address + ":" + port + "/" }); + _signalTransport.Send(new HtmlTransportStartedMessage { Uri = "http://" + address + ":" + listenUri.Port + "/" }); } async void AcceptWorker() From 60a2e37e18cf32d293fe5c49ca621f40ee0a2c31 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sat, 21 Dec 2019 00:03:53 +0300 Subject: [PATCH 006/663] Refactored GPU acceleration, implemented OpelGlTextureBitmap --- src/Avalonia.Base/Utilities/DisposableLock.cs | 50 + src/Avalonia.Native/GlPlatformFeature.cs | 29 +- src/Avalonia.OpenGL/Avalonia.OpenGL.csproj | 2 + src/Avalonia.OpenGL/EglContext.cs | 33 +- src/Avalonia.OpenGL/EglDisplay.cs | 6 - src/Avalonia.OpenGL/EglGlPlatformFeature.cs | 14 +- src/Avalonia.OpenGL/EglGlPlatformSurface.cs | 12 +- src/Avalonia.OpenGL/EglInterface.cs | 16 + src/Avalonia.OpenGL/GlConsts.cs | 4674 ++++++++++++++++- src/Avalonia.OpenGL/GlInterface.cs | 57 +- src/Avalonia.OpenGL/IGlContext.cs | 6 +- src/Avalonia.OpenGL/IGlDisplay.cs | 1 - .../IOpenGlAwarePlatformRenderInterface.cs | 9 + .../IWindowingPlatformGlFeature.cs | 3 +- .../Imaging/IOpenGlTextureBitmapImpl.cs | 13 + .../Imaging/OpenGlTextureBitmap.cs | 46 + .../Rendering/ManagedDeferredRendererLock.cs | 3 +- src/Avalonia.X11/Glx/Glx.cs | 21 +- src/Avalonia.X11/Glx/GlxContext.cs | 46 +- src/Avalonia.X11/Glx/GlxDisplay.cs | 53 +- src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs | 10 +- src/Avalonia.X11/Glx/GlxPlatformFeature.cs | 5 +- .../Output/DrmOutput.cs | 35 +- src/Skia/Avalonia.Skia/DrawingContextImpl.cs | 4 +- .../{ICustomSkiaGpu.cs => Gpu/ISkiaGpu.cs} | 15 +- .../ISkiaGpuRenderSession.cs} | 2 +- .../ISkiaGpuRenderTarget.cs} | 7 +- .../{ => Gpu/OpenGl}/GlRenderTarget.cs | 71 +- .../Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs | 43 + .../Gpu/OpenGlTextureBitmapImpl.cs | 81 + .../SkiaGpuRenderTarget.cs} | 12 +- .../Avalonia.Skia/PlatformRenderInterface.cs | 59 +- src/Skia/Avalonia.Skia/SkiaOptions.cs | 2 +- 33 files changed, 5278 insertions(+), 162 deletions(-) create mode 100644 src/Avalonia.Base/Utilities/DisposableLock.cs create mode 100644 src/Avalonia.OpenGL/IOpenGlAwarePlatformRenderInterface.cs create mode 100644 src/Avalonia.OpenGL/Imaging/IOpenGlTextureBitmapImpl.cs create mode 100644 src/Avalonia.OpenGL/Imaging/OpenGlTextureBitmap.cs rename src/Skia/Avalonia.Skia/{ICustomSkiaGpu.cs => Gpu/ISkiaGpu.cs} (68%) rename src/Skia/Avalonia.Skia/{ICustomSkiaRenderSession.cs => Gpu/ISkiaGpuRenderSession.cs} (92%) rename src/Skia/Avalonia.Skia/{ICustomSkiaRenderTarget.cs => Gpu/ISkiaGpuRenderTarget.cs} (71%) rename src/Skia/Avalonia.Skia/{ => Gpu/OpenGl}/GlRenderTarget.cs (55%) create mode 100644 src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs create mode 100644 src/Skia/Avalonia.Skia/Gpu/OpenGlTextureBitmapImpl.cs rename src/Skia/Avalonia.Skia/{CustomRenderTarget.cs => Gpu/SkiaGpuRenderTarget.cs} (69%) diff --git a/src/Avalonia.Base/Utilities/DisposableLock.cs b/src/Avalonia.Base/Utilities/DisposableLock.cs new file mode 100644 index 0000000000..b06e97da00 --- /dev/null +++ b/src/Avalonia.Base/Utilities/DisposableLock.cs @@ -0,0 +1,50 @@ +using System; +using System.Threading; + +namespace Avalonia.Utilities +{ + public class DisposableLock + { + private readonly object _lock = new object(); + + /// + /// Tries to take a lock + /// + /// IDisposable if succeeded to obtain the lock + public IDisposable TryLock() + { + if (Monitor.TryEnter(_lock)) + return new UnlockDisposable(_lock); + return null; + } + + /// + /// Enters a waiting lock + /// + public IDisposable Lock() + { + Monitor.Enter(_lock); + return new UnlockDisposable(_lock); + } + + private sealed class UnlockDisposable : IDisposable + { + private object _lock; + + public UnlockDisposable(object @lock) + { + _lock = @lock; + } + + public void Dispose() + { + object @lock = Interlocked.Exchange(ref _lock, null); + + if (@lock != null) + { + Monitor.Exit(@lock); + } + } + } + } +} diff --git a/src/Avalonia.Native/GlPlatformFeature.cs b/src/Avalonia.Native/GlPlatformFeature.cs index e39680bdee..e2231dae65 100644 --- a/src/Avalonia.Native/GlPlatformFeature.cs +++ b/src/Avalonia.Native/GlPlatformFeature.cs @@ -2,21 +2,35 @@ using Avalonia.OpenGL; using Avalonia.Native.Interop; using System.Drawing; +using System.Reactive.Disposables; using Avalonia.Threading; namespace Avalonia.Native { class GlPlatformFeature : IWindowingPlatformGlFeature { - + private GlContext _immediateContext; public GlPlatformFeature(IAvnGlFeature feature) { Display = new GlDisplay(feature.ObtainDisplay()); - ImmediateContext = new GlContext(Display, feature.ObtainImmediateContext()); + _immediateContext = new GlContext(Display, feature.ObtainImmediateContext()); } - public IGlContext ImmediateContext { get; } public GlDisplay Display { get; } + IGlDisplay IWindowingPlatformGlFeature.Display => Display; + + public IGlContext CreateContext() + { + if (_immediateContext != null) + { + var rv = _immediateContext; + _immediateContext = null; + return rv; + } + + throw new PlatformNotSupportedException( + "OSX backend haven't switched to the new model yet, so there are no custom contexts, sorry"); + } } class GlDisplay : IGlDisplay @@ -58,9 +72,16 @@ namespace Avalonia.Native public IGlDisplay Display { get; } - public void MakeCurrent() + public IDisposable MakeCurrent() { Context.MakeCurrent(); + // HACK: OSX backend haven't switched to the new model, so there are only 2 pre-created contexts now + return Disposable.Empty; + } + + public void Dispose() + { + // HACK: OSX backend haven't switched to the new model, so there are only 2 pre-created contexts now } } diff --git a/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj b/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj index 310d0c8dba..72132eed93 100644 --- a/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj +++ b/src/Avalonia.OpenGL/Avalonia.OpenGL.csproj @@ -2,10 +2,12 @@ netstandard2.0 + true + diff --git a/src/Avalonia.OpenGL/EglContext.cs b/src/Avalonia.OpenGL/EglContext.cs index a39000f198..9acac74cb7 100644 --- a/src/Avalonia.OpenGL/EglContext.cs +++ b/src/Avalonia.OpenGL/EglContext.cs @@ -1,6 +1,7 @@ using System; using System.Reactive.Disposables; using System.Threading; +using static Avalonia.OpenGL.EglConsts; namespace Avalonia.OpenGL { @@ -28,17 +29,45 @@ namespace Avalonia.OpenGL return Disposable.Create(() => Monitor.Exit(_lock)); } - public void MakeCurrent() + class RestoreContext : IDisposable { + private readonly EglInterface _egl; + private readonly IntPtr _display; + private IntPtr _context, _read, _draw; + + public RestoreContext(EglInterface egl) + { + _egl = egl; + _display = _egl.GetCurrentDisplay(); + _context = _egl.GetCurrentContext(); + _read = _egl.GetCurrentSurface(EGL_READ); + _draw = _egl.GetCurrentSurface(EGL_DRAW); + } + + public void Dispose() => _egl.MakeCurrent(_display, _draw, _read, _context); + } + + public IDisposable MakeCurrent() + { + var old = new RestoreContext(_egl); if (!_egl.MakeCurrent(_disp.Handle, IntPtr.Zero, IntPtr.Zero, Context)) throw OpenGlException.GetFormattedException("eglMakeCurrent", _egl); + return old; } - public void MakeCurrent(EglSurface surface) + public IDisposable MakeCurrent(EglSurface surface) { + var old = new RestoreContext(_egl); var surf = surface ?? OffscreenSurface; if (!_egl.MakeCurrent(_disp.Handle, surf.DangerousGetHandle(), surf.DangerousGetHandle(), Context)) throw OpenGlException.GetFormattedException("eglMakeCurrent", _egl); + return old; + } + + public void Dispose() + { + _egl.DestroyContext(_disp.Handle, Context); + OffscreenSurface?.Dispose(); } } } diff --git a/src/Avalonia.OpenGL/EglDisplay.cs b/src/Avalonia.OpenGL/EglDisplay.cs index 66418c0e15..57787243b2 100644 --- a/src/Avalonia.OpenGL/EglDisplay.cs +++ b/src/Avalonia.OpenGL/EglDisplay.cs @@ -169,12 +169,6 @@ namespace Avalonia.OpenGL return rv; } - public void ClearContext() - { - if (!_egl.MakeCurrent(_display, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero)) - throw OpenGlException.GetFormattedException("eglMakeCurrent", _egl); - } - public EglSurface CreateWindowSurface(IntPtr window) { var s = _egl.CreateWindowSurface(_display, _config, window, new[] {EGL_NONE, EGL_NONE}); diff --git a/src/Avalonia.OpenGL/EglGlPlatformFeature.cs b/src/Avalonia.OpenGL/EglGlPlatformFeature.cs index 5f5064fba5..ff7df67921 100644 --- a/src/Avalonia.OpenGL/EglGlPlatformFeature.cs +++ b/src/Avalonia.OpenGL/EglGlPlatformFeature.cs @@ -5,8 +5,12 @@ namespace Avalonia.OpenGL { public class EglGlPlatformFeature : IWindowingPlatformGlFeature { - public IGlDisplay Display { get; set; } - public IGlContext ImmediateContext { get; set; } + private EglDisplay _display; + public IGlDisplay Display => _display; + public IGlContext CreateContext() + { + return _display.CreateContext(DeferredContext); + } public EglContext DeferredContext { get; set; } public static void TryInitialize() @@ -21,12 +25,10 @@ namespace Avalonia.OpenGL try { var disp = new EglDisplay(); - var ctx = disp.CreateContext(null); return new EglGlPlatformFeature { - Display = disp, - ImmediateContext = ctx, - DeferredContext = (EglContext)disp.CreateContext(ctx) + _display = disp, + DeferredContext = disp.CreateContext(null) }; } catch(Exception e) diff --git a/src/Avalonia.OpenGL/EglGlPlatformSurface.cs b/src/Avalonia.OpenGL/EglGlPlatformSurface.cs index d2e4543af3..2bb2e4e65e 100644 --- a/src/Avalonia.OpenGL/EglGlPlatformSurface.cs +++ b/src/Avalonia.OpenGL/EglGlPlatformSurface.cs @@ -58,12 +58,12 @@ namespace Avalonia.OpenGL { if (IsCorrupted) throw new RenderTargetCorruptedException(); - _context.MakeCurrent(_glSurface); + var restoreContext = _context.MakeCurrent(_glSurface); _display.EglInterface.WaitClient(); _display.EglInterface.WaitGL(); _display.EglInterface.WaitNative(); - return new Session(_display, _context, _glSurface, _info, l); + return new Session(_display, _context, _glSurface, _info, l, restoreContext); } catch { @@ -79,17 +79,19 @@ namespace Avalonia.OpenGL private readonly IEglWindowGlPlatformSurfaceInfo _info; private readonly EglDisplay _display; private IDisposable _lock; - + private readonly IDisposable _restoreContext; + public Session(EglDisplay display, EglContext context, EglSurface glSurface, IEglWindowGlPlatformSurfaceInfo info, - IDisposable @lock) + IDisposable @lock, IDisposable restoreContext) { _context = context; _display = display; _glSurface = glSurface; _info = info; _lock = @lock; + _restoreContext = restoreContext; } public void Dispose() @@ -100,7 +102,7 @@ namespace Avalonia.OpenGL _display.EglInterface.WaitClient(); _display.EglInterface.WaitGL(); _display.EglInterface.WaitNative(); - _context.Display.ClearContext(); + _restoreContext.Dispose(); _lock.Dispose(); } diff --git a/src/Avalonia.OpenGL/EglInterface.cs b/src/Avalonia.OpenGL/EglInterface.cs index 47088972a4..cdc5f38f33 100644 --- a/src/Avalonia.OpenGL/EglInterface.cs +++ b/src/Avalonia.OpenGL/EglInterface.cs @@ -87,6 +87,10 @@ namespace Avalonia.OpenGL IntPtr share, int[] attrs); [GlEntryPoint("eglCreateContext")] public EglCreateContext CreateContext { get; } + + public delegate bool EglDestroyContext(IntPtr display, IntPtr context); + [GlEntryPoint("eglDestroyContext")] + public EglDestroyContext DestroyContext { get; } public delegate IntPtr EglCreatePBufferSurface(IntPtr display, IntPtr config, int[] attrs); [GlEntryPoint("eglCreatePbufferSurface")] @@ -96,6 +100,18 @@ namespace Avalonia.OpenGL [GlEntryPoint("eglMakeCurrent")] public EglMakeCurrent MakeCurrent { get; } + public delegate IntPtr EglGetCurrentContext(); + [GlEntryPoint("eglGetCurrentContext")] + public EglGetCurrentContext GetCurrentContext { get; } + + public delegate IntPtr EglGetCurrentDisplay(); + [GlEntryPoint("eglGetCurrentDisplay")] + public EglGetCurrentContext GetCurrentDisplay { get; } + + public delegate IntPtr EglGetCurrentSurface(int readDraw); + [GlEntryPoint("eglGetCurrentSurface")] + public EglGetCurrentSurface GetCurrentSurface { get; } + public delegate void EglDisplaySurfaceVoidDelegate(IntPtr display, IntPtr surface); [GlEntryPoint("eglDestroySurface")] public EglDisplaySurfaceVoidDelegate DestroySurface { get; } diff --git a/src/Avalonia.OpenGL/GlConsts.cs b/src/Avalonia.OpenGL/GlConsts.cs index 275f351e3e..2a6aa0d14d 100644 --- a/src/Avalonia.OpenGL/GlConsts.cs +++ b/src/Avalonia.OpenGL/GlConsts.cs @@ -786,7 +786,4679 @@ namespace Avalonia.OpenGL // glext.h - + public const int GL_BLEND_DST_RGB = 0x80C8; + public const int GL_BLEND_SRC_RGB = 0x80C9; + public const int GL_BLEND_DST_ALPHA = 0x80CA; + public const int GL_BLEND_SRC_ALPHA = 0x80CB; + public const int GL_POINT_FADE_THRESHOLD_SIZE = 0x8128; + public const int GL_DEPTH_COMPONENT16 = 0x81A5; + public const int GL_DEPTH_COMPONENT24 = 0x81A6; + public const int GL_DEPTH_COMPONENT32 = 0x81A7; + public const int GL_MIRRORED_REPEAT = 0x8370; + public const int GL_MAX_TEXTURE_LOD_BIAS = 0x84FD; + public const int GL_TEXTURE_LOD_BIAS = 0x8501; + public const int GL_INCR_WRAP = 0x8507; + public const int GL_DECR_WRAP = 0x8508; + public const int GL_TEXTURE_DEPTH_SIZE = 0x884A; + public const int GL_TEXTURE_COMPARE_MODE = 0x884C; + public const int GL_TEXTURE_COMPARE_FUNC = 0x884D; + public const int GL_POINT_SIZE_MIN = 0x8126; + public const int GL_POINT_SIZE_MAX = 0x8127; + public const int GL_POINT_DISTANCE_ATTENUATION = 0x8129; + public const int GL_GENERATE_MIPMAP = 0x8191; + public const int GL_GENERATE_MIPMAP_HINT = 0x8192; + public const int GL_FOG_COORDINATE_SOURCE = 0x8450; + public const int GL_FOG_COORDINATE = 0x8451; + public const int GL_FRAGMENT_DEPTH = 0x8452; + public const int GL_CURRENT_FOG_COORDINATE = 0x8453; + public const int GL_FOG_COORDINATE_ARRAY_TYPE = 0x8454; + public const int GL_FOG_COORDINATE_ARRAY_STRIDE = 0x8455; + public const int GL_FOG_COORDINATE_ARRAY_POINTER = 0x8456; + public const int GL_FOG_COORDINATE_ARRAY = 0x8457; + public const int GL_COLOR_SUM = 0x8458; + public const int GL_CURRENT_SECONDARY_COLOR = 0x8459; + public const int GL_SECONDARY_COLOR_ARRAY_SIZE = 0x845A; + public const int GL_SECONDARY_COLOR_ARRAY_TYPE = 0x845B; + public const int GL_SECONDARY_COLOR_ARRAY_STRIDE = 0x845C; + public const int GL_SECONDARY_COLOR_ARRAY_POINTER = 0x845D; + public const int GL_SECONDARY_COLOR_ARRAY = 0x845E; + public const int GL_TEXTURE_FILTER_CONTROL = 0x8500; + public const int GL_DEPTH_TEXTURE_MODE = 0x884B; + public const int GL_COMPARE_R_TO_TEXTURE = 0x884E; + public const int GL_BUFFER_SIZE = 0x8764; + public const int GL_BUFFER_USAGE = 0x8765; + public const int GL_QUERY_COUNTER_BITS = 0x8864; + public const int GL_CURRENT_QUERY = 0x8865; + public const int GL_QUERY_RESULT = 0x8866; + public const int GL_QUERY_RESULT_AVAILABLE = 0x8867; + public const int GL_ARRAY_BUFFER = 0x8892; + public const int GL_ELEMENT_ARRAY_BUFFER = 0x8893; + public const int GL_ARRAY_BUFFER_BINDING = 0x8894; + public const int GL_ELEMENT_ARRAY_BUFFER_BINDING = 0x8895; + public const int GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING = 0x889F; + public const int GL_READ_ONLY = 0x88B8; + public const int GL_WRITE_ONLY = 0x88B9; + public const int GL_READ_WRITE = 0x88BA; + public const int GL_BUFFER_ACCESS = 0x88BB; + public const int GL_BUFFER_MAPPED = 0x88BC; + public const int GL_BUFFER_MAP_POINTER = 0x88BD; + public const int GL_STREAM_DRAW = 0x88E0; + public const int GL_STREAM_READ = 0x88E1; + public const int GL_STREAM_COPY = 0x88E2; + public const int GL_STATIC_DRAW = 0x88E4; + public const int GL_STATIC_READ = 0x88E5; + public const int GL_STATIC_COPY = 0x88E6; + public const int GL_DYNAMIC_DRAW = 0x88E8; + public const int GL_DYNAMIC_READ = 0x88E9; + public const int GL_DYNAMIC_COPY = 0x88EA; + public const int GL_SAMPLES_PASSED = 0x8914; + public const int GL_SRC1_ALPHA = 0x8589; + public const int GL_VERTEX_ARRAY_BUFFER_BINDING = 0x8896; + public const int GL_NORMAL_ARRAY_BUFFER_BINDING = 0x8897; + public const int GL_COLOR_ARRAY_BUFFER_BINDING = 0x8898; + public const int GL_INDEX_ARRAY_BUFFER_BINDING = 0x8899; + public const int GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING = 0x889A; + public const int GL_EDGE_FLAG_ARRAY_BUFFER_BINDING = 0x889B; + public const int GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING = 0x889C; + public const int GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING = 0x889D; + public const int GL_WEIGHT_ARRAY_BUFFER_BINDING = 0x889E; + public const int GL_FOG_COORD_SRC = 0x8450; + public const int GL_FOG_COORD = 0x8451; + public const int GL_CURRENT_FOG_COORD = 0x8453; + public const int GL_FOG_COORD_ARRAY_TYPE = 0x8454; + public const int GL_FOG_COORD_ARRAY_STRIDE = 0x8455; + public const int GL_FOG_COORD_ARRAY_POINTER = 0x8456; + public const int GL_FOG_COORD_ARRAY = 0x8457; + public const int GL_FOG_COORD_ARRAY_BUFFER_BINDING = 0x889D; + public const int GL_SRC0_RGB = 0x8580; + public const int GL_SRC1_RGB = 0x8581; + public const int GL_SRC2_RGB = 0x8582; + public const int GL_SRC0_ALPHA = 0x8588; + public const int GL_SRC2_ALPHA = 0x858A; + public const int GL_BLEND_EQUATION_RGB = 0x8009; + public const int GL_VERTEX_ATTRIB_ARRAY_ENABLED = 0x8622; + public const int GL_VERTEX_ATTRIB_ARRAY_SIZE = 0x8623; + public const int GL_VERTEX_ATTRIB_ARRAY_STRIDE = 0x8624; + public const int GL_VERTEX_ATTRIB_ARRAY_TYPE = 0x8625; + public const int GL_CURRENT_VERTEX_ATTRIB = 0x8626; + public const int GL_VERTEX_PROGRAM_POINT_SIZE = 0x8642; + public const int GL_VERTEX_ATTRIB_ARRAY_POINTER = 0x8645; + public const int GL_STENCIL_BACK_FUNC = 0x8800; + public const int GL_STENCIL_BACK_FAIL = 0x8801; + public const int GL_STENCIL_BACK_PASS_DEPTH_FAIL = 0x8802; + public const int GL_STENCIL_BACK_PASS_DEPTH_PASS = 0x8803; + public const int GL_MAX_DRAW_BUFFERS = 0x8824; + public const int GL_DRAW_BUFFER0 = 0x8825; + public const int GL_DRAW_BUFFER1 = 0x8826; + public const int GL_DRAW_BUFFER2 = 0x8827; + public const int GL_DRAW_BUFFER3 = 0x8828; + public const int GL_DRAW_BUFFER4 = 0x8829; + public const int GL_DRAW_BUFFER5 = 0x882A; + public const int GL_DRAW_BUFFER6 = 0x882B; + public const int GL_DRAW_BUFFER7 = 0x882C; + public const int GL_DRAW_BUFFER8 = 0x882D; + public const int GL_DRAW_BUFFER9 = 0x882E; + public const int GL_DRAW_BUFFER10 = 0x882F; + public const int GL_DRAW_BUFFER11 = 0x8830; + public const int GL_DRAW_BUFFER12 = 0x8831; + public const int GL_DRAW_BUFFER13 = 0x8832; + public const int GL_DRAW_BUFFER14 = 0x8833; + public const int GL_DRAW_BUFFER15 = 0x8834; + public const int GL_BLEND_EQUATION_ALPHA = 0x883D; + public const int GL_MAX_VERTEX_ATTRIBS = 0x8869; + public const int GL_VERTEX_ATTRIB_ARRAY_NORMALIZED = 0x886A; + public const int GL_MAX_TEXTURE_IMAGE_UNITS = 0x8872; + public const int GL_FRAGMENT_SHADER = 0x8B30; + public const int GL_VERTEX_SHADER = 0x8B31; + public const int GL_MAX_FRAGMENT_UNIFORM_COMPONENTS = 0x8B49; + public const int GL_MAX_VERTEX_UNIFORM_COMPONENTS = 0x8B4A; + public const int GL_MAX_VARYING_FLOATS = 0x8B4B; + public const int GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS = 0x8B4C; + public const int GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS = 0x8B4D; + public const int GL_SHADER_TYPE = 0x8B4F; + public const int GL_FLOAT_VEC2 = 0x8B50; + public const int GL_FLOAT_VEC3 = 0x8B51; + public const int GL_FLOAT_VEC4 = 0x8B52; + public const int GL_INT_VEC2 = 0x8B53; + public const int GL_INT_VEC3 = 0x8B54; + public const int GL_INT_VEC4 = 0x8B55; + public const int GL_BOOL = 0x8B56; + public const int GL_BOOL_VEC2 = 0x8B57; + public const int GL_BOOL_VEC3 = 0x8B58; + public const int GL_BOOL_VEC4 = 0x8B59; + public const int GL_FLOAT_MAT2 = 0x8B5A; + public const int GL_FLOAT_MAT3 = 0x8B5B; + public const int GL_FLOAT_MAT4 = 0x8B5C; + public const int GL_SAMPLER_1D = 0x8B5D; + public const int GL_SAMPLER_2D = 0x8B5E; + public const int GL_SAMPLER_3D = 0x8B5F; + public const int GL_SAMPLER_CUBE = 0x8B60; + public const int GL_SAMPLER_1D_SHADOW = 0x8B61; + public const int GL_SAMPLER_2D_SHADOW = 0x8B62; + public const int GL_DELETE_STATUS = 0x8B80; + public const int GL_COMPILE_STATUS = 0x8B81; + public const int GL_LINK_STATUS = 0x8B82; + public const int GL_VALIDATE_STATUS = 0x8B83; + public const int GL_INFO_LOG_LENGTH = 0x8B84; + public const int GL_ATTACHED_SHADERS = 0x8B85; + public const int GL_ACTIVE_UNIFORMS = 0x8B86; + public const int GL_ACTIVE_UNIFORM_MAX_LENGTH = 0x8B87; + public const int GL_SHADER_SOURCE_LENGTH = 0x8B88; + public const int GL_ACTIVE_ATTRIBUTES = 0x8B89; + public const int GL_ACTIVE_ATTRIBUTE_MAX_LENGTH = 0x8B8A; + public const int GL_FRAGMENT_SHADER_DERIVATIVE_HINT = 0x8B8B; + public const int GL_SHADING_LANGUAGE_VERSION = 0x8B8C; + public const int GL_CURRENT_PROGRAM = 0x8B8D; + public const int GL_POINT_SPRITE_COORD_ORIGIN = 0x8CA0; + public const int GL_LOWER_LEFT = 0x8CA1; + public const int GL_UPPER_LEFT = 0x8CA2; + public const int GL_STENCIL_BACK_REF = 0x8CA3; + public const int GL_STENCIL_BACK_VALUE_MASK = 0x8CA4; + public const int GL_STENCIL_BACK_WRITEMASK = 0x8CA5; + public const int GL_VERTEX_PROGRAM_TWO_SIDE = 0x8643; + public const int GL_POINT_SPRITE = 0x8861; + public const int GL_COORD_REPLACE = 0x8862; + public const int GL_MAX_TEXTURE_COORDS = 0x8871; + public const int GL_VERSION_2_1 = 1; + public const int GL_PIXEL_PACK_BUFFER = 0x88EB; + public const int GL_PIXEL_UNPACK_BUFFER = 0x88EC; + public const int GL_PIXEL_PACK_BUFFER_BINDING = 0x88ED; + public const int GL_PIXEL_UNPACK_BUFFER_BINDING = 0x88EF; + public const int GL_FLOAT_MAT2x3 = 0x8B65; + public const int GL_FLOAT_MAT2x4 = 0x8B66; + public const int GL_FLOAT_MAT3x2 = 0x8B67; + public const int GL_FLOAT_MAT3x4 = 0x8B68; + public const int GL_FLOAT_MAT4x2 = 0x8B69; + public const int GL_FLOAT_MAT4x3 = 0x8B6A; + public const int GL_SRGB = 0x8C40; + public const int GL_SRGB8 = 0x8C41; + public const int GL_SRGB_ALPHA = 0x8C42; + public const int GL_SRGB8_ALPHA8 = 0x8C43; + public const int GL_COMPRESSED_SRGB = 0x8C48; + public const int GL_COMPRESSED_SRGB_ALPHA = 0x8C49; + public const int GL_CURRENT_RASTER_SECONDARY_COLOR = 0x845F; + public const int GL_SLUMINANCE_ALPHA = 0x8C44; + public const int GL_SLUMINANCE8_ALPHA8 = 0x8C45; + public const int GL_SLUMINANCE = 0x8C46; + public const int GL_SLUMINANCE8 = 0x8C47; + public const int GL_COMPRESSED_SLUMINANCE = 0x8C4A; + public const int GL_COMPRESSED_SLUMINANCE_ALPHA = 0x8C4B; + public const int GL_VERSION_3_0 = 1; + public const int GL_COMPARE_REF_TO_TEXTURE = 0x884E; + public const int GL_CLIP_DISTANCE0 = 0x3000; + public const int GL_CLIP_DISTANCE1 = 0x3001; + public const int GL_CLIP_DISTANCE2 = 0x3002; + public const int GL_CLIP_DISTANCE3 = 0x3003; + public const int GL_CLIP_DISTANCE4 = 0x3004; + public const int GL_CLIP_DISTANCE5 = 0x3005; + public const int GL_CLIP_DISTANCE6 = 0x3006; + public const int GL_CLIP_DISTANCE7 = 0x3007; + public const int GL_MAX_CLIP_DISTANCES = 0x0D32; + public const int GL_MAJOR_VERSION = 0x821B; + public const int GL_MINOR_VERSION = 0x821C; + public const int GL_NUM_EXTENSIONS = 0x821D; + public const int GL_CONTEXT_FLAGS = 0x821E; + public const int GL_COMPRESSED_RED = 0x8225; + public const int GL_COMPRESSED_RG = 0x8226; + public const int GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT = 0x00000001; + public const int GL_RGBA32F = 0x8814; + public const int GL_RGB32F = 0x8815; + public const int GL_RGBA16F = 0x881A; + public const int GL_RGB16F = 0x881B; + public const int GL_VERTEX_ATTRIB_ARRAY_INTEGER = 0x88FD; + public const int GL_MAX_ARRAY_TEXTURE_LAYERS = 0x88FF; + public const int GL_MIN_PROGRAM_TEXEL_OFFSET = 0x8904; + public const int GL_MAX_PROGRAM_TEXEL_OFFSET = 0x8905; + public const int GL_CLAMP_READ_COLOR = 0x891C; + public const int GL_FIXED_ONLY = 0x891D; + public const int GL_MAX_VARYING_COMPONENTS = 0x8B4B; + public const int GL_TEXTURE_1D_ARRAY = 0x8C18; + public const int GL_PROXY_TEXTURE_1D_ARRAY = 0x8C19; + public const int GL_TEXTURE_2D_ARRAY = 0x8C1A; + public const int GL_PROXY_TEXTURE_2D_ARRAY = 0x8C1B; + public const int GL_TEXTURE_BINDING_1D_ARRAY = 0x8C1C; + public const int GL_TEXTURE_BINDING_2D_ARRAY = 0x8C1D; + public const int GL_R11F_G11F_B10F = 0x8C3A; + public const int GL_UNSIGNED_INT_10F_11F_11F_REV = 0x8C3B; + public const int GL_RGB9_E5 = 0x8C3D; + public const int GL_UNSIGNED_INT_5_9_9_9_REV = 0x8C3E; + public const int GL_TEXTURE_SHARED_SIZE = 0x8C3F; + public const int GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH = 0x8C76; + public const int GL_TRANSFORM_FEEDBACK_BUFFER_MODE = 0x8C7F; + public const int GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS = 0x8C80; + public const int GL_TRANSFORM_FEEDBACK_VARYINGS = 0x8C83; + public const int GL_TRANSFORM_FEEDBACK_BUFFER_START = 0x8C84; + public const int GL_TRANSFORM_FEEDBACK_BUFFER_SIZE = 0x8C85; + public const int GL_PRIMITIVES_GENERATED = 0x8C87; + public const int GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN = 0x8C88; + public const int GL_RASTERIZER_DISCARD = 0x8C89; + public const int GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS = 0x8C8A; + public const int GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS = 0x8C8B; + public const int GL_INTERLEAVED_ATTRIBS = 0x8C8C; + public const int GL_SEPARATE_ATTRIBS = 0x8C8D; + public const int GL_TRANSFORM_FEEDBACK_BUFFER = 0x8C8E; + public const int GL_TRANSFORM_FEEDBACK_BUFFER_BINDING = 0x8C8F; + public const int GL_RGBA32UI = 0x8D70; + public const int GL_RGB32UI = 0x8D71; + public const int GL_RGBA16UI = 0x8D76; + public const int GL_RGB16UI = 0x8D77; + public const int GL_RGBA8UI = 0x8D7C; + public const int GL_RGB8UI = 0x8D7D; + public const int GL_RGBA32I = 0x8D82; + public const int GL_RGB32I = 0x8D83; + public const int GL_RGBA16I = 0x8D88; + public const int GL_RGB16I = 0x8D89; + public const int GL_RGBA8I = 0x8D8E; + public const int GL_RGB8I = 0x8D8F; + public const int GL_RED_INTEGER = 0x8D94; + public const int GL_GREEN_INTEGER = 0x8D95; + public const int GL_BLUE_INTEGER = 0x8D96; + public const int GL_RGB_INTEGER = 0x8D98; + public const int GL_RGBA_INTEGER = 0x8D99; + public const int GL_BGR_INTEGER = 0x8D9A; + public const int GL_BGRA_INTEGER = 0x8D9B; + public const int GL_SAMPLER_1D_ARRAY = 0x8DC0; + public const int GL_SAMPLER_2D_ARRAY = 0x8DC1; + public const int GL_SAMPLER_1D_ARRAY_SHADOW = 0x8DC3; + public const int GL_SAMPLER_2D_ARRAY_SHADOW = 0x8DC4; + public const int GL_SAMPLER_CUBE_SHADOW = 0x8DC5; + public const int GL_UNSIGNED_INT_VEC2 = 0x8DC6; + public const int GL_UNSIGNED_INT_VEC3 = 0x8DC7; + public const int GL_UNSIGNED_INT_VEC4 = 0x8DC8; + public const int GL_INT_SAMPLER_1D = 0x8DC9; + public const int GL_INT_SAMPLER_2D = 0x8DCA; + public const int GL_INT_SAMPLER_3D = 0x8DCB; + public const int GL_INT_SAMPLER_CUBE = 0x8DCC; + public const int GL_INT_SAMPLER_1D_ARRAY = 0x8DCE; + public const int GL_INT_SAMPLER_2D_ARRAY = 0x8DCF; + public const int GL_UNSIGNED_INT_SAMPLER_1D = 0x8DD1; + public const int GL_UNSIGNED_INT_SAMPLER_2D = 0x8DD2; + public const int GL_UNSIGNED_INT_SAMPLER_3D = 0x8DD3; + public const int GL_UNSIGNED_INT_SAMPLER_CUBE = 0x8DD4; + public const int GL_UNSIGNED_INT_SAMPLER_1D_ARRAY = 0x8DD6; + public const int GL_UNSIGNED_INT_SAMPLER_2D_ARRAY = 0x8DD7; + public const int GL_QUERY_WAIT = 0x8E13; + public const int GL_QUERY_NO_WAIT = 0x8E14; + public const int GL_QUERY_BY_REGION_WAIT = 0x8E15; + public const int GL_QUERY_BY_REGION_NO_WAIT = 0x8E16; + public const int GL_BUFFER_ACCESS_FLAGS = 0x911F; + public const int GL_BUFFER_MAP_LENGTH = 0x9120; + public const int GL_BUFFER_MAP_OFFSET = 0x9121; + public const int GL_DEPTH_COMPONENT32F = 0x8CAC; + public const int GL_DEPTH32F_STENCIL8 = 0x8CAD; + public const int GL_FLOAT_32_UNSIGNED_INT_24_8_REV = 0x8DAD; + public const int GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING = 0x8210; + public const int GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE = 0x8211; + public const int GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE = 0x8212; + public const int GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE = 0x8213; + public const int GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE = 0x8214; + public const int GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE = 0x8215; + public const int GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE = 0x8216; + public const int GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE = 0x8217; + public const int GL_FRAMEBUFFER_DEFAULT = 0x8218; + public const int GL_FRAMEBUFFER_UNDEFINED = 0x8219; + public const int GL_DEPTH_STENCIL_ATTACHMENT = 0x821A; + public const int GL_MAX_RENDERBUFFER_SIZE = 0x84E8; + public const int GL_DEPTH_STENCIL = 0x84F9; + public const int GL_UNSIGNED_INT_24_8 = 0x84FA; + public const int GL_DEPTH24_STENCIL8 = 0x88F0; + public const int GL_TEXTURE_STENCIL_SIZE = 0x88F1; + public const int GL_TEXTURE_RED_TYPE = 0x8C10; + public const int GL_TEXTURE_GREEN_TYPE = 0x8C11; + public const int GL_TEXTURE_BLUE_TYPE = 0x8C12; + public const int GL_TEXTURE_ALPHA_TYPE = 0x8C13; + public const int GL_TEXTURE_DEPTH_TYPE = 0x8C16; + public const int GL_UNSIGNED_NORMALIZED = 0x8C17; public const int GL_FRAMEBUFFER_BINDING = 0x8CA6; + public const int GL_DRAW_FRAMEBUFFER_BINDING = 0x8CA6; + public const int GL_RENDERBUFFER_BINDING = 0x8CA7; + public const int GL_READ_FRAMEBUFFER = 0x8CA8; + public const int GL_DRAW_FRAMEBUFFER = 0x8CA9; + public const int GL_READ_FRAMEBUFFER_BINDING = 0x8CAA; + public const int GL_RENDERBUFFER_SAMPLES = 0x8CAB; + public const int GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = 0x8CD0; + public const int GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME = 0x8CD1; + public const int GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL = 0x8CD2; + public const int GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE = 0x8CD3; + public const int GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER = 0x8CD4; + public const int GL_FRAMEBUFFER_COMPLETE = 0x8CD5; + public const int GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT = 0x8CD6; + public const int GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT = 0x8CD7; + public const int GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER = 0x8CDB; + public const int GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER = 0x8CDC; + public const int GL_FRAMEBUFFER_UNSUPPORTED = 0x8CDD; + public const int GL_MAX_COLOR_ATTACHMENTS = 0x8CDF; + public const int GL_COLOR_ATTACHMENT0 = 0x8CE0; + public const int GL_COLOR_ATTACHMENT1 = 0x8CE1; + public const int GL_COLOR_ATTACHMENT2 = 0x8CE2; + public const int GL_COLOR_ATTACHMENT3 = 0x8CE3; + public const int GL_COLOR_ATTACHMENT4 = 0x8CE4; + public const int GL_COLOR_ATTACHMENT5 = 0x8CE5; + public const int GL_COLOR_ATTACHMENT6 = 0x8CE6; + public const int GL_COLOR_ATTACHMENT7 = 0x8CE7; + public const int GL_COLOR_ATTACHMENT8 = 0x8CE8; + public const int GL_COLOR_ATTACHMENT9 = 0x8CE9; + public const int GL_COLOR_ATTACHMENT10 = 0x8CEA; + public const int GL_COLOR_ATTACHMENT11 = 0x8CEB; + public const int GL_COLOR_ATTACHMENT12 = 0x8CEC; + public const int GL_COLOR_ATTACHMENT13 = 0x8CED; + public const int GL_COLOR_ATTACHMENT14 = 0x8CEE; + public const int GL_COLOR_ATTACHMENT15 = 0x8CEF; + public const int GL_COLOR_ATTACHMENT16 = 0x8CF0; + public const int GL_COLOR_ATTACHMENT17 = 0x8CF1; + public const int GL_COLOR_ATTACHMENT18 = 0x8CF2; + public const int GL_COLOR_ATTACHMENT19 = 0x8CF3; + public const int GL_COLOR_ATTACHMENT20 = 0x8CF4; + public const int GL_COLOR_ATTACHMENT21 = 0x8CF5; + public const int GL_COLOR_ATTACHMENT22 = 0x8CF6; + public const int GL_COLOR_ATTACHMENT23 = 0x8CF7; + public const int GL_COLOR_ATTACHMENT24 = 0x8CF8; + public const int GL_COLOR_ATTACHMENT25 = 0x8CF9; + public const int GL_COLOR_ATTACHMENT26 = 0x8CFA; + public const int GL_COLOR_ATTACHMENT27 = 0x8CFB; + public const int GL_COLOR_ATTACHMENT28 = 0x8CFC; + public const int GL_COLOR_ATTACHMENT29 = 0x8CFD; + public const int GL_COLOR_ATTACHMENT30 = 0x8CFE; + public const int GL_COLOR_ATTACHMENT31 = 0x8CFF; + public const int GL_DEPTH_ATTACHMENT = 0x8D00; + public const int GL_STENCIL_ATTACHMENT = 0x8D20; + public const int GL_FRAMEBUFFER = 0x8D40; + public const int GL_RENDERBUFFER = 0x8D41; + public const int GL_RENDERBUFFER_WIDTH = 0x8D42; + public const int GL_RENDERBUFFER_HEIGHT = 0x8D43; + public const int GL_RENDERBUFFER_INTERNAL_FORMAT = 0x8D44; + public const int GL_STENCIL_INDEX1 = 0x8D46; + public const int GL_STENCIL_INDEX4 = 0x8D47; + public const int GL_STENCIL_INDEX8 = 0x8D48; + public const int GL_STENCIL_INDEX16 = 0x8D49; + public const int GL_RENDERBUFFER_RED_SIZE = 0x8D50; + public const int GL_RENDERBUFFER_GREEN_SIZE = 0x8D51; + public const int GL_RENDERBUFFER_BLUE_SIZE = 0x8D52; + public const int GL_RENDERBUFFER_ALPHA_SIZE = 0x8D53; + public const int GL_RENDERBUFFER_DEPTH_SIZE = 0x8D54; + public const int GL_RENDERBUFFER_STENCIL_SIZE = 0x8D55; + public const int GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE = 0x8D56; + public const int GL_MAX_SAMPLES = 0x8D57; + public const int GL_INDEX = 0x8222; + public const int GL_TEXTURE_LUMINANCE_TYPE = 0x8C14; + public const int GL_TEXTURE_INTENSITY_TYPE = 0x8C15; + public const int GL_FRAMEBUFFER_SRGB = 0x8DB9; + public const int GL_HALF_FLOAT = 0x140B; + public const int GL_MAP_READ_BIT = 0x0001; + public const int GL_MAP_WRITE_BIT = 0x0002; + public const int GL_MAP_INVALIDATE_RANGE_BIT = 0x0004; + public const int GL_MAP_INVALIDATE_BUFFER_BIT = 0x0008; + public const int GL_MAP_FLUSH_EXPLICIT_BIT = 0x0010; + public const int GL_MAP_UNSYNCHRONIZED_BIT = 0x0020; + public const int GL_COMPRESSED_RED_RGTC1 = 0x8DBB; + public const int GL_COMPRESSED_SIGNED_RED_RGTC1 = 0x8DBC; + public const int GL_COMPRESSED_RG_RGTC2 = 0x8DBD; + public const int GL_COMPRESSED_SIGNED_RG_RGTC2 = 0x8DBE; + public const int GL_RG = 0x8227; + public const int GL_RG_INTEGER = 0x8228; + public const int GL_R8 = 0x8229; + public const int GL_R16 = 0x822A; + public const int GL_RG8 = 0x822B; + public const int GL_RG16 = 0x822C; + public const int GL_R16F = 0x822D; + public const int GL_R32F = 0x822E; + public const int GL_RG16F = 0x822F; + public const int GL_RG32F = 0x8230; + public const int GL_R8I = 0x8231; + public const int GL_R8UI = 0x8232; + public const int GL_R16I = 0x8233; + public const int GL_R16UI = 0x8234; + public const int GL_R32I = 0x8235; + public const int GL_R32UI = 0x8236; + public const int GL_RG8I = 0x8237; + public const int GL_RG8UI = 0x8238; + public const int GL_RG16I = 0x8239; + public const int GL_RG16UI = 0x823A; + public const int GL_RG32I = 0x823B; + public const int GL_RG32UI = 0x823C; + public const int GL_VERTEX_ARRAY_BINDING = 0x85B5; + public const int GL_CLAMP_VERTEX_COLOR = 0x891A; + public const int GL_CLAMP_FRAGMENT_COLOR = 0x891B; + public const int GL_ALPHA_INTEGER = 0x8D97; + public const int GL_VERSION_3_1 = 1; + public const int GL_SAMPLER_2D_RECT = 0x8B63; + public const int GL_SAMPLER_2D_RECT_SHADOW = 0x8B64; + public const int GL_SAMPLER_BUFFER = 0x8DC2; + public const int GL_INT_SAMPLER_2D_RECT = 0x8DCD; + public const int GL_INT_SAMPLER_BUFFER = 0x8DD0; + public const int GL_UNSIGNED_INT_SAMPLER_2D_RECT = 0x8DD5; + public const int GL_UNSIGNED_INT_SAMPLER_BUFFER = 0x8DD8; + public const int GL_TEXTURE_BUFFER = 0x8C2A; + public const int GL_MAX_TEXTURE_BUFFER_SIZE = 0x8C2B; + public const int GL_TEXTURE_BINDING_BUFFER = 0x8C2C; + public const int GL_TEXTURE_BUFFER_DATA_STORE_BINDING = 0x8C2D; + public const int GL_TEXTURE_RECTANGLE = 0x84F5; + public const int GL_TEXTURE_BINDING_RECTANGLE = 0x84F6; + public const int GL_PROXY_TEXTURE_RECTANGLE = 0x84F7; + public const int GL_MAX_RECTANGLE_TEXTURE_SIZE = 0x84F8; + public const int GL_R8_SNORM = 0x8F94; + public const int GL_RG8_SNORM = 0x8F95; + public const int GL_RGB8_SNORM = 0x8F96; + public const int GL_RGBA8_SNORM = 0x8F97; + public const int GL_R16_SNORM = 0x8F98; + public const int GL_RG16_SNORM = 0x8F99; + public const int GL_RGB16_SNORM = 0x8F9A; + public const int GL_RGBA16_SNORM = 0x8F9B; + public const int GL_SIGNED_NORMALIZED = 0x8F9C; + public const int GL_PRIMITIVE_RESTART = 0x8F9D; + public const int GL_PRIMITIVE_RESTART_INDEX = 0x8F9E; + public const int GL_COPY_READ_BUFFER = 0x8F36; + public const int GL_COPY_WRITE_BUFFER = 0x8F37; + public const int GL_UNIFORM_BUFFER = 0x8A11; + public const int GL_UNIFORM_BUFFER_BINDING = 0x8A28; + public const int GL_UNIFORM_BUFFER_START = 0x8A29; + public const int GL_UNIFORM_BUFFER_SIZE = 0x8A2A; + public const int GL_MAX_VERTEX_UNIFORM_BLOCKS = 0x8A2B; + public const int GL_MAX_GEOMETRY_UNIFORM_BLOCKS = 0x8A2C; + public const int GL_MAX_FRAGMENT_UNIFORM_BLOCKS = 0x8A2D; + public const int GL_MAX_COMBINED_UNIFORM_BLOCKS = 0x8A2E; + public const int GL_MAX_UNIFORM_BUFFER_BINDINGS = 0x8A2F; + public const int GL_MAX_UNIFORM_BLOCK_SIZE = 0x8A30; + public const int GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS = 0x8A31; + public const int GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS = 0x8A32; + public const int GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS = 0x8A33; + public const int GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT = 0x8A34; + public const int GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH = 0x8A35; + public const int GL_ACTIVE_UNIFORM_BLOCKS = 0x8A36; + public const int GL_UNIFORM_TYPE = 0x8A37; + public const int GL_UNIFORM_SIZE = 0x8A38; + public const int GL_UNIFORM_NAME_LENGTH = 0x8A39; + public const int GL_UNIFORM_BLOCK_INDEX = 0x8A3A; + public const int GL_UNIFORM_OFFSET = 0x8A3B; + public const int GL_UNIFORM_ARRAY_STRIDE = 0x8A3C; + public const int GL_UNIFORM_MATRIX_STRIDE = 0x8A3D; + public const int GL_UNIFORM_IS_ROW_MAJOR = 0x8A3E; + public const int GL_UNIFORM_BLOCK_BINDING = 0x8A3F; + public const int GL_UNIFORM_BLOCK_DATA_SIZE = 0x8A40; + public const int GL_UNIFORM_BLOCK_NAME_LENGTH = 0x8A41; + public const int GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS = 0x8A42; + public const int GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES = 0x8A43; + public const int GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER = 0x8A44; + public const int GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER = 0x8A45; + public const int GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER = 0x8A46; + public const int GL_INVALID_INDEX = -1; + public const int GL_VERSION_3_2 = 1; + public const int GL_CONTEXT_CORE_PROFILE_BIT = 0x00000001; + public const int GL_CONTEXT_COMPATIBILITY_PROFILE_BIT = 0x00000002; + public const int GL_LINES_ADJACENCY = 0x000A; + public const int GL_LINE_STRIP_ADJACENCY = 0x000B; + public const int GL_TRIANGLES_ADJACENCY = 0x000C; + public const int GL_TRIANGLE_STRIP_ADJACENCY = 0x000D; + public const int GL_PROGRAM_POINT_SIZE = 0x8642; + public const int GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS = 0x8C29; + public const int GL_FRAMEBUFFER_ATTACHMENT_LAYERED = 0x8DA7; + public const int GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS = 0x8DA8; + public const int GL_GEOMETRY_SHADER = 0x8DD9; + public const int GL_GEOMETRY_VERTICES_OUT = 0x8916; + public const int GL_GEOMETRY_INPUT_TYPE = 0x8917; + public const int GL_GEOMETRY_OUTPUT_TYPE = 0x8918; + public const int GL_MAX_GEOMETRY_UNIFORM_COMPONENTS = 0x8DDF; + public const int GL_MAX_GEOMETRY_OUTPUT_VERTICES = 0x8DE0; + public const int GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS = 0x8DE1; + public const int GL_MAX_VERTEX_OUTPUT_COMPONENTS = 0x9122; + public const int GL_MAX_GEOMETRY_INPUT_COMPONENTS = 0x9123; + public const int GL_MAX_GEOMETRY_OUTPUT_COMPONENTS = 0x9124; + public const int GL_MAX_FRAGMENT_INPUT_COMPONENTS = 0x9125; + public const int GL_CONTEXT_PROFILE_MASK = 0x9126; + public const int GL_DEPTH_CLAMP = 0x864F; + public const int GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION = 0x8E4C; + public const int GL_FIRST_VERTEX_CONVENTION = 0x8E4D; + public const int GL_LAST_VERTEX_CONVENTION = 0x8E4E; + public const int GL_PROVOKING_VERTEX = 0x8E4F; + public const int GL_TEXTURE_CUBE_MAP_SEAMLESS = 0x884F; + public const int GL_MAX_SERVER_WAIT_TIMEOUT = 0x9111; + public const int GL_OBJECT_TYPE = 0x9112; + public const int GL_SYNC_CONDITION = 0x9113; + public const int GL_SYNC_STATUS = 0x9114; + public const int GL_SYNC_FLAGS = 0x9115; + public const int GL_SYNC_FENCE = 0x9116; + public const int GL_SYNC_GPU_COMMANDS_COMPLETE = 0x9117; + public const int GL_UNSIGNALED = 0x9118; + public const int GL_SIGNALED = 0x9119; + public const int GL_ALREADY_SIGNALED = 0x911A; + public const int GL_TIMEOUT_EXPIRED = 0x911B; + public const int GL_CONDITION_SATISFIED = 0x911C; + public const int GL_WAIT_FAILED = 0x911D; + public const int GL_SYNC_FLUSH_COMMANDS_BIT = 0x00000001; + public const int GL_SAMPLE_POSITION = 0x8E50; + public const int GL_SAMPLE_MASK = 0x8E51; + public const int GL_SAMPLE_MASK_VALUE = 0x8E52; + public const int GL_MAX_SAMPLE_MASK_WORDS = 0x8E59; + public const int GL_TEXTURE_2D_MULTISAMPLE = 0x9100; + public const int GL_PROXY_TEXTURE_2D_MULTISAMPLE = 0x9101; + public const int GL_TEXTURE_2D_MULTISAMPLE_ARRAY = 0x9102; + public const int GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY = 0x9103; + public const int GL_TEXTURE_BINDING_2D_MULTISAMPLE = 0x9104; + public const int GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY = 0x9105; + public const int GL_TEXTURE_SAMPLES = 0x9106; + public const int GL_TEXTURE_FIXED_SAMPLE_LOCATIONS = 0x9107; + public const int GL_SAMPLER_2D_MULTISAMPLE = 0x9108; + public const int GL_INT_SAMPLER_2D_MULTISAMPLE = 0x9109; + public const int GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE = 0x910A; + public const int GL_SAMPLER_2D_MULTISAMPLE_ARRAY = 0x910B; + public const int GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY = 0x910C; + public const int GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY = 0x910D; + public const int GL_MAX_COLOR_TEXTURE_SAMPLES = 0x910E; + public const int GL_MAX_DEPTH_TEXTURE_SAMPLES = 0x910F; + public const int GL_MAX_INTEGER_SAMPLES = 0x9110; + public const int GL_VERSION_3_3 = 1; + public const int GL_VERTEX_ATTRIB_ARRAY_DIVISOR = 0x88FE; + public const int GL_SRC1_COLOR = 0x88F9; + public const int GL_ONE_MINUS_SRC1_COLOR = 0x88FA; + public const int GL_ONE_MINUS_SRC1_ALPHA = 0x88FB; + public const int GL_MAX_DUAL_SOURCE_DRAW_BUFFERS = 0x88FC; + public const int GL_ANY_SAMPLES_PASSED = 0x8C2F; + public const int GL_SAMPLER_BINDING = 0x8919; + public const int GL_RGB10_A2UI = 0x906F; + public const int GL_TEXTURE_SWIZZLE_R = 0x8E42; + public const int GL_TEXTURE_SWIZZLE_G = 0x8E43; + public const int GL_TEXTURE_SWIZZLE_B = 0x8E44; + public const int GL_TEXTURE_SWIZZLE_A = 0x8E45; + public const int GL_TEXTURE_SWIZZLE_RGBA = 0x8E46; + public const int GL_TIME_ELAPSED = 0x88BF; + public const int GL_TIMESTAMP = 0x8E28; + public const int GL_INT_2_10_10_10_REV = 0x8D9F; + public const int GL_VERSION_4_0 = 1; + public const int GL_SAMPLE_SHADING = 0x8C36; + public const int GL_MIN_SAMPLE_SHADING_VALUE = 0x8C37; + public const int GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET = 0x8E5E; + public const int GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET = 0x8E5F; + public const int GL_TEXTURE_CUBE_MAP_ARRAY = 0x9009; + public const int GL_TEXTURE_BINDING_CUBE_MAP_ARRAY = 0x900A; + public const int GL_PROXY_TEXTURE_CUBE_MAP_ARRAY = 0x900B; + public const int GL_SAMPLER_CUBE_MAP_ARRAY = 0x900C; + public const int GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW = 0x900D; + public const int GL_INT_SAMPLER_CUBE_MAP_ARRAY = 0x900E; + public const int GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY = 0x900F; + public const int GL_DRAW_INDIRECT_BUFFER = 0x8F3F; + public const int GL_DRAW_INDIRECT_BUFFER_BINDING = 0x8F43; + public const int GL_GEOMETRY_SHADER_INVOCATIONS = 0x887F; + public const int GL_MAX_GEOMETRY_SHADER_INVOCATIONS = 0x8E5A; + public const int GL_MIN_FRAGMENT_INTERPOLATION_OFFSET = 0x8E5B; + public const int GL_MAX_FRAGMENT_INTERPOLATION_OFFSET = 0x8E5C; + public const int GL_FRAGMENT_INTERPOLATION_OFFSET_BITS = 0x8E5D; + public const int GL_MAX_VERTEX_STREAMS = 0x8E71; + public const int GL_DOUBLE_VEC2 = 0x8FFC; + public const int GL_DOUBLE_VEC3 = 0x8FFD; + public const int GL_DOUBLE_VEC4 = 0x8FFE; + public const int GL_DOUBLE_MAT2 = 0x8F46; + public const int GL_DOUBLE_MAT3 = 0x8F47; + public const int GL_DOUBLE_MAT4 = 0x8F48; + public const int GL_DOUBLE_MAT2x3 = 0x8F49; + public const int GL_DOUBLE_MAT2x4 = 0x8F4A; + public const int GL_DOUBLE_MAT3x2 = 0x8F4B; + public const int GL_DOUBLE_MAT3x4 = 0x8F4C; + public const int GL_DOUBLE_MAT4x2 = 0x8F4D; + public const int GL_DOUBLE_MAT4x3 = 0x8F4E; + public const int GL_ACTIVE_SUBROUTINES = 0x8DE5; + public const int GL_ACTIVE_SUBROUTINE_UNIFORMS = 0x8DE6; + public const int GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS = 0x8E47; + public const int GL_ACTIVE_SUBROUTINE_MAX_LENGTH = 0x8E48; + public const int GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH = 0x8E49; + public const int GL_MAX_SUBROUTINES = 0x8DE7; + public const int GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS = 0x8DE8; + public const int GL_NUM_COMPATIBLE_SUBROUTINES = 0x8E4A; + public const int GL_COMPATIBLE_SUBROUTINES = 0x8E4B; + public const int GL_PATCHES = 0x000E; + public const int GL_PATCH_VERTICES = 0x8E72; + public const int GL_PATCH_DEFAULT_INNER_LEVEL = 0x8E73; + public const int GL_PATCH_DEFAULT_OUTER_LEVEL = 0x8E74; + public const int GL_TESS_CONTROL_OUTPUT_VERTICES = 0x8E75; + public const int GL_TESS_GEN_MODE = 0x8E76; + public const int GL_TESS_GEN_SPACING = 0x8E77; + public const int GL_TESS_GEN_VERTEX_ORDER = 0x8E78; + public const int GL_TESS_GEN_POINT_MODE = 0x8E79; + public const int GL_ISOLINES = 0x8E7A; + public const int GL_FRACTIONAL_ODD = 0x8E7B; + public const int GL_FRACTIONAL_EVEN = 0x8E7C; + public const int GL_MAX_PATCH_VERTICES = 0x8E7D; + public const int GL_MAX_TESS_GEN_LEVEL = 0x8E7E; + public const int GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS = 0x8E7F; + public const int GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS = 0x8E80; + public const int GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS = 0x8E81; + public const int GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS = 0x8E82; + public const int GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS = 0x8E83; + public const int GL_MAX_TESS_PATCH_COMPONENTS = 0x8E84; + public const int GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS = 0x8E85; + public const int GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS = 0x8E86; + public const int GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS = 0x8E89; + public const int GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS = 0x8E8A; + public const int GL_MAX_TESS_CONTROL_INPUT_COMPONENTS = 0x886C; + public const int GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS = 0x886D; + public const int GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS = 0x8E1E; + public const int GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS = 0x8E1F; + public const int GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER = 0x84F0; + public const int GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER = 0x84F1; + public const int GL_TESS_EVALUATION_SHADER = 0x8E87; + public const int GL_TESS_CONTROL_SHADER = 0x8E88; + public const int GL_TRANSFORM_FEEDBACK = 0x8E22; + public const int GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED = 0x8E23; + public const int GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE = 0x8E24; + public const int GL_TRANSFORM_FEEDBACK_BINDING = 0x8E25; + public const int GL_MAX_TRANSFORM_FEEDBACK_BUFFERS = 0x8E70; + public const int GL_VERSION_4_1 = 1; + public const int GL_FIXED = 0x140C; + public const int GL_IMPLEMENTATION_COLOR_READ_TYPE = 0x8B9A; + public const int GL_IMPLEMENTATION_COLOR_READ_FORMAT = 0x8B9B; + public const int GL_LOW_FLOAT = 0x8DF0; + public const int GL_MEDIUM_FLOAT = 0x8DF1; + public const int GL_HIGH_FLOAT = 0x8DF2; + public const int GL_LOW_INT = 0x8DF3; + public const int GL_MEDIUM_INT = 0x8DF4; + public const int GL_HIGH_INT = 0x8DF5; + public const int GL_SHADER_COMPILER = 0x8DFA; + public const int GL_SHADER_BINARY_FORMATS = 0x8DF8; + public const int GL_NUM_SHADER_BINARY_FORMATS = 0x8DF9; + public const int GL_MAX_VERTEX_UNIFORM_VECTORS = 0x8DFB; + public const int GL_MAX_VARYING_VECTORS = 0x8DFC; + public const int GL_MAX_FRAGMENT_UNIFORM_VECTORS = 0x8DFD; + public const int GL_RGB565 = 0x8D62; + public const int GL_PROGRAM_BINARY_RETRIEVABLE_HINT = 0x8257; + public const int GL_PROGRAM_BINARY_LENGTH = 0x8741; + public const int GL_NUM_PROGRAM_BINARY_FORMATS = 0x87FE; + public const int GL_PROGRAM_BINARY_FORMATS = 0x87FF; + public const int GL_VERTEX_SHADER_BIT = 0x00000001; + public const int GL_FRAGMENT_SHADER_BIT = 0x00000002; + public const int GL_GEOMETRY_SHADER_BIT = 0x00000004; + public const int GL_TESS_CONTROL_SHADER_BIT = 0x00000008; + public const int GL_TESS_EVALUATION_SHADER_BIT = 0x00000010; + public const int GL_ALL_SHADER_BITS = -1; + public const int GL_PROGRAM_SEPARABLE = 0x8258; + public const int GL_ACTIVE_PROGRAM = 0x8259; + public const int GL_PROGRAM_PIPELINE_BINDING = 0x825A; + public const int GL_MAX_VIEWPORTS = 0x825B; + public const int GL_VIEWPORT_SUBPIXEL_BITS = 0x825C; + public const int GL_VIEWPORT_BOUNDS_RANGE = 0x825D; + public const int GL_LAYER_PROVOKING_VERTEX = 0x825E; + public const int GL_VIEWPORT_INDEX_PROVOKING_VERTEX = 0x825F; + public const int GL_UNDEFINED_VERTEX = 0x8260; + public const int GL_VERSION_4_2 = 1; + public const int GL_COPY_READ_BUFFER_BINDING = 0x8F36; + public const int GL_COPY_WRITE_BUFFER_BINDING = 0x8F37; + public const int GL_TRANSFORM_FEEDBACK_ACTIVE = 0x8E24; + public const int GL_TRANSFORM_FEEDBACK_PAUSED = 0x8E23; + public const int GL_UNPACK_COMPRESSED_BLOCK_WIDTH = 0x9127; + public const int GL_UNPACK_COMPRESSED_BLOCK_HEIGHT = 0x9128; + public const int GL_UNPACK_COMPRESSED_BLOCK_DEPTH = 0x9129; + public const int GL_UNPACK_COMPRESSED_BLOCK_SIZE = 0x912A; + public const int GL_PACK_COMPRESSED_BLOCK_WIDTH = 0x912B; + public const int GL_PACK_COMPRESSED_BLOCK_HEIGHT = 0x912C; + public const int GL_PACK_COMPRESSED_BLOCK_DEPTH = 0x912D; + public const int GL_PACK_COMPRESSED_BLOCK_SIZE = 0x912E; + public const int GL_NUM_SAMPLE_COUNTS = 0x9380; + public const int GL_MIN_MAP_BUFFER_ALIGNMENT = 0x90BC; + public const int GL_ATOMIC_COUNTER_BUFFER = 0x92C0; + public const int GL_ATOMIC_COUNTER_BUFFER_BINDING = 0x92C1; + public const int GL_ATOMIC_COUNTER_BUFFER_START = 0x92C2; + public const int GL_ATOMIC_COUNTER_BUFFER_SIZE = 0x92C3; + public const int GL_ATOMIC_COUNTER_BUFFER_DATA_SIZE = 0x92C4; + public const int GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS = 0x92C5; + public const int GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTER_INDICES = 0x92C6; + public const int GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_VERTEX_SHADER = 0x92C7; + public const int GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_CONTROL_SHADER = 0x92C8; + public const int GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_EVALUATION_SHADER = 0x92C9; + public const int GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_GEOMETRY_SHADER = 0x92CA; + public const int GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_FRAGMENT_SHADER = 0x92CB; + public const int GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS = 0x92CC; + public const int GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS = 0x92CD; + public const int GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS = 0x92CE; + public const int GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS = 0x92CF; + public const int GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS = 0x92D0; + public const int GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS = 0x92D1; + public const int GL_MAX_VERTEX_ATOMIC_COUNTERS = 0x92D2; + public const int GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS = 0x92D3; + public const int GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS = 0x92D4; + public const int GL_MAX_GEOMETRY_ATOMIC_COUNTERS = 0x92D5; + public const int GL_MAX_FRAGMENT_ATOMIC_COUNTERS = 0x92D6; + public const int GL_MAX_COMBINED_ATOMIC_COUNTERS = 0x92D7; + public const int GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE = 0x92D8; + public const int GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS = 0x92DC; + public const int GL_ACTIVE_ATOMIC_COUNTER_BUFFERS = 0x92D9; + public const int GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX = 0x92DA; + public const int GL_UNSIGNED_INT_ATOMIC_COUNTER = 0x92DB; + public const int GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT = 0x00000001; + public const int GL_ELEMENT_ARRAY_BARRIER_BIT = 0x00000002; + public const int GL_UNIFORM_BARRIER_BIT = 0x00000004; + public const int GL_TEXTURE_FETCH_BARRIER_BIT = 0x00000008; + public const int GL_SHADER_IMAGE_ACCESS_BARRIER_BIT = 0x00000020; + public const int GL_COMMAND_BARRIER_BIT = 0x00000040; + public const int GL_PIXEL_BUFFER_BARRIER_BIT = 0x00000080; + public const int GL_TEXTURE_UPDATE_BARRIER_BIT = 0x00000100; + public const int GL_BUFFER_UPDATE_BARRIER_BIT = 0x00000200; + public const int GL_FRAMEBUFFER_BARRIER_BIT = 0x00000400; + public const int GL_TRANSFORM_FEEDBACK_BARRIER_BIT = 0x00000800; + public const int GL_ATOMIC_COUNTER_BARRIER_BIT = 0x00001000; + public const int GL_ALL_BARRIER_BITS = -1; + public const int GL_MAX_IMAGE_UNITS = 0x8F38; + public const int GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS = 0x8F39; + public const int GL_IMAGE_BINDING_NAME = 0x8F3A; + public const int GL_IMAGE_BINDING_LEVEL = 0x8F3B; + public const int GL_IMAGE_BINDING_LAYERED = 0x8F3C; + public const int GL_IMAGE_BINDING_LAYER = 0x8F3D; + public const int GL_IMAGE_BINDING_ACCESS = 0x8F3E; + public const int GL_IMAGE_1D = 0x904C; + public const int GL_IMAGE_2D = 0x904D; + public const int GL_IMAGE_3D = 0x904E; + public const int GL_IMAGE_2D_RECT = 0x904F; + public const int GL_IMAGE_CUBE = 0x9050; + public const int GL_IMAGE_BUFFER = 0x9051; + public const int GL_IMAGE_1D_ARRAY = 0x9052; + public const int GL_IMAGE_2D_ARRAY = 0x9053; + public const int GL_IMAGE_CUBE_MAP_ARRAY = 0x9054; + public const int GL_IMAGE_2D_MULTISAMPLE = 0x9055; + public const int GL_IMAGE_2D_MULTISAMPLE_ARRAY = 0x9056; + public const int GL_INT_IMAGE_1D = 0x9057; + public const int GL_INT_IMAGE_2D = 0x9058; + public const int GL_INT_IMAGE_3D = 0x9059; + public const int GL_INT_IMAGE_2D_RECT = 0x905A; + public const int GL_INT_IMAGE_CUBE = 0x905B; + public const int GL_INT_IMAGE_BUFFER = 0x905C; + public const int GL_INT_IMAGE_1D_ARRAY = 0x905D; + public const int GL_INT_IMAGE_2D_ARRAY = 0x905E; + public const int GL_INT_IMAGE_CUBE_MAP_ARRAY = 0x905F; + public const int GL_INT_IMAGE_2D_MULTISAMPLE = 0x9060; + public const int GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY = 0x9061; + public const int GL_UNSIGNED_INT_IMAGE_1D = 0x9062; + public const int GL_UNSIGNED_INT_IMAGE_2D = 0x9063; + public const int GL_UNSIGNED_INT_IMAGE_3D = 0x9064; + public const int GL_UNSIGNED_INT_IMAGE_2D_RECT = 0x9065; + public const int GL_UNSIGNED_INT_IMAGE_CUBE = 0x9066; + public const int GL_UNSIGNED_INT_IMAGE_BUFFER = 0x9067; + public const int GL_UNSIGNED_INT_IMAGE_1D_ARRAY = 0x9068; + public const int GL_UNSIGNED_INT_IMAGE_2D_ARRAY = 0x9069; + public const int GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY = 0x906A; + public const int GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE = 0x906B; + public const int GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY = 0x906C; + public const int GL_MAX_IMAGE_SAMPLES = 0x906D; + public const int GL_IMAGE_BINDING_FORMAT = 0x906E; + public const int GL_IMAGE_FORMAT_COMPATIBILITY_TYPE = 0x90C7; + public const int GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE = 0x90C8; + public const int GL_IMAGE_FORMAT_COMPATIBILITY_BY_CLASS = 0x90C9; + public const int GL_MAX_VERTEX_IMAGE_UNIFORMS = 0x90CA; + public const int GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS = 0x90CB; + public const int GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS = 0x90CC; + public const int GL_MAX_GEOMETRY_IMAGE_UNIFORMS = 0x90CD; + public const int GL_MAX_FRAGMENT_IMAGE_UNIFORMS = 0x90CE; + public const int GL_MAX_COMBINED_IMAGE_UNIFORMS = 0x90CF; + public const int GL_COMPRESSED_RGBA_BPTC_UNORM = 0x8E8C; + public const int GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM = 0x8E8D; + public const int GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT = 0x8E8E; + public const int GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT = 0x8E8F; + public const int GL_TEXTURE_IMMUTABLE_FORMAT = 0x912F; + public const int GL_VERSION_4_3 = 1; + public const int GL_NUM_SHADING_LANGUAGE_VERSIONS = 0x82E9; + public const int GL_VERTEX_ATTRIB_ARRAY_LONG = 0x874E; + public const int GL_COMPRESSED_RGB8_ETC2 = 0x9274; + public const int GL_COMPRESSED_SRGB8_ETC2 = 0x9275; + public const int GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9276; + public const int GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9277; + public const int GL_COMPRESSED_RGBA8_ETC2_EAC = 0x9278; + public const int GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC = 0x9279; + public const int GL_COMPRESSED_R11_EAC = 0x9270; + public const int GL_COMPRESSED_SIGNED_R11_EAC = 0x9271; + public const int GL_COMPRESSED_RG11_EAC = 0x9272; + public const int GL_COMPRESSED_SIGNED_RG11_EAC = 0x9273; + public const int GL_PRIMITIVE_RESTART_FIXED_INDEX = 0x8D69; + public const int GL_ANY_SAMPLES_PASSED_CONSERVATIVE = 0x8D6A; + public const int GL_MAX_ELEMENT_INDEX = 0x8D6B; + public const int GL_COMPUTE_SHADER = 0x91B9; + public const int GL_MAX_COMPUTE_UNIFORM_BLOCKS = 0x91BB; + public const int GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS = 0x91BC; + public const int GL_MAX_COMPUTE_IMAGE_UNIFORMS = 0x91BD; + public const int GL_MAX_COMPUTE_SHARED_MEMORY_SIZE = 0x8262; + public const int GL_MAX_COMPUTE_UNIFORM_COMPONENTS = 0x8263; + public const int GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS = 0x8264; + public const int GL_MAX_COMPUTE_ATOMIC_COUNTERS = 0x8265; + public const int GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS = 0x8266; + public const int GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS = 0x90EB; + public const int GL_MAX_COMPUTE_WORK_GROUP_COUNT = 0x91BE; + public const int GL_MAX_COMPUTE_WORK_GROUP_SIZE = 0x91BF; + public const int GL_COMPUTE_WORK_GROUP_SIZE = 0x8267; + public const int GL_UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER = 0x90EC; + public const int GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER = 0x90ED; + public const int GL_DISPATCH_INDIRECT_BUFFER = 0x90EE; + public const int GL_DISPATCH_INDIRECT_BUFFER_BINDING = 0x90EF; + public const int GL_COMPUTE_SHADER_BIT = 0x00000020; + public const int GL_DEBUG_OUTPUT_SYNCHRONOUS = 0x8242; + public const int GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH = 0x8243; + public const int GL_DEBUG_CALLBACK_FUNCTION = 0x8244; + public const int GL_DEBUG_CALLBACK_USER_PARAM = 0x8245; + public const int GL_DEBUG_SOURCE_API = 0x8246; + public const int GL_DEBUG_SOURCE_WINDOW_SYSTEM = 0x8247; + public const int GL_DEBUG_SOURCE_SHADER_COMPILER = 0x8248; + public const int GL_DEBUG_SOURCE_THIRD_PARTY = 0x8249; + public const int GL_DEBUG_SOURCE_APPLICATION = 0x824A; + public const int GL_DEBUG_SOURCE_OTHER = 0x824B; + public const int GL_DEBUG_TYPE_ERROR = 0x824C; + public const int GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR = 0x824D; + public const int GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR = 0x824E; + public const int GL_DEBUG_TYPE_PORTABILITY = 0x824F; + public const int GL_DEBUG_TYPE_PERFORMANCE = 0x8250; + public const int GL_DEBUG_TYPE_OTHER = 0x8251; + public const int GL_MAX_DEBUG_MESSAGE_LENGTH = 0x9143; + public const int GL_MAX_DEBUG_LOGGED_MESSAGES = 0x9144; + public const int GL_DEBUG_LOGGED_MESSAGES = 0x9145; + public const int GL_DEBUG_SEVERITY_HIGH = 0x9146; + public const int GL_DEBUG_SEVERITY_MEDIUM = 0x9147; + public const int GL_DEBUG_SEVERITY_LOW = 0x9148; + public const int GL_DEBUG_TYPE_MARKER = 0x8268; + public const int GL_DEBUG_TYPE_PUSH_GROUP = 0x8269; + public const int GL_DEBUG_TYPE_POP_GROUP = 0x826A; + public const int GL_DEBUG_SEVERITY_NOTIFICATION = 0x826B; + public const int GL_MAX_DEBUG_GROUP_STACK_DEPTH = 0x826C; + public const int GL_DEBUG_GROUP_STACK_DEPTH = 0x826D; + public const int GL_BUFFER = 0x82E0; + public const int GL_SHADER = 0x82E1; + public const int GL_PROGRAM = 0x82E2; + public const int GL_QUERY = 0x82E3; + public const int GL_PROGRAM_PIPELINE = 0x82E4; + public const int GL_SAMPLER = 0x82E6; + public const int GL_MAX_LABEL_LENGTH = 0x82E8; + public const int GL_DEBUG_OUTPUT = 0x92E0; + public const int GL_CONTEXT_FLAG_DEBUG_BIT = 0x00000002; + public const int GL_MAX_UNIFORM_LOCATIONS = 0x826E; + public const int GL_FRAMEBUFFER_DEFAULT_WIDTH = 0x9310; + public const int GL_FRAMEBUFFER_DEFAULT_HEIGHT = 0x9311; + public const int GL_FRAMEBUFFER_DEFAULT_LAYERS = 0x9312; + public const int GL_FRAMEBUFFER_DEFAULT_SAMPLES = 0x9313; + public const int GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS = 0x9314; + public const int GL_MAX_FRAMEBUFFER_WIDTH = 0x9315; + public const int GL_MAX_FRAMEBUFFER_HEIGHT = 0x9316; + public const int GL_MAX_FRAMEBUFFER_LAYERS = 0x9317; + public const int GL_MAX_FRAMEBUFFER_SAMPLES = 0x9318; + public const int GL_INTERNALFORMAT_SUPPORTED = 0x826F; + public const int GL_INTERNALFORMAT_PREFERRED = 0x8270; + public const int GL_INTERNALFORMAT_RED_SIZE = 0x8271; + public const int GL_INTERNALFORMAT_GREEN_SIZE = 0x8272; + public const int GL_INTERNALFORMAT_BLUE_SIZE = 0x8273; + public const int GL_INTERNALFORMAT_ALPHA_SIZE = 0x8274; + public const int GL_INTERNALFORMAT_DEPTH_SIZE = 0x8275; + public const int GL_INTERNALFORMAT_STENCIL_SIZE = 0x8276; + public const int GL_INTERNALFORMAT_SHARED_SIZE = 0x8277; + public const int GL_INTERNALFORMAT_RED_TYPE = 0x8278; + public const int GL_INTERNALFORMAT_GREEN_TYPE = 0x8279; + public const int GL_INTERNALFORMAT_BLUE_TYPE = 0x827A; + public const int GL_INTERNALFORMAT_ALPHA_TYPE = 0x827B; + public const int GL_INTERNALFORMAT_DEPTH_TYPE = 0x827C; + public const int GL_INTERNALFORMAT_STENCIL_TYPE = 0x827D; + public const int GL_MAX_WIDTH = 0x827E; + public const int GL_MAX_HEIGHT = 0x827F; + public const int GL_MAX_DEPTH = 0x8280; + public const int GL_MAX_LAYERS = 0x8281; + public const int GL_MAX_COMBINED_DIMENSIONS = 0x8282; + public const int GL_COLOR_COMPONENTS = 0x8283; + public const int GL_DEPTH_COMPONENTS = 0x8284; + public const int GL_STENCIL_COMPONENTS = 0x8285; + public const int GL_COLOR_RENDERABLE = 0x8286; + public const int GL_DEPTH_RENDERABLE = 0x8287; + public const int GL_STENCIL_RENDERABLE = 0x8288; + public const int GL_FRAMEBUFFER_RENDERABLE = 0x8289; + public const int GL_FRAMEBUFFER_RENDERABLE_LAYERED = 0x828A; + public const int GL_FRAMEBUFFER_BLEND = 0x828B; + public const int GL_READ_PIXELS = 0x828C; + public const int GL_READ_PIXELS_FORMAT = 0x828D; + public const int GL_READ_PIXELS_TYPE = 0x828E; + public const int GL_TEXTURE_IMAGE_FORMAT = 0x828F; + public const int GL_TEXTURE_IMAGE_TYPE = 0x8290; + public const int GL_GET_TEXTURE_IMAGE_FORMAT = 0x8291; + public const int GL_GET_TEXTURE_IMAGE_TYPE = 0x8292; + public const int GL_MIPMAP = 0x8293; + public const int GL_MANUAL_GENERATE_MIPMAP = 0x8294; + public const int GL_AUTO_GENERATE_MIPMAP = 0x8295; + public const int GL_COLOR_ENCODING = 0x8296; + public const int GL_SRGB_READ = 0x8297; + public const int GL_SRGB_WRITE = 0x8298; + public const int GL_FILTER = 0x829A; + public const int GL_VERTEX_TEXTURE = 0x829B; + public const int GL_TESS_CONTROL_TEXTURE = 0x829C; + public const int GL_TESS_EVALUATION_TEXTURE = 0x829D; + public const int GL_GEOMETRY_TEXTURE = 0x829E; + public const int GL_FRAGMENT_TEXTURE = 0x829F; + public const int GL_COMPUTE_TEXTURE = 0x82A0; + public const int GL_TEXTURE_SHADOW = 0x82A1; + public const int GL_TEXTURE_GATHER = 0x82A2; + public const int GL_TEXTURE_GATHER_SHADOW = 0x82A3; + public const int GL_SHADER_IMAGE_LOAD = 0x82A4; + public const int GL_SHADER_IMAGE_STORE = 0x82A5; + public const int GL_SHADER_IMAGE_ATOMIC = 0x82A6; + public const int GL_IMAGE_TEXEL_SIZE = 0x82A7; + public const int GL_IMAGE_COMPATIBILITY_CLASS = 0x82A8; + public const int GL_IMAGE_PIXEL_FORMAT = 0x82A9; + public const int GL_IMAGE_PIXEL_TYPE = 0x82AA; + public const int GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_TEST = 0x82AC; + public const int GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_TEST = 0x82AD; + public const int GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_WRITE = 0x82AE; + public const int GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_WRITE = 0x82AF; + public const int GL_TEXTURE_COMPRESSED_BLOCK_WIDTH = 0x82B1; + public const int GL_TEXTURE_COMPRESSED_BLOCK_HEIGHT = 0x82B2; + public const int GL_TEXTURE_COMPRESSED_BLOCK_SIZE = 0x82B3; + public const int GL_CLEAR_BUFFER = 0x82B4; + public const int GL_TEXTURE_VIEW = 0x82B5; + public const int GL_VIEW_COMPATIBILITY_CLASS = 0x82B6; + public const int GL_FULL_SUPPORT = 0x82B7; + public const int GL_CAVEAT_SUPPORT = 0x82B8; + public const int GL_IMAGE_CLASS_4_X_32 = 0x82B9; + public const int GL_IMAGE_CLASS_2_X_32 = 0x82BA; + public const int GL_IMAGE_CLASS_1_X_32 = 0x82BB; + public const int GL_IMAGE_CLASS_4_X_16 = 0x82BC; + public const int GL_IMAGE_CLASS_2_X_16 = 0x82BD; + public const int GL_IMAGE_CLASS_1_X_16 = 0x82BE; + public const int GL_IMAGE_CLASS_4_X_8 = 0x82BF; + public const int GL_IMAGE_CLASS_2_X_8 = 0x82C0; + public const int GL_IMAGE_CLASS_1_X_8 = 0x82C1; + public const int GL_IMAGE_CLASS_11_11_10 = 0x82C2; + public const int GL_IMAGE_CLASS_10_10_10_2 = 0x82C3; + public const int GL_VIEW_CLASS_128_BITS = 0x82C4; + public const int GL_VIEW_CLASS_96_BITS = 0x82C5; + public const int GL_VIEW_CLASS_64_BITS = 0x82C6; + public const int GL_VIEW_CLASS_48_BITS = 0x82C7; + public const int GL_VIEW_CLASS_32_BITS = 0x82C8; + public const int GL_VIEW_CLASS_24_BITS = 0x82C9; + public const int GL_VIEW_CLASS_16_BITS = 0x82CA; + public const int GL_VIEW_CLASS_8_BITS = 0x82CB; + public const int GL_VIEW_CLASS_S3TC_DXT1_RGB = 0x82CC; + public const int GL_VIEW_CLASS_S3TC_DXT1_RGBA = 0x82CD; + public const int GL_VIEW_CLASS_S3TC_DXT3_RGBA = 0x82CE; + public const int GL_VIEW_CLASS_S3TC_DXT5_RGBA = 0x82CF; + public const int GL_VIEW_CLASS_RGTC1_RED = 0x82D0; + public const int GL_VIEW_CLASS_RGTC2_RG = 0x82D1; + public const int GL_VIEW_CLASS_BPTC_UNORM = 0x82D2; + public const int GL_VIEW_CLASS_BPTC_FLOAT = 0x82D3; + public const int GL_UNIFORM = 0x92E1; + public const int GL_UNIFORM_BLOCK = 0x92E2; + public const int GL_PROGRAM_INPUT = 0x92E3; + public const int GL_PROGRAM_OUTPUT = 0x92E4; + public const int GL_BUFFER_VARIABLE = 0x92E5; + public const int GL_SHADER_STORAGE_BLOCK = 0x92E6; + public const int GL_VERTEX_SUBROUTINE = 0x92E8; + public const int GL_TESS_CONTROL_SUBROUTINE = 0x92E9; + public const int GL_TESS_EVALUATION_SUBROUTINE = 0x92EA; + public const int GL_GEOMETRY_SUBROUTINE = 0x92EB; + public const int GL_FRAGMENT_SUBROUTINE = 0x92EC; + public const int GL_COMPUTE_SUBROUTINE = 0x92ED; + public const int GL_VERTEX_SUBROUTINE_UNIFORM = 0x92EE; + public const int GL_TESS_CONTROL_SUBROUTINE_UNIFORM = 0x92EF; + public const int GL_TESS_EVALUATION_SUBROUTINE_UNIFORM = 0x92F0; + public const int GL_GEOMETRY_SUBROUTINE_UNIFORM = 0x92F1; + public const int GL_FRAGMENT_SUBROUTINE_UNIFORM = 0x92F2; + public const int GL_COMPUTE_SUBROUTINE_UNIFORM = 0x92F3; + public const int GL_TRANSFORM_FEEDBACK_VARYING = 0x92F4; + public const int GL_ACTIVE_RESOURCES = 0x92F5; + public const int GL_MAX_NAME_LENGTH = 0x92F6; + public const int GL_MAX_NUM_ACTIVE_VARIABLES = 0x92F7; + public const int GL_MAX_NUM_COMPATIBLE_SUBROUTINES = 0x92F8; + public const int GL_NAME_LENGTH = 0x92F9; + public const int GL_TYPE = 0x92FA; + public const int GL_ARRAY_SIZE = 0x92FB; + public const int GL_OFFSET = 0x92FC; + public const int GL_BLOCK_INDEX = 0x92FD; + public const int GL_ARRAY_STRIDE = 0x92FE; + public const int GL_MATRIX_STRIDE = 0x92FF; + public const int GL_IS_ROW_MAJOR = 0x9300; + public const int GL_ATOMIC_COUNTER_BUFFER_INDEX = 0x9301; + public const int GL_BUFFER_BINDING = 0x9302; + public const int GL_BUFFER_DATA_SIZE = 0x9303; + public const int GL_NUM_ACTIVE_VARIABLES = 0x9304; + public const int GL_ACTIVE_VARIABLES = 0x9305; + public const int GL_REFERENCED_BY_VERTEX_SHADER = 0x9306; + public const int GL_REFERENCED_BY_TESS_CONTROL_SHADER = 0x9307; + public const int GL_REFERENCED_BY_TESS_EVALUATION_SHADER = 0x9308; + public const int GL_REFERENCED_BY_GEOMETRY_SHADER = 0x9309; + public const int GL_REFERENCED_BY_FRAGMENT_SHADER = 0x930A; + public const int GL_REFERENCED_BY_COMPUTE_SHADER = 0x930B; + public const int GL_TOP_LEVEL_ARRAY_SIZE = 0x930C; + public const int GL_TOP_LEVEL_ARRAY_STRIDE = 0x930D; + public const int GL_LOCATION = 0x930E; + public const int GL_LOCATION_INDEX = 0x930F; + public const int GL_IS_PER_PATCH = 0x92E7; + public const int GL_SHADER_STORAGE_BUFFER = 0x90D2; + public const int GL_SHADER_STORAGE_BUFFER_BINDING = 0x90D3; + public const int GL_SHADER_STORAGE_BUFFER_START = 0x90D4; + public const int GL_SHADER_STORAGE_BUFFER_SIZE = 0x90D5; + public const int GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS = 0x90D6; + public const int GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS = 0x90D7; + public const int GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS = 0x90D8; + public const int GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS = 0x90D9; + public const int GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS = 0x90DA; + public const int GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS = 0x90DB; + public const int GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS = 0x90DC; + public const int GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS = 0x90DD; + public const int GL_MAX_SHADER_STORAGE_BLOCK_SIZE = 0x90DE; + public const int GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT = 0x90DF; + public const int GL_SHADER_STORAGE_BARRIER_BIT = 0x00002000; + public const int GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES = 0x8F39; + public const int GL_DEPTH_STENCIL_TEXTURE_MODE = 0x90EA; + public const int GL_TEXTURE_BUFFER_OFFSET = 0x919D; + public const int GL_TEXTURE_BUFFER_SIZE = 0x919E; + public const int GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT = 0x919F; + public const int GL_TEXTURE_VIEW_MIN_LEVEL = 0x82DB; + public const int GL_TEXTURE_VIEW_NUM_LEVELS = 0x82DC; + public const int GL_TEXTURE_VIEW_MIN_LAYER = 0x82DD; + public const int GL_TEXTURE_VIEW_NUM_LAYERS = 0x82DE; + public const int GL_TEXTURE_IMMUTABLE_LEVELS = 0x82DF; + public const int GL_VERTEX_ATTRIB_BINDING = 0x82D4; + public const int GL_VERTEX_ATTRIB_RELATIVE_OFFSET = 0x82D5; + public const int GL_VERTEX_BINDING_DIVISOR = 0x82D6; + public const int GL_VERTEX_BINDING_OFFSET = 0x82D7; + public const int GL_VERTEX_BINDING_STRIDE = 0x82D8; + public const int GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET = 0x82D9; + public const int GL_MAX_VERTEX_ATTRIB_BINDINGS = 0x82DA; + public const int GL_VERTEX_BINDING_BUFFER = 0x8F4F; + public const int GL_DISPLAY_LIST = 0x82E7; + public const int GL_VERSION_4_4 = 1; + public const int GL_MAX_VERTEX_ATTRIB_STRIDE = 0x82E5; + public const int GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED = 0x8221; + public const int GL_TEXTURE_BUFFER_BINDING = 0x8C2A; + public const int GL_MAP_PERSISTENT_BIT = 0x0040; + public const int GL_MAP_COHERENT_BIT = 0x0080; + public const int GL_DYNAMIC_STORAGE_BIT = 0x0100; + public const int GL_CLIENT_STORAGE_BIT = 0x0200; + public const int GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT = 0x00004000; + public const int GL_BUFFER_IMMUTABLE_STORAGE = 0x821F; + public const int GL_BUFFER_STORAGE_FLAGS = 0x8220; + public const int GL_CLEAR_TEXTURE = 0x9365; + public const int GL_LOCATION_COMPONENT = 0x934A; + public const int GL_TRANSFORM_FEEDBACK_BUFFER_INDEX = 0x934B; + public const int GL_TRANSFORM_FEEDBACK_BUFFER_STRIDE = 0x934C; + public const int GL_QUERY_BUFFER = 0x9192; + public const int GL_QUERY_BUFFER_BARRIER_BIT = 0x00008000; + public const int GL_QUERY_BUFFER_BINDING = 0x9193; + public const int GL_QUERY_RESULT_NO_WAIT = 0x9194; + public const int GL_MIRROR_CLAMP_TO_EDGE = 0x8743; + public const int GL_NEGATIVE_ONE_TO_ONE = 0x935E; + public const int GL_ZERO_TO_ONE = 0x935F; + public const int GL_CLIP_ORIGIN = 0x935C; + public const int GL_CLIP_DEPTH_MODE = 0x935D; + public const int GL_QUERY_WAIT_INVERTED = 0x8E17; + public const int GL_QUERY_NO_WAIT_INVERTED = 0x8E18; + public const int GL_QUERY_BY_REGION_WAIT_INVERTED = 0x8E19; + public const int GL_QUERY_BY_REGION_NO_WAIT_INVERTED = 0x8E1A; + public const int GL_MAX_CULL_DISTANCES = 0x82F9; + public const int GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES = 0x82FA; + public const int GL_TEXTURE_TARGET = 0x1006; + public const int GL_QUERY_TARGET = 0x82EA; + public const int GL_GUILTY_CONTEXT_RESET = 0x8253; + public const int GL_INNOCENT_CONTEXT_RESET = 0x8254; + public const int GL_UNKNOWN_CONTEXT_RESET = 0x8255; + public const int GL_RESET_NOTIFICATION_STRATEGY = 0x8256; + public const int GL_LOSE_CONTEXT_ON_RESET = 0x8252; + public const int GL_NO_RESET_NOTIFICATION = 0x8261; + public const int GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT = 0x00000004; + public const int GL_CONTEXT_RELEASE_BEHAVIOR = 0x82FB; + public const int GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH = 0x82FC; + public const int GL_VERSION_4_6 = 1; + public const int GL_SHADER_BINARY_FORMAT_SPIR_V = 0x9551; + public const int GL_SPIR_V_BINARY = 0x9552; + public const int GL_PARAMETER_BUFFER = 0x80EE; + public const int GL_PARAMETER_BUFFER_BINDING = 0x80EF; + public const int GL_CONTEXT_FLAG_NO_ERROR_BIT = 0x00000008; + public const int GL_VERTICES_SUBMITTED = 0x82EE; + public const int GL_PRIMITIVES_SUBMITTED = 0x82EF; + public const int GL_VERTEX_SHADER_INVOCATIONS = 0x82F0; + public const int GL_TESS_CONTROL_SHADER_PATCHES = 0x82F1; + public const int GL_TESS_EVALUATION_SHADER_INVOCATIONS = 0x82F2; + public const int GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED = 0x82F3; + public const int GL_FRAGMENT_SHADER_INVOCATIONS = 0x82F4; + public const int GL_COMPUTE_SHADER_INVOCATIONS = 0x82F5; + public const int GL_CLIPPING_INPUT_PRIMITIVES = 0x82F6; + public const int GL_CLIPPING_OUTPUT_PRIMITIVES = 0x82F7; + public const int GL_POLYGON_OFFSET_CLAMP = 0x8E1B; + public const int GL_SPIR_V_EXTENSIONS = 0x9553; + public const int GL_NUM_SPIR_V_EXTENSIONS = 0x9554; + public const int GL_TEXTURE_MAX_ANISOTROPY = 0x84FE; + public const int GL_MAX_TEXTURE_MAX_ANISOTROPY = 0x84FF; + public const int GL_TRANSFORM_FEEDBACK_OVERFLOW = 0x82EC; + public const int GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW = 0x82ED; + public const int GL_ARB_ES2_compatibility = 1; + public const int GL_ARB_ES3_1_compatibility = 1; + public const int GL_ARB_ES3_2_compatibility = 1; + public const int GL_PRIMITIVE_BOUNDING_BOX_ARB = 0x92BE; + public const int GL_MULTISAMPLE_LINE_WIDTH_RANGE_ARB = 0x9381; + public const int GL_MULTISAMPLE_LINE_WIDTH_GRANULARITY_ARB = 0x9382; + public const int GL_ARB_ES3_compatibility = 1; + public const int GL_ARB_arrays_of_arrays = 1; + public const int GL_ARB_base_instance = 1; + public const int GL_ARB_bindless_texture = 1; + public const int GL_UNSIGNED_INT64_ARB = 0x140F; + public const int GL_ARB_blend_func_extended = 1; + public const int GL_ARB_buffer_storage = 1; + public const int GL_ARB_cl_event = 1; + public const int GL_SYNC_CL_EVENT_ARB = 0x8240; + public const int GL_SYNC_CL_EVENT_COMPLETE_ARB = 0x8241; + public const int GL_ARB_clear_buffer_object = 1; + public const int GL_ARB_clear_texture = 1; + public const int GL_ARB_clip_control = 1; + public const int GL_ARB_color_buffer_float = 1; + public const int GL_RGBA_FLOAT_MODE_ARB = 0x8820; + public const int GL_CLAMP_VERTEX_COLOR_ARB = 0x891A; + public const int GL_CLAMP_FRAGMENT_COLOR_ARB = 0x891B; + public const int GL_CLAMP_READ_COLOR_ARB = 0x891C; + public const int GL_FIXED_ONLY_ARB = 0x891D; + public const int GL_ARB_compatibility = 1; + public const int GL_ARB_compressed_texture_pixel_storage = 1; + public const int GL_ARB_compute_shader = 1; + public const int GL_ARB_compute_variable_group_size = 1; + public const int GL_MAX_COMPUTE_VARIABLE_GROUP_INVOCATIONS_ARB = 0x9344; + public const int GL_MAX_COMPUTE_FIXED_GROUP_INVOCATIONS_ARB = 0x90EB; + public const int GL_MAX_COMPUTE_VARIABLE_GROUP_SIZE_ARB = 0x9345; + public const int GL_MAX_COMPUTE_FIXED_GROUP_SIZE_ARB = 0x91BF; + public const int GL_ARB_conditional_render_inverted = 1; + public const int GL_ARB_conservative_depth = 1; + public const int GL_ARB_copy_buffer = 1; + public const int GL_ARB_copy_image = 1; + public const int GL_ARB_cull_distance = 1; + public const int GL_ARB_debug_output = 1; + public const int GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB = 0x8242; + public const int GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB = 0x8243; + public const int GL_DEBUG_CALLBACK_FUNCTION_ARB = 0x8244; + public const int GL_DEBUG_CALLBACK_USER_PARAM_ARB = 0x8245; + public const int GL_DEBUG_SOURCE_API_ARB = 0x8246; + public const int GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB = 0x8247; + public const int GL_DEBUG_SOURCE_SHADER_COMPILER_ARB = 0x8248; + public const int GL_DEBUG_SOURCE_THIRD_PARTY_ARB = 0x8249; + public const int GL_DEBUG_SOURCE_APPLICATION_ARB = 0x824A; + public const int GL_DEBUG_SOURCE_OTHER_ARB = 0x824B; + public const int GL_DEBUG_TYPE_ERROR_ARB = 0x824C; + public const int GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB = 0x824D; + public const int GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB = 0x824E; + public const int GL_DEBUG_TYPE_PORTABILITY_ARB = 0x824F; + public const int GL_DEBUG_TYPE_PERFORMANCE_ARB = 0x8250; + public const int GL_DEBUG_TYPE_OTHER_ARB = 0x8251; + public const int GL_MAX_DEBUG_MESSAGE_LENGTH_ARB = 0x9143; + public const int GL_MAX_DEBUG_LOGGED_MESSAGES_ARB = 0x9144; + public const int GL_DEBUG_LOGGED_MESSAGES_ARB = 0x9145; + public const int GL_DEBUG_SEVERITY_HIGH_ARB = 0x9146; + public const int GL_DEBUG_SEVERITY_MEDIUM_ARB = 0x9147; + public const int GL_DEBUG_SEVERITY_LOW_ARB = 0x9148; + public const int GL_ARB_depth_buffer_float = 1; + public const int GL_ARB_depth_clamp = 1; + public const int GL_ARB_depth_texture = 1; + public const int GL_DEPTH_COMPONENT16_ARB = 0x81A5; + public const int GL_DEPTH_COMPONENT24_ARB = 0x81A6; + public const int GL_DEPTH_COMPONENT32_ARB = 0x81A7; + public const int GL_TEXTURE_DEPTH_SIZE_ARB = 0x884A; + public const int GL_DEPTH_TEXTURE_MODE_ARB = 0x884B; + public const int GL_ARB_derivative_control = 1; + public const int GL_ARB_direct_state_access = 1; + public const int GL_ARB_draw_buffers = 1; + public const int GL_MAX_DRAW_BUFFERS_ARB = 0x8824; + public const int GL_DRAW_BUFFER0_ARB = 0x8825; + public const int GL_DRAW_BUFFER1_ARB = 0x8826; + public const int GL_DRAW_BUFFER2_ARB = 0x8827; + public const int GL_DRAW_BUFFER3_ARB = 0x8828; + public const int GL_DRAW_BUFFER4_ARB = 0x8829; + public const int GL_DRAW_BUFFER5_ARB = 0x882A; + public const int GL_DRAW_BUFFER6_ARB = 0x882B; + public const int GL_DRAW_BUFFER7_ARB = 0x882C; + public const int GL_DRAW_BUFFER8_ARB = 0x882D; + public const int GL_DRAW_BUFFER9_ARB = 0x882E; + public const int GL_DRAW_BUFFER10_ARB = 0x882F; + public const int GL_DRAW_BUFFER11_ARB = 0x8830; + public const int GL_DRAW_BUFFER12_ARB = 0x8831; + public const int GL_DRAW_BUFFER13_ARB = 0x8832; + public const int GL_DRAW_BUFFER14_ARB = 0x8833; + public const int GL_DRAW_BUFFER15_ARB = 0x8834; + public const int GL_ARB_draw_buffers_blend = 1; + public const int GL_ARB_draw_elements_base_vertex = 1; + public const int GL_ARB_draw_indirect = 1; + public const int GL_ARB_draw_instanced = 1; + public const int GL_ARB_enhanced_layouts = 1; + public const int GL_ARB_explicit_attrib_location = 1; + public const int GL_ARB_explicit_uniform_location = 1; + public const int GL_ARB_fragment_coord_conventions = 1; + public const int GL_ARB_fragment_layer_viewport = 1; + public const int GL_ARB_fragment_program = 1; + public const int GL_FRAGMENT_PROGRAM_ARB = 0x8804; + public const int GL_PROGRAM_FORMAT_ASCII_ARB = 0x8875; + public const int GL_PROGRAM_LENGTH_ARB = 0x8627; + public const int GL_PROGRAM_FORMAT_ARB = 0x8876; + public const int GL_PROGRAM_BINDING_ARB = 0x8677; + public const int GL_PROGRAM_INSTRUCTIONS_ARB = 0x88A0; + public const int GL_MAX_PROGRAM_INSTRUCTIONS_ARB = 0x88A1; + public const int GL_PROGRAM_NATIVE_INSTRUCTIONS_ARB = 0x88A2; + public const int GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB = 0x88A3; + public const int GL_PROGRAM_TEMPORARIES_ARB = 0x88A4; + public const int GL_MAX_PROGRAM_TEMPORARIES_ARB = 0x88A5; + public const int GL_PROGRAM_NATIVE_TEMPORARIES_ARB = 0x88A6; + public const int GL_MAX_PROGRAM_NATIVE_TEMPORARIES_ARB = 0x88A7; + public const int GL_PROGRAM_PARAMETERS_ARB = 0x88A8; + public const int GL_MAX_PROGRAM_PARAMETERS_ARB = 0x88A9; + public const int GL_PROGRAM_NATIVE_PARAMETERS_ARB = 0x88AA; + public const int GL_MAX_PROGRAM_NATIVE_PARAMETERS_ARB = 0x88AB; + public const int GL_PROGRAM_ATTRIBS_ARB = 0x88AC; + public const int GL_MAX_PROGRAM_ATTRIBS_ARB = 0x88AD; + public const int GL_PROGRAM_NATIVE_ATTRIBS_ARB = 0x88AE; + public const int GL_MAX_PROGRAM_NATIVE_ATTRIBS_ARB = 0x88AF; + public const int GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB = 0x88B4; + public const int GL_MAX_PROGRAM_ENV_PARAMETERS_ARB = 0x88B5; + public const int GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB = 0x88B6; + public const int GL_PROGRAM_ALU_INSTRUCTIONS_ARB = 0x8805; + public const int GL_PROGRAM_TEX_INSTRUCTIONS_ARB = 0x8806; + public const int GL_PROGRAM_TEX_INDIRECTIONS_ARB = 0x8807; + public const int GL_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB = 0x8808; + public const int GL_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB = 0x8809; + public const int GL_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB = 0x880A; + public const int GL_MAX_PROGRAM_ALU_INSTRUCTIONS_ARB = 0x880B; + public const int GL_MAX_PROGRAM_TEX_INSTRUCTIONS_ARB = 0x880C; + public const int GL_MAX_PROGRAM_TEX_INDIRECTIONS_ARB = 0x880D; + public const int GL_MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB = 0x880E; + public const int GL_MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB = 0x880F; + public const int GL_MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB = 0x8810; + public const int GL_PROGRAM_STRING_ARB = 0x8628; + public const int GL_PROGRAM_ERROR_POSITION_ARB = 0x864B; + public const int GL_CURRENT_MATRIX_ARB = 0x8641; + public const int GL_TRANSPOSE_CURRENT_MATRIX_ARB = 0x88B7; + public const int GL_CURRENT_MATRIX_STACK_DEPTH_ARB = 0x8640; + public const int GL_MAX_PROGRAM_MATRICES_ARB = 0x862F; + public const int GL_MAX_PROGRAM_MATRIX_STACK_DEPTH_ARB = 0x862E; + public const int GL_MAX_TEXTURE_COORDS_ARB = 0x8871; + public const int GL_MAX_TEXTURE_IMAGE_UNITS_ARB = 0x8872; + public const int GL_PROGRAM_ERROR_STRING_ARB = 0x8874; + public const int GL_MATRIX0_ARB = 0x88C0; + public const int GL_MATRIX1_ARB = 0x88C1; + public const int GL_MATRIX2_ARB = 0x88C2; + public const int GL_MATRIX3_ARB = 0x88C3; + public const int GL_MATRIX4_ARB = 0x88C4; + public const int GL_MATRIX5_ARB = 0x88C5; + public const int GL_MATRIX6_ARB = 0x88C6; + public const int GL_MATRIX7_ARB = 0x88C7; + public const int GL_MATRIX8_ARB = 0x88C8; + public const int GL_MATRIX9_ARB = 0x88C9; + public const int GL_MATRIX10_ARB = 0x88CA; + public const int GL_MATRIX11_ARB = 0x88CB; + public const int GL_MATRIX12_ARB = 0x88CC; + public const int GL_MATRIX13_ARB = 0x88CD; + public const int GL_MATRIX14_ARB = 0x88CE; + public const int GL_MATRIX15_ARB = 0x88CF; + public const int GL_MATRIX16_ARB = 0x88D0; + public const int GL_MATRIX17_ARB = 0x88D1; + public const int GL_MATRIX18_ARB = 0x88D2; + public const int GL_MATRIX19_ARB = 0x88D3; + public const int GL_MATRIX20_ARB = 0x88D4; + public const int GL_MATRIX21_ARB = 0x88D5; + public const int GL_MATRIX22_ARB = 0x88D6; + public const int GL_MATRIX23_ARB = 0x88D7; + public const int GL_MATRIX24_ARB = 0x88D8; + public const int GL_MATRIX25_ARB = 0x88D9; + public const int GL_MATRIX26_ARB = 0x88DA; + public const int GL_MATRIX27_ARB = 0x88DB; + public const int GL_MATRIX28_ARB = 0x88DC; + public const int GL_MATRIX29_ARB = 0x88DD; + public const int GL_MATRIX30_ARB = 0x88DE; + public const int GL_MATRIX31_ARB = 0x88DF; + public const int GL_ARB_fragment_program_shadow = 1; + public const int GL_ARB_fragment_shader = 1; + public const int GL_FRAGMENT_SHADER_ARB = 0x8B30; + public const int GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB = 0x8B49; + public const int GL_FRAGMENT_SHADER_DERIVATIVE_HINT_ARB = 0x8B8B; + public const int GL_ARB_fragment_shader_interlock = 1; + public const int GL_ARB_framebuffer_no_attachments = 1; + public const int GL_ARB_framebuffer_object = 1; + public const int GL_ARB_framebuffer_sRGB = 1; + public const int GL_ARB_geometry_shader4 = 1; + public const int GL_LINES_ADJACENCY_ARB = 0x000A; + public const int GL_LINE_STRIP_ADJACENCY_ARB = 0x000B; + public const int GL_TRIANGLES_ADJACENCY_ARB = 0x000C; + public const int GL_TRIANGLE_STRIP_ADJACENCY_ARB = 0x000D; + public const int GL_PROGRAM_POINT_SIZE_ARB = 0x8642; + public const int GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_ARB = 0x8C29; + public const int GL_FRAMEBUFFER_ATTACHMENT_LAYERED_ARB = 0x8DA7; + public const int GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_ARB = 0x8DA8; + public const int GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_ARB = 0x8DA9; + public const int GL_GEOMETRY_SHADER_ARB = 0x8DD9; + public const int GL_GEOMETRY_VERTICES_OUT_ARB = 0x8DDA; + public const int GL_GEOMETRY_INPUT_TYPE_ARB = 0x8DDB; + public const int GL_GEOMETRY_OUTPUT_TYPE_ARB = 0x8DDC; + public const int GL_MAX_GEOMETRY_VARYING_COMPONENTS_ARB = 0x8DDD; + public const int GL_MAX_VERTEX_VARYING_COMPONENTS_ARB = 0x8DDE; + public const int GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_ARB = 0x8DDF; + public const int GL_MAX_GEOMETRY_OUTPUT_VERTICES_ARB = 0x8DE0; + public const int GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_ARB = 0x8DE1; + public const int GL_ARB_get_program_binary = 1; + public const int GL_ARB_get_texture_sub_image = 1; + public const int GL_ARB_gl_spirv = 1; + public const int GL_SHADER_BINARY_FORMAT_SPIR_V_ARB = 0x9551; + public const int GL_SPIR_V_BINARY_ARB = 0x9552; + public const int GL_ARB_gpu_shader5 = 1; + public const int GL_ARB_gpu_shader_fp64 = 1; + public const int GL_ARB_gpu_shader_int64 = 1; + public const int GL_INT64_ARB = 0x140E; + public const int GL_INT64_VEC2_ARB = 0x8FE9; + public const int GL_INT64_VEC3_ARB = 0x8FEA; + public const int GL_INT64_VEC4_ARB = 0x8FEB; + public const int GL_UNSIGNED_INT64_VEC2_ARB = 0x8FF5; + public const int GL_UNSIGNED_INT64_VEC3_ARB = 0x8FF6; + public const int GL_UNSIGNED_INT64_VEC4_ARB = 0x8FF7; + public const int GL_ARB_half_float_pixel = 1; + public const int GL_HALF_FLOAT_ARB = 0x140B; + public const int GL_ARB_half_float_vertex = 1; + public const int GL_ARB_imaging = 1; + public const int GL_ARB_indirect_parameters = 1; + public const int GL_PARAMETER_BUFFER_ARB = 0x80EE; + public const int GL_PARAMETER_BUFFER_BINDING_ARB = 0x80EF; + public const int GL_ARB_instanced_arrays = 1; + public const int GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ARB = 0x88FE; + public const int GL_ARB_internalformat_query = 1; + public const int GL_ARB_internalformat_query2 = 1; + public const int GL_SRGB_DECODE_ARB = 0x8299; + public const int GL_ARB_invalidate_subdata = 1; + public const int GL_ARB_map_buffer_alignment = 1; + public const int GL_ARB_map_buffer_range = 1; + public const int GL_ARB_matrix_palette = 1; + public const int GL_MATRIX_PALETTE_ARB = 0x8840; + public const int GL_MAX_MATRIX_PALETTE_STACK_DEPTH_ARB = 0x8841; + public const int GL_MAX_PALETTE_MATRICES_ARB = 0x8842; + public const int GL_CURRENT_PALETTE_MATRIX_ARB = 0x8843; + public const int GL_MATRIX_INDEX_ARRAY_ARB = 0x8844; + public const int GL_CURRENT_MATRIX_INDEX_ARB = 0x8845; + public const int GL_MATRIX_INDEX_ARRAY_SIZE_ARB = 0x8846; + public const int GL_MATRIX_INDEX_ARRAY_TYPE_ARB = 0x8847; + public const int GL_MATRIX_INDEX_ARRAY_STRIDE_ARB = 0x8848; + public const int GL_MATRIX_INDEX_ARRAY_POINTER_ARB = 0x8849; + public const int GL_ARB_multi_bind = 1; + public const int GL_ARB_multi_draw_indirect = 1; + public const int GL_ARB_multisample = 1; + public const int GL_MULTISAMPLE_ARB = 0x809D; + public const int GL_SAMPLE_ALPHA_TO_COVERAGE_ARB = 0x809E; + public const int GL_SAMPLE_ALPHA_TO_ONE_ARB = 0x809F; + public const int GL_SAMPLE_COVERAGE_ARB = 0x80A0; + public const int GL_SAMPLE_BUFFERS_ARB = 0x80A8; + public const int GL_SAMPLES_ARB = 0x80A9; + public const int GL_SAMPLE_COVERAGE_VALUE_ARB = 0x80AA; + public const int GL_SAMPLE_COVERAGE_INVERT_ARB = 0x80AB; + public const int GL_MULTISAMPLE_BIT_ARB = 0x20000000; + public const int GL_ARB_occlusion_query = 1; + public const int GL_QUERY_COUNTER_BITS_ARB = 0x8864; + public const int GL_CURRENT_QUERY_ARB = 0x8865; + public const int GL_QUERY_RESULT_ARB = 0x8866; + public const int GL_QUERY_RESULT_AVAILABLE_ARB = 0x8867; + public const int GL_SAMPLES_PASSED_ARB = 0x8914; + public const int GL_ARB_occlusion_query2 = 1; + public const int GL_ARB_parallel_shader_compile = 1; + public const int GL_MAX_SHADER_COMPILER_THREADS_ARB = 0x91B0; + public const int GL_COMPLETION_STATUS_ARB = 0x91B1; + public const int GL_ARB_pipeline_statistics_query = 1; + public const int GL_VERTICES_SUBMITTED_ARB = 0x82EE; + public const int GL_PRIMITIVES_SUBMITTED_ARB = 0x82EF; + public const int GL_VERTEX_SHADER_INVOCATIONS_ARB = 0x82F0; + public const int GL_TESS_CONTROL_SHADER_PATCHES_ARB = 0x82F1; + public const int GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB = 0x82F2; + public const int GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB = 0x82F3; + public const int GL_FRAGMENT_SHADER_INVOCATIONS_ARB = 0x82F4; + public const int GL_COMPUTE_SHADER_INVOCATIONS_ARB = 0x82F5; + public const int GL_CLIPPING_INPUT_PRIMITIVES_ARB = 0x82F6; + public const int GL_CLIPPING_OUTPUT_PRIMITIVES_ARB = 0x82F7; + public const int GL_ARB_pixel_buffer_object = 1; + public const int GL_PIXEL_PACK_BUFFER_ARB = 0x88EB; + public const int GL_PIXEL_UNPACK_BUFFER_ARB = 0x88EC; + public const int GL_PIXEL_PACK_BUFFER_BINDING_ARB = 0x88ED; + public const int GL_PIXEL_UNPACK_BUFFER_BINDING_ARB = 0x88EF; + public const int GL_ARB_point_parameters = 1; + public const int GL_POINT_SIZE_MIN_ARB = 0x8126; + public const int GL_POINT_SIZE_MAX_ARB = 0x8127; + public const int GL_POINT_FADE_THRESHOLD_SIZE_ARB = 0x8128; + public const int GL_POINT_DISTANCE_ATTENUATION_ARB = 0x8129; + public const int GL_ARB_point_sprite = 1; + public const int GL_POINT_SPRITE_ARB = 0x8861; + public const int GL_COORD_REPLACE_ARB = 0x8862; + public const int GL_ARB_polygon_offset_clamp = 1; + public const int GL_ARB_post_depth_coverage = 1; + public const int GL_ARB_program_interface_query = 1; + public const int GL_ARB_provoking_vertex = 1; + public const int GL_ARB_query_buffer_object = 1; + public const int GL_ARB_robust_buffer_access_behavior = 1; + public const int GL_ARB_robustness = 1; + public const int GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB = 0x00000004; + public const int GL_LOSE_CONTEXT_ON_RESET_ARB = 0x8252; + public const int GL_GUILTY_CONTEXT_RESET_ARB = 0x8253; + public const int GL_INNOCENT_CONTEXT_RESET_ARB = 0x8254; + public const int GL_UNKNOWN_CONTEXT_RESET_ARB = 0x8255; + public const int GL_RESET_NOTIFICATION_STRATEGY_ARB = 0x8256; + public const int GL_NO_RESET_NOTIFICATION_ARB = 0x8261; + public const int GL_ARB_robustness_isolation = 1; + public const int GL_ARB_sample_locations = 1; + public const int GL_SAMPLE_LOCATION_SUBPIXEL_BITS_ARB = 0x933D; + public const int GL_SAMPLE_LOCATION_PIXEL_GRID_WIDTH_ARB = 0x933E; + public const int GL_SAMPLE_LOCATION_PIXEL_GRID_HEIGHT_ARB = 0x933F; + public const int GL_PROGRAMMABLE_SAMPLE_LOCATION_TABLE_SIZE_ARB = 0x9340; + public const int GL_SAMPLE_LOCATION_ARB = 0x8E50; + public const int GL_PROGRAMMABLE_SAMPLE_LOCATION_ARB = 0x9341; + public const int GL_FRAMEBUFFER_PROGRAMMABLE_SAMPLE_LOCATIONS_ARB = 0x9342; + public const int GL_FRAMEBUFFER_SAMPLE_LOCATION_PIXEL_GRID_ARB = 0x9343; + public const int GL_ARB_sample_shading = 1; + public const int GL_SAMPLE_SHADING_ARB = 0x8C36; + public const int GL_MIN_SAMPLE_SHADING_VALUE_ARB = 0x8C37; + public const int GL_ARB_sampler_objects = 1; + public const int GL_ARB_seamless_cube_map = 1; + public const int GL_ARB_seamless_cubemap_per_texture = 1; + public const int GL_ARB_separate_shader_objects = 1; + public const int GL_ARB_shader_atomic_counter_ops = 1; + public const int GL_ARB_shader_atomic_counters = 1; + public const int GL_ARB_shader_ballot = 1; + public const int GL_ARB_shader_bit_encoding = 1; + public const int GL_ARB_shader_clock = 1; + public const int GL_ARB_shader_draw_parameters = 1; + public const int GL_ARB_shader_group_vote = 1; + public const int GL_ARB_shader_image_load_store = 1; + public const int GL_ARB_shader_image_size = 1; + public const int GL_ARB_shader_objects = 1; + public const int GL_PROGRAM_OBJECT_ARB = 0x8B40; + public const int GL_SHADER_OBJECT_ARB = 0x8B48; + public const int GL_OBJECT_TYPE_ARB = 0x8B4E; + public const int GL_OBJECT_SUBTYPE_ARB = 0x8B4F; + public const int GL_FLOAT_VEC2_ARB = 0x8B50; + public const int GL_FLOAT_VEC3_ARB = 0x8B51; + public const int GL_FLOAT_VEC4_ARB = 0x8B52; + public const int GL_INT_VEC2_ARB = 0x8B53; + public const int GL_INT_VEC3_ARB = 0x8B54; + public const int GL_INT_VEC4_ARB = 0x8B55; + public const int GL_BOOL_ARB = 0x8B56; + public const int GL_BOOL_VEC2_ARB = 0x8B57; + public const int GL_BOOL_VEC3_ARB = 0x8B58; + public const int GL_BOOL_VEC4_ARB = 0x8B59; + public const int GL_FLOAT_MAT2_ARB = 0x8B5A; + public const int GL_FLOAT_MAT3_ARB = 0x8B5B; + public const int GL_FLOAT_MAT4_ARB = 0x8B5C; + public const int GL_SAMPLER_1D_ARB = 0x8B5D; + public const int GL_SAMPLER_2D_ARB = 0x8B5E; + public const int GL_SAMPLER_3D_ARB = 0x8B5F; + public const int GL_SAMPLER_CUBE_ARB = 0x8B60; + public const int GL_SAMPLER_1D_SHADOW_ARB = 0x8B61; + public const int GL_SAMPLER_2D_SHADOW_ARB = 0x8B62; + public const int GL_SAMPLER_2D_RECT_ARB = 0x8B63; + public const int GL_SAMPLER_2D_RECT_SHADOW_ARB = 0x8B64; + public const int GL_OBJECT_DELETE_STATUS_ARB = 0x8B80; + public const int GL_OBJECT_COMPILE_STATUS_ARB = 0x8B81; + public const int GL_OBJECT_LINK_STATUS_ARB = 0x8B82; + public const int GL_OBJECT_VALIDATE_STATUS_ARB = 0x8B83; + public const int GL_OBJECT_INFO_LOG_LENGTH_ARB = 0x8B84; + public const int GL_OBJECT_ATTACHED_OBJECTS_ARB = 0x8B85; + public const int GL_OBJECT_ACTIVE_UNIFORMS_ARB = 0x8B86; + public const int GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB = 0x8B87; + public const int GL_OBJECT_SHADER_SOURCE_LENGTH_ARB = 0x8B88; + public const int GL_ARB_shader_precision = 1; + public const int GL_ARB_shader_stencil_export = 1; + public const int GL_ARB_shader_storage_buffer_object = 1; + public const int GL_ARB_shader_subroutine = 1; + public const int GL_ARB_shader_texture_image_samples = 1; + public const int GL_ARB_shader_texture_lod = 1; + public const int GL_ARB_shader_viewport_layer_array = 1; + public const int GL_ARB_shading_language_100 = 1; + public const int GL_SHADING_LANGUAGE_VERSION_ARB = 0x8B8C; + public const int GL_ARB_shading_language_420pack = 1; + public const int GL_ARB_shading_language_include = 1; + public const int GL_SHADER_INCLUDE_ARB = 0x8DAE; + public const int GL_NAMED_STRING_LENGTH_ARB = 0x8DE9; + public const int GL_NAMED_STRING_TYPE_ARB = 0x8DEA; + public const int GL_ARB_shading_language_packing = 1; + public const int GL_ARB_shadow = 1; + public const int GL_TEXTURE_COMPARE_MODE_ARB = 0x884C; + public const int GL_TEXTURE_COMPARE_FUNC_ARB = 0x884D; + public const int GL_COMPARE_R_TO_TEXTURE_ARB = 0x884E; + public const int GL_ARB_shadow_ambient = 1; + public const int GL_TEXTURE_COMPARE_FAIL_VALUE_ARB = 0x80BF; + public const int GL_ARB_sparse_buffer = 1; + public const int GL_SPARSE_STORAGE_BIT_ARB = 0x0400; + public const int GL_SPARSE_BUFFER_PAGE_SIZE_ARB = 0x82F8; + public const int GL_ARB_sparse_texture = 1; + public const int GL_TEXTURE_SPARSE_ARB = 0x91A6; + public const int GL_VIRTUAL_PAGE_SIZE_INDEX_ARB = 0x91A7; + public const int GL_NUM_SPARSE_LEVELS_ARB = 0x91AA; + public const int GL_NUM_VIRTUAL_PAGE_SIZES_ARB = 0x91A8; + public const int GL_VIRTUAL_PAGE_SIZE_X_ARB = 0x9195; + public const int GL_VIRTUAL_PAGE_SIZE_Y_ARB = 0x9196; + public const int GL_VIRTUAL_PAGE_SIZE_Z_ARB = 0x9197; + public const int GL_MAX_SPARSE_TEXTURE_SIZE_ARB = 0x9198; + public const int GL_MAX_SPARSE_3D_TEXTURE_SIZE_ARB = 0x9199; + public const int GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS_ARB = 0x919A; + public const int GL_SPARSE_TEXTURE_FULL_ARRAY_CUBE_MIPMAPS_ARB = 0x91A9; + public const int GL_ARB_sparse_texture2 = 1; + public const int GL_ARB_sparse_texture_clamp = 1; + public const int GL_ARB_spirv_extensions = 1; + public const int GL_ARB_stencil_texturing = 1; + public const int GL_ARB_sync = 1; + public const int GL_ARB_tessellation_shader = 1; + public const int GL_ARB_texture_barrier = 1; + public const int GL_ARB_texture_border_clamp = 1; + public const int GL_CLAMP_TO_BORDER_ARB = 0x812D; + public const int GL_ARB_texture_buffer_object = 1; + public const int GL_TEXTURE_BUFFER_ARB = 0x8C2A; + public const int GL_MAX_TEXTURE_BUFFER_SIZE_ARB = 0x8C2B; + public const int GL_TEXTURE_BINDING_BUFFER_ARB = 0x8C2C; + public const int GL_TEXTURE_BUFFER_DATA_STORE_BINDING_ARB = 0x8C2D; + public const int GL_TEXTURE_BUFFER_FORMAT_ARB = 0x8C2E; + public const int GL_ARB_texture_buffer_object_rgb32 = 1; + public const int GL_ARB_texture_buffer_range = 1; + public const int GL_ARB_texture_compression = 1; + public const int GL_COMPRESSED_ALPHA_ARB = 0x84E9; + public const int GL_COMPRESSED_LUMINANCE_ARB = 0x84EA; + public const int GL_COMPRESSED_LUMINANCE_ALPHA_ARB = 0x84EB; + public const int GL_COMPRESSED_INTENSITY_ARB = 0x84EC; + public const int GL_COMPRESSED_RGB_ARB = 0x84ED; + public const int GL_COMPRESSED_RGBA_ARB = 0x84EE; + public const int GL_TEXTURE_COMPRESSION_HINT_ARB = 0x84EF; + public const int GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB = 0x86A0; + public const int GL_TEXTURE_COMPRESSED_ARB = 0x86A1; + public const int GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB = 0x86A2; + public const int GL_COMPRESSED_TEXTURE_FORMATS_ARB = 0x86A3; + public const int GL_ARB_texture_compression_bptc = 1; + public const int GL_COMPRESSED_RGBA_BPTC_UNORM_ARB = 0x8E8C; + public const int GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB = 0x8E8D; + public const int GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB = 0x8E8E; + public const int GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB = 0x8E8F; + public const int GL_ARB_texture_compression_rgtc = 1; + public const int GL_ARB_texture_cube_map = 1; + public const int GL_NORMAL_MAP_ARB = 0x8511; + public const int GL_REFLECTION_MAP_ARB = 0x8512; + public const int GL_TEXTURE_CUBE_MAP_ARB = 0x8513; + public const int GL_TEXTURE_BINDING_CUBE_MAP_ARB = 0x8514; + public const int GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB = 0x8515; + public const int GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB = 0x8516; + public const int GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB = 0x8517; + public const int GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB = 0x8518; + public const int GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB = 0x8519; + public const int GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB = 0x851A; + public const int GL_PROXY_TEXTURE_CUBE_MAP_ARB = 0x851B; + public const int GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB = 0x851C; + public const int GL_ARB_texture_cube_map_array = 1; + public const int GL_TEXTURE_CUBE_MAP_ARRAY_ARB = 0x9009; + public const int GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_ARB = 0x900A; + public const int GL_PROXY_TEXTURE_CUBE_MAP_ARRAY_ARB = 0x900B; + public const int GL_SAMPLER_CUBE_MAP_ARRAY_ARB = 0x900C; + public const int GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_ARB = 0x900D; + public const int GL_INT_SAMPLER_CUBE_MAP_ARRAY_ARB = 0x900E; + public const int GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_ARB = 0x900F; + public const int GL_ARB_texture_env_add = 1; + public const int GL_ARB_texture_env_combine = 1; + public const int GL_COMBINE_ARB = 0x8570; + public const int GL_COMBINE_RGB_ARB = 0x8571; + public const int GL_COMBINE_ALPHA_ARB = 0x8572; + public const int GL_SOURCE0_RGB_ARB = 0x8580; + public const int GL_SOURCE1_RGB_ARB = 0x8581; + public const int GL_SOURCE2_RGB_ARB = 0x8582; + public const int GL_SOURCE0_ALPHA_ARB = 0x8588; + public const int GL_SOURCE1_ALPHA_ARB = 0x8589; + public const int GL_SOURCE2_ALPHA_ARB = 0x858A; + public const int GL_OPERAND0_RGB_ARB = 0x8590; + public const int GL_OPERAND1_RGB_ARB = 0x8591; + public const int GL_OPERAND2_RGB_ARB = 0x8592; + public const int GL_OPERAND0_ALPHA_ARB = 0x8598; + public const int GL_OPERAND1_ALPHA_ARB = 0x8599; + public const int GL_OPERAND2_ALPHA_ARB = 0x859A; + public const int GL_RGB_SCALE_ARB = 0x8573; + public const int GL_ADD_SIGNED_ARB = 0x8574; + public const int GL_INTERPOLATE_ARB = 0x8575; + public const int GL_SUBTRACT_ARB = 0x84E7; + public const int GL_CONSTANT_ARB = 0x8576; + public const int GL_PRIMARY_COLOR_ARB = 0x8577; + public const int GL_PREVIOUS_ARB = 0x8578; + public const int GL_ARB_texture_env_crossbar = 1; + public const int GL_ARB_texture_env_dot3 = 1; + public const int GL_DOT3_RGB_ARB = 0x86AE; + public const int GL_DOT3_RGBA_ARB = 0x86AF; + public const int GL_ARB_texture_filter_anisotropic = 1; + public const int GL_ARB_texture_filter_minmax = 1; + public const int GL_TEXTURE_REDUCTION_MODE_ARB = 0x9366; + public const int GL_WEIGHTED_AVERAGE_ARB = 0x9367; + public const int GL_ARB_texture_float = 1; + public const int GL_TEXTURE_RED_TYPE_ARB = 0x8C10; + public const int GL_TEXTURE_GREEN_TYPE_ARB = 0x8C11; + public const int GL_TEXTURE_BLUE_TYPE_ARB = 0x8C12; + public const int GL_TEXTURE_ALPHA_TYPE_ARB = 0x8C13; + public const int GL_TEXTURE_LUMINANCE_TYPE_ARB = 0x8C14; + public const int GL_TEXTURE_INTENSITY_TYPE_ARB = 0x8C15; + public const int GL_TEXTURE_DEPTH_TYPE_ARB = 0x8C16; + public const int GL_UNSIGNED_NORMALIZED_ARB = 0x8C17; + public const int GL_RGBA32F_ARB = 0x8814; + public const int GL_RGB32F_ARB = 0x8815; + public const int GL_ALPHA32F_ARB = 0x8816; + public const int GL_INTENSITY32F_ARB = 0x8817; + public const int GL_LUMINANCE32F_ARB = 0x8818; + public const int GL_LUMINANCE_ALPHA32F_ARB = 0x8819; + public const int GL_RGBA16F_ARB = 0x881A; + public const int GL_RGB16F_ARB = 0x881B; + public const int GL_ALPHA16F_ARB = 0x881C; + public const int GL_INTENSITY16F_ARB = 0x881D; + public const int GL_LUMINANCE16F_ARB = 0x881E; + public const int GL_LUMINANCE_ALPHA16F_ARB = 0x881F; + public const int GL_ARB_texture_gather = 1; + public const int GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_ARB = 0x8E5E; + public const int GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_ARB = 0x8E5F; + public const int GL_MAX_PROGRAM_TEXTURE_GATHER_COMPONENTS_ARB = 0x8F9F; + public const int GL_ARB_texture_mirror_clamp_to_edge = 1; + public const int GL_ARB_texture_mirrored_repeat = 1; + public const int GL_MIRRORED_REPEAT_ARB = 0x8370; + public const int GL_ARB_texture_multisample = 1; + public const int GL_ARB_texture_non_power_of_two = 1; + public const int GL_ARB_texture_query_levels = 1; + public const int GL_ARB_texture_query_lod = 1; + public const int GL_ARB_texture_rectangle = 1; + public const int GL_TEXTURE_RECTANGLE_ARB = 0x84F5; + public const int GL_TEXTURE_BINDING_RECTANGLE_ARB = 0x84F6; + public const int GL_PROXY_TEXTURE_RECTANGLE_ARB = 0x84F7; + public const int GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB = 0x84F8; + public const int GL_ARB_texture_rg = 1; + public const int GL_ARB_texture_rgb10_a2ui = 1; + public const int GL_ARB_texture_stencil8 = 1; + public const int GL_ARB_texture_storage = 1; + public const int GL_ARB_texture_storage_multisample = 1; + public const int GL_ARB_texture_swizzle = 1; + public const int GL_ARB_texture_view = 1; + public const int GL_ARB_timer_query = 1; + public const int GL_ARB_transform_feedback2 = 1; + public const int GL_ARB_transform_feedback3 = 1; + public const int GL_ARB_transform_feedback_instanced = 1; + public const int GL_ARB_transform_feedback_overflow_query = 1; + public const int GL_TRANSFORM_FEEDBACK_OVERFLOW_ARB = 0x82EC; + public const int GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW_ARB = 0x82ED; + public const int GL_ARB_transpose_matrix = 1; + public const int GL_TRANSPOSE_MODELVIEW_MATRIX_ARB = 0x84E3; + public const int GL_TRANSPOSE_PROJECTION_MATRIX_ARB = 0x84E4; + public const int GL_TRANSPOSE_TEXTURE_MATRIX_ARB = 0x84E5; + public const int GL_TRANSPOSE_COLOR_MATRIX_ARB = 0x84E6; + public const int GL_ARB_uniform_buffer_object = 1; + public const int GL_ARB_vertex_array_bgra = 1; + public const int GL_ARB_vertex_array_object = 1; + public const int GL_ARB_vertex_attrib_64bit = 1; + public const int GL_ARB_vertex_attrib_binding = 1; + public const int GL_ARB_vertex_blend = 1; + public const int GL_MAX_VERTEX_UNITS_ARB = 0x86A4; + public const int GL_ACTIVE_VERTEX_UNITS_ARB = 0x86A5; + public const int GL_WEIGHT_SUM_UNITY_ARB = 0x86A6; + public const int GL_VERTEX_BLEND_ARB = 0x86A7; + public const int GL_CURRENT_WEIGHT_ARB = 0x86A8; + public const int GL_WEIGHT_ARRAY_TYPE_ARB = 0x86A9; + public const int GL_WEIGHT_ARRAY_STRIDE_ARB = 0x86AA; + public const int GL_WEIGHT_ARRAY_SIZE_ARB = 0x86AB; + public const int GL_WEIGHT_ARRAY_POINTER_ARB = 0x86AC; + public const int GL_WEIGHT_ARRAY_ARB = 0x86AD; + public const int GL_MODELVIEW0_ARB = 0x1700; + public const int GL_MODELVIEW1_ARB = 0x850A; + public const int GL_MODELVIEW2_ARB = 0x8722; + public const int GL_MODELVIEW3_ARB = 0x8723; + public const int GL_MODELVIEW4_ARB = 0x8724; + public const int GL_MODELVIEW5_ARB = 0x8725; + public const int GL_MODELVIEW6_ARB = 0x8726; + public const int GL_MODELVIEW7_ARB = 0x8727; + public const int GL_MODELVIEW8_ARB = 0x8728; + public const int GL_MODELVIEW9_ARB = 0x8729; + public const int GL_MODELVIEW10_ARB = 0x872A; + public const int GL_MODELVIEW11_ARB = 0x872B; + public const int GL_MODELVIEW12_ARB = 0x872C; + public const int GL_MODELVIEW13_ARB = 0x872D; + public const int GL_MODELVIEW14_ARB = 0x872E; + public const int GL_MODELVIEW15_ARB = 0x872F; + public const int GL_MODELVIEW16_ARB = 0x8730; + public const int GL_MODELVIEW17_ARB = 0x8731; + public const int GL_MODELVIEW18_ARB = 0x8732; + public const int GL_MODELVIEW19_ARB = 0x8733; + public const int GL_MODELVIEW20_ARB = 0x8734; + public const int GL_MODELVIEW21_ARB = 0x8735; + public const int GL_MODELVIEW22_ARB = 0x8736; + public const int GL_MODELVIEW23_ARB = 0x8737; + public const int GL_MODELVIEW24_ARB = 0x8738; + public const int GL_MODELVIEW25_ARB = 0x8739; + public const int GL_MODELVIEW26_ARB = 0x873A; + public const int GL_MODELVIEW27_ARB = 0x873B; + public const int GL_MODELVIEW28_ARB = 0x873C; + public const int GL_MODELVIEW29_ARB = 0x873D; + public const int GL_MODELVIEW30_ARB = 0x873E; + public const int GL_MODELVIEW31_ARB = 0x873F; + public const int GL_ARB_vertex_buffer_object = 1; + public const int GL_BUFFER_SIZE_ARB = 0x8764; + public const int GL_BUFFER_USAGE_ARB = 0x8765; + public const int GL_ARRAY_BUFFER_ARB = 0x8892; + public const int GL_ELEMENT_ARRAY_BUFFER_ARB = 0x8893; + public const int GL_ARRAY_BUFFER_BINDING_ARB = 0x8894; + public const int GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB = 0x8895; + public const int GL_VERTEX_ARRAY_BUFFER_BINDING_ARB = 0x8896; + public const int GL_NORMAL_ARRAY_BUFFER_BINDING_ARB = 0x8897; + public const int GL_COLOR_ARRAY_BUFFER_BINDING_ARB = 0x8898; + public const int GL_INDEX_ARRAY_BUFFER_BINDING_ARB = 0x8899; + public const int GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB = 0x889A; + public const int GL_EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB = 0x889B; + public const int GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB = 0x889C; + public const int GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB = 0x889D; + public const int GL_WEIGHT_ARRAY_BUFFER_BINDING_ARB = 0x889E; + public const int GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB = 0x889F; + public const int GL_READ_ONLY_ARB = 0x88B8; + public const int GL_WRITE_ONLY_ARB = 0x88B9; + public const int GL_READ_WRITE_ARB = 0x88BA; + public const int GL_BUFFER_ACCESS_ARB = 0x88BB; + public const int GL_BUFFER_MAPPED_ARB = 0x88BC; + public const int GL_BUFFER_MAP_POINTER_ARB = 0x88BD; + public const int GL_STREAM_DRAW_ARB = 0x88E0; + public const int GL_STREAM_READ_ARB = 0x88E1; + public const int GL_STREAM_COPY_ARB = 0x88E2; + public const int GL_STATIC_DRAW_ARB = 0x88E4; + public const int GL_STATIC_READ_ARB = 0x88E5; + public const int GL_STATIC_COPY_ARB = 0x88E6; + public const int GL_DYNAMIC_DRAW_ARB = 0x88E8; + public const int GL_DYNAMIC_READ_ARB = 0x88E9; + public const int GL_DYNAMIC_COPY_ARB = 0x88EA; + public const int GL_ARB_vertex_program = 1; + public const int GL_COLOR_SUM_ARB = 0x8458; + public const int GL_VERTEX_PROGRAM_ARB = 0x8620; + public const int GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB = 0x8622; + public const int GL_VERTEX_ATTRIB_ARRAY_SIZE_ARB = 0x8623; + public const int GL_VERTEX_ATTRIB_ARRAY_STRIDE_ARB = 0x8624; + public const int GL_VERTEX_ATTRIB_ARRAY_TYPE_ARB = 0x8625; + public const int GL_CURRENT_VERTEX_ATTRIB_ARB = 0x8626; + public const int GL_VERTEX_PROGRAM_POINT_SIZE_ARB = 0x8642; + public const int GL_VERTEX_PROGRAM_TWO_SIDE_ARB = 0x8643; + public const int GL_VERTEX_ATTRIB_ARRAY_POINTER_ARB = 0x8645; + public const int GL_MAX_VERTEX_ATTRIBS_ARB = 0x8869; + public const int GL_VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB = 0x886A; + public const int GL_PROGRAM_ADDRESS_REGISTERS_ARB = 0x88B0; + public const int GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB = 0x88B1; + public const int GL_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB = 0x88B2; + public const int GL_MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB = 0x88B3; + public const int GL_ARB_vertex_shader = 1; + public const int GL_VERTEX_SHADER_ARB = 0x8B31; + public const int GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB = 0x8B4A; + public const int GL_MAX_VARYING_FLOATS_ARB = 0x8B4B; + public const int GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB = 0x8B4C; + public const int GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB = 0x8B4D; + public const int GL_OBJECT_ACTIVE_ATTRIBUTES_ARB = 0x8B89; + public const int GL_OBJECT_ACTIVE_ATTRIBUTE_MAX_LENGTH_ARB = 0x8B8A; + public const int GL_ARB_vertex_type_10f_11f_11f_rev = 1; + public const int GL_ARB_vertex_type_2_10_10_10_rev = 1; + public const int GL_ARB_viewport_array = 1; + public const int GL_ARB_window_pos = 1; + public const int GL_KHR_blend_equation_advanced = 1; + public const int GL_MULTIPLY_KHR = 0x9294; + public const int GL_SCREEN_KHR = 0x9295; + public const int GL_OVERLAY_KHR = 0x9296; + public const int GL_DARKEN_KHR = 0x9297; + public const int GL_LIGHTEN_KHR = 0x9298; + public const int GL_COLORDODGE_KHR = 0x9299; + public const int GL_COLORBURN_KHR = 0x929A; + public const int GL_HARDLIGHT_KHR = 0x929B; + public const int GL_SOFTLIGHT_KHR = 0x929C; + public const int GL_DIFFERENCE_KHR = 0x929E; + public const int GL_EXCLUSION_KHR = 0x92A0; + public const int GL_HSL_HUE_KHR = 0x92AD; + public const int GL_HSL_SATURATION_KHR = 0x92AE; + public const int GL_HSL_COLOR_KHR = 0x92AF; + public const int GL_HSL_LUMINOSITY_KHR = 0x92B0; + public const int GL_KHR_blend_equation_advanced_coherent = 1; + public const int GL_BLEND_ADVANCED_COHERENT_KHR = 0x9285; + public const int GL_KHR_context_flush_control = 1; + public const int GL_KHR_debug = 1; + public const int GL_KHR_no_error = 1; + public const int GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR = 0x00000008; + public const int GL_KHR_parallel_shader_compile = 1; + public const int GL_MAX_SHADER_COMPILER_THREADS_KHR = 0x91B0; + public const int GL_COMPLETION_STATUS_KHR = 0x91B1; + public const int GL_KHR_robust_buffer_access_behavior = 1; + public const int GL_KHR_robustness = 1; + public const int GL_CONTEXT_ROBUST_ACCESS = 0x90F3; + public const int GL_KHR_texture_compression_astc_hdr = 1; + public const int GL_COMPRESSED_RGBA_ASTC_4x4_KHR = 0x93B0; + public const int GL_COMPRESSED_RGBA_ASTC_5x4_KHR = 0x93B1; + public const int GL_COMPRESSED_RGBA_ASTC_5x5_KHR = 0x93B2; + public const int GL_COMPRESSED_RGBA_ASTC_6x5_KHR = 0x93B3; + public const int GL_COMPRESSED_RGBA_ASTC_6x6_KHR = 0x93B4; + public const int GL_COMPRESSED_RGBA_ASTC_8x5_KHR = 0x93B5; + public const int GL_COMPRESSED_RGBA_ASTC_8x6_KHR = 0x93B6; + public const int GL_COMPRESSED_RGBA_ASTC_8x8_KHR = 0x93B7; + public const int GL_COMPRESSED_RGBA_ASTC_10x5_KHR = 0x93B8; + public const int GL_COMPRESSED_RGBA_ASTC_10x6_KHR = 0x93B9; + public const int GL_COMPRESSED_RGBA_ASTC_10x8_KHR = 0x93BA; + public const int GL_COMPRESSED_RGBA_ASTC_10x10_KHR = 0x93BB; + public const int GL_COMPRESSED_RGBA_ASTC_12x10_KHR = 0x93BC; + public const int GL_COMPRESSED_RGBA_ASTC_12x12_KHR = 0x93BD; + public const int GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR = 0x93D0; + public const int GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR = 0x93D1; + public const int GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR = 0x93D2; + public const int GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR = 0x93D3; + public const int GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR = 0x93D4; + public const int GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR = 0x93D5; + public const int GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR = 0x93D6; + public const int GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR = 0x93D7; + public const int GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR = 0x93D8; + public const int GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR = 0x93D9; + public const int GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR = 0x93DA; + public const int GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR = 0x93DB; + public const int GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR = 0x93DC; + public const int GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR = 0x93DD; + public const int GL_KHR_texture_compression_astc_ldr = 1; + public const int GL_KHR_texture_compression_astc_sliced_3d = 1; + public const int GL_OES_byte_coordinates = 1; + public const int GL_OES_compressed_paletted_texture = 1; + public const int GL_PALETTE4_RGB8_OES = 0x8B90; + public const int GL_PALETTE4_RGBA8_OES = 0x8B91; + public const int GL_PALETTE4_R5_G6_B5_OES = 0x8B92; + public const int GL_PALETTE4_RGBA4_OES = 0x8B93; + public const int GL_PALETTE4_RGB5_A1_OES = 0x8B94; + public const int GL_PALETTE8_RGB8_OES = 0x8B95; + public const int GL_PALETTE8_RGBA8_OES = 0x8B96; + public const int GL_PALETTE8_R5_G6_B5_OES = 0x8B97; + public const int GL_PALETTE8_RGBA4_OES = 0x8B98; + public const int GL_PALETTE8_RGB5_A1_OES = 0x8B99; + public const int GL_OES_fixed_point = 1; + public const int GL_FIXED_OES = 0x140C; + public const int GL_OES_query_matrix = 1; + public const int GL_OES_read_format = 1; + public const int GL_IMPLEMENTATION_COLOR_READ_TYPE_OES = 0x8B9A; + public const int GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES = 0x8B9B; + public const int GL_OES_single_precision = 1; + public const int GL_3DFX_multisample = 1; + public const int GL_MULTISAMPLE_3DFX = 0x86B2; + public const int GL_SAMPLE_BUFFERS_3DFX = 0x86B3; + public const int GL_SAMPLES_3DFX = 0x86B4; + public const int GL_MULTISAMPLE_BIT_3DFX = 0x20000000; + public const int GL_3DFX_tbuffer = 1; + public const int GL_3DFX_texture_compression_FXT1 = 1; + public const int GL_COMPRESSED_RGB_FXT1_3DFX = 0x86B0; + public const int GL_COMPRESSED_RGBA_FXT1_3DFX = 0x86B1; + public const int GL_AMD_blend_minmax_factor = 1; + public const int GL_FACTOR_MIN_AMD = 0x901C; + public const int GL_FACTOR_MAX_AMD = 0x901D; + public const int GL_AMD_conservative_depth = 1; + public const int GL_AMD_debug_output = 1; + public const int GL_MAX_DEBUG_MESSAGE_LENGTH_AMD = 0x9143; + public const int GL_MAX_DEBUG_LOGGED_MESSAGES_AMD = 0x9144; + public const int GL_DEBUG_LOGGED_MESSAGES_AMD = 0x9145; + public const int GL_DEBUG_SEVERITY_HIGH_AMD = 0x9146; + public const int GL_DEBUG_SEVERITY_MEDIUM_AMD = 0x9147; + public const int GL_DEBUG_SEVERITY_LOW_AMD = 0x9148; + public const int GL_DEBUG_CATEGORY_API_ERROR_AMD = 0x9149; + public const int GL_DEBUG_CATEGORY_WINDOW_SYSTEM_AMD = 0x914A; + public const int GL_DEBUG_CATEGORY_DEPRECATION_AMD = 0x914B; + public const int GL_DEBUG_CATEGORY_UNDEFINED_BEHAVIOR_AMD = 0x914C; + public const int GL_DEBUG_CATEGORY_PERFORMANCE_AMD = 0x914D; + public const int GL_DEBUG_CATEGORY_SHADER_COMPILER_AMD = 0x914E; + public const int GL_DEBUG_CATEGORY_APPLICATION_AMD = 0x914F; + public const int GL_DEBUG_CATEGORY_OTHER_AMD = 0x9150; + public const int GL_AMD_depth_clamp_separate = 1; + public const int GL_DEPTH_CLAMP_NEAR_AMD = 0x901E; + public const int GL_DEPTH_CLAMP_FAR_AMD = 0x901F; + public const int GL_AMD_draw_buffers_blend = 1; + public const int GL_AMD_framebuffer_multisample_advanced = 1; + public const int GL_RENDERBUFFER_STORAGE_SAMPLES_AMD = 0x91B2; + public const int GL_MAX_COLOR_FRAMEBUFFER_SAMPLES_AMD = 0x91B3; + public const int GL_MAX_COLOR_FRAMEBUFFER_STORAGE_SAMPLES_AMD = 0x91B4; + public const int GL_MAX_DEPTH_STENCIL_FRAMEBUFFER_SAMPLES_AMD = 0x91B5; + public const int GL_NUM_SUPPORTED_MULTISAMPLE_MODES_AMD = 0x91B6; + public const int GL_SUPPORTED_MULTISAMPLE_MODES_AMD = 0x91B7; + public const int GL_AMD_framebuffer_sample_positions = 1; + public const int GL_SUBSAMPLE_DISTANCE_AMD = 0x883F; + public const int GL_PIXELS_PER_SAMPLE_PATTERN_X_AMD = 0x91AE; + public const int GL_PIXELS_PER_SAMPLE_PATTERN_Y_AMD = 0x91AF; + public const int GL_ALL_PIXELS_AMD = -1; + public const int GL_AMD_gcn_shader = 1; + public const int GL_AMD_gpu_shader_half_float = 1; + public const int GL_FLOAT16_NV = 0x8FF8; + public const int GL_FLOAT16_VEC2_NV = 0x8FF9; + public const int GL_FLOAT16_VEC3_NV = 0x8FFA; + public const int GL_FLOAT16_VEC4_NV = 0x8FFB; + public const int GL_FLOAT16_MAT2_AMD = 0x91C5; + public const int GL_FLOAT16_MAT3_AMD = 0x91C6; + public const int GL_FLOAT16_MAT4_AMD = 0x91C7; + public const int GL_FLOAT16_MAT2x3_AMD = 0x91C8; + public const int GL_FLOAT16_MAT2x4_AMD = 0x91C9; + public const int GL_FLOAT16_MAT3x2_AMD = 0x91CA; + public const int GL_FLOAT16_MAT3x4_AMD = 0x91CB; + public const int GL_FLOAT16_MAT4x2_AMD = 0x91CC; + public const int GL_FLOAT16_MAT4x3_AMD = 0x91CD; + public const int GL_AMD_gpu_shader_int16 = 1; + public const int GL_AMD_gpu_shader_int64 = 1; + public const int GL_INT64_NV = 0x140E; + public const int GL_UNSIGNED_INT64_NV = 0x140F; + public const int GL_INT8_NV = 0x8FE0; + public const int GL_INT8_VEC2_NV = 0x8FE1; + public const int GL_INT8_VEC3_NV = 0x8FE2; + public const int GL_INT8_VEC4_NV = 0x8FE3; + public const int GL_INT16_NV = 0x8FE4; + public const int GL_INT16_VEC2_NV = 0x8FE5; + public const int GL_INT16_VEC3_NV = 0x8FE6; + public const int GL_INT16_VEC4_NV = 0x8FE7; + public const int GL_INT64_VEC2_NV = 0x8FE9; + public const int GL_INT64_VEC3_NV = 0x8FEA; + public const int GL_INT64_VEC4_NV = 0x8FEB; + public const int GL_UNSIGNED_INT8_NV = 0x8FEC; + public const int GL_UNSIGNED_INT8_VEC2_NV = 0x8FED; + public const int GL_UNSIGNED_INT8_VEC3_NV = 0x8FEE; + public const int GL_UNSIGNED_INT8_VEC4_NV = 0x8FEF; + public const int GL_UNSIGNED_INT16_NV = 0x8FF0; + public const int GL_UNSIGNED_INT16_VEC2_NV = 0x8FF1; + public const int GL_UNSIGNED_INT16_VEC3_NV = 0x8FF2; + public const int GL_UNSIGNED_INT16_VEC4_NV = 0x8FF3; + public const int GL_UNSIGNED_INT64_VEC2_NV = 0x8FF5; + public const int GL_UNSIGNED_INT64_VEC3_NV = 0x8FF6; + public const int GL_UNSIGNED_INT64_VEC4_NV = 0x8FF7; + public const int GL_AMD_interleaved_elements = 1; + public const int GL_VERTEX_ELEMENT_SWIZZLE_AMD = 0x91A4; + public const int GL_VERTEX_ID_SWIZZLE_AMD = 0x91A5; + public const int GL_AMD_multi_draw_indirect = 1; + public const int GL_AMD_name_gen_delete = 1; + public const int GL_DATA_BUFFER_AMD = 0x9151; + public const int GL_PERFORMANCE_MONITOR_AMD = 0x9152; + public const int GL_QUERY_OBJECT_AMD = 0x9153; + public const int GL_VERTEX_ARRAY_OBJECT_AMD = 0x9154; + public const int GL_SAMPLER_OBJECT_AMD = 0x9155; + public const int GL_AMD_occlusion_query_event = 1; + public const int GL_OCCLUSION_QUERY_EVENT_MASK_AMD = 0x874F; + public const int GL_QUERY_DEPTH_PASS_EVENT_BIT_AMD = 0x00000001; + public const int GL_QUERY_DEPTH_FAIL_EVENT_BIT_AMD = 0x00000002; + public const int GL_QUERY_STENCIL_FAIL_EVENT_BIT_AMD = 0x00000004; + public const int GL_QUERY_DEPTH_BOUNDS_FAIL_EVENT_BIT_AMD = 0x00000008; + public const int GL_QUERY_ALL_EVENT_BITS_AMD = -1; + public const int GL_AMD_performance_monitor = 1; + public const int GL_COUNTER_TYPE_AMD = 0x8BC0; + public const int GL_COUNTER_RANGE_AMD = 0x8BC1; + public const int GL_UNSIGNED_INT64_AMD = 0x8BC2; + public const int GL_PERCENTAGE_AMD = 0x8BC3; + public const int GL_PERFMON_RESULT_AVAILABLE_AMD = 0x8BC4; + public const int GL_PERFMON_RESULT_SIZE_AMD = 0x8BC5; + public const int GL_PERFMON_RESULT_AMD = 0x8BC6; + public const int GL_AMD_pinned_memory = 1; + public const int GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD = 0x9160; + public const int GL_AMD_query_buffer_object = 1; + public const int GL_QUERY_BUFFER_AMD = 0x9192; + public const int GL_QUERY_BUFFER_BINDING_AMD = 0x9193; + public const int GL_QUERY_RESULT_NO_WAIT_AMD = 0x9194; + public const int GL_AMD_sample_positions = 1; + public const int GL_AMD_seamless_cubemap_per_texture = 1; + public const int GL_AMD_shader_atomic_counter_ops = 1; + public const int GL_AMD_shader_ballot = 1; + public const int GL_AMD_shader_explicit_vertex_parameter = 1; + public const int GL_AMD_shader_gpu_shader_half_float_fetch = 1; + public const int GL_AMD_shader_image_load_store_lod = 1; + public const int GL_AMD_shader_stencil_export = 1; + public const int GL_AMD_shader_trinary_minmax = 1; + public const int GL_AMD_sparse_texture = 1; + public const int GL_VIRTUAL_PAGE_SIZE_X_AMD = 0x9195; + public const int GL_VIRTUAL_PAGE_SIZE_Y_AMD = 0x9196; + public const int GL_VIRTUAL_PAGE_SIZE_Z_AMD = 0x9197; + public const int GL_MAX_SPARSE_TEXTURE_SIZE_AMD = 0x9198; + public const int GL_MAX_SPARSE_3D_TEXTURE_SIZE_AMD = 0x9199; + public const int GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS = 0x919A; + public const int GL_MIN_SPARSE_LEVEL_AMD = 0x919B; + public const int GL_MIN_LOD_WARNING_AMD = 0x919C; + public const int GL_TEXTURE_STORAGE_SPARSE_BIT_AMD = 0x00000001; + public const int GL_AMD_stencil_operation_extended = 1; + public const int GL_SET_AMD = 0x874A; + public const int GL_REPLACE_VALUE_AMD = 0x874B; + public const int GL_STENCIL_OP_VALUE_AMD = 0x874C; + public const int GL_STENCIL_BACK_OP_VALUE_AMD = 0x874D; + public const int GL_AMD_texture_gather_bias_lod = 1; + public const int GL_AMD_texture_texture4 = 1; + public const int GL_AMD_transform_feedback3_lines_triangles = 1; + public const int GL_AMD_transform_feedback4 = 1; + public const int GL_STREAM_RASTERIZATION_AMD = 0x91A0; + public const int GL_AMD_vertex_shader_layer = 1; + public const int GL_AMD_vertex_shader_tessellator = 1; + public const int GL_SAMPLER_BUFFER_AMD = 0x9001; + public const int GL_INT_SAMPLER_BUFFER_AMD = 0x9002; + public const int GL_UNSIGNED_INT_SAMPLER_BUFFER_AMD = 0x9003; + public const int GL_TESSELLATION_MODE_AMD = 0x9004; + public const int GL_TESSELLATION_FACTOR_AMD = 0x9005; + public const int GL_DISCRETE_AMD = 0x9006; + public const int GL_CONTINUOUS_AMD = 0x9007; + public const int GL_AMD_vertex_shader_viewport_index = 1; + public const int GL_APPLE_aux_depth_stencil = 1; + public const int GL_AUX_DEPTH_STENCIL_APPLE = 0x8A14; + public const int GL_APPLE_client_storage = 1; + public const int GL_UNPACK_CLIENT_STORAGE_APPLE = 0x85B2; + public const int GL_APPLE_element_array = 1; + public const int GL_ELEMENT_ARRAY_APPLE = 0x8A0C; + public const int GL_ELEMENT_ARRAY_TYPE_APPLE = 0x8A0D; + public const int GL_ELEMENT_ARRAY_POINTER_APPLE = 0x8A0E; + public const int GL_APPLE_fence = 1; + public const int GL_DRAW_PIXELS_APPLE = 0x8A0A; + public const int GL_FENCE_APPLE = 0x8A0B; + public const int GL_APPLE_float_pixels = 1; + public const int GL_HALF_APPLE = 0x140B; + public const int GL_RGBA_FLOAT32_APPLE = 0x8814; + public const int GL_RGB_FLOAT32_APPLE = 0x8815; + public const int GL_ALPHA_FLOAT32_APPLE = 0x8816; + public const int GL_INTENSITY_FLOAT32_APPLE = 0x8817; + public const int GL_LUMINANCE_FLOAT32_APPLE = 0x8818; + public const int GL_LUMINANCE_ALPHA_FLOAT32_APPLE = 0x8819; + public const int GL_RGBA_FLOAT16_APPLE = 0x881A; + public const int GL_RGB_FLOAT16_APPLE = 0x881B; + public const int GL_ALPHA_FLOAT16_APPLE = 0x881C; + public const int GL_INTENSITY_FLOAT16_APPLE = 0x881D; + public const int GL_LUMINANCE_FLOAT16_APPLE = 0x881E; + public const int GL_LUMINANCE_ALPHA_FLOAT16_APPLE = 0x881F; + public const int GL_COLOR_FLOAT_APPLE = 0x8A0F; + public const int GL_APPLE_flush_buffer_range = 1; + public const int GL_BUFFER_SERIALIZED_MODIFY_APPLE = 0x8A12; + public const int GL_BUFFER_FLUSHING_UNMAP_APPLE = 0x8A13; + public const int GL_APPLE_object_purgeable = 1; + public const int GL_BUFFER_OBJECT_APPLE = 0x85B3; + public const int GL_RELEASED_APPLE = 0x8A19; + public const int GL_VOLATILE_APPLE = 0x8A1A; + public const int GL_RETAINED_APPLE = 0x8A1B; + public const int GL_UNDEFINED_APPLE = 0x8A1C; + public const int GL_PURGEABLE_APPLE = 0x8A1D; + public const int GL_APPLE_rgb_422 = 1; + public const int GL_RGB_422_APPLE = 0x8A1F; + public const int GL_UNSIGNED_SHORT_8_8_APPLE = 0x85BA; + public const int GL_UNSIGNED_SHORT_8_8_REV_APPLE = 0x85BB; + public const int GL_RGB_RAW_422_APPLE = 0x8A51; + public const int GL_APPLE_row_bytes = 1; + public const int GL_PACK_ROW_BYTES_APPLE = 0x8A15; + public const int GL_UNPACK_ROW_BYTES_APPLE = 0x8A16; + public const int GL_APPLE_specular_vector = 1; + public const int GL_LIGHT_MODEL_SPECULAR_VECTOR_APPLE = 0x85B0; + public const int GL_APPLE_texture_range = 1; + public const int GL_TEXTURE_RANGE_LENGTH_APPLE = 0x85B7; + public const int GL_TEXTURE_RANGE_POINTER_APPLE = 0x85B8; + public const int GL_TEXTURE_STORAGE_HINT_APPLE = 0x85BC; + public const int GL_STORAGE_PRIVATE_APPLE = 0x85BD; + public const int GL_STORAGE_CACHED_APPLE = 0x85BE; + public const int GL_STORAGE_SHARED_APPLE = 0x85BF; + public const int GL_APPLE_transform_hint = 1; + public const int GL_TRANSFORM_HINT_APPLE = 0x85B1; + public const int GL_APPLE_vertex_array_object = 1; + public const int GL_VERTEX_ARRAY_BINDING_APPLE = 0x85B5; + public const int GL_APPLE_vertex_array_range = 1; + public const int GL_VERTEX_ARRAY_RANGE_APPLE = 0x851D; + public const int GL_VERTEX_ARRAY_RANGE_LENGTH_APPLE = 0x851E; + public const int GL_VERTEX_ARRAY_STORAGE_HINT_APPLE = 0x851F; + public const int GL_VERTEX_ARRAY_RANGE_POINTER_APPLE = 0x8521; + public const int GL_STORAGE_CLIENT_APPLE = 0x85B4; + public const int GL_APPLE_vertex_program_evaluators = 1; + public const int GL_VERTEX_ATTRIB_MAP1_APPLE = 0x8A00; + public const int GL_VERTEX_ATTRIB_MAP2_APPLE = 0x8A01; + public const int GL_VERTEX_ATTRIB_MAP1_SIZE_APPLE = 0x8A02; + public const int GL_VERTEX_ATTRIB_MAP1_COEFF_APPLE = 0x8A03; + public const int GL_VERTEX_ATTRIB_MAP1_ORDER_APPLE = 0x8A04; + public const int GL_VERTEX_ATTRIB_MAP1_DOMAIN_APPLE = 0x8A05; + public const int GL_VERTEX_ATTRIB_MAP2_SIZE_APPLE = 0x8A06; + public const int GL_VERTEX_ATTRIB_MAP2_COEFF_APPLE = 0x8A07; + public const int GL_VERTEX_ATTRIB_MAP2_ORDER_APPLE = 0x8A08; + public const int GL_VERTEX_ATTRIB_MAP2_DOMAIN_APPLE = 0x8A09; + public const int GL_APPLE_ycbcr_422 = 1; + public const int GL_YCBCR_422_APPLE = 0x85B9; + public const int GL_ATI_draw_buffers = 1; + public const int GL_MAX_DRAW_BUFFERS_ATI = 0x8824; + public const int GL_DRAW_BUFFER0_ATI = 0x8825; + public const int GL_DRAW_BUFFER1_ATI = 0x8826; + public const int GL_DRAW_BUFFER2_ATI = 0x8827; + public const int GL_DRAW_BUFFER3_ATI = 0x8828; + public const int GL_DRAW_BUFFER4_ATI = 0x8829; + public const int GL_DRAW_BUFFER5_ATI = 0x882A; + public const int GL_DRAW_BUFFER6_ATI = 0x882B; + public const int GL_DRAW_BUFFER7_ATI = 0x882C; + public const int GL_DRAW_BUFFER8_ATI = 0x882D; + public const int GL_DRAW_BUFFER9_ATI = 0x882E; + public const int GL_DRAW_BUFFER10_ATI = 0x882F; + public const int GL_DRAW_BUFFER11_ATI = 0x8830; + public const int GL_DRAW_BUFFER12_ATI = 0x8831; + public const int GL_DRAW_BUFFER13_ATI = 0x8832; + public const int GL_DRAW_BUFFER14_ATI = 0x8833; + public const int GL_DRAW_BUFFER15_ATI = 0x8834; + public const int GL_ATI_element_array = 1; + public const int GL_ELEMENT_ARRAY_ATI = 0x8768; + public const int GL_ELEMENT_ARRAY_TYPE_ATI = 0x8769; + public const int GL_ELEMENT_ARRAY_POINTER_ATI = 0x876A; + public const int GL_ATI_envmap_bumpmap = 1; + public const int GL_BUMP_ROT_MATRIX_ATI = 0x8775; + public const int GL_BUMP_ROT_MATRIX_SIZE_ATI = 0x8776; + public const int GL_BUMP_NUM_TEX_UNITS_ATI = 0x8777; + public const int GL_BUMP_TEX_UNITS_ATI = 0x8778; + public const int GL_DUDV_ATI = 0x8779; + public const int GL_DU8DV8_ATI = 0x877A; + public const int GL_BUMP_ENVMAP_ATI = 0x877B; + public const int GL_BUMP_TARGET_ATI = 0x877C; + public const int GL_ATI_fragment_shader = 1; + public const int GL_FRAGMENT_SHADER_ATI = 0x8920; + public const int GL_REG_0_ATI = 0x8921; + public const int GL_REG_1_ATI = 0x8922; + public const int GL_REG_2_ATI = 0x8923; + public const int GL_REG_3_ATI = 0x8924; + public const int GL_REG_4_ATI = 0x8925; + public const int GL_REG_5_ATI = 0x8926; + public const int GL_REG_6_ATI = 0x8927; + public const int GL_REG_7_ATI = 0x8928; + public const int GL_REG_8_ATI = 0x8929; + public const int GL_REG_9_ATI = 0x892A; + public const int GL_REG_10_ATI = 0x892B; + public const int GL_REG_11_ATI = 0x892C; + public const int GL_REG_12_ATI = 0x892D; + public const int GL_REG_13_ATI = 0x892E; + public const int GL_REG_14_ATI = 0x892F; + public const int GL_REG_15_ATI = 0x8930; + public const int GL_REG_16_ATI = 0x8931; + public const int GL_REG_17_ATI = 0x8932; + public const int GL_REG_18_ATI = 0x8933; + public const int GL_REG_19_ATI = 0x8934; + public const int GL_REG_20_ATI = 0x8935; + public const int GL_REG_21_ATI = 0x8936; + public const int GL_REG_22_ATI = 0x8937; + public const int GL_REG_23_ATI = 0x8938; + public const int GL_REG_24_ATI = 0x8939; + public const int GL_REG_25_ATI = 0x893A; + public const int GL_REG_26_ATI = 0x893B; + public const int GL_REG_27_ATI = 0x893C; + public const int GL_REG_28_ATI = 0x893D; + public const int GL_REG_29_ATI = 0x893E; + public const int GL_REG_30_ATI = 0x893F; + public const int GL_REG_31_ATI = 0x8940; + public const int GL_CON_0_ATI = 0x8941; + public const int GL_CON_1_ATI = 0x8942; + public const int GL_CON_2_ATI = 0x8943; + public const int GL_CON_3_ATI = 0x8944; + public const int GL_CON_4_ATI = 0x8945; + public const int GL_CON_5_ATI = 0x8946; + public const int GL_CON_6_ATI = 0x8947; + public const int GL_CON_7_ATI = 0x8948; + public const int GL_CON_8_ATI = 0x8949; + public const int GL_CON_9_ATI = 0x894A; + public const int GL_CON_10_ATI = 0x894B; + public const int GL_CON_11_ATI = 0x894C; + public const int GL_CON_12_ATI = 0x894D; + public const int GL_CON_13_ATI = 0x894E; + public const int GL_CON_14_ATI = 0x894F; + public const int GL_CON_15_ATI = 0x8950; + public const int GL_CON_16_ATI = 0x8951; + public const int GL_CON_17_ATI = 0x8952; + public const int GL_CON_18_ATI = 0x8953; + public const int GL_CON_19_ATI = 0x8954; + public const int GL_CON_20_ATI = 0x8955; + public const int GL_CON_21_ATI = 0x8956; + public const int GL_CON_22_ATI = 0x8957; + public const int GL_CON_23_ATI = 0x8958; + public const int GL_CON_24_ATI = 0x8959; + public const int GL_CON_25_ATI = 0x895A; + public const int GL_CON_26_ATI = 0x895B; + public const int GL_CON_27_ATI = 0x895C; + public const int GL_CON_28_ATI = 0x895D; + public const int GL_CON_29_ATI = 0x895E; + public const int GL_CON_30_ATI = 0x895F; + public const int GL_CON_31_ATI = 0x8960; + public const int GL_MOV_ATI = 0x8961; + public const int GL_ADD_ATI = 0x8963; + public const int GL_MUL_ATI = 0x8964; + public const int GL_SUB_ATI = 0x8965; + public const int GL_DOT3_ATI = 0x8966; + public const int GL_DOT4_ATI = 0x8967; + public const int GL_MAD_ATI = 0x8968; + public const int GL_LERP_ATI = 0x8969; + public const int GL_CND_ATI = 0x896A; + public const int GL_CND0_ATI = 0x896B; + public const int GL_DOT2_ADD_ATI = 0x896C; + public const int GL_SECONDARY_INTERPOLATOR_ATI = 0x896D; + public const int GL_NUM_FRAGMENT_REGISTERS_ATI = 0x896E; + public const int GL_NUM_FRAGMENT_CONSTANTS_ATI = 0x896F; + public const int GL_NUM_PASSES_ATI = 0x8970; + public const int GL_NUM_INSTRUCTIONS_PER_PASS_ATI = 0x8971; + public const int GL_NUM_INSTRUCTIONS_TOTAL_ATI = 0x8972; + public const int GL_NUM_INPUT_INTERPOLATOR_COMPONENTS_ATI = 0x8973; + public const int GL_NUM_LOOPBACK_COMPONENTS_ATI = 0x8974; + public const int GL_COLOR_ALPHA_PAIRING_ATI = 0x8975; + public const int GL_SWIZZLE_STR_ATI = 0x8976; + public const int GL_SWIZZLE_STQ_ATI = 0x8977; + public const int GL_SWIZZLE_STR_DR_ATI = 0x8978; + public const int GL_SWIZZLE_STQ_DQ_ATI = 0x8979; + public const int GL_SWIZZLE_STRQ_ATI = 0x897A; + public const int GL_SWIZZLE_STRQ_DQ_ATI = 0x897B; + public const int GL_RED_BIT_ATI = 0x00000001; + public const int GL_GREEN_BIT_ATI = 0x00000002; + public const int GL_BLUE_BIT_ATI = 0x00000004; + public const int GL_2X_BIT_ATI = 0x00000001; + public const int GL_4X_BIT_ATI = 0x00000002; + public const int GL_8X_BIT_ATI = 0x00000004; + public const int GL_HALF_BIT_ATI = 0x00000008; + public const int GL_QUARTER_BIT_ATI = 0x00000010; + public const int GL_EIGHTH_BIT_ATI = 0x00000020; + public const int GL_SATURATE_BIT_ATI = 0x00000040; + public const int GL_COMP_BIT_ATI = 0x00000002; + public const int GL_NEGATE_BIT_ATI = 0x00000004; + public const int GL_BIAS_BIT_ATI = 0x00000008; + public const int GL_ATI_map_object_buffer = 1; + public const int GL_ATI_meminfo = 1; + public const int GL_VBO_FREE_MEMORY_ATI = 0x87FB; + public const int GL_TEXTURE_FREE_MEMORY_ATI = 0x87FC; + public const int GL_RENDERBUFFER_FREE_MEMORY_ATI = 0x87FD; + public const int GL_ATI_pixel_format_float = 1; + public const int GL_RGBA_FLOAT_MODE_ATI = 0x8820; + public const int GL_COLOR_CLEAR_UNCLAMPED_VALUE_ATI = 0x8835; + public const int GL_ATI_pn_triangles = 1; + public const int GL_PN_TRIANGLES_ATI = 0x87F0; + public const int GL_MAX_PN_TRIANGLES_TESSELATION_LEVEL_ATI = 0x87F1; + public const int GL_PN_TRIANGLES_POINT_MODE_ATI = 0x87F2; + public const int GL_PN_TRIANGLES_NORMAL_MODE_ATI = 0x87F3; + public const int GL_PN_TRIANGLES_TESSELATION_LEVEL_ATI = 0x87F4; + public const int GL_PN_TRIANGLES_POINT_MODE_LINEAR_ATI = 0x87F5; + public const int GL_PN_TRIANGLES_POINT_MODE_CUBIC_ATI = 0x87F6; + public const int GL_PN_TRIANGLES_NORMAL_MODE_LINEAR_ATI = 0x87F7; + public const int GL_PN_TRIANGLES_NORMAL_MODE_QUADRATIC_ATI = 0x87F8; + public const int GL_ATI_separate_stencil = 1; + public const int GL_STENCIL_BACK_FUNC_ATI = 0x8800; + public const int GL_STENCIL_BACK_FAIL_ATI = 0x8801; + public const int GL_STENCIL_BACK_PASS_DEPTH_FAIL_ATI = 0x8802; + public const int GL_STENCIL_BACK_PASS_DEPTH_PASS_ATI = 0x8803; + public const int GL_ATI_text_fragment_shader = 1; + public const int GL_TEXT_FRAGMENT_SHADER_ATI = 0x8200; + public const int GL_ATI_texture_env_combine3 = 1; + public const int GL_MODULATE_ADD_ATI = 0x8744; + public const int GL_MODULATE_SIGNED_ADD_ATI = 0x8745; + public const int GL_MODULATE_SUBTRACT_ATI = 0x8746; + public const int GL_ATI_texture_float = 1; + public const int GL_RGBA_FLOAT32_ATI = 0x8814; + public const int GL_RGB_FLOAT32_ATI = 0x8815; + public const int GL_ALPHA_FLOAT32_ATI = 0x8816; + public const int GL_INTENSITY_FLOAT32_ATI = 0x8817; + public const int GL_LUMINANCE_FLOAT32_ATI = 0x8818; + public const int GL_LUMINANCE_ALPHA_FLOAT32_ATI = 0x8819; + public const int GL_RGBA_FLOAT16_ATI = 0x881A; + public const int GL_RGB_FLOAT16_ATI = 0x881B; + public const int GL_ALPHA_FLOAT16_ATI = 0x881C; + public const int GL_INTENSITY_FLOAT16_ATI = 0x881D; + public const int GL_LUMINANCE_FLOAT16_ATI = 0x881E; + public const int GL_LUMINANCE_ALPHA_FLOAT16_ATI = 0x881F; + public const int GL_ATI_texture_mirror_once = 1; + public const int GL_MIRROR_CLAMP_ATI = 0x8742; + public const int GL_MIRROR_CLAMP_TO_EDGE_ATI = 0x8743; + public const int GL_ATI_vertex_array_object = 1; + public const int GL_STATIC_ATI = 0x8760; + public const int GL_DYNAMIC_ATI = 0x8761; + public const int GL_PRESERVE_ATI = 0x8762; + public const int GL_DISCARD_ATI = 0x8763; + public const int GL_OBJECT_BUFFER_SIZE_ATI = 0x8764; + public const int GL_OBJECT_BUFFER_USAGE_ATI = 0x8765; + public const int GL_ARRAY_OBJECT_BUFFER_ATI = 0x8766; + public const int GL_ARRAY_OBJECT_OFFSET_ATI = 0x8767; + public const int GL_ATI_vertex_attrib_array_object = 1; + public const int GL_ATI_vertex_streams = 1; + public const int GL_MAX_VERTEX_STREAMS_ATI = 0x876B; + public const int GL_VERTEX_STREAM0_ATI = 0x876C; + public const int GL_VERTEX_STREAM1_ATI = 0x876D; + public const int GL_VERTEX_STREAM2_ATI = 0x876E; + public const int GL_VERTEX_STREAM3_ATI = 0x876F; + public const int GL_VERTEX_STREAM4_ATI = 0x8770; + public const int GL_VERTEX_STREAM5_ATI = 0x8771; + public const int GL_VERTEX_STREAM6_ATI = 0x8772; + public const int GL_VERTEX_STREAM7_ATI = 0x8773; + public const int GL_VERTEX_SOURCE_ATI = 0x8774; + public const int GL_EXT_422_pixels = 1; + public const int GL_422_EXT = 0x80CC; + public const int GL_422_REV_EXT = 0x80CD; + public const int GL_422_AVERAGE_EXT = 0x80CE; + public const int GL_422_REV_AVERAGE_EXT = 0x80CF; + public const int GL_EXT_EGL_image_storage = 1; + public const int GL_EXT_abgr = 1; + public const int GL_ABGR_EXT = 0x8000; + public const int GL_EXT_bgra = 1; + public const int GL_BGR_EXT = 0x80E0; + public const int GL_BGRA_EXT = 0x80E1; + public const int GL_EXT_bindable_uniform = 1; + public const int GL_MAX_VERTEX_BINDABLE_UNIFORMS_EXT = 0x8DE2; + public const int GL_MAX_FRAGMENT_BINDABLE_UNIFORMS_EXT = 0x8DE3; + public const int GL_MAX_GEOMETRY_BINDABLE_UNIFORMS_EXT = 0x8DE4; + public const int GL_MAX_BINDABLE_UNIFORM_SIZE_EXT = 0x8DED; + public const int GL_UNIFORM_BUFFER_EXT = 0x8DEE; + public const int GL_UNIFORM_BUFFER_BINDING_EXT = 0x8DEF; + public const int GL_EXT_blend_color = 1; + public const int GL_CONSTANT_COLOR_EXT = 0x8001; + public const int GL_ONE_MINUS_CONSTANT_COLOR_EXT = 0x8002; + public const int GL_CONSTANT_ALPHA_EXT = 0x8003; + public const int GL_ONE_MINUS_CONSTANT_ALPHA_EXT = 0x8004; + public const int GL_BLEND_COLOR_EXT = 0x8005; + public const int GL_EXT_blend_equation_separate = 1; + public const int GL_BLEND_EQUATION_RGB_EXT = 0x8009; + public const int GL_BLEND_EQUATION_ALPHA_EXT = 0x883D; + public const int GL_EXT_blend_func_separate = 1; + public const int GL_BLEND_DST_RGB_EXT = 0x80C8; + public const int GL_BLEND_SRC_RGB_EXT = 0x80C9; + public const int GL_BLEND_DST_ALPHA_EXT = 0x80CA; + public const int GL_BLEND_SRC_ALPHA_EXT = 0x80CB; + public const int GL_EXT_blend_logic_op = 1; + public const int GL_EXT_blend_minmax = 1; + public const int GL_MIN_EXT = 0x8007; + public const int GL_MAX_EXT = 0x8008; + public const int GL_FUNC_ADD_EXT = 0x8006; + public const int GL_BLEND_EQUATION_EXT = 0x8009; + public const int GL_EXT_blend_subtract = 1; + public const int GL_FUNC_SUBTRACT_EXT = 0x800A; + public const int GL_FUNC_REVERSE_SUBTRACT_EXT = 0x800B; + public const int GL_EXT_clip_volume_hint = 1; + public const int GL_CLIP_VOLUME_CLIPPING_HINT_EXT = 0x80F0; + public const int GL_EXT_cmyka = 1; + public const int GL_CMYK_EXT = 0x800C; + public const int GL_CMYKA_EXT = 0x800D; + public const int GL_PACK_CMYK_HINT_EXT = 0x800E; + public const int GL_UNPACK_CMYK_HINT_EXT = 0x800F; + public const int GL_EXT_color_subtable = 1; + public const int GL_EXT_compiled_vertex_array = 1; + public const int GL_ARRAY_ELEMENT_LOCK_FIRST_EXT = 0x81A8; + public const int GL_ARRAY_ELEMENT_LOCK_COUNT_EXT = 0x81A9; + public const int GL_EXT_convolution = 1; + public const int GL_CONVOLUTION_1D_EXT = 0x8010; + public const int GL_CONVOLUTION_2D_EXT = 0x8011; + public const int GL_SEPARABLE_2D_EXT = 0x8012; + public const int GL_CONVOLUTION_BORDER_MODE_EXT = 0x8013; + public const int GL_CONVOLUTION_FILTER_SCALE_EXT = 0x8014; + public const int GL_CONVOLUTION_FILTER_BIAS_EXT = 0x8015; + public const int GL_REDUCE_EXT = 0x8016; + public const int GL_CONVOLUTION_FORMAT_EXT = 0x8017; + public const int GL_CONVOLUTION_WIDTH_EXT = 0x8018; + public const int GL_CONVOLUTION_HEIGHT_EXT = 0x8019; + public const int GL_MAX_CONVOLUTION_WIDTH_EXT = 0x801A; + public const int GL_MAX_CONVOLUTION_HEIGHT_EXT = 0x801B; + public const int GL_POST_CONVOLUTION_RED_SCALE_EXT = 0x801C; + public const int GL_POST_CONVOLUTION_GREEN_SCALE_EXT = 0x801D; + public const int GL_POST_CONVOLUTION_BLUE_SCALE_EXT = 0x801E; + public const int GL_POST_CONVOLUTION_ALPHA_SCALE_EXT = 0x801F; + public const int GL_POST_CONVOLUTION_RED_BIAS_EXT = 0x8020; + public const int GL_POST_CONVOLUTION_GREEN_BIAS_EXT = 0x8021; + public const int GL_POST_CONVOLUTION_BLUE_BIAS_EXT = 0x8022; + public const int GL_POST_CONVOLUTION_ALPHA_BIAS_EXT = 0x8023; + public const int GL_EXT_coordinate_frame = 1; + public const int GL_TANGENT_ARRAY_EXT = 0x8439; + public const int GL_BINORMAL_ARRAY_EXT = 0x843A; + public const int GL_CURRENT_TANGENT_EXT = 0x843B; + public const int GL_CURRENT_BINORMAL_EXT = 0x843C; + public const int GL_TANGENT_ARRAY_TYPE_EXT = 0x843E; + public const int GL_TANGENT_ARRAY_STRIDE_EXT = 0x843F; + public const int GL_BINORMAL_ARRAY_TYPE_EXT = 0x8440; + public const int GL_BINORMAL_ARRAY_STRIDE_EXT = 0x8441; + public const int GL_TANGENT_ARRAY_POINTER_EXT = 0x8442; + public const int GL_BINORMAL_ARRAY_POINTER_EXT = 0x8443; + public const int GL_MAP1_TANGENT_EXT = 0x8444; + public const int GL_MAP2_TANGENT_EXT = 0x8445; + public const int GL_MAP1_BINORMAL_EXT = 0x8446; + public const int GL_MAP2_BINORMAL_EXT = 0x8447; + public const int GL_EXT_copy_texture = 1; + public const int GL_EXT_cull_vertex = 1; + public const int GL_CULL_VERTEX_EXT = 0x81AA; + public const int GL_CULL_VERTEX_EYE_POSITION_EXT = 0x81AB; + public const int GL_CULL_VERTEX_OBJECT_POSITION_EXT = 0x81AC; + public const int GL_EXT_debug_label = 1; + public const int GL_PROGRAM_PIPELINE_OBJECT_EXT = 0x8A4F; + public const int GL_PROGRAM_OBJECT_EXT = 0x8B40; + public const int GL_SHADER_OBJECT_EXT = 0x8B48; + public const int GL_BUFFER_OBJECT_EXT = 0x9151; + public const int GL_QUERY_OBJECT_EXT = 0x9153; + public const int GL_VERTEX_ARRAY_OBJECT_EXT = 0x9154; + public const int GL_EXT_debug_marker = 1; + public const int GL_EXT_depth_bounds_test = 1; + public const int GL_DEPTH_BOUNDS_TEST_EXT = 0x8890; + public const int GL_DEPTH_BOUNDS_EXT = 0x8891; + public const int GL_EXT_direct_state_access = 1; + public const int GL_PROGRAM_MATRIX_EXT = 0x8E2D; + public const int GL_TRANSPOSE_PROGRAM_MATRIX_EXT = 0x8E2E; + public const int GL_PROGRAM_MATRIX_STACK_DEPTH_EXT = 0x8E2F; + public const int GL_EXT_draw_buffers2 = 1; + public const int GL_EXT_draw_instanced = 1; + public const int GL_EXT_draw_range_elements = 1; + public const int GL_MAX_ELEMENTS_VERTICES_EXT = 0x80E8; + public const int GL_MAX_ELEMENTS_INDICES_EXT = 0x80E9; + public const int GL_EXT_external_buffer = 1; + public const int GL_EXT_fog_coord = 1; + public const int GL_FOG_COORDINATE_SOURCE_EXT = 0x8450; + public const int GL_FOG_COORDINATE_EXT = 0x8451; + public const int GL_FRAGMENT_DEPTH_EXT = 0x8452; + public const int GL_CURRENT_FOG_COORDINATE_EXT = 0x8453; + public const int GL_FOG_COORDINATE_ARRAY_TYPE_EXT = 0x8454; + public const int GL_FOG_COORDINATE_ARRAY_STRIDE_EXT = 0x8455; + public const int GL_FOG_COORDINATE_ARRAY_POINTER_EXT = 0x8456; + public const int GL_FOG_COORDINATE_ARRAY_EXT = 0x8457; + public const int GL_EXT_framebuffer_blit = 1; + public const int GL_READ_FRAMEBUFFER_EXT = 0x8CA8; + public const int GL_DRAW_FRAMEBUFFER_EXT = 0x8CA9; + public const int GL_DRAW_FRAMEBUFFER_BINDING_EXT = 0x8CA6; + public const int GL_READ_FRAMEBUFFER_BINDING_EXT = 0x8CAA; + public const int GL_EXT_framebuffer_multisample = 1; + public const int GL_RENDERBUFFER_SAMPLES_EXT = 0x8CAB; + public const int GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT = 0x8D56; + public const int GL_MAX_SAMPLES_EXT = 0x8D57; + public const int GL_EXT_framebuffer_multisample_blit_scaled = 1; + public const int GL_SCALED_RESOLVE_FASTEST_EXT = 0x90BA; + public const int GL_SCALED_RESOLVE_NICEST_EXT = 0x90BB; + public const int GL_EXT_framebuffer_object = 1; + public const int GL_INVALID_FRAMEBUFFER_OPERATION_EXT = 0x0506; + public const int GL_MAX_RENDERBUFFER_SIZE_EXT = 0x84E8; + public const int GL_FRAMEBUFFER_BINDING_EXT = 0x8CA6; + public const int GL_RENDERBUFFER_BINDING_EXT = 0x8CA7; + public const int GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT = 0x8CD0; + public const int GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT = 0x8CD1; + public const int GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT = 0x8CD2; + public const int GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT = 0x8CD3; + public const int GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT = 0x8CD4; + public const int GL_FRAMEBUFFER_COMPLETE_EXT = 0x8CD5; + public const int GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT = 0x8CD6; + public const int GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT = 0x8CD7; + public const int GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT = 0x8CD9; + public const int GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT = 0x8CDA; + public const int GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT = 0x8CDB; + public const int GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT = 0x8CDC; + public const int GL_FRAMEBUFFER_UNSUPPORTED_EXT = 0x8CDD; + public const int GL_MAX_COLOR_ATTACHMENTS_EXT = 0x8CDF; + public const int GL_COLOR_ATTACHMENT0_EXT = 0x8CE0; + public const int GL_COLOR_ATTACHMENT1_EXT = 0x8CE1; + public const int GL_COLOR_ATTACHMENT2_EXT = 0x8CE2; + public const int GL_COLOR_ATTACHMENT3_EXT = 0x8CE3; + public const int GL_COLOR_ATTACHMENT4_EXT = 0x8CE4; + public const int GL_COLOR_ATTACHMENT5_EXT = 0x8CE5; + public const int GL_COLOR_ATTACHMENT6_EXT = 0x8CE6; + public const int GL_COLOR_ATTACHMENT7_EXT = 0x8CE7; + public const int GL_COLOR_ATTACHMENT8_EXT = 0x8CE8; + public const int GL_COLOR_ATTACHMENT9_EXT = 0x8CE9; + public const int GL_COLOR_ATTACHMENT10_EXT = 0x8CEA; + public const int GL_COLOR_ATTACHMENT11_EXT = 0x8CEB; + public const int GL_COLOR_ATTACHMENT12_EXT = 0x8CEC; + public const int GL_COLOR_ATTACHMENT13_EXT = 0x8CED; + public const int GL_COLOR_ATTACHMENT14_EXT = 0x8CEE; + public const int GL_COLOR_ATTACHMENT15_EXT = 0x8CEF; + public const int GL_DEPTH_ATTACHMENT_EXT = 0x8D00; + public const int GL_STENCIL_ATTACHMENT_EXT = 0x8D20; + public const int GL_FRAMEBUFFER_EXT = 0x8D40; + public const int GL_RENDERBUFFER_EXT = 0x8D41; + public const int GL_RENDERBUFFER_WIDTH_EXT = 0x8D42; + public const int GL_RENDERBUFFER_HEIGHT_EXT = 0x8D43; + public const int GL_RENDERBUFFER_INTERNAL_FORMAT_EXT = 0x8D44; + public const int GL_STENCIL_INDEX1_EXT = 0x8D46; + public const int GL_STENCIL_INDEX4_EXT = 0x8D47; + public const int GL_STENCIL_INDEX8_EXT = 0x8D48; + public const int GL_STENCIL_INDEX16_EXT = 0x8D49; + public const int GL_RENDERBUFFER_RED_SIZE_EXT = 0x8D50; + public const int GL_RENDERBUFFER_GREEN_SIZE_EXT = 0x8D51; + public const int GL_RENDERBUFFER_BLUE_SIZE_EXT = 0x8D52; + public const int GL_RENDERBUFFER_ALPHA_SIZE_EXT = 0x8D53; + public const int GL_RENDERBUFFER_DEPTH_SIZE_EXT = 0x8D54; + public const int GL_RENDERBUFFER_STENCIL_SIZE_EXT = 0x8D55; + public const int GL_EXT_framebuffer_sRGB = 1; + public const int GL_FRAMEBUFFER_SRGB_EXT = 0x8DB9; + public const int GL_FRAMEBUFFER_SRGB_CAPABLE_EXT = 0x8DBA; + public const int GL_EXT_geometry_shader4 = 1; + public const int GL_GEOMETRY_SHADER_EXT = 0x8DD9; + public const int GL_GEOMETRY_VERTICES_OUT_EXT = 0x8DDA; + public const int GL_GEOMETRY_INPUT_TYPE_EXT = 0x8DDB; + public const int GL_GEOMETRY_OUTPUT_TYPE_EXT = 0x8DDC; + public const int GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT = 0x8C29; + public const int GL_MAX_GEOMETRY_VARYING_COMPONENTS_EXT = 0x8DDD; + public const int GL_MAX_VERTEX_VARYING_COMPONENTS_EXT = 0x8DDE; + public const int GL_MAX_VARYING_COMPONENTS_EXT = 0x8B4B; + public const int GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT = 0x8DDF; + public const int GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT = 0x8DE0; + public const int GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT = 0x8DE1; + public const int GL_LINES_ADJACENCY_EXT = 0x000A; + public const int GL_LINE_STRIP_ADJACENCY_EXT = 0x000B; + public const int GL_TRIANGLES_ADJACENCY_EXT = 0x000C; + public const int GL_TRIANGLE_STRIP_ADJACENCY_EXT = 0x000D; + public const int GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT = 0x8DA8; + public const int GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_EXT = 0x8DA9; + public const int GL_FRAMEBUFFER_ATTACHMENT_LAYERED_EXT = 0x8DA7; + public const int GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT = 0x8CD4; + public const int GL_PROGRAM_POINT_SIZE_EXT = 0x8642; + public const int GL_EXT_gpu_program_parameters = 1; + public const int GL_EXT_gpu_shader4 = 1; + public const int GL_VERTEX_ATTRIB_ARRAY_INTEGER_EXT = 0x88FD; + public const int GL_SAMPLER_1D_ARRAY_EXT = 0x8DC0; + public const int GL_SAMPLER_2D_ARRAY_EXT = 0x8DC1; + public const int GL_SAMPLER_BUFFER_EXT = 0x8DC2; + public const int GL_SAMPLER_1D_ARRAY_SHADOW_EXT = 0x8DC3; + public const int GL_SAMPLER_2D_ARRAY_SHADOW_EXT = 0x8DC4; + public const int GL_SAMPLER_CUBE_SHADOW_EXT = 0x8DC5; + public const int GL_UNSIGNED_INT_VEC2_EXT = 0x8DC6; + public const int GL_UNSIGNED_INT_VEC3_EXT = 0x8DC7; + public const int GL_UNSIGNED_INT_VEC4_EXT = 0x8DC8; + public const int GL_INT_SAMPLER_1D_EXT = 0x8DC9; + public const int GL_INT_SAMPLER_2D_EXT = 0x8DCA; + public const int GL_INT_SAMPLER_3D_EXT = 0x8DCB; + public const int GL_INT_SAMPLER_CUBE_EXT = 0x8DCC; + public const int GL_INT_SAMPLER_2D_RECT_EXT = 0x8DCD; + public const int GL_INT_SAMPLER_1D_ARRAY_EXT = 0x8DCE; + public const int GL_INT_SAMPLER_2D_ARRAY_EXT = 0x8DCF; + public const int GL_INT_SAMPLER_BUFFER_EXT = 0x8DD0; + public const int GL_UNSIGNED_INT_SAMPLER_1D_EXT = 0x8DD1; + public const int GL_UNSIGNED_INT_SAMPLER_2D_EXT = 0x8DD2; + public const int GL_UNSIGNED_INT_SAMPLER_3D_EXT = 0x8DD3; + public const int GL_UNSIGNED_INT_SAMPLER_CUBE_EXT = 0x8DD4; + public const int GL_UNSIGNED_INT_SAMPLER_2D_RECT_EXT = 0x8DD5; + public const int GL_UNSIGNED_INT_SAMPLER_1D_ARRAY_EXT = 0x8DD6; + public const int GL_UNSIGNED_INT_SAMPLER_2D_ARRAY_EXT = 0x8DD7; + public const int GL_UNSIGNED_INT_SAMPLER_BUFFER_EXT = 0x8DD8; + public const int GL_MIN_PROGRAM_TEXEL_OFFSET_EXT = 0x8904; + public const int GL_MAX_PROGRAM_TEXEL_OFFSET_EXT = 0x8905; + public const int GL_EXT_histogram = 1; + public const int GL_HISTOGRAM_EXT = 0x8024; + public const int GL_PROXY_HISTOGRAM_EXT = 0x8025; + public const int GL_HISTOGRAM_WIDTH_EXT = 0x8026; + public const int GL_HISTOGRAM_FORMAT_EXT = 0x8027; + public const int GL_HISTOGRAM_RED_SIZE_EXT = 0x8028; + public const int GL_HISTOGRAM_GREEN_SIZE_EXT = 0x8029; + public const int GL_HISTOGRAM_BLUE_SIZE_EXT = 0x802A; + public const int GL_HISTOGRAM_ALPHA_SIZE_EXT = 0x802B; + public const int GL_HISTOGRAM_LUMINANCE_SIZE_EXT = 0x802C; + public const int GL_HISTOGRAM_SINK_EXT = 0x802D; + public const int GL_MINMAX_EXT = 0x802E; + public const int GL_MINMAX_FORMAT_EXT = 0x802F; + public const int GL_MINMAX_SINK_EXT = 0x8030; + public const int GL_TABLE_TOO_LARGE_EXT = 0x8031; + public const int GL_EXT_index_array_formats = 1; + public const int GL_IUI_V2F_EXT = 0x81AD; + public const int GL_IUI_V3F_EXT = 0x81AE; + public const int GL_IUI_N3F_V2F_EXT = 0x81AF; + public const int GL_IUI_N3F_V3F_EXT = 0x81B0; + public const int GL_T2F_IUI_V2F_EXT = 0x81B1; + public const int GL_T2F_IUI_V3F_EXT = 0x81B2; + public const int GL_T2F_IUI_N3F_V2F_EXT = 0x81B3; + public const int GL_T2F_IUI_N3F_V3F_EXT = 0x81B4; + public const int GL_EXT_index_func = 1; + public const int GL_INDEX_TEST_EXT = 0x81B5; + public const int GL_INDEX_TEST_FUNC_EXT = 0x81B6; + public const int GL_INDEX_TEST_REF_EXT = 0x81B7; + public const int GL_EXT_index_material = 1; + public const int GL_INDEX_MATERIAL_EXT = 0x81B8; + public const int GL_INDEX_MATERIAL_PARAMETER_EXT = 0x81B9; + public const int GL_INDEX_MATERIAL_FACE_EXT = 0x81BA; + public const int GL_EXT_index_texture = 1; + public const int GL_EXT_light_texture = 1; + public const int GL_FRAGMENT_MATERIAL_EXT = 0x8349; + public const int GL_FRAGMENT_NORMAL_EXT = 0x834A; + public const int GL_FRAGMENT_COLOR_EXT = 0x834C; + public const int GL_ATTENUATION_EXT = 0x834D; + public const int GL_SHADOW_ATTENUATION_EXT = 0x834E; + public const int GL_TEXTURE_APPLICATION_MODE_EXT = 0x834F; + public const int GL_TEXTURE_LIGHT_EXT = 0x8350; + public const int GL_TEXTURE_MATERIAL_FACE_EXT = 0x8351; + public const int GL_TEXTURE_MATERIAL_PARAMETER_EXT = 0x8352; + public const int GL_EXT_memory_object = 1; + public const int GL_TEXTURE_TILING_EXT = 0x9580; + public const int GL_DEDICATED_MEMORY_OBJECT_EXT = 0x9581; + public const int GL_PROTECTED_MEMORY_OBJECT_EXT = 0x959B; + public const int GL_NUM_TILING_TYPES_EXT = 0x9582; + public const int GL_TILING_TYPES_EXT = 0x9583; + public const int GL_OPTIMAL_TILING_EXT = 0x9584; + public const int GL_LINEAR_TILING_EXT = 0x9585; + public const int GL_NUM_DEVICE_UUIDS_EXT = 0x9596; + public const int GL_DEVICE_UUID_EXT = 0x9597; + public const int GL_DRIVER_UUID_EXT = 0x9598; + public const int GL_UUID_SIZE_EXT = 16; + public const int GL_EXT_memory_object_fd = 1; + public const int GL_HANDLE_TYPE_OPAQUE_FD_EXT = 0x9586; + public const int GL_EXT_memory_object_win32 = 1; + public const int GL_HANDLE_TYPE_OPAQUE_WIN32_EXT = 0x9587; + public const int GL_HANDLE_TYPE_OPAQUE_WIN32_KMT_EXT = 0x9588; + public const int GL_DEVICE_LUID_EXT = 0x9599; + public const int GL_DEVICE_NODE_MASK_EXT = 0x959A; + public const int GL_LUID_SIZE_EXT = 8; + public const int GL_HANDLE_TYPE_D3D12_TILEPOOL_EXT = 0x9589; + public const int GL_HANDLE_TYPE_D3D12_RESOURCE_EXT = 0x958A; + public const int GL_HANDLE_TYPE_D3D11_IMAGE_EXT = 0x958B; + public const int GL_HANDLE_TYPE_D3D11_IMAGE_KMT_EXT = 0x958C; + public const int GL_EXT_misc_attribute = 1; + public const int GL_EXT_multi_draw_arrays = 1; + public const int GL_EXT_multisample = 1; + public const int GL_MULTISAMPLE_EXT = 0x809D; + public const int GL_SAMPLE_ALPHA_TO_MASK_EXT = 0x809E; + public const int GL_SAMPLE_ALPHA_TO_ONE_EXT = 0x809F; + public const int GL_SAMPLE_MASK_EXT = 0x80A0; + public const int GL_1PASS_EXT = 0x80A1; + public const int GL_2PASS_0_EXT = 0x80A2; + public const int GL_2PASS_1_EXT = 0x80A3; + public const int GL_4PASS_0_EXT = 0x80A4; + public const int GL_4PASS_1_EXT = 0x80A5; + public const int GL_4PASS_2_EXT = 0x80A6; + public const int GL_4PASS_3_EXT = 0x80A7; + public const int GL_SAMPLE_BUFFERS_EXT = 0x80A8; + public const int GL_SAMPLES_EXT = 0x80A9; + public const int GL_SAMPLE_MASK_VALUE_EXT = 0x80AA; + public const int GL_SAMPLE_MASK_INVERT_EXT = 0x80AB; + public const int GL_SAMPLE_PATTERN_EXT = 0x80AC; + public const int GL_MULTISAMPLE_BIT_EXT = 0x20000000; + public const int GL_EXT_packed_depth_stencil = 1; + public const int GL_DEPTH_STENCIL_EXT = 0x84F9; + public const int GL_UNSIGNED_INT_24_8_EXT = 0x84FA; + public const int GL_DEPTH24_STENCIL8_EXT = 0x88F0; + public const int GL_TEXTURE_STENCIL_SIZE_EXT = 0x88F1; + public const int GL_EXT_packed_float = 1; + public const int GL_R11F_G11F_B10F_EXT = 0x8C3A; + public const int GL_UNSIGNED_INT_10F_11F_11F_REV_EXT = 0x8C3B; + public const int GL_RGBA_SIGNED_COMPONENTS_EXT = 0x8C3C; + public const int GL_EXT_packed_pixels = 1; + public const int GL_UNSIGNED_BYTE_3_3_2_EXT = 0x8032; + public const int GL_UNSIGNED_SHORT_4_4_4_4_EXT = 0x8033; + public const int GL_UNSIGNED_SHORT_5_5_5_1_EXT = 0x8034; + public const int GL_UNSIGNED_INT_8_8_8_8_EXT = 0x8035; + public const int GL_UNSIGNED_INT_10_10_10_2_EXT = 0x8036; + public const int GL_EXT_paletted_texture = 1; + public const int GL_COLOR_INDEX1_EXT = 0x80E2; + public const int GL_COLOR_INDEX2_EXT = 0x80E3; + public const int GL_COLOR_INDEX4_EXT = 0x80E4; + public const int GL_COLOR_INDEX8_EXT = 0x80E5; + public const int GL_COLOR_INDEX12_EXT = 0x80E6; + public const int GL_COLOR_INDEX16_EXT = 0x80E7; + public const int GL_TEXTURE_INDEX_SIZE_EXT = 0x80ED; + public const int GL_EXT_pixel_buffer_object = 1; + public const int GL_PIXEL_PACK_BUFFER_EXT = 0x88EB; + public const int GL_PIXEL_UNPACK_BUFFER_EXT = 0x88EC; + public const int GL_PIXEL_PACK_BUFFER_BINDING_EXT = 0x88ED; + public const int GL_PIXEL_UNPACK_BUFFER_BINDING_EXT = 0x88EF; + public const int GL_EXT_pixel_transform = 1; + public const int GL_PIXEL_TRANSFORM_2D_EXT = 0x8330; + public const int GL_PIXEL_MAG_FILTER_EXT = 0x8331; + public const int GL_PIXEL_MIN_FILTER_EXT = 0x8332; + public const int GL_PIXEL_CUBIC_WEIGHT_EXT = 0x8333; + public const int GL_CUBIC_EXT = 0x8334; + public const int GL_AVERAGE_EXT = 0x8335; + public const int GL_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT = 0x8336; + public const int GL_MAX_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT = 0x8337; + public const int GL_PIXEL_TRANSFORM_2D_MATRIX_EXT = 0x8338; + public const int GL_EXT_pixel_transform_color_table = 1; + public const int GL_EXT_point_parameters = 1; + public const int GL_POINT_SIZE_MIN_EXT = 0x8126; + public const int GL_POINT_SIZE_MAX_EXT = 0x8127; + public const int GL_POINT_FADE_THRESHOLD_SIZE_EXT = 0x8128; + public const int GL_DISTANCE_ATTENUATION_EXT = 0x8129; + public const int GL_EXT_polygon_offset = 1; + public const int GL_POLYGON_OFFSET_EXT = 0x8037; + public const int GL_POLYGON_OFFSET_FACTOR_EXT = 0x8038; + public const int GL_POLYGON_OFFSET_BIAS_EXT = 0x8039; + public const int GL_EXT_polygon_offset_clamp = 1; + public const int GL_POLYGON_OFFSET_CLAMP_EXT = 0x8E1B; + public const int GL_EXT_post_depth_coverage = 1; + public const int GL_EXT_provoking_vertex = 1; + public const int GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION_EXT = 0x8E4C; + public const int GL_FIRST_VERTEX_CONVENTION_EXT = 0x8E4D; + public const int GL_LAST_VERTEX_CONVENTION_EXT = 0x8E4E; + public const int GL_PROVOKING_VERTEX_EXT = 0x8E4F; + public const int GL_EXT_raster_multisample = 1; + public const int GL_RASTER_MULTISAMPLE_EXT = 0x9327; + public const int GL_RASTER_SAMPLES_EXT = 0x9328; + public const int GL_MAX_RASTER_SAMPLES_EXT = 0x9329; + public const int GL_RASTER_FIXED_SAMPLE_LOCATIONS_EXT = 0x932A; + public const int GL_MULTISAMPLE_RASTERIZATION_ALLOWED_EXT = 0x932B; + public const int GL_EFFECTIVE_RASTER_SAMPLES_EXT = 0x932C; + public const int GL_EXT_rescale_normal = 1; + public const int GL_RESCALE_NORMAL_EXT = 0x803A; + public const int GL_EXT_secondary_color = 1; + public const int GL_COLOR_SUM_EXT = 0x8458; + public const int GL_CURRENT_SECONDARY_COLOR_EXT = 0x8459; + public const int GL_SECONDARY_COLOR_ARRAY_SIZE_EXT = 0x845A; + public const int GL_SECONDARY_COLOR_ARRAY_TYPE_EXT = 0x845B; + public const int GL_SECONDARY_COLOR_ARRAY_STRIDE_EXT = 0x845C; + public const int GL_SECONDARY_COLOR_ARRAY_POINTER_EXT = 0x845D; + public const int GL_SECONDARY_COLOR_ARRAY_EXT = 0x845E; + public const int GL_EXT_semaphore = 1; + public const int GL_LAYOUT_GENERAL_EXT = 0x958D; + public const int GL_LAYOUT_COLOR_ATTACHMENT_EXT = 0x958E; + public const int GL_LAYOUT_DEPTH_STENCIL_ATTACHMENT_EXT = 0x958F; + public const int GL_LAYOUT_DEPTH_STENCIL_READ_ONLY_EXT = 0x9590; + public const int GL_LAYOUT_SHADER_READ_ONLY_EXT = 0x9591; + public const int GL_LAYOUT_TRANSFER_SRC_EXT = 0x9592; + public const int GL_LAYOUT_TRANSFER_DST_EXT = 0x9593; + public const int GL_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_EXT = 0x9530; + public const int GL_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_EXT = 0x9531; + public const int GL_EXT_semaphore_fd = 1; + public const int GL_EXT_semaphore_win32 = 1; + public const int GL_HANDLE_TYPE_D3D12_FENCE_EXT = 0x9594; + public const int GL_D3D12_FENCE_VALUE_EXT = 0x9595; + public const int GL_EXT_separate_shader_objects = 1; + public const int GL_ACTIVE_PROGRAM_EXT = 0x8B8D; + public const int GL_EXT_separate_specular_color = 1; + public const int GL_LIGHT_MODEL_COLOR_CONTROL_EXT = 0x81F8; + public const int GL_SINGLE_COLOR_EXT = 0x81F9; + public const int GL_SEPARATE_SPECULAR_COLOR_EXT = 0x81FA; + public const int GL_EXT_shader_framebuffer_fetch = 1; + public const int GL_FRAGMENT_SHADER_DISCARDS_SAMPLES_EXT = 0x8A52; + public const int GL_EXT_shader_framebuffer_fetch_non_coherent = 1; + public const int GL_EXT_shader_image_load_formatted = 1; + public const int GL_EXT_shader_image_load_store = 1; + public const int GL_MAX_IMAGE_UNITS_EXT = 0x8F38; + public const int GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS_EXT = 0x8F39; + public const int GL_IMAGE_BINDING_NAME_EXT = 0x8F3A; + public const int GL_IMAGE_BINDING_LEVEL_EXT = 0x8F3B; + public const int GL_IMAGE_BINDING_LAYERED_EXT = 0x8F3C; + public const int GL_IMAGE_BINDING_LAYER_EXT = 0x8F3D; + public const int GL_IMAGE_BINDING_ACCESS_EXT = 0x8F3E; + public const int GL_IMAGE_1D_EXT = 0x904C; + public const int GL_IMAGE_2D_EXT = 0x904D; + public const int GL_IMAGE_3D_EXT = 0x904E; + public const int GL_IMAGE_2D_RECT_EXT = 0x904F; + public const int GL_IMAGE_CUBE_EXT = 0x9050; + public const int GL_IMAGE_BUFFER_EXT = 0x9051; + public const int GL_IMAGE_1D_ARRAY_EXT = 0x9052; + public const int GL_IMAGE_2D_ARRAY_EXT = 0x9053; + public const int GL_IMAGE_CUBE_MAP_ARRAY_EXT = 0x9054; + public const int GL_IMAGE_2D_MULTISAMPLE_EXT = 0x9055; + public const int GL_IMAGE_2D_MULTISAMPLE_ARRAY_EXT = 0x9056; + public const int GL_INT_IMAGE_1D_EXT = 0x9057; + public const int GL_INT_IMAGE_2D_EXT = 0x9058; + public const int GL_INT_IMAGE_3D_EXT = 0x9059; + public const int GL_INT_IMAGE_2D_RECT_EXT = 0x905A; + public const int GL_INT_IMAGE_CUBE_EXT = 0x905B; + public const int GL_INT_IMAGE_BUFFER_EXT = 0x905C; + public const int GL_INT_IMAGE_1D_ARRAY_EXT = 0x905D; + public const int GL_INT_IMAGE_2D_ARRAY_EXT = 0x905E; + public const int GL_INT_IMAGE_CUBE_MAP_ARRAY_EXT = 0x905F; + public const int GL_INT_IMAGE_2D_MULTISAMPLE_EXT = 0x9060; + public const int GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY_EXT = 0x9061; + public const int GL_UNSIGNED_INT_IMAGE_1D_EXT = 0x9062; + public const int GL_UNSIGNED_INT_IMAGE_2D_EXT = 0x9063; + public const int GL_UNSIGNED_INT_IMAGE_3D_EXT = 0x9064; + public const int GL_UNSIGNED_INT_IMAGE_2D_RECT_EXT = 0x9065; + public const int GL_UNSIGNED_INT_IMAGE_CUBE_EXT = 0x9066; + public const int GL_UNSIGNED_INT_IMAGE_BUFFER_EXT = 0x9067; + public const int GL_UNSIGNED_INT_IMAGE_1D_ARRAY_EXT = 0x9068; + public const int GL_UNSIGNED_INT_IMAGE_2D_ARRAY_EXT = 0x9069; + public const int GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY_EXT = 0x906A; + public const int GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_EXT = 0x906B; + public const int GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY_EXT = 0x906C; + public const int GL_MAX_IMAGE_SAMPLES_EXT = 0x906D; + public const int GL_IMAGE_BINDING_FORMAT_EXT = 0x906E; + public const int GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT_EXT = 0x00000001; + public const int GL_ELEMENT_ARRAY_BARRIER_BIT_EXT = 0x00000002; + public const int GL_UNIFORM_BARRIER_BIT_EXT = 0x00000004; + public const int GL_TEXTURE_FETCH_BARRIER_BIT_EXT = 0x00000008; + public const int GL_SHADER_IMAGE_ACCESS_BARRIER_BIT_EXT = 0x00000020; + public const int GL_COMMAND_BARRIER_BIT_EXT = 0x00000040; + public const int GL_PIXEL_BUFFER_BARRIER_BIT_EXT = 0x00000080; + public const int GL_TEXTURE_UPDATE_BARRIER_BIT_EXT = 0x00000100; + public const int GL_BUFFER_UPDATE_BARRIER_BIT_EXT = 0x00000200; + public const int GL_FRAMEBUFFER_BARRIER_BIT_EXT = 0x00000400; + public const int GL_TRANSFORM_FEEDBACK_BARRIER_BIT_EXT = 0x00000800; + public const int GL_ATOMIC_COUNTER_BARRIER_BIT_EXT = 0x00001000; + public const int GL_ALL_BARRIER_BITS_EXT = -1; + public const int GL_EXT_shader_integer_mix = 1; + public const int GL_EXT_shadow_funcs = 1; + public const int GL_EXT_shared_texture_palette = 1; + public const int GL_SHARED_TEXTURE_PALETTE_EXT = 0x81FB; + public const int GL_EXT_sparse_texture2 = 1; + public const int GL_EXT_stencil_clear_tag = 1; + public const int GL_STENCIL_TAG_BITS_EXT = 0x88F2; + public const int GL_STENCIL_CLEAR_TAG_VALUE_EXT = 0x88F3; + public const int GL_EXT_stencil_two_side = 1; + public const int GL_STENCIL_TEST_TWO_SIDE_EXT = 0x8910; + public const int GL_ACTIVE_STENCIL_FACE_EXT = 0x8911; + public const int GL_EXT_stencil_wrap = 1; + public const int GL_INCR_WRAP_EXT = 0x8507; + public const int GL_DECR_WRAP_EXT = 0x8508; + public const int GL_EXT_subtexture = 1; + public const int GL_EXT_texture = 1; + public const int GL_ALPHA4_EXT = 0x803B; + public const int GL_ALPHA8_EXT = 0x803C; + public const int GL_ALPHA12_EXT = 0x803D; + public const int GL_ALPHA16_EXT = 0x803E; + public const int GL_LUMINANCE4_EXT = 0x803F; + public const int GL_LUMINANCE8_EXT = 0x8040; + public const int GL_LUMINANCE12_EXT = 0x8041; + public const int GL_LUMINANCE16_EXT = 0x8042; + public const int GL_LUMINANCE4_ALPHA4_EXT = 0x8043; + public const int GL_LUMINANCE6_ALPHA2_EXT = 0x8044; + public const int GL_LUMINANCE8_ALPHA8_EXT = 0x8045; + public const int GL_LUMINANCE12_ALPHA4_EXT = 0x8046; + public const int GL_LUMINANCE12_ALPHA12_EXT = 0x8047; + public const int GL_LUMINANCE16_ALPHA16_EXT = 0x8048; + public const int GL_INTENSITY_EXT = 0x8049; + public const int GL_INTENSITY4_EXT = 0x804A; + public const int GL_INTENSITY8_EXT = 0x804B; + public const int GL_INTENSITY12_EXT = 0x804C; + public const int GL_INTENSITY16_EXT = 0x804D; + public const int GL_RGB2_EXT = 0x804E; + public const int GL_RGB4_EXT = 0x804F; + public const int GL_RGB5_EXT = 0x8050; + public const int GL_RGB8_EXT = 0x8051; + public const int GL_RGB10_EXT = 0x8052; + public const int GL_RGB12_EXT = 0x8053; + public const int GL_RGB16_EXT = 0x8054; + public const int GL_RGBA2_EXT = 0x8055; + public const int GL_RGBA4_EXT = 0x8056; + public const int GL_RGB5_A1_EXT = 0x8057; + public const int GL_RGBA8_EXT = 0x8058; + public const int GL_RGB10_A2_EXT = 0x8059; + public const int GL_RGBA12_EXT = 0x805A; + public const int GL_RGBA16_EXT = 0x805B; + public const int GL_TEXTURE_RED_SIZE_EXT = 0x805C; + public const int GL_TEXTURE_GREEN_SIZE_EXT = 0x805D; + public const int GL_TEXTURE_BLUE_SIZE_EXT = 0x805E; + public const int GL_TEXTURE_ALPHA_SIZE_EXT = 0x805F; + public const int GL_TEXTURE_LUMINANCE_SIZE_EXT = 0x8060; + public const int GL_TEXTURE_INTENSITY_SIZE_EXT = 0x8061; + public const int GL_REPLACE_EXT = 0x8062; + public const int GL_PROXY_TEXTURE_1D_EXT = 0x8063; + public const int GL_PROXY_TEXTURE_2D_EXT = 0x8064; + public const int GL_TEXTURE_TOO_LARGE_EXT = 0x8065; + public const int GL_EXT_texture3D = 1; + public const int GL_PACK_SKIP_IMAGES_EXT = 0x806B; + public const int GL_PACK_IMAGE_HEIGHT_EXT = 0x806C; + public const int GL_UNPACK_SKIP_IMAGES_EXT = 0x806D; + public const int GL_UNPACK_IMAGE_HEIGHT_EXT = 0x806E; + public const int GL_TEXTURE_3D_EXT = 0x806F; + public const int GL_PROXY_TEXTURE_3D_EXT = 0x8070; + public const int GL_TEXTURE_DEPTH_EXT = 0x8071; + public const int GL_TEXTURE_WRAP_R_EXT = 0x8072; + public const int GL_MAX_3D_TEXTURE_SIZE_EXT = 0x8073; + public const int GL_EXT_texture_array = 1; + public const int GL_TEXTURE_1D_ARRAY_EXT = 0x8C18; + public const int GL_PROXY_TEXTURE_1D_ARRAY_EXT = 0x8C19; + public const int GL_TEXTURE_2D_ARRAY_EXT = 0x8C1A; + public const int GL_PROXY_TEXTURE_2D_ARRAY_EXT = 0x8C1B; + public const int GL_TEXTURE_BINDING_1D_ARRAY_EXT = 0x8C1C; + public const int GL_TEXTURE_BINDING_2D_ARRAY_EXT = 0x8C1D; + public const int GL_MAX_ARRAY_TEXTURE_LAYERS_EXT = 0x88FF; + public const int GL_COMPARE_REF_DEPTH_TO_TEXTURE_EXT = 0x884E; + public const int GL_EXT_texture_buffer_object = 1; + public const int GL_TEXTURE_BUFFER_EXT = 0x8C2A; + public const int GL_MAX_TEXTURE_BUFFER_SIZE_EXT = 0x8C2B; + public const int GL_TEXTURE_BINDING_BUFFER_EXT = 0x8C2C; + public const int GL_TEXTURE_BUFFER_DATA_STORE_BINDING_EXT = 0x8C2D; + public const int GL_TEXTURE_BUFFER_FORMAT_EXT = 0x8C2E; + public const int GL_EXT_texture_compression_latc = 1; + public const int GL_COMPRESSED_LUMINANCE_LATC1_EXT = 0x8C70; + public const int GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT = 0x8C71; + public const int GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT = 0x8C72; + public const int GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT = 0x8C73; + public const int GL_EXT_texture_compression_rgtc = 1; + public const int GL_COMPRESSED_RED_RGTC1_EXT = 0x8DBB; + public const int GL_COMPRESSED_SIGNED_RED_RGTC1_EXT = 0x8DBC; + public const int GL_COMPRESSED_RED_GREEN_RGTC2_EXT = 0x8DBD; + public const int GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT = 0x8DBE; + public const int GL_EXT_texture_compression_s3tc = 1; + public const int GL_COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0; + public const int GL_COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1; + public const int GL_COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2; + public const int GL_COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3; + public const int GL_EXT_texture_cube_map = 1; + public const int GL_NORMAL_MAP_EXT = 0x8511; + public const int GL_REFLECTION_MAP_EXT = 0x8512; + public const int GL_TEXTURE_CUBE_MAP_EXT = 0x8513; + public const int GL_TEXTURE_BINDING_CUBE_MAP_EXT = 0x8514; + public const int GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT = 0x8515; + public const int GL_TEXTURE_CUBE_MAP_NEGATIVE_X_EXT = 0x8516; + public const int GL_TEXTURE_CUBE_MAP_POSITIVE_Y_EXT = 0x8517; + public const int GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_EXT = 0x8518; + public const int GL_TEXTURE_CUBE_MAP_POSITIVE_Z_EXT = 0x8519; + public const int GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_EXT = 0x851A; + public const int GL_PROXY_TEXTURE_CUBE_MAP_EXT = 0x851B; + public const int GL_MAX_CUBE_MAP_TEXTURE_SIZE_EXT = 0x851C; + public const int GL_EXT_texture_env_add = 1; + public const int GL_EXT_texture_env_combine = 1; + public const int GL_COMBINE_EXT = 0x8570; + public const int GL_COMBINE_RGB_EXT = 0x8571; + public const int GL_COMBINE_ALPHA_EXT = 0x8572; + public const int GL_RGB_SCALE_EXT = 0x8573; + public const int GL_ADD_SIGNED_EXT = 0x8574; + public const int GL_INTERPOLATE_EXT = 0x8575; + public const int GL_CONSTANT_EXT = 0x8576; + public const int GL_PRIMARY_COLOR_EXT = 0x8577; + public const int GL_PREVIOUS_EXT = 0x8578; + public const int GL_SOURCE0_RGB_EXT = 0x8580; + public const int GL_SOURCE1_RGB_EXT = 0x8581; + public const int GL_SOURCE2_RGB_EXT = 0x8582; + public const int GL_SOURCE0_ALPHA_EXT = 0x8588; + public const int GL_SOURCE1_ALPHA_EXT = 0x8589; + public const int GL_SOURCE2_ALPHA_EXT = 0x858A; + public const int GL_OPERAND0_RGB_EXT = 0x8590; + public const int GL_OPERAND1_RGB_EXT = 0x8591; + public const int GL_OPERAND2_RGB_EXT = 0x8592; + public const int GL_OPERAND0_ALPHA_EXT = 0x8598; + public const int GL_OPERAND1_ALPHA_EXT = 0x8599; + public const int GL_OPERAND2_ALPHA_EXT = 0x859A; + public const int GL_EXT_texture_env_dot3 = 1; + public const int GL_DOT3_RGB_EXT = 0x8740; + public const int GL_DOT3_RGBA_EXT = 0x8741; + public const int GL_EXT_texture_filter_anisotropic = 1; + public const int GL_TEXTURE_MAX_ANISOTROPY_EXT = 0x84FE; + public const int GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT = 0x84FF; + public const int GL_EXT_texture_filter_minmax = 1; + public const int GL_TEXTURE_REDUCTION_MODE_EXT = 0x9366; + public const int GL_WEIGHTED_AVERAGE_EXT = 0x9367; + public const int GL_EXT_texture_integer = 1; + public const int GL_RGBA32UI_EXT = 0x8D70; + public const int GL_RGB32UI_EXT = 0x8D71; + public const int GL_ALPHA32UI_EXT = 0x8D72; + public const int GL_INTENSITY32UI_EXT = 0x8D73; + public const int GL_LUMINANCE32UI_EXT = 0x8D74; + public const int GL_LUMINANCE_ALPHA32UI_EXT = 0x8D75; + public const int GL_RGBA16UI_EXT = 0x8D76; + public const int GL_RGB16UI_EXT = 0x8D77; + public const int GL_ALPHA16UI_EXT = 0x8D78; + public const int GL_INTENSITY16UI_EXT = 0x8D79; + public const int GL_LUMINANCE16UI_EXT = 0x8D7A; + public const int GL_LUMINANCE_ALPHA16UI_EXT = 0x8D7B; + public const int GL_RGBA8UI_EXT = 0x8D7C; + public const int GL_RGB8UI_EXT = 0x8D7D; + public const int GL_ALPHA8UI_EXT = 0x8D7E; + public const int GL_INTENSITY8UI_EXT = 0x8D7F; + public const int GL_LUMINANCE8UI_EXT = 0x8D80; + public const int GL_LUMINANCE_ALPHA8UI_EXT = 0x8D81; + public const int GL_RGBA32I_EXT = 0x8D82; + public const int GL_RGB32I_EXT = 0x8D83; + public const int GL_ALPHA32I_EXT = 0x8D84; + public const int GL_INTENSITY32I_EXT = 0x8D85; + public const int GL_LUMINANCE32I_EXT = 0x8D86; + public const int GL_LUMINANCE_ALPHA32I_EXT = 0x8D87; + public const int GL_RGBA16I_EXT = 0x8D88; + public const int GL_RGB16I_EXT = 0x8D89; + public const int GL_ALPHA16I_EXT = 0x8D8A; + public const int GL_INTENSITY16I_EXT = 0x8D8B; + public const int GL_LUMINANCE16I_EXT = 0x8D8C; + public const int GL_LUMINANCE_ALPHA16I_EXT = 0x8D8D; + public const int GL_RGBA8I_EXT = 0x8D8E; + public const int GL_RGB8I_EXT = 0x8D8F; + public const int GL_ALPHA8I_EXT = 0x8D90; + public const int GL_INTENSITY8I_EXT = 0x8D91; + public const int GL_LUMINANCE8I_EXT = 0x8D92; + public const int GL_LUMINANCE_ALPHA8I_EXT = 0x8D93; + public const int GL_RED_INTEGER_EXT = 0x8D94; + public const int GL_GREEN_INTEGER_EXT = 0x8D95; + public const int GL_BLUE_INTEGER_EXT = 0x8D96; + public const int GL_ALPHA_INTEGER_EXT = 0x8D97; + public const int GL_RGB_INTEGER_EXT = 0x8D98; + public const int GL_RGBA_INTEGER_EXT = 0x8D99; + public const int GL_BGR_INTEGER_EXT = 0x8D9A; + public const int GL_BGRA_INTEGER_EXT = 0x8D9B; + public const int GL_LUMINANCE_INTEGER_EXT = 0x8D9C; + public const int GL_LUMINANCE_ALPHA_INTEGER_EXT = 0x8D9D; + public const int GL_RGBA_INTEGER_MODE_EXT = 0x8D9E; + public const int GL_EXT_texture_lod_bias = 1; + public const int GL_MAX_TEXTURE_LOD_BIAS_EXT = 0x84FD; + public const int GL_TEXTURE_FILTER_CONTROL_EXT = 0x8500; + public const int GL_TEXTURE_LOD_BIAS_EXT = 0x8501; + public const int GL_EXT_texture_mirror_clamp = 1; + public const int GL_MIRROR_CLAMP_EXT = 0x8742; + public const int GL_MIRROR_CLAMP_TO_EDGE_EXT = 0x8743; + public const int GL_MIRROR_CLAMP_TO_BORDER_EXT = 0x8912; + public const int GL_EXT_texture_object = 1; + public const int GL_TEXTURE_PRIORITY_EXT = 0x8066; + public const int GL_TEXTURE_RESIDENT_EXT = 0x8067; + public const int GL_TEXTURE_1D_BINDING_EXT = 0x8068; + public const int GL_TEXTURE_2D_BINDING_EXT = 0x8069; + public const int GL_TEXTURE_3D_BINDING_EXT = 0x806A; + public const int GL_EXT_texture_perturb_normal = 1; + public const int GL_PERTURB_EXT = 0x85AE; + public const int GL_TEXTURE_NORMAL_EXT = 0x85AF; + public const int GL_EXT_texture_sRGB = 1; + public const int GL_SRGB_EXT = 0x8C40; + public const int GL_SRGB8_EXT = 0x8C41; + public const int GL_SRGB_ALPHA_EXT = 0x8C42; + public const int GL_SRGB8_ALPHA8_EXT = 0x8C43; + public const int GL_SLUMINANCE_ALPHA_EXT = 0x8C44; + public const int GL_SLUMINANCE8_ALPHA8_EXT = 0x8C45; + public const int GL_SLUMINANCE_EXT = 0x8C46; + public const int GL_SLUMINANCE8_EXT = 0x8C47; + public const int GL_COMPRESSED_SRGB_EXT = 0x8C48; + public const int GL_COMPRESSED_SRGB_ALPHA_EXT = 0x8C49; + public const int GL_COMPRESSED_SLUMINANCE_EXT = 0x8C4A; + public const int GL_COMPRESSED_SLUMINANCE_ALPHA_EXT = 0x8C4B; + public const int GL_COMPRESSED_SRGB_S3TC_DXT1_EXT = 0x8C4C; + public const int GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT = 0x8C4D; + public const int GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT = 0x8C4E; + public const int GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT = 0x8C4F; + public const int GL_EXT_texture_sRGB_decode = 1; + public const int GL_TEXTURE_SRGB_DECODE_EXT = 0x8A48; + public const int GL_DECODE_EXT = 0x8A49; + public const int GL_SKIP_DECODE_EXT = 0x8A4A; + public const int GL_EXT_texture_shared_exponent = 1; + public const int GL_RGB9_E5_EXT = 0x8C3D; + public const int GL_UNSIGNED_INT_5_9_9_9_REV_EXT = 0x8C3E; + public const int GL_TEXTURE_SHARED_SIZE_EXT = 0x8C3F; + public const int GL_EXT_texture_snorm = 1; + public const int GL_ALPHA_SNORM = 0x9010; + public const int GL_LUMINANCE_SNORM = 0x9011; + public const int GL_LUMINANCE_ALPHA_SNORM = 0x9012; + public const int GL_INTENSITY_SNORM = 0x9013; + public const int GL_ALPHA8_SNORM = 0x9014; + public const int GL_LUMINANCE8_SNORM = 0x9015; + public const int GL_LUMINANCE8_ALPHA8_SNORM = 0x9016; + public const int GL_INTENSITY8_SNORM = 0x9017; + public const int GL_ALPHA16_SNORM = 0x9018; + public const int GL_LUMINANCE16_SNORM = 0x9019; + public const int GL_LUMINANCE16_ALPHA16_SNORM = 0x901A; + public const int GL_INTENSITY16_SNORM = 0x901B; + public const int GL_RED_SNORM = 0x8F90; + public const int GL_RG_SNORM = 0x8F91; + public const int GL_RGB_SNORM = 0x8F92; + public const int GL_RGBA_SNORM = 0x8F93; + public const int GL_EXT_texture_swizzle = 1; + public const int GL_TEXTURE_SWIZZLE_R_EXT = 0x8E42; + public const int GL_TEXTURE_SWIZZLE_G_EXT = 0x8E43; + public const int GL_TEXTURE_SWIZZLE_B_EXT = 0x8E44; + public const int GL_TEXTURE_SWIZZLE_A_EXT = 0x8E45; + public const int GL_TEXTURE_SWIZZLE_RGBA_EXT = 0x8E46; + public const int GL_EXT_timer_query = 1; + public const int GL_TIME_ELAPSED_EXT = 0x88BF; + public const int GL_EXT_transform_feedback = 1; + public const int GL_TRANSFORM_FEEDBACK_BUFFER_EXT = 0x8C8E; + public const int GL_TRANSFORM_FEEDBACK_BUFFER_START_EXT = 0x8C84; + public const int GL_TRANSFORM_FEEDBACK_BUFFER_SIZE_EXT = 0x8C85; + public const int GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_EXT = 0x8C8F; + public const int GL_INTERLEAVED_ATTRIBS_EXT = 0x8C8C; + public const int GL_SEPARATE_ATTRIBS_EXT = 0x8C8D; + public const int GL_PRIMITIVES_GENERATED_EXT = 0x8C87; + public const int GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_EXT = 0x8C88; + public const int GL_RASTERIZER_DISCARD_EXT = 0x8C89; + public const int GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_EXT = 0x8C8A; + public const int GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_EXT = 0x8C8B; + public const int GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_EXT = 0x8C80; + public const int GL_TRANSFORM_FEEDBACK_VARYINGS_EXT = 0x8C83; + public const int GL_TRANSFORM_FEEDBACK_BUFFER_MODE_EXT = 0x8C7F; + public const int GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH_EXT = 0x8C76; + public const int GL_EXT_vertex_array = 1; + public const int GL_VERTEX_ARRAY_EXT = 0x8074; + public const int GL_NORMAL_ARRAY_EXT = 0x8075; + public const int GL_COLOR_ARRAY_EXT = 0x8076; + public const int GL_INDEX_ARRAY_EXT = 0x8077; + public const int GL_TEXTURE_COORD_ARRAY_EXT = 0x8078; + public const int GL_EDGE_FLAG_ARRAY_EXT = 0x8079; + public const int GL_VERTEX_ARRAY_SIZE_EXT = 0x807A; + public const int GL_VERTEX_ARRAY_TYPE_EXT = 0x807B; + public const int GL_VERTEX_ARRAY_STRIDE_EXT = 0x807C; + public const int GL_VERTEX_ARRAY_COUNT_EXT = 0x807D; + public const int GL_NORMAL_ARRAY_TYPE_EXT = 0x807E; + public const int GL_NORMAL_ARRAY_STRIDE_EXT = 0x807F; + public const int GL_NORMAL_ARRAY_COUNT_EXT = 0x8080; + public const int GL_COLOR_ARRAY_SIZE_EXT = 0x8081; + public const int GL_COLOR_ARRAY_TYPE_EXT = 0x8082; + public const int GL_COLOR_ARRAY_STRIDE_EXT = 0x8083; + public const int GL_COLOR_ARRAY_COUNT_EXT = 0x8084; + public const int GL_INDEX_ARRAY_TYPE_EXT = 0x8085; + public const int GL_INDEX_ARRAY_STRIDE_EXT = 0x8086; + public const int GL_INDEX_ARRAY_COUNT_EXT = 0x8087; + public const int GL_TEXTURE_COORD_ARRAY_SIZE_EXT = 0x8088; + public const int GL_TEXTURE_COORD_ARRAY_TYPE_EXT = 0x8089; + public const int GL_TEXTURE_COORD_ARRAY_STRIDE_EXT = 0x808A; + public const int GL_TEXTURE_COORD_ARRAY_COUNT_EXT = 0x808B; + public const int GL_EDGE_FLAG_ARRAY_STRIDE_EXT = 0x808C; + public const int GL_EDGE_FLAG_ARRAY_COUNT_EXT = 0x808D; + public const int GL_VERTEX_ARRAY_POINTER_EXT = 0x808E; + public const int GL_NORMAL_ARRAY_POINTER_EXT = 0x808F; + public const int GL_COLOR_ARRAY_POINTER_EXT = 0x8090; + public const int GL_INDEX_ARRAY_POINTER_EXT = 0x8091; + public const int GL_TEXTURE_COORD_ARRAY_POINTER_EXT = 0x8092; + public const int GL_EDGE_FLAG_ARRAY_POINTER_EXT = 0x8093; + public const int GL_EXT_vertex_array_bgra = 1; + public const int GL_EXT_vertex_attrib_64bit = 1; + public const int GL_DOUBLE_VEC2_EXT = 0x8FFC; + public const int GL_DOUBLE_VEC3_EXT = 0x8FFD; + public const int GL_DOUBLE_VEC4_EXT = 0x8FFE; + public const int GL_DOUBLE_MAT2_EXT = 0x8F46; + public const int GL_DOUBLE_MAT3_EXT = 0x8F47; + public const int GL_DOUBLE_MAT4_EXT = 0x8F48; + public const int GL_DOUBLE_MAT2x3_EXT = 0x8F49; + public const int GL_DOUBLE_MAT2x4_EXT = 0x8F4A; + public const int GL_DOUBLE_MAT3x2_EXT = 0x8F4B; + public const int GL_DOUBLE_MAT3x4_EXT = 0x8F4C; + public const int GL_DOUBLE_MAT4x2_EXT = 0x8F4D; + public const int GL_DOUBLE_MAT4x3_EXT = 0x8F4E; + public const int GL_EXT_vertex_shader = 1; + public const int GL_VERTEX_SHADER_EXT = 0x8780; + public const int GL_VERTEX_SHADER_BINDING_EXT = 0x8781; + public const int GL_OP_INDEX_EXT = 0x8782; + public const int GL_OP_NEGATE_EXT = 0x8783; + public const int GL_OP_DOT3_EXT = 0x8784; + public const int GL_OP_DOT4_EXT = 0x8785; + public const int GL_OP_MUL_EXT = 0x8786; + public const int GL_OP_ADD_EXT = 0x8787; + public const int GL_OP_MADD_EXT = 0x8788; + public const int GL_OP_FRAC_EXT = 0x8789; + public const int GL_OP_MAX_EXT = 0x878A; + public const int GL_OP_MIN_EXT = 0x878B; + public const int GL_OP_SET_GE_EXT = 0x878C; + public const int GL_OP_SET_LT_EXT = 0x878D; + public const int GL_OP_CLAMP_EXT = 0x878E; + public const int GL_OP_FLOOR_EXT = 0x878F; + public const int GL_OP_ROUND_EXT = 0x8790; + public const int GL_OP_EXP_BASE_2_EXT = 0x8791; + public const int GL_OP_LOG_BASE_2_EXT = 0x8792; + public const int GL_OP_POWER_EXT = 0x8793; + public const int GL_OP_RECIP_EXT = 0x8794; + public const int GL_OP_RECIP_SQRT_EXT = 0x8795; + public const int GL_OP_SUB_EXT = 0x8796; + public const int GL_OP_CROSS_PRODUCT_EXT = 0x8797; + public const int GL_OP_MULTIPLY_MATRIX_EXT = 0x8798; + public const int GL_OP_MOV_EXT = 0x8799; + public const int GL_OUTPUT_VERTEX_EXT = 0x879A; + public const int GL_OUTPUT_COLOR0_EXT = 0x879B; + public const int GL_OUTPUT_COLOR1_EXT = 0x879C; + public const int GL_OUTPUT_TEXTURE_COORD0_EXT = 0x879D; + public const int GL_OUTPUT_TEXTURE_COORD1_EXT = 0x879E; + public const int GL_OUTPUT_TEXTURE_COORD2_EXT = 0x879F; + public const int GL_OUTPUT_TEXTURE_COORD3_EXT = 0x87A0; + public const int GL_OUTPUT_TEXTURE_COORD4_EXT = 0x87A1; + public const int GL_OUTPUT_TEXTURE_COORD5_EXT = 0x87A2; + public const int GL_OUTPUT_TEXTURE_COORD6_EXT = 0x87A3; + public const int GL_OUTPUT_TEXTURE_COORD7_EXT = 0x87A4; + public const int GL_OUTPUT_TEXTURE_COORD8_EXT = 0x87A5; + public const int GL_OUTPUT_TEXTURE_COORD9_EXT = 0x87A6; + public const int GL_OUTPUT_TEXTURE_COORD10_EXT = 0x87A7; + public const int GL_OUTPUT_TEXTURE_COORD11_EXT = 0x87A8; + public const int GL_OUTPUT_TEXTURE_COORD12_EXT = 0x87A9; + public const int GL_OUTPUT_TEXTURE_COORD13_EXT = 0x87AA; + public const int GL_OUTPUT_TEXTURE_COORD14_EXT = 0x87AB; + public const int GL_OUTPUT_TEXTURE_COORD15_EXT = 0x87AC; + public const int GL_OUTPUT_TEXTURE_COORD16_EXT = 0x87AD; + public const int GL_OUTPUT_TEXTURE_COORD17_EXT = 0x87AE; + public const int GL_OUTPUT_TEXTURE_COORD18_EXT = 0x87AF; + public const int GL_OUTPUT_TEXTURE_COORD19_EXT = 0x87B0; + public const int GL_OUTPUT_TEXTURE_COORD20_EXT = 0x87B1; + public const int GL_OUTPUT_TEXTURE_COORD21_EXT = 0x87B2; + public const int GL_OUTPUT_TEXTURE_COORD22_EXT = 0x87B3; + public const int GL_OUTPUT_TEXTURE_COORD23_EXT = 0x87B4; + public const int GL_OUTPUT_TEXTURE_COORD24_EXT = 0x87B5; + public const int GL_OUTPUT_TEXTURE_COORD25_EXT = 0x87B6; + public const int GL_OUTPUT_TEXTURE_COORD26_EXT = 0x87B7; + public const int GL_OUTPUT_TEXTURE_COORD27_EXT = 0x87B8; + public const int GL_OUTPUT_TEXTURE_COORD28_EXT = 0x87B9; + public const int GL_OUTPUT_TEXTURE_COORD29_EXT = 0x87BA; + public const int GL_OUTPUT_TEXTURE_COORD30_EXT = 0x87BB; + public const int GL_OUTPUT_TEXTURE_COORD31_EXT = 0x87BC; + public const int GL_OUTPUT_FOG_EXT = 0x87BD; + public const int GL_SCALAR_EXT = 0x87BE; + public const int GL_VECTOR_EXT = 0x87BF; + public const int GL_MATRIX_EXT = 0x87C0; + public const int GL_VARIANT_EXT = 0x87C1; + public const int GL_INVARIANT_EXT = 0x87C2; + public const int GL_LOCAL_CONSTANT_EXT = 0x87C3; + public const int GL_LOCAL_EXT = 0x87C4; + public const int GL_MAX_VERTEX_SHADER_INSTRUCTIONS_EXT = 0x87C5; + public const int GL_MAX_VERTEX_SHADER_VARIANTS_EXT = 0x87C6; + public const int GL_MAX_VERTEX_SHADER_INVARIANTS_EXT = 0x87C7; + public const int GL_MAX_VERTEX_SHADER_LOCAL_CONSTANTS_EXT = 0x87C8; + public const int GL_MAX_VERTEX_SHADER_LOCALS_EXT = 0x87C9; + public const int GL_MAX_OPTIMIZED_VERTEX_SHADER_INSTRUCTIONS_EXT = 0x87CA; + public const int GL_MAX_OPTIMIZED_VERTEX_SHADER_VARIANTS_EXT = 0x87CB; + public const int GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCAL_CONSTANTS_EXT = 0x87CC; + public const int GL_MAX_OPTIMIZED_VERTEX_SHADER_INVARIANTS_EXT = 0x87CD; + public const int GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCALS_EXT = 0x87CE; + public const int GL_VERTEX_SHADER_INSTRUCTIONS_EXT = 0x87CF; + public const int GL_VERTEX_SHADER_VARIANTS_EXT = 0x87D0; + public const int GL_VERTEX_SHADER_INVARIANTS_EXT = 0x87D1; + public const int GL_VERTEX_SHADER_LOCAL_CONSTANTS_EXT = 0x87D2; + public const int GL_VERTEX_SHADER_LOCALS_EXT = 0x87D3; + public const int GL_VERTEX_SHADER_OPTIMIZED_EXT = 0x87D4; + public const int GL_X_EXT = 0x87D5; + public const int GL_Y_EXT = 0x87D6; + public const int GL_Z_EXT = 0x87D7; + public const int GL_W_EXT = 0x87D8; + public const int GL_NEGATIVE_X_EXT = 0x87D9; + public const int GL_NEGATIVE_Y_EXT = 0x87DA; + public const int GL_NEGATIVE_Z_EXT = 0x87DB; + public const int GL_NEGATIVE_W_EXT = 0x87DC; + public const int GL_ZERO_EXT = 0x87DD; + public const int GL_ONE_EXT = 0x87DE; + public const int GL_NEGATIVE_ONE_EXT = 0x87DF; + public const int GL_NORMALIZED_RANGE_EXT = 0x87E0; + public const int GL_FULL_RANGE_EXT = 0x87E1; + public const int GL_CURRENT_VERTEX_EXT = 0x87E2; + public const int GL_MVP_MATRIX_EXT = 0x87E3; + public const int GL_VARIANT_VALUE_EXT = 0x87E4; + public const int GL_VARIANT_DATATYPE_EXT = 0x87E5; + public const int GL_VARIANT_ARRAY_STRIDE_EXT = 0x87E6; + public const int GL_VARIANT_ARRAY_TYPE_EXT = 0x87E7; + public const int GL_VARIANT_ARRAY_EXT = 0x87E8; + public const int GL_VARIANT_ARRAY_POINTER_EXT = 0x87E9; + public const int GL_INVARIANT_VALUE_EXT = 0x87EA; + public const int GL_INVARIANT_DATATYPE_EXT = 0x87EB; + public const int GL_LOCAL_CONSTANT_VALUE_EXT = 0x87EC; + public const int GL_LOCAL_CONSTANT_DATATYPE_EXT = 0x87ED; + public const int GL_EXT_vertex_weighting = 1; + public const int GL_MODELVIEW0_STACK_DEPTH_EXT = 0x0BA3; + public const int GL_MODELVIEW1_STACK_DEPTH_EXT = 0x8502; + public const int GL_MODELVIEW0_MATRIX_EXT = 0x0BA6; + public const int GL_MODELVIEW1_MATRIX_EXT = 0x8506; + public const int GL_VERTEX_WEIGHTING_EXT = 0x8509; + public const int GL_MODELVIEW0_EXT = 0x1700; + public const int GL_MODELVIEW1_EXT = 0x850A; + public const int GL_CURRENT_VERTEX_WEIGHT_EXT = 0x850B; + public const int GL_VERTEX_WEIGHT_ARRAY_EXT = 0x850C; + public const int GL_VERTEX_WEIGHT_ARRAY_SIZE_EXT = 0x850D; + public const int GL_VERTEX_WEIGHT_ARRAY_TYPE_EXT = 0x850E; + public const int GL_VERTEX_WEIGHT_ARRAY_STRIDE_EXT = 0x850F; + public const int GL_VERTEX_WEIGHT_ARRAY_POINTER_EXT = 0x8510; + public const int GL_EXT_win32_keyed_mutex = 1; + public const int GL_EXT_window_rectangles = 1; + public const int GL_INCLUSIVE_EXT = 0x8F10; + public const int GL_EXCLUSIVE_EXT = 0x8F11; + public const int GL_WINDOW_RECTANGLE_EXT = 0x8F12; + public const int GL_WINDOW_RECTANGLE_MODE_EXT = 0x8F13; + public const int GL_MAX_WINDOW_RECTANGLES_EXT = 0x8F14; + public const int GL_NUM_WINDOW_RECTANGLES_EXT = 0x8F15; + public const int GL_EXT_x11_sync_object = 1; + public const int GL_SYNC_X11_FENCE_EXT = 0x90E1; + public const int GL_GREMEDY_frame_terminator = 1; + public const int GL_GREMEDY_string_marker = 1; + public const int GL_HP_convolution_border_modes = 1; + public const int GL_IGNORE_BORDER_HP = 0x8150; + public const int GL_CONSTANT_BORDER_HP = 0x8151; + public const int GL_REPLICATE_BORDER_HP = 0x8153; + public const int GL_CONVOLUTION_BORDER_COLOR_HP = 0x8154; + public const int GL_HP_image_transform = 1; + public const int GL_IMAGE_SCALE_X_HP = 0x8155; + public const int GL_IMAGE_SCALE_Y_HP = 0x8156; + public const int GL_IMAGE_TRANSLATE_X_HP = 0x8157; + public const int GL_IMAGE_TRANSLATE_Y_HP = 0x8158; + public const int GL_IMAGE_ROTATE_ANGLE_HP = 0x8159; + public const int GL_IMAGE_ROTATE_ORIGIN_X_HP = 0x815A; + public const int GL_IMAGE_ROTATE_ORIGIN_Y_HP = 0x815B; + public const int GL_IMAGE_MAG_FILTER_HP = 0x815C; + public const int GL_IMAGE_MIN_FILTER_HP = 0x815D; + public const int GL_IMAGE_CUBIC_WEIGHT_HP = 0x815E; + public const int GL_CUBIC_HP = 0x815F; + public const int GL_AVERAGE_HP = 0x8160; + public const int GL_IMAGE_TRANSFORM_2D_HP = 0x8161; + public const int GL_POST_IMAGE_TRANSFORM_COLOR_TABLE_HP = 0x8162; + public const int GL_PROXY_POST_IMAGE_TRANSFORM_COLOR_TABLE_HP = 0x8163; + public const int GL_HP_occlusion_test = 1; + public const int GL_OCCLUSION_TEST_HP = 0x8165; + public const int GL_OCCLUSION_TEST_RESULT_HP = 0x8166; + public const int GL_HP_texture_lighting = 1; + public const int GL_TEXTURE_LIGHTING_MODE_HP = 0x8167; + public const int GL_TEXTURE_POST_SPECULAR_HP = 0x8168; + public const int GL_TEXTURE_PRE_SPECULAR_HP = 0x8169; + public const int GL_IBM_cull_vertex = 1; + public const int GL_CULL_VERTEX_IBM = 103050; + public const int GL_IBM_multimode_draw_arrays = 1; + public const int GL_IBM_rasterpos_clip = 1; + public const int GL_RASTER_POSITION_UNCLIPPED_IBM = 0x19262; + public const int GL_IBM_static_data = 1; + public const int GL_ALL_STATIC_DATA_IBM = 103060; + public const int GL_STATIC_VERTEX_ARRAY_IBM = 103061; + public const int GL_IBM_texture_mirrored_repeat = 1; + public const int GL_MIRRORED_REPEAT_IBM = 0x8370; + public const int GL_IBM_vertex_array_lists = 1; + public const int GL_VERTEX_ARRAY_LIST_IBM = 103070; + public const int GL_NORMAL_ARRAY_LIST_IBM = 103071; + public const int GL_COLOR_ARRAY_LIST_IBM = 103072; + public const int GL_INDEX_ARRAY_LIST_IBM = 103073; + public const int GL_TEXTURE_COORD_ARRAY_LIST_IBM = 103074; + public const int GL_EDGE_FLAG_ARRAY_LIST_IBM = 103075; + public const int GL_FOG_COORDINATE_ARRAY_LIST_IBM = 103076; + public const int GL_SECONDARY_COLOR_ARRAY_LIST_IBM = 103077; + public const int GL_VERTEX_ARRAY_LIST_STRIDE_IBM = 103080; + public const int GL_NORMAL_ARRAY_LIST_STRIDE_IBM = 103081; + public const int GL_COLOR_ARRAY_LIST_STRIDE_IBM = 103082; + public const int GL_INDEX_ARRAY_LIST_STRIDE_IBM = 103083; + public const int GL_TEXTURE_COORD_ARRAY_LIST_STRIDE_IBM = 103084; + public const int GL_EDGE_FLAG_ARRAY_LIST_STRIDE_IBM = 103085; + public const int GL_FOG_COORDINATE_ARRAY_LIST_STRIDE_IBM = 103086; + public const int GL_SECONDARY_COLOR_ARRAY_LIST_STRIDE_IBM = 103087; + public const int GL_INGR_blend_func_separate = 1; + public const int GL_INGR_color_clamp = 1; + public const int GL_RED_MIN_CLAMP_INGR = 0x8560; + public const int GL_GREEN_MIN_CLAMP_INGR = 0x8561; + public const int GL_BLUE_MIN_CLAMP_INGR = 0x8562; + public const int GL_ALPHA_MIN_CLAMP_INGR = 0x8563; + public const int GL_RED_MAX_CLAMP_INGR = 0x8564; + public const int GL_GREEN_MAX_CLAMP_INGR = 0x8565; + public const int GL_BLUE_MAX_CLAMP_INGR = 0x8566; + public const int GL_ALPHA_MAX_CLAMP_INGR = 0x8567; + public const int GL_INGR_interlace_read = 1; + public const int GL_INTERLACE_READ_INGR = 0x8568; + public const int GL_INTEL_blackhole_render = 1; + public const int GL_BLACKHOLE_RENDER_INTEL = 0x83FC; + public const int GL_INTEL_conservative_rasterization = 1; + public const int GL_CONSERVATIVE_RASTERIZATION_INTEL = 0x83FE; + public const int GL_INTEL_fragment_shader_ordering = 1; + public const int GL_INTEL_framebuffer_CMAA = 1; + public const int GL_INTEL_map_texture = 1; + public const int GL_TEXTURE_MEMORY_LAYOUT_INTEL = 0x83FF; + public const int GL_LAYOUT_DEFAULT_INTEL = 0; + public const int GL_LAYOUT_LINEAR_INTEL = 1; + public const int GL_LAYOUT_LINEAR_CPU_CACHED_INTEL = 2; + public const int GL_INTEL_parallel_arrays = 1; + public const int GL_PARALLEL_ARRAYS_INTEL = 0x83F4; + public const int GL_VERTEX_ARRAY_PARALLEL_POINTERS_INTEL = 0x83F5; + public const int GL_NORMAL_ARRAY_PARALLEL_POINTERS_INTEL = 0x83F6; + public const int GL_COLOR_ARRAY_PARALLEL_POINTERS_INTEL = 0x83F7; + public const int GL_TEXTURE_COORD_ARRAY_PARALLEL_POINTERS_INTEL = 0x83F8; + public const int GL_INTEL_performance_query = 1; + public const int GL_PERFQUERY_SINGLE_CONTEXT_INTEL = 0x00000000; + public const int GL_PERFQUERY_GLOBAL_CONTEXT_INTEL = 0x00000001; + public const int GL_PERFQUERY_WAIT_INTEL = 0x83FB; + public const int GL_PERFQUERY_FLUSH_INTEL = 0x83FA; + public const int GL_PERFQUERY_DONOT_FLUSH_INTEL = 0x83F9; + public const int GL_PERFQUERY_COUNTER_EVENT_INTEL = 0x94F0; + public const int GL_PERFQUERY_COUNTER_DURATION_NORM_INTEL = 0x94F1; + public const int GL_PERFQUERY_COUNTER_DURATION_RAW_INTEL = 0x94F2; + public const int GL_PERFQUERY_COUNTER_THROUGHPUT_INTEL = 0x94F3; + public const int GL_PERFQUERY_COUNTER_RAW_INTEL = 0x94F4; + public const int GL_PERFQUERY_COUNTER_TIMESTAMP_INTEL = 0x94F5; + public const int GL_PERFQUERY_COUNTER_DATA_UINT32_INTEL = 0x94F8; + public const int GL_PERFQUERY_COUNTER_DATA_UINT64_INTEL = 0x94F9; + public const int GL_PERFQUERY_COUNTER_DATA_FLOAT_INTEL = 0x94FA; + public const int GL_PERFQUERY_COUNTER_DATA_DOUBLE_INTEL = 0x94FB; + public const int GL_PERFQUERY_COUNTER_DATA_BOOL32_INTEL = 0x94FC; + public const int GL_PERFQUERY_QUERY_NAME_LENGTH_MAX_INTEL = 0x94FD; + public const int GL_PERFQUERY_COUNTER_NAME_LENGTH_MAX_INTEL = 0x94FE; + public const int GL_PERFQUERY_COUNTER_DESC_LENGTH_MAX_INTEL = 0x94FF; + public const int GL_PERFQUERY_GPA_EXTENDED_COUNTERS_INTEL = 0x9500; + public const int GL_MESAX_texture_stack = 1; + public const int GL_TEXTURE_1D_STACK_MESAX = 0x8759; + public const int GL_TEXTURE_2D_STACK_MESAX = 0x875A; + public const int GL_PROXY_TEXTURE_1D_STACK_MESAX = 0x875B; + public const int GL_PROXY_TEXTURE_2D_STACK_MESAX = 0x875C; + public const int GL_TEXTURE_1D_STACK_BINDING_MESAX = 0x875D; + public const int GL_TEXTURE_2D_STACK_BINDING_MESAX = 0x875E; + public const int GL_MESA_pack_invert = 1; + public const int GL_PACK_INVERT_MESA = 0x8758; + public const int GL_MESA_program_binary_formats = 1; + public const int GL_PROGRAM_BINARY_FORMAT_MESA = 0x875F; + public const int GL_MESA_resize_buffers = 1; + public const int GL_MESA_shader_integer_functions = 1; + public const int GL_MESA_tile_raster_order = 1; + public const int GL_TILE_RASTER_ORDER_FIXED_MESA = 0x8BB8; + public const int GL_TILE_RASTER_ORDER_INCREASING_X_MESA = 0x8BB9; + public const int GL_TILE_RASTER_ORDER_INCREASING_Y_MESA = 0x8BBA; + public const int GL_MESA_window_pos = 1; + public const int GL_MESA_ycbcr_texture = 1; + public const int GL_UNSIGNED_SHORT_8_8_MESA = 0x85BA; + public const int GL_UNSIGNED_SHORT_8_8_REV_MESA = 0x85BB; + public const int GL_YCBCR_MESA = 0x8757; + public const int GL_NVX_blend_equation_advanced_multi_draw_buffers = 1; + public const int GL_NVX_conditional_render = 1; + public const int GL_NVX_gpu_memory_info = 1; + public const int GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX = 0x9047; + public const int GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX = 0x9048; + public const int GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX = 0x9049; + public const int GL_GPU_MEMORY_INFO_EVICTION_COUNT_NVX = 0x904A; + public const int GL_GPU_MEMORY_INFO_EVICTED_MEMORY_NVX = 0x904B; + public const int GL_NVX_linked_gpu_multicast = 1; + public const int GL_LGPU_SEPARATE_STORAGE_BIT_NVX = 0x0800; + public const int GL_MAX_LGPU_GPUS_NVX = 0x92BA; + public const int GL_NV_alpha_to_coverage_dither_control = 1; + public const int GL_ALPHA_TO_COVERAGE_DITHER_DEFAULT_NV = 0x934D; + public const int GL_ALPHA_TO_COVERAGE_DITHER_ENABLE_NV = 0x934E; + public const int GL_ALPHA_TO_COVERAGE_DITHER_DISABLE_NV = 0x934F; + public const int GL_ALPHA_TO_COVERAGE_DITHER_MODE_NV = 0x92BF; + public const int GL_NV_bindless_multi_draw_indirect = 1; + public const int GL_NV_bindless_multi_draw_indirect_count = 1; + public const int GL_NV_bindless_texture = 1; + public const int GL_NV_blend_equation_advanced = 1; + public const int GL_BLEND_OVERLAP_NV = 0x9281; + public const int GL_BLEND_PREMULTIPLIED_SRC_NV = 0x9280; + public const int GL_BLUE_NV = 0x1905; + public const int GL_COLORBURN_NV = 0x929A; + public const int GL_COLORDODGE_NV = 0x9299; + public const int GL_CONJOINT_NV = 0x9284; + public const int GL_CONTRAST_NV = 0x92A1; + public const int GL_DARKEN_NV = 0x9297; + public const int GL_DIFFERENCE_NV = 0x929E; + public const int GL_DISJOINT_NV = 0x9283; + public const int GL_DST_ATOP_NV = 0x928F; + public const int GL_DST_IN_NV = 0x928B; + public const int GL_DST_NV = 0x9287; + public const int GL_DST_OUT_NV = 0x928D; + public const int GL_DST_OVER_NV = 0x9289; + public const int GL_EXCLUSION_NV = 0x92A0; + public const int GL_GREEN_NV = 0x1904; + public const int GL_HARDLIGHT_NV = 0x929B; + public const int GL_HARDMIX_NV = 0x92A9; + public const int GL_HSL_COLOR_NV = 0x92AF; + public const int GL_HSL_HUE_NV = 0x92AD; + public const int GL_HSL_LUMINOSITY_NV = 0x92B0; + public const int GL_HSL_SATURATION_NV = 0x92AE; + public const int GL_INVERT_OVG_NV = 0x92B4; + public const int GL_INVERT_RGB_NV = 0x92A3; + public const int GL_LIGHTEN_NV = 0x9298; + public const int GL_LINEARBURN_NV = 0x92A5; + public const int GL_LINEARDODGE_NV = 0x92A4; + public const int GL_LINEARLIGHT_NV = 0x92A7; + public const int GL_MINUS_CLAMPED_NV = 0x92B3; + public const int GL_MINUS_NV = 0x929F; + public const int GL_MULTIPLY_NV = 0x9294; + public const int GL_OVERLAY_NV = 0x9296; + public const int GL_PINLIGHT_NV = 0x92A8; + public const int GL_PLUS_CLAMPED_ALPHA_NV = 0x92B2; + public const int GL_PLUS_CLAMPED_NV = 0x92B1; + public const int GL_PLUS_DARKER_NV = 0x9292; + public const int GL_PLUS_NV = 0x9291; + public const int GL_RED_NV = 0x1903; + public const int GL_SCREEN_NV = 0x9295; + public const int GL_SOFTLIGHT_NV = 0x929C; + public const int GL_SRC_ATOP_NV = 0x928E; + public const int GL_SRC_IN_NV = 0x928A; + public const int GL_SRC_NV = 0x9286; + public const int GL_SRC_OUT_NV = 0x928C; + public const int GL_SRC_OVER_NV = 0x9288; + public const int GL_UNCORRELATED_NV = 0x9282; + public const int GL_VIVIDLIGHT_NV = 0x92A6; + public const int GL_XOR_NV = 0x1506; + public const int GL_NV_blend_equation_advanced_coherent = 1; + public const int GL_BLEND_ADVANCED_COHERENT_NV = 0x9285; + public const int GL_NV_blend_minmax_factor = 1; + public const int GL_NV_blend_square = 1; + public const int GL_NV_clip_space_w_scaling = 1; + public const int GL_VIEWPORT_POSITION_W_SCALE_NV = 0x937C; + public const int GL_VIEWPORT_POSITION_W_SCALE_X_COEFF_NV = 0x937D; + public const int GL_VIEWPORT_POSITION_W_SCALE_Y_COEFF_NV = 0x937E; + public const int GL_NV_command_list = 1; + public const int GL_TERMINATE_SEQUENCE_COMMAND_NV = 0x0000; + public const int GL_NOP_COMMAND_NV = 0x0001; + public const int GL_DRAW_ELEMENTS_COMMAND_NV = 0x0002; + public const int GL_DRAW_ARRAYS_COMMAND_NV = 0x0003; + public const int GL_DRAW_ELEMENTS_STRIP_COMMAND_NV = 0x0004; + public const int GL_DRAW_ARRAYS_STRIP_COMMAND_NV = 0x0005; + public const int GL_DRAW_ELEMENTS_INSTANCED_COMMAND_NV = 0x0006; + public const int GL_DRAW_ARRAYS_INSTANCED_COMMAND_NV = 0x0007; + public const int GL_ELEMENT_ADDRESS_COMMAND_NV = 0x0008; + public const int GL_ATTRIBUTE_ADDRESS_COMMAND_NV = 0x0009; + public const int GL_UNIFORM_ADDRESS_COMMAND_NV = 0x000A; + public const int GL_BLEND_COLOR_COMMAND_NV = 0x000B; + public const int GL_STENCIL_REF_COMMAND_NV = 0x000C; + public const int GL_LINE_WIDTH_COMMAND_NV = 0x000D; + public const int GL_POLYGON_OFFSET_COMMAND_NV = 0x000E; + public const int GL_ALPHA_REF_COMMAND_NV = 0x000F; + public const int GL_VIEWPORT_COMMAND_NV = 0x0010; + public const int GL_SCISSOR_COMMAND_NV = 0x0011; + public const int GL_FRONT_FACE_COMMAND_NV = 0x0012; + public const int GL_NV_compute_program5 = 1; + public const int GL_COMPUTE_PROGRAM_NV = 0x90FB; + public const int GL_COMPUTE_PROGRAM_PARAMETER_BUFFER_NV = 0x90FC; + public const int GL_NV_conditional_render = 1; + public const int GL_QUERY_WAIT_NV = 0x8E13; + public const int GL_QUERY_NO_WAIT_NV = 0x8E14; + public const int GL_QUERY_BY_REGION_WAIT_NV = 0x8E15; + public const int GL_QUERY_BY_REGION_NO_WAIT_NV = 0x8E16; + public const int GL_NV_conservative_raster = 1; + public const int GL_CONSERVATIVE_RASTERIZATION_NV = 0x9346; + public const int GL_SUBPIXEL_PRECISION_BIAS_X_BITS_NV = 0x9347; + public const int GL_SUBPIXEL_PRECISION_BIAS_Y_BITS_NV = 0x9348; + public const int GL_MAX_SUBPIXEL_PRECISION_BIAS_BITS_NV = 0x9349; + public const int GL_NV_conservative_raster_dilate = 1; + public const int GL_CONSERVATIVE_RASTER_DILATE_NV = 0x9379; + public const int GL_CONSERVATIVE_RASTER_DILATE_RANGE_NV = 0x937A; + public const int GL_CONSERVATIVE_RASTER_DILATE_GRANULARITY_NV = 0x937B; + public const int GL_NV_conservative_raster_pre_snap = 1; + public const int GL_CONSERVATIVE_RASTER_MODE_PRE_SNAP_NV = 0x9550; + public const int GL_NV_conservative_raster_pre_snap_triangles = 1; + public const int GL_CONSERVATIVE_RASTER_MODE_NV = 0x954D; + public const int GL_CONSERVATIVE_RASTER_MODE_POST_SNAP_NV = 0x954E; + public const int GL_CONSERVATIVE_RASTER_MODE_PRE_SNAP_TRIANGLES_NV = 0x954F; + public const int GL_NV_conservative_raster_underestimation = 1; + public const int GL_NV_copy_depth_to_color = 1; + public const int GL_DEPTH_STENCIL_TO_RGBA_NV = 0x886E; + public const int GL_DEPTH_STENCIL_TO_BGRA_NV = 0x886F; + public const int GL_NV_copy_image = 1; + public const int GL_NV_deep_texture3D = 1; + public const int GL_MAX_DEEP_3D_TEXTURE_WIDTH_HEIGHT_NV = 0x90D0; + public const int GL_MAX_DEEP_3D_TEXTURE_DEPTH_NV = 0x90D1; + public const int GL_NV_depth_buffer_float = 1; + public const int GL_DEPTH_COMPONENT32F_NV = 0x8DAB; + public const int GL_DEPTH32F_STENCIL8_NV = 0x8DAC; + public const int GL_FLOAT_32_UNSIGNED_INT_24_8_REV_NV = 0x8DAD; + public const int GL_DEPTH_BUFFER_FLOAT_MODE_NV = 0x8DAF; + public const int GL_NV_depth_clamp = 1; + public const int GL_DEPTH_CLAMP_NV = 0x864F; + public const int GL_NV_draw_texture = 1; + public const int GL_NV_draw_vulkan_image = 1; + public const int GL_NV_evaluators = 1; + public const int GL_EVAL_2D_NV = 0x86C0; + public const int GL_EVAL_TRIANGULAR_2D_NV = 0x86C1; + public const int GL_MAP_TESSELLATION_NV = 0x86C2; + public const int GL_MAP_ATTRIB_U_ORDER_NV = 0x86C3; + public const int GL_MAP_ATTRIB_V_ORDER_NV = 0x86C4; + public const int GL_EVAL_FRACTIONAL_TESSELLATION_NV = 0x86C5; + public const int GL_EVAL_VERTEX_ATTRIB0_NV = 0x86C6; + public const int GL_EVAL_VERTEX_ATTRIB1_NV = 0x86C7; + public const int GL_EVAL_VERTEX_ATTRIB2_NV = 0x86C8; + public const int GL_EVAL_VERTEX_ATTRIB3_NV = 0x86C9; + public const int GL_EVAL_VERTEX_ATTRIB4_NV = 0x86CA; + public const int GL_EVAL_VERTEX_ATTRIB5_NV = 0x86CB; + public const int GL_EVAL_VERTEX_ATTRIB6_NV = 0x86CC; + public const int GL_EVAL_VERTEX_ATTRIB7_NV = 0x86CD; + public const int GL_EVAL_VERTEX_ATTRIB8_NV = 0x86CE; + public const int GL_EVAL_VERTEX_ATTRIB9_NV = 0x86CF; + public const int GL_EVAL_VERTEX_ATTRIB10_NV = 0x86D0; + public const int GL_EVAL_VERTEX_ATTRIB11_NV = 0x86D1; + public const int GL_EVAL_VERTEX_ATTRIB12_NV = 0x86D2; + public const int GL_EVAL_VERTEX_ATTRIB13_NV = 0x86D3; + public const int GL_EVAL_VERTEX_ATTRIB14_NV = 0x86D4; + public const int GL_EVAL_VERTEX_ATTRIB15_NV = 0x86D5; + public const int GL_MAX_MAP_TESSELLATION_NV = 0x86D6; + public const int GL_MAX_RATIONAL_EVAL_ORDER_NV = 0x86D7; + public const int GL_NV_explicit_multisample = 1; + public const int GL_SAMPLE_POSITION_NV = 0x8E50; + public const int GL_SAMPLE_MASK_NV = 0x8E51; + public const int GL_SAMPLE_MASK_VALUE_NV = 0x8E52; + public const int GL_TEXTURE_BINDING_RENDERBUFFER_NV = 0x8E53; + public const int GL_TEXTURE_RENDERBUFFER_DATA_STORE_BINDING_NV = 0x8E54; + public const int GL_TEXTURE_RENDERBUFFER_NV = 0x8E55; + public const int GL_SAMPLER_RENDERBUFFER_NV = 0x8E56; + public const int GL_INT_SAMPLER_RENDERBUFFER_NV = 0x8E57; + public const int GL_UNSIGNED_INT_SAMPLER_RENDERBUFFER_NV = 0x8E58; + public const int GL_MAX_SAMPLE_MASK_WORDS_NV = 0x8E59; + public const int GL_NV_fence = 1; + public const int GL_ALL_COMPLETED_NV = 0x84F2; + public const int GL_FENCE_STATUS_NV = 0x84F3; + public const int GL_FENCE_CONDITION_NV = 0x84F4; + public const int GL_NV_fill_rectangle = 1; + public const int GL_FILL_RECTANGLE_NV = 0x933C; + public const int GL_NV_float_buffer = 1; + public const int GL_FLOAT_R_NV = 0x8880; + public const int GL_FLOAT_RG_NV = 0x8881; + public const int GL_FLOAT_RGB_NV = 0x8882; + public const int GL_FLOAT_RGBA_NV = 0x8883; + public const int GL_FLOAT_R16_NV = 0x8884; + public const int GL_FLOAT_R32_NV = 0x8885; + public const int GL_FLOAT_RG16_NV = 0x8886; + public const int GL_FLOAT_RG32_NV = 0x8887; + public const int GL_FLOAT_RGB16_NV = 0x8888; + public const int GL_FLOAT_RGB32_NV = 0x8889; + public const int GL_FLOAT_RGBA16_NV = 0x888A; + public const int GL_FLOAT_RGBA32_NV = 0x888B; + public const int GL_TEXTURE_FLOAT_COMPONENTS_NV = 0x888C; + public const int GL_FLOAT_CLEAR_COLOR_VALUE_NV = 0x888D; + public const int GL_FLOAT_RGBA_MODE_NV = 0x888E; + public const int GL_NV_fog_distance = 1; + public const int GL_FOG_DISTANCE_MODE_NV = 0x855A; + public const int GL_EYE_RADIAL_NV = 0x855B; + public const int GL_EYE_PLANE_ABSOLUTE_NV = 0x855C; + public const int GL_NV_fragment_coverage_to_color = 1; + public const int GL_FRAGMENT_COVERAGE_TO_COLOR_NV = 0x92DD; + public const int GL_FRAGMENT_COVERAGE_COLOR_NV = 0x92DE; + public const int GL_NV_fragment_program = 1; + public const int GL_MAX_FRAGMENT_PROGRAM_LOCAL_PARAMETERS_NV = 0x8868; + public const int GL_FRAGMENT_PROGRAM_NV = 0x8870; + public const int GL_MAX_TEXTURE_COORDS_NV = 0x8871; + public const int GL_MAX_TEXTURE_IMAGE_UNITS_NV = 0x8872; + public const int GL_FRAGMENT_PROGRAM_BINDING_NV = 0x8873; + public const int GL_PROGRAM_ERROR_STRING_NV = 0x8874; + public const int GL_NV_fragment_program2 = 1; + public const int GL_MAX_PROGRAM_EXEC_INSTRUCTIONS_NV = 0x88F4; + public const int GL_MAX_PROGRAM_CALL_DEPTH_NV = 0x88F5; + public const int GL_MAX_PROGRAM_IF_DEPTH_NV = 0x88F6; + public const int GL_MAX_PROGRAM_LOOP_DEPTH_NV = 0x88F7; + public const int GL_MAX_PROGRAM_LOOP_COUNT_NV = 0x88F8; + public const int GL_NV_fragment_program4 = 1; + public const int GL_NV_fragment_program_option = 1; + public const int GL_NV_fragment_shader_interlock = 1; + public const int GL_NV_framebuffer_mixed_samples = 1; + public const int GL_COVERAGE_MODULATION_TABLE_NV = 0x9331; + public const int GL_COLOR_SAMPLES_NV = 0x8E20; + public const int GL_DEPTH_SAMPLES_NV = 0x932D; + public const int GL_STENCIL_SAMPLES_NV = 0x932E; + public const int GL_MIXED_DEPTH_SAMPLES_SUPPORTED_NV = 0x932F; + public const int GL_MIXED_STENCIL_SAMPLES_SUPPORTED_NV = 0x9330; + public const int GL_COVERAGE_MODULATION_NV = 0x9332; + public const int GL_COVERAGE_MODULATION_TABLE_SIZE_NV = 0x9333; + public const int GL_NV_framebuffer_multisample_coverage = 1; + public const int GL_RENDERBUFFER_COVERAGE_SAMPLES_NV = 0x8CAB; + public const int GL_RENDERBUFFER_COLOR_SAMPLES_NV = 0x8E10; + public const int GL_MAX_MULTISAMPLE_COVERAGE_MODES_NV = 0x8E11; + public const int GL_MULTISAMPLE_COVERAGE_MODES_NV = 0x8E12; + public const int GL_NV_geometry_program4 = 1; + public const int GL_GEOMETRY_PROGRAM_NV = 0x8C26; + public const int GL_MAX_PROGRAM_OUTPUT_VERTICES_NV = 0x8C27; + public const int GL_MAX_PROGRAM_TOTAL_OUTPUT_COMPONENTS_NV = 0x8C28; + public const int GL_NV_geometry_shader4 = 1; + public const int GL_NV_geometry_shader_passthrough = 1; + public const int GL_NV_gpu_multicast = 1; + public const int GL_PER_GPU_STORAGE_BIT_NV = 0x0800; + public const int GL_MULTICAST_GPUS_NV = 0x92BA; + public const int GL_RENDER_GPU_MASK_NV = 0x9558; + public const int GL_PER_GPU_STORAGE_NV = 0x9548; + public const int GL_MULTICAST_PROGRAMMABLE_SAMPLE_LOCATION_NV = 0x9549; + public const int GL_NV_gpu_program4 = 1; + public const int GL_MIN_PROGRAM_TEXEL_OFFSET_NV = 0x8904; + public const int GL_MAX_PROGRAM_TEXEL_OFFSET_NV = 0x8905; + public const int GL_PROGRAM_ATTRIB_COMPONENTS_NV = 0x8906; + public const int GL_PROGRAM_RESULT_COMPONENTS_NV = 0x8907; + public const int GL_MAX_PROGRAM_ATTRIB_COMPONENTS_NV = 0x8908; + public const int GL_MAX_PROGRAM_RESULT_COMPONENTS_NV = 0x8909; + public const int GL_MAX_PROGRAM_GENERIC_ATTRIBS_NV = 0x8DA5; + public const int GL_MAX_PROGRAM_GENERIC_RESULTS_NV = 0x8DA6; + public const int GL_NV_gpu_program5 = 1; + public const int GL_MAX_GEOMETRY_PROGRAM_INVOCATIONS_NV = 0x8E5A; + public const int GL_MIN_FRAGMENT_INTERPOLATION_OFFSET_NV = 0x8E5B; + public const int GL_MAX_FRAGMENT_INTERPOLATION_OFFSET_NV = 0x8E5C; + public const int GL_FRAGMENT_PROGRAM_INTERPOLATION_OFFSET_BITS_NV = 0x8E5D; + public const int GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_NV = 0x8E5E; + public const int GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_NV = 0x8E5F; + public const int GL_MAX_PROGRAM_SUBROUTINE_PARAMETERS_NV = 0x8F44; + public const int GL_MAX_PROGRAM_SUBROUTINE_NUM_NV = 0x8F45; + public const int GL_NV_gpu_program5_mem_extended = 1; + public const int GL_NV_gpu_shader5 = 1; + public const int GL_NV_half_float = 1; + public const int GL_HALF_FLOAT_NV = 0x140B; + public const int GL_NV_internalformat_sample_query = 1; + public const int GL_MULTISAMPLES_NV = 0x9371; + public const int GL_SUPERSAMPLE_SCALE_X_NV = 0x9372; + public const int GL_SUPERSAMPLE_SCALE_Y_NV = 0x9373; + public const int GL_CONFORMANT_NV = 0x9374; + public const int GL_NV_light_max_exponent = 1; + public const int GL_MAX_SHININESS_NV = 0x8504; + public const int GL_MAX_SPOT_EXPONENT_NV = 0x8505; + public const int GL_NV_multisample_coverage = 1; + public const int GL_NV_multisample_filter_hint = 1; + public const int GL_MULTISAMPLE_FILTER_HINT_NV = 0x8534; + public const int GL_NV_occlusion_query = 1; + public const int GL_PIXEL_COUNTER_BITS_NV = 0x8864; + public const int GL_CURRENT_OCCLUSION_QUERY_ID_NV = 0x8865; + public const int GL_PIXEL_COUNT_NV = 0x8866; + public const int GL_PIXEL_COUNT_AVAILABLE_NV = 0x8867; + public const int GL_NV_packed_depth_stencil = 1; + public const int GL_DEPTH_STENCIL_NV = 0x84F9; + public const int GL_UNSIGNED_INT_24_8_NV = 0x84FA; + public const int GL_NV_parameter_buffer_object = 1; + public const int GL_MAX_PROGRAM_PARAMETER_BUFFER_BINDINGS_NV = 0x8DA0; + public const int GL_MAX_PROGRAM_PARAMETER_BUFFER_SIZE_NV = 0x8DA1; + public const int GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV = 0x8DA2; + public const int GL_GEOMETRY_PROGRAM_PARAMETER_BUFFER_NV = 0x8DA3; + public const int GL_FRAGMENT_PROGRAM_PARAMETER_BUFFER_NV = 0x8DA4; + public const int GL_NV_parameter_buffer_object2 = 1; + public const int GL_NV_path_rendering = 1; + public const int GL_PATH_FORMAT_SVG_NV = 0x9070; + public const int GL_PATH_FORMAT_PS_NV = 0x9071; + public const int GL_STANDARD_FONT_NAME_NV = 0x9072; + public const int GL_SYSTEM_FONT_NAME_NV = 0x9073; + public const int GL_FILE_NAME_NV = 0x9074; + public const int GL_PATH_STROKE_WIDTH_NV = 0x9075; + public const int GL_PATH_END_CAPS_NV = 0x9076; + public const int GL_PATH_INITIAL_END_CAP_NV = 0x9077; + public const int GL_PATH_TERMINAL_END_CAP_NV = 0x9078; + public const int GL_PATH_JOIN_STYLE_NV = 0x9079; + public const int GL_PATH_MITER_LIMIT_NV = 0x907A; + public const int GL_PATH_DASH_CAPS_NV = 0x907B; + public const int GL_PATH_INITIAL_DASH_CAP_NV = 0x907C; + public const int GL_PATH_TERMINAL_DASH_CAP_NV = 0x907D; + public const int GL_PATH_DASH_OFFSET_NV = 0x907E; + public const int GL_PATH_CLIENT_LENGTH_NV = 0x907F; + public const int GL_PATH_FILL_MODE_NV = 0x9080; + public const int GL_PATH_FILL_MASK_NV = 0x9081; + public const int GL_PATH_FILL_COVER_MODE_NV = 0x9082; + public const int GL_PATH_STROKE_COVER_MODE_NV = 0x9083; + public const int GL_PATH_STROKE_MASK_NV = 0x9084; + public const int GL_COUNT_UP_NV = 0x9088; + public const int GL_COUNT_DOWN_NV = 0x9089; + public const int GL_PATH_OBJECT_BOUNDING_BOX_NV = 0x908A; + public const int GL_CONVEX_HULL_NV = 0x908B; + public const int GL_BOUNDING_BOX_NV = 0x908D; + public const int GL_TRANSLATE_X_NV = 0x908E; + public const int GL_TRANSLATE_Y_NV = 0x908F; + public const int GL_TRANSLATE_2D_NV = 0x9090; + public const int GL_TRANSLATE_3D_NV = 0x9091; + public const int GL_AFFINE_2D_NV = 0x9092; + public const int GL_AFFINE_3D_NV = 0x9094; + public const int GL_TRANSPOSE_AFFINE_2D_NV = 0x9096; + public const int GL_TRANSPOSE_AFFINE_3D_NV = 0x9098; + public const int GL_UTF8_NV = 0x909A; + public const int GL_UTF16_NV = 0x909B; + public const int GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV = 0x909C; + public const int GL_PATH_COMMAND_COUNT_NV = 0x909D; + public const int GL_PATH_COORD_COUNT_NV = 0x909E; + public const int GL_PATH_DASH_ARRAY_COUNT_NV = 0x909F; + public const int GL_PATH_COMPUTED_LENGTH_NV = 0x90A0; + public const int GL_PATH_FILL_BOUNDING_BOX_NV = 0x90A1; + public const int GL_PATH_STROKE_BOUNDING_BOX_NV = 0x90A2; + public const int GL_SQUARE_NV = 0x90A3; + public const int GL_ROUND_NV = 0x90A4; + public const int GL_TRIANGULAR_NV = 0x90A5; + public const int GL_BEVEL_NV = 0x90A6; + public const int GL_MITER_REVERT_NV = 0x90A7; + public const int GL_MITER_TRUNCATE_NV = 0x90A8; + public const int GL_SKIP_MISSING_GLYPH_NV = 0x90A9; + public const int GL_USE_MISSING_GLYPH_NV = 0x90AA; + public const int GL_PATH_ERROR_POSITION_NV = 0x90AB; + public const int GL_ACCUM_ADJACENT_PAIRS_NV = 0x90AD; + public const int GL_ADJACENT_PAIRS_NV = 0x90AE; + public const int GL_FIRST_TO_REST_NV = 0x90AF; + public const int GL_PATH_GEN_MODE_NV = 0x90B0; + public const int GL_PATH_GEN_COEFF_NV = 0x90B1; + public const int GL_PATH_GEN_COMPONENTS_NV = 0x90B3; + public const int GL_PATH_STENCIL_FUNC_NV = 0x90B7; + public const int GL_PATH_STENCIL_REF_NV = 0x90B8; + public const int GL_PATH_STENCIL_VALUE_MASK_NV = 0x90B9; + public const int GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV = 0x90BD; + public const int GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV = 0x90BE; + public const int GL_PATH_COVER_DEPTH_FUNC_NV = 0x90BF; + public const int GL_PATH_DASH_OFFSET_RESET_NV = 0x90B4; + public const int GL_MOVE_TO_RESETS_NV = 0x90B5; + public const int GL_MOVE_TO_CONTINUES_NV = 0x90B6; + public const int GL_CLOSE_PATH_NV = 0x00; + public const int GL_MOVE_TO_NV = 0x02; + public const int GL_RELATIVE_MOVE_TO_NV = 0x03; + public const int GL_LINE_TO_NV = 0x04; + public const int GL_RELATIVE_LINE_TO_NV = 0x05; + public const int GL_HORIZONTAL_LINE_TO_NV = 0x06; + public const int GL_RELATIVE_HORIZONTAL_LINE_TO_NV = 0x07; + public const int GL_VERTICAL_LINE_TO_NV = 0x08; + public const int GL_RELATIVE_VERTICAL_LINE_TO_NV = 0x09; + public const int GL_QUADRATIC_CURVE_TO_NV = 0x0A; + public const int GL_RELATIVE_QUADRATIC_CURVE_TO_NV = 0x0B; + public const int GL_CUBIC_CURVE_TO_NV = 0x0C; + public const int GL_RELATIVE_CUBIC_CURVE_TO_NV = 0x0D; + public const int GL_SMOOTH_QUADRATIC_CURVE_TO_NV = 0x0E; + public const int GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV = 0x0F; + public const int GL_SMOOTH_CUBIC_CURVE_TO_NV = 0x10; + public const int GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV = 0x11; + public const int GL_SMALL_CCW_ARC_TO_NV = 0x12; + public const int GL_RELATIVE_SMALL_CCW_ARC_TO_NV = 0x13; + public const int GL_SMALL_CW_ARC_TO_NV = 0x14; + public const int GL_RELATIVE_SMALL_CW_ARC_TO_NV = 0x15; + public const int GL_LARGE_CCW_ARC_TO_NV = 0x16; + public const int GL_RELATIVE_LARGE_CCW_ARC_TO_NV = 0x17; + public const int GL_LARGE_CW_ARC_TO_NV = 0x18; + public const int GL_RELATIVE_LARGE_CW_ARC_TO_NV = 0x19; + public const int GL_RESTART_PATH_NV = 0xF0; + public const int GL_DUP_FIRST_CUBIC_CURVE_TO_NV = 0xF2; + public const int GL_DUP_LAST_CUBIC_CURVE_TO_NV = 0xF4; + public const int GL_RECT_NV = 0xF6; + public const int GL_CIRCULAR_CCW_ARC_TO_NV = 0xF8; + public const int GL_CIRCULAR_CW_ARC_TO_NV = 0xFA; + public const int GL_CIRCULAR_TANGENT_ARC_TO_NV = 0xFC; + public const int GL_ARC_TO_NV = 0xFE; + public const int GL_RELATIVE_ARC_TO_NV = 0xFF; + public const int GL_BOLD_BIT_NV = 0x01; + public const int GL_ITALIC_BIT_NV = 0x02; + public const int GL_GLYPH_WIDTH_BIT_NV = 0x01; + public const int GL_GLYPH_HEIGHT_BIT_NV = 0x02; + public const int GL_GLYPH_HORIZONTAL_BEARING_X_BIT_NV = 0x04; + public const int GL_GLYPH_HORIZONTAL_BEARING_Y_BIT_NV = 0x08; + public const int GL_GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV = 0x10; + public const int GL_GLYPH_VERTICAL_BEARING_X_BIT_NV = 0x20; + public const int GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV = 0x40; + public const int GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV = 0x80; + public const int GL_GLYPH_HAS_KERNING_BIT_NV = 0x100; + public const int GL_FONT_X_MIN_BOUNDS_BIT_NV = 0x00010000; + public const int GL_FONT_Y_MIN_BOUNDS_BIT_NV = 0x00020000; + public const int GL_FONT_X_MAX_BOUNDS_BIT_NV = 0x00040000; + public const int GL_FONT_Y_MAX_BOUNDS_BIT_NV = 0x00080000; + public const int GL_FONT_UNITS_PER_EM_BIT_NV = 0x00100000; + public const int GL_FONT_ASCENDER_BIT_NV = 0x00200000; + public const int GL_FONT_DESCENDER_BIT_NV = 0x00400000; + public const int GL_FONT_HEIGHT_BIT_NV = 0x00800000; + public const int GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV = 0x01000000; + public const int GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV = 0x02000000; + public const int GL_FONT_UNDERLINE_POSITION_BIT_NV = 0x04000000; + public const int GL_FONT_UNDERLINE_THICKNESS_BIT_NV = 0x08000000; + public const int GL_FONT_HAS_KERNING_BIT_NV = 0x10000000; + public const int GL_ROUNDED_RECT_NV = 0xE8; + public const int GL_RELATIVE_ROUNDED_RECT_NV = 0xE9; + public const int GL_ROUNDED_RECT2_NV = 0xEA; + public const int GL_RELATIVE_ROUNDED_RECT2_NV = 0xEB; + public const int GL_ROUNDED_RECT4_NV = 0xEC; + public const int GL_RELATIVE_ROUNDED_RECT4_NV = 0xED; + public const int GL_ROUNDED_RECT8_NV = 0xEE; + public const int GL_RELATIVE_ROUNDED_RECT8_NV = 0xEF; + public const int GL_RELATIVE_RECT_NV = 0xF7; + public const int GL_FONT_GLYPHS_AVAILABLE_NV = 0x9368; + public const int GL_FONT_TARGET_UNAVAILABLE_NV = 0x9369; + public const int GL_FONT_UNAVAILABLE_NV = 0x936A; + public const int GL_FONT_UNINTELLIGIBLE_NV = 0x936B; + public const int GL_CONIC_CURVE_TO_NV = 0x1A; + public const int GL_RELATIVE_CONIC_CURVE_TO_NV = 0x1B; + public const int GL_FONT_NUM_GLYPH_INDICES_BIT_NV = 0x20000000; + public const int GL_STANDARD_FONT_FORMAT_NV = 0x936C; + public const int GL_2_BYTES_NV = 0x1407; + public const int GL_3_BYTES_NV = 0x1408; + public const int GL_4_BYTES_NV = 0x1409; + public const int GL_EYE_LINEAR_NV = 0x2400; + public const int GL_OBJECT_LINEAR_NV = 0x2401; + public const int GL_CONSTANT_NV = 0x8576; + public const int GL_PATH_FOG_GEN_MODE_NV = 0x90AC; + public const int GL_PRIMARY_COLOR_NV = 0x852C; + public const int GL_SECONDARY_COLOR_NV = 0x852D; + public const int GL_PATH_GEN_COLOR_FORMAT_NV = 0x90B2; + public const int GL_PATH_PROJECTION_NV = 0x1701; + public const int GL_PATH_MODELVIEW_NV = 0x1700; + public const int GL_PATH_MODELVIEW_STACK_DEPTH_NV = 0x0BA3; + public const int GL_PATH_MODELVIEW_MATRIX_NV = 0x0BA6; + public const int GL_PATH_MAX_MODELVIEW_STACK_DEPTH_NV = 0x0D36; + public const int GL_PATH_TRANSPOSE_MODELVIEW_MATRIX_NV = 0x84E3; + public const int GL_PATH_PROJECTION_STACK_DEPTH_NV = 0x0BA4; + public const int GL_PATH_PROJECTION_MATRIX_NV = 0x0BA7; + public const int GL_PATH_MAX_PROJECTION_STACK_DEPTH_NV = 0x0D38; + public const int GL_PATH_TRANSPOSE_PROJECTION_MATRIX_NV = 0x84E4; + public const int GL_FRAGMENT_INPUT_NV = 0x936D; + public const int GL_NV_path_rendering_shared_edge = 1; + public const int GL_SHARED_EDGE_NV = 0xC0; + public const int GL_NV_pixel_data_range = 1; + public const int GL_WRITE_PIXEL_DATA_RANGE_NV = 0x8878; + public const int GL_READ_PIXEL_DATA_RANGE_NV = 0x8879; + public const int GL_WRITE_PIXEL_DATA_RANGE_LENGTH_NV = 0x887A; + public const int GL_READ_PIXEL_DATA_RANGE_LENGTH_NV = 0x887B; + public const int GL_WRITE_PIXEL_DATA_RANGE_POINTER_NV = 0x887C; + public const int GL_READ_PIXEL_DATA_RANGE_POINTER_NV = 0x887D; + public const int GL_NV_point_sprite = 1; + public const int GL_POINT_SPRITE_NV = 0x8861; + public const int GL_COORD_REPLACE_NV = 0x8862; + public const int GL_POINT_SPRITE_R_MODE_NV = 0x8863; + public const int GL_NV_present_video = 1; + public const int GL_FRAME_NV = 0x8E26; + public const int GL_FIELDS_NV = 0x8E27; + public const int GL_CURRENT_TIME_NV = 0x8E28; + public const int GL_NUM_FILL_STREAMS_NV = 0x8E29; + public const int GL_PRESENT_TIME_NV = 0x8E2A; + public const int GL_PRESENT_DURATION_NV = 0x8E2B; + public const int GL_NV_primitive_restart = 1; + public const int GL_PRIMITIVE_RESTART_NV = 0x8558; + public const int GL_PRIMITIVE_RESTART_INDEX_NV = 0x8559; + public const int GL_NV_query_resource = 1; + public const int GL_QUERY_RESOURCE_TYPE_VIDMEM_ALLOC_NV = 0x9540; + public const int GL_QUERY_RESOURCE_MEMTYPE_VIDMEM_NV = 0x9542; + public const int GL_QUERY_RESOURCE_SYS_RESERVED_NV = 0x9544; + public const int GL_QUERY_RESOURCE_TEXTURE_NV = 0x9545; + public const int GL_QUERY_RESOURCE_RENDERBUFFER_NV = 0x9546; + public const int GL_QUERY_RESOURCE_BUFFEROBJECT_NV = 0x9547; + public const int GL_NV_query_resource_tag = 1; + public const int GL_NV_register_combiners = 1; + public const int GL_REGISTER_COMBINERS_NV = 0x8522; + public const int GL_VARIABLE_A_NV = 0x8523; + public const int GL_VARIABLE_B_NV = 0x8524; + public const int GL_VARIABLE_C_NV = 0x8525; + public const int GL_VARIABLE_D_NV = 0x8526; + public const int GL_VARIABLE_E_NV = 0x8527; + public const int GL_VARIABLE_F_NV = 0x8528; + public const int GL_VARIABLE_G_NV = 0x8529; + public const int GL_CONSTANT_COLOR0_NV = 0x852A; + public const int GL_CONSTANT_COLOR1_NV = 0x852B; + public const int GL_SPARE0_NV = 0x852E; + public const int GL_SPARE1_NV = 0x852F; + public const int GL_DISCARD_NV = 0x8530; + public const int GL_E_TIMES_F_NV = 0x8531; + public const int GL_SPARE0_PLUS_SECONDARY_COLOR_NV = 0x8532; + public const int GL_UNSIGNED_IDENTITY_NV = 0x8536; + public const int GL_UNSIGNED_INVERT_NV = 0x8537; + public const int GL_EXPAND_NORMAL_NV = 0x8538; + public const int GL_EXPAND_NEGATE_NV = 0x8539; + public const int GL_HALF_BIAS_NORMAL_NV = 0x853A; + public const int GL_HALF_BIAS_NEGATE_NV = 0x853B; + public const int GL_SIGNED_IDENTITY_NV = 0x853C; + public const int GL_SIGNED_NEGATE_NV = 0x853D; + public const int GL_SCALE_BY_TWO_NV = 0x853E; + public const int GL_SCALE_BY_FOUR_NV = 0x853F; + public const int GL_SCALE_BY_ONE_HALF_NV = 0x8540; + public const int GL_BIAS_BY_NEGATIVE_ONE_HALF_NV = 0x8541; + public const int GL_COMBINER_INPUT_NV = 0x8542; + public const int GL_COMBINER_MAPPING_NV = 0x8543; + public const int GL_COMBINER_COMPONENT_USAGE_NV = 0x8544; + public const int GL_COMBINER_AB_DOT_PRODUCT_NV = 0x8545; + public const int GL_COMBINER_CD_DOT_PRODUCT_NV = 0x8546; + public const int GL_COMBINER_MUX_SUM_NV = 0x8547; + public const int GL_COMBINER_SCALE_NV = 0x8548; + public const int GL_COMBINER_BIAS_NV = 0x8549; + public const int GL_COMBINER_AB_OUTPUT_NV = 0x854A; + public const int GL_COMBINER_CD_OUTPUT_NV = 0x854B; + public const int GL_COMBINER_SUM_OUTPUT_NV = 0x854C; + public const int GL_MAX_GENERAL_COMBINERS_NV = 0x854D; + public const int GL_NUM_GENERAL_COMBINERS_NV = 0x854E; + public const int GL_COLOR_SUM_CLAMP_NV = 0x854F; + public const int GL_COMBINER0_NV = 0x8550; + public const int GL_COMBINER1_NV = 0x8551; + public const int GL_COMBINER2_NV = 0x8552; + public const int GL_COMBINER3_NV = 0x8553; + public const int GL_COMBINER4_NV = 0x8554; + public const int GL_COMBINER5_NV = 0x8555; + public const int GL_COMBINER6_NV = 0x8556; + public const int GL_COMBINER7_NV = 0x8557; + public const int GL_NV_register_combiners2 = 1; + public const int GL_PER_STAGE_CONSTANTS_NV = 0x8535; + public const int GL_NV_robustness_video_memory_purge = 1; + public const int GL_PURGED_CONTEXT_RESET_NV = 0x92BB; + public const int GL_NV_sample_locations = 1; + public const int GL_SAMPLE_LOCATION_SUBPIXEL_BITS_NV = 0x933D; + public const int GL_SAMPLE_LOCATION_PIXEL_GRID_WIDTH_NV = 0x933E; + public const int GL_SAMPLE_LOCATION_PIXEL_GRID_HEIGHT_NV = 0x933F; + public const int GL_PROGRAMMABLE_SAMPLE_LOCATION_TABLE_SIZE_NV = 0x9340; + public const int GL_SAMPLE_LOCATION_NV = 0x8E50; + public const int GL_PROGRAMMABLE_SAMPLE_LOCATION_NV = 0x9341; + public const int GL_FRAMEBUFFER_PROGRAMMABLE_SAMPLE_LOCATIONS_NV = 0x9342; + public const int GL_FRAMEBUFFER_SAMPLE_LOCATION_PIXEL_GRID_NV = 0x9343; + public const int GL_NV_sample_mask_override_coverage = 1; + public const int GL_NV_shader_atomic_counters = 1; + public const int GL_NV_shader_atomic_float = 1; + public const int GL_NV_shader_atomic_float64 = 1; + public const int GL_NV_shader_atomic_fp16_vector = 1; + public const int GL_NV_shader_atomic_int64 = 1; + public const int GL_NV_shader_buffer_load = 1; + public const int GL_BUFFER_GPU_ADDRESS_NV = 0x8F1D; + public const int GL_GPU_ADDRESS_NV = 0x8F34; + public const int GL_MAX_SHADER_BUFFER_ADDRESS_NV = 0x8F35; + public const int GL_NV_shader_buffer_store = 1; + public const int GL_SHADER_GLOBAL_ACCESS_BARRIER_BIT_NV = 0x00000010; + public const int GL_NV_shader_storage_buffer_object = 1; + public const int GL_NV_shader_thread_group = 1; + public const int GL_WARP_SIZE_NV = 0x9339; + public const int GL_WARPS_PER_SM_NV = 0x933A; + public const int GL_SM_COUNT_NV = 0x933B; + public const int GL_NV_shader_thread_shuffle = 1; + public const int GL_NV_stereo_view_rendering = 1; + public const int GL_NV_tessellation_program5 = 1; + public const int GL_MAX_PROGRAM_PATCH_ATTRIBS_NV = 0x86D8; + public const int GL_TESS_CONTROL_PROGRAM_NV = 0x891E; + public const int GL_TESS_EVALUATION_PROGRAM_NV = 0x891F; + public const int GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV = 0x8C74; + public const int GL_TESS_EVALUATION_PROGRAM_PARAMETER_BUFFER_NV = 0x8C75; + public const int GL_NV_texgen_emboss = 1; + public const int GL_EMBOSS_LIGHT_NV = 0x855D; + public const int GL_EMBOSS_CONSTANT_NV = 0x855E; + public const int GL_EMBOSS_MAP_NV = 0x855F; + public const int GL_NV_texgen_reflection = 1; + public const int GL_NORMAL_MAP_NV = 0x8511; + public const int GL_REFLECTION_MAP_NV = 0x8512; + public const int GL_NV_texture_barrier = 1; + public const int GL_NV_texture_compression_vtc = 1; + public const int GL_NV_texture_env_combine4 = 1; + public const int GL_COMBINE4_NV = 0x8503; + public const int GL_SOURCE3_RGB_NV = 0x8583; + public const int GL_SOURCE3_ALPHA_NV = 0x858B; + public const int GL_OPERAND3_RGB_NV = 0x8593; + public const int GL_OPERAND3_ALPHA_NV = 0x859B; + public const int GL_NV_texture_expand_normal = 1; + public const int GL_TEXTURE_UNSIGNED_REMAP_MODE_NV = 0x888F; + public const int GL_NV_texture_multisample = 1; + public const int GL_TEXTURE_COVERAGE_SAMPLES_NV = 0x9045; + public const int GL_TEXTURE_COLOR_SAMPLES_NV = 0x9046; + public const int GL_NV_texture_rectangle = 1; + public const int GL_TEXTURE_RECTANGLE_NV = 0x84F5; + public const int GL_TEXTURE_BINDING_RECTANGLE_NV = 0x84F6; + public const int GL_PROXY_TEXTURE_RECTANGLE_NV = 0x84F7; + public const int GL_MAX_RECTANGLE_TEXTURE_SIZE_NV = 0x84F8; + public const int GL_NV_texture_rectangle_compressed = 1; + public const int GL_NV_texture_shader = 1; + public const int GL_OFFSET_TEXTURE_RECTANGLE_NV = 0x864C; + public const int GL_OFFSET_TEXTURE_RECTANGLE_SCALE_NV = 0x864D; + public const int GL_DOT_PRODUCT_TEXTURE_RECTANGLE_NV = 0x864E; + public const int GL_RGBA_UNSIGNED_DOT_PRODUCT_MAPPING_NV = 0x86D9; + public const int GL_UNSIGNED_INT_S8_S8_8_8_NV = 0x86DA; + public const int GL_UNSIGNED_INT_8_8_S8_S8_REV_NV = 0x86DB; + public const int GL_DSDT_MAG_INTENSITY_NV = 0x86DC; + public const int GL_SHADER_CONSISTENT_NV = 0x86DD; + public const int GL_TEXTURE_SHADER_NV = 0x86DE; + public const int GL_SHADER_OPERATION_NV = 0x86DF; + public const int GL_CULL_MODES_NV = 0x86E0; + public const int GL_OFFSET_TEXTURE_MATRIX_NV = 0x86E1; + public const int GL_OFFSET_TEXTURE_SCALE_NV = 0x86E2; + public const int GL_OFFSET_TEXTURE_BIAS_NV = 0x86E3; + public const int GL_OFFSET_TEXTURE_2D_MATRIX_NV = 0x86E1; + public const int GL_OFFSET_TEXTURE_2D_SCALE_NV = 0x86E2; + public const int GL_OFFSET_TEXTURE_2D_BIAS_NV = 0x86E3; + public const int GL_PREVIOUS_TEXTURE_INPUT_NV = 0x86E4; + public const int GL_CONST_EYE_NV = 0x86E5; + public const int GL_PASS_THROUGH_NV = 0x86E6; + public const int GL_CULL_FRAGMENT_NV = 0x86E7; + public const int GL_OFFSET_TEXTURE_2D_NV = 0x86E8; + public const int GL_DEPENDENT_AR_TEXTURE_2D_NV = 0x86E9; + public const int GL_DEPENDENT_GB_TEXTURE_2D_NV = 0x86EA; + public const int GL_DOT_PRODUCT_NV = 0x86EC; + public const int GL_DOT_PRODUCT_DEPTH_REPLACE_NV = 0x86ED; + public const int GL_DOT_PRODUCT_TEXTURE_2D_NV = 0x86EE; + public const int GL_DOT_PRODUCT_TEXTURE_CUBE_MAP_NV = 0x86F0; + public const int GL_DOT_PRODUCT_DIFFUSE_CUBE_MAP_NV = 0x86F1; + public const int GL_DOT_PRODUCT_REFLECT_CUBE_MAP_NV = 0x86F2; + public const int GL_DOT_PRODUCT_CONST_EYE_REFLECT_CUBE_MAP_NV = 0x86F3; + public const int GL_HILO_NV = 0x86F4; + public const int GL_DSDT_NV = 0x86F5; + public const int GL_DSDT_MAG_NV = 0x86F6; + public const int GL_DSDT_MAG_VIB_NV = 0x86F7; + public const int GL_HILO16_NV = 0x86F8; + public const int GL_SIGNED_HILO_NV = 0x86F9; + public const int GL_SIGNED_HILO16_NV = 0x86FA; + public const int GL_SIGNED_RGBA_NV = 0x86FB; + public const int GL_SIGNED_RGBA8_NV = 0x86FC; + public const int GL_SIGNED_RGB_NV = 0x86FE; + public const int GL_SIGNED_RGB8_NV = 0x86FF; + public const int GL_SIGNED_LUMINANCE_NV = 0x8701; + public const int GL_SIGNED_LUMINANCE8_NV = 0x8702; + public const int GL_SIGNED_LUMINANCE_ALPHA_NV = 0x8703; + public const int GL_SIGNED_LUMINANCE8_ALPHA8_NV = 0x8704; + public const int GL_SIGNED_ALPHA_NV = 0x8705; + public const int GL_SIGNED_ALPHA8_NV = 0x8706; + public const int GL_SIGNED_INTENSITY_NV = 0x8707; + public const int GL_SIGNED_INTENSITY8_NV = 0x8708; + public const int GL_DSDT8_NV = 0x8709; + public const int GL_DSDT8_MAG8_NV = 0x870A; + public const int GL_DSDT8_MAG8_INTENSITY8_NV = 0x870B; + public const int GL_SIGNED_RGB_UNSIGNED_ALPHA_NV = 0x870C; + public const int GL_SIGNED_RGB8_UNSIGNED_ALPHA8_NV = 0x870D; + public const int GL_HI_SCALE_NV = 0x870E; + public const int GL_LO_SCALE_NV = 0x870F; + public const int GL_DS_SCALE_NV = 0x8710; + public const int GL_DT_SCALE_NV = 0x8711; + public const int GL_MAGNITUDE_SCALE_NV = 0x8712; + public const int GL_VIBRANCE_SCALE_NV = 0x8713; + public const int GL_HI_BIAS_NV = 0x8714; + public const int GL_LO_BIAS_NV = 0x8715; + public const int GL_DS_BIAS_NV = 0x8716; + public const int GL_DT_BIAS_NV = 0x8717; + public const int GL_MAGNITUDE_BIAS_NV = 0x8718; + public const int GL_VIBRANCE_BIAS_NV = 0x8719; + public const int GL_TEXTURE_BORDER_VALUES_NV = 0x871A; + public const int GL_TEXTURE_HI_SIZE_NV = 0x871B; + public const int GL_TEXTURE_LO_SIZE_NV = 0x871C; + public const int GL_TEXTURE_DS_SIZE_NV = 0x871D; + public const int GL_TEXTURE_DT_SIZE_NV = 0x871E; + public const int GL_TEXTURE_MAG_SIZE_NV = 0x871F; + public const int GL_NV_texture_shader2 = 1; + public const int GL_DOT_PRODUCT_TEXTURE_3D_NV = 0x86EF; + public const int GL_NV_texture_shader3 = 1; + public const int GL_OFFSET_PROJECTIVE_TEXTURE_2D_NV = 0x8850; + public const int GL_OFFSET_PROJECTIVE_TEXTURE_2D_SCALE_NV = 0x8851; + public const int GL_OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_NV = 0x8852; + public const int GL_OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_SCALE_NV = 0x8853; + public const int GL_OFFSET_HILO_TEXTURE_2D_NV = 0x8854; + public const int GL_OFFSET_HILO_TEXTURE_RECTANGLE_NV = 0x8855; + public const int GL_OFFSET_HILO_PROJECTIVE_TEXTURE_2D_NV = 0x8856; + public const int GL_OFFSET_HILO_PROJECTIVE_TEXTURE_RECTANGLE_NV = 0x8857; + public const int GL_DEPENDENT_HILO_TEXTURE_2D_NV = 0x8858; + public const int GL_DEPENDENT_RGB_TEXTURE_3D_NV = 0x8859; + public const int GL_DEPENDENT_RGB_TEXTURE_CUBE_MAP_NV = 0x885A; + public const int GL_DOT_PRODUCT_PASS_THROUGH_NV = 0x885B; + public const int GL_DOT_PRODUCT_TEXTURE_1D_NV = 0x885C; + public const int GL_DOT_PRODUCT_AFFINE_DEPTH_REPLACE_NV = 0x885D; + public const int GL_HILO8_NV = 0x885E; + public const int GL_SIGNED_HILO8_NV = 0x885F; + public const int GL_FORCE_BLUE_TO_ONE_NV = 0x8860; + public const int GL_NV_transform_feedback = 1; + public const int GL_BACK_PRIMARY_COLOR_NV = 0x8C77; + public const int GL_BACK_SECONDARY_COLOR_NV = 0x8C78; + public const int GL_TEXTURE_COORD_NV = 0x8C79; + public const int GL_CLIP_DISTANCE_NV = 0x8C7A; + public const int GL_VERTEX_ID_NV = 0x8C7B; + public const int GL_PRIMITIVE_ID_NV = 0x8C7C; + public const int GL_GENERIC_ATTRIB_NV = 0x8C7D; + public const int GL_TRANSFORM_FEEDBACK_ATTRIBS_NV = 0x8C7E; + public const int GL_TRANSFORM_FEEDBACK_BUFFER_MODE_NV = 0x8C7F; + public const int GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_NV = 0x8C80; + public const int GL_ACTIVE_VARYINGS_NV = 0x8C81; + public const int GL_ACTIVE_VARYING_MAX_LENGTH_NV = 0x8C82; + public const int GL_TRANSFORM_FEEDBACK_VARYINGS_NV = 0x8C83; + public const int GL_TRANSFORM_FEEDBACK_BUFFER_START_NV = 0x8C84; + public const int GL_TRANSFORM_FEEDBACK_BUFFER_SIZE_NV = 0x8C85; + public const int GL_TRANSFORM_FEEDBACK_RECORD_NV = 0x8C86; + public const int GL_PRIMITIVES_GENERATED_NV = 0x8C87; + public const int GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_NV = 0x8C88; + public const int GL_RASTERIZER_DISCARD_NV = 0x8C89; + public const int GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_NV = 0x8C8A; + public const int GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_NV = 0x8C8B; + public const int GL_INTERLEAVED_ATTRIBS_NV = 0x8C8C; + public const int GL_SEPARATE_ATTRIBS_NV = 0x8C8D; + public const int GL_TRANSFORM_FEEDBACK_BUFFER_NV = 0x8C8E; + public const int GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_NV = 0x8C8F; + public const int GL_LAYER_NV = 0x8DAA; + public const int GL_NEXT_BUFFER_NV = -2; + public const int GL_SKIP_COMPONENTS4_NV = -3; + public const int GL_SKIP_COMPONENTS3_NV = -4; + public const int GL_SKIP_COMPONENTS2_NV = -5; + public const int GL_SKIP_COMPONENTS1_NV = -6; + public const int GL_NV_transform_feedback2 = 1; + public const int GL_TRANSFORM_FEEDBACK_NV = 0x8E22; + public const int GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED_NV = 0x8E23; + public const int GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE_NV = 0x8E24; + public const int GL_TRANSFORM_FEEDBACK_BINDING_NV = 0x8E25; + public const int GL_NV_uniform_buffer_unified_memory = 1; + public const int GL_UNIFORM_BUFFER_UNIFIED_NV = 0x936E; + public const int GL_UNIFORM_BUFFER_ADDRESS_NV = 0x936F; + public const int GL_UNIFORM_BUFFER_LENGTH_NV = 0x9370; + public const int GL_NV_vdpau_interop = 1; + public const int GL_SURFACE_STATE_NV = 0x86EB; + public const int GL_SURFACE_REGISTERED_NV = 0x86FD; + public const int GL_SURFACE_MAPPED_NV = 0x8700; + public const int GL_WRITE_DISCARD_NV = 0x88BE; + public const int GL_NV_vertex_array_range = 1; + public const int GL_VERTEX_ARRAY_RANGE_NV = 0x851D; + public const int GL_VERTEX_ARRAY_RANGE_LENGTH_NV = 0x851E; + public const int GL_VERTEX_ARRAY_RANGE_VALID_NV = 0x851F; + public const int GL_MAX_VERTEX_ARRAY_RANGE_ELEMENT_NV = 0x8520; + public const int GL_VERTEX_ARRAY_RANGE_POINTER_NV = 0x8521; + public const int GL_NV_vertex_array_range2 = 1; + public const int GL_VERTEX_ARRAY_RANGE_WITHOUT_FLUSH_NV = 0x8533; + public const int GL_NV_vertex_attrib_integer_64bit = 1; + public const int GL_NV_vertex_buffer_unified_memory = 1; + public const int GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV = 0x8F1E; + public const int GL_ELEMENT_ARRAY_UNIFIED_NV = 0x8F1F; + public const int GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV = 0x8F20; + public const int GL_VERTEX_ARRAY_ADDRESS_NV = 0x8F21; + public const int GL_NORMAL_ARRAY_ADDRESS_NV = 0x8F22; + public const int GL_COLOR_ARRAY_ADDRESS_NV = 0x8F23; + public const int GL_INDEX_ARRAY_ADDRESS_NV = 0x8F24; + public const int GL_TEXTURE_COORD_ARRAY_ADDRESS_NV = 0x8F25; + public const int GL_EDGE_FLAG_ARRAY_ADDRESS_NV = 0x8F26; + public const int GL_SECONDARY_COLOR_ARRAY_ADDRESS_NV = 0x8F27; + public const int GL_FOG_COORD_ARRAY_ADDRESS_NV = 0x8F28; + public const int GL_ELEMENT_ARRAY_ADDRESS_NV = 0x8F29; + public const int GL_VERTEX_ATTRIB_ARRAY_LENGTH_NV = 0x8F2A; + public const int GL_VERTEX_ARRAY_LENGTH_NV = 0x8F2B; + public const int GL_NORMAL_ARRAY_LENGTH_NV = 0x8F2C; + public const int GL_COLOR_ARRAY_LENGTH_NV = 0x8F2D; + public const int GL_INDEX_ARRAY_LENGTH_NV = 0x8F2E; + public const int GL_TEXTURE_COORD_ARRAY_LENGTH_NV = 0x8F2F; + public const int GL_EDGE_FLAG_ARRAY_LENGTH_NV = 0x8F30; + public const int GL_SECONDARY_COLOR_ARRAY_LENGTH_NV = 0x8F31; + public const int GL_FOG_COORD_ARRAY_LENGTH_NV = 0x8F32; + public const int GL_ELEMENT_ARRAY_LENGTH_NV = 0x8F33; + public const int GL_DRAW_INDIRECT_UNIFIED_NV = 0x8F40; + public const int GL_DRAW_INDIRECT_ADDRESS_NV = 0x8F41; + public const int GL_DRAW_INDIRECT_LENGTH_NV = 0x8F42; + public const int GL_NV_vertex_program = 1; + public const int GL_VERTEX_PROGRAM_NV = 0x8620; + public const int GL_VERTEX_STATE_PROGRAM_NV = 0x8621; + public const int GL_ATTRIB_ARRAY_SIZE_NV = 0x8623; + public const int GL_ATTRIB_ARRAY_STRIDE_NV = 0x8624; + public const int GL_ATTRIB_ARRAY_TYPE_NV = 0x8625; + public const int GL_CURRENT_ATTRIB_NV = 0x8626; + public const int GL_PROGRAM_LENGTH_NV = 0x8627; + public const int GL_PROGRAM_STRING_NV = 0x8628; + public const int GL_MODELVIEW_PROJECTION_NV = 0x8629; + public const int GL_IDENTITY_NV = 0x862A; + public const int GL_INVERSE_NV = 0x862B; + public const int GL_TRANSPOSE_NV = 0x862C; + public const int GL_INVERSE_TRANSPOSE_NV = 0x862D; + public const int GL_MAX_TRACK_MATRIX_STACK_DEPTH_NV = 0x862E; + public const int GL_MAX_TRACK_MATRICES_NV = 0x862F; + public const int GL_MATRIX0_NV = 0x8630; + public const int GL_MATRIX1_NV = 0x8631; + public const int GL_MATRIX2_NV = 0x8632; + public const int GL_MATRIX3_NV = 0x8633; + public const int GL_MATRIX4_NV = 0x8634; + public const int GL_MATRIX5_NV = 0x8635; + public const int GL_MATRIX6_NV = 0x8636; + public const int GL_MATRIX7_NV = 0x8637; + public const int GL_CURRENT_MATRIX_STACK_DEPTH_NV = 0x8640; + public const int GL_CURRENT_MATRIX_NV = 0x8641; + public const int GL_VERTEX_PROGRAM_POINT_SIZE_NV = 0x8642; + public const int GL_VERTEX_PROGRAM_TWO_SIDE_NV = 0x8643; + public const int GL_PROGRAM_PARAMETER_NV = 0x8644; + public const int GL_ATTRIB_ARRAY_POINTER_NV = 0x8645; + public const int GL_PROGRAM_TARGET_NV = 0x8646; + public const int GL_PROGRAM_RESIDENT_NV = 0x8647; + public const int GL_TRACK_MATRIX_NV = 0x8648; + public const int GL_TRACK_MATRIX_TRANSFORM_NV = 0x8649; + public const int GL_VERTEX_PROGRAM_BINDING_NV = 0x864A; + public const int GL_PROGRAM_ERROR_POSITION_NV = 0x864B; + public const int GL_VERTEX_ATTRIB_ARRAY0_NV = 0x8650; + public const int GL_VERTEX_ATTRIB_ARRAY1_NV = 0x8651; + public const int GL_VERTEX_ATTRIB_ARRAY2_NV = 0x8652; + public const int GL_VERTEX_ATTRIB_ARRAY3_NV = 0x8653; + public const int GL_VERTEX_ATTRIB_ARRAY4_NV = 0x8654; + public const int GL_VERTEX_ATTRIB_ARRAY5_NV = 0x8655; + public const int GL_VERTEX_ATTRIB_ARRAY6_NV = 0x8656; + public const int GL_VERTEX_ATTRIB_ARRAY7_NV = 0x8657; + public const int GL_VERTEX_ATTRIB_ARRAY8_NV = 0x8658; + public const int GL_VERTEX_ATTRIB_ARRAY9_NV = 0x8659; + public const int GL_VERTEX_ATTRIB_ARRAY10_NV = 0x865A; + public const int GL_VERTEX_ATTRIB_ARRAY11_NV = 0x865B; + public const int GL_VERTEX_ATTRIB_ARRAY12_NV = 0x865C; + public const int GL_VERTEX_ATTRIB_ARRAY13_NV = 0x865D; + public const int GL_VERTEX_ATTRIB_ARRAY14_NV = 0x865E; + public const int GL_VERTEX_ATTRIB_ARRAY15_NV = 0x865F; + public const int GL_MAP1_VERTEX_ATTRIB0_4_NV = 0x8660; + public const int GL_MAP1_VERTEX_ATTRIB1_4_NV = 0x8661; + public const int GL_MAP1_VERTEX_ATTRIB2_4_NV = 0x8662; + public const int GL_MAP1_VERTEX_ATTRIB3_4_NV = 0x8663; + public const int GL_MAP1_VERTEX_ATTRIB4_4_NV = 0x8664; + public const int GL_MAP1_VERTEX_ATTRIB5_4_NV = 0x8665; + public const int GL_MAP1_VERTEX_ATTRIB6_4_NV = 0x8666; + public const int GL_MAP1_VERTEX_ATTRIB7_4_NV = 0x8667; + public const int GL_MAP1_VERTEX_ATTRIB8_4_NV = 0x8668; + public const int GL_MAP1_VERTEX_ATTRIB9_4_NV = 0x8669; + public const int GL_MAP1_VERTEX_ATTRIB10_4_NV = 0x866A; + public const int GL_MAP1_VERTEX_ATTRIB11_4_NV = 0x866B; + public const int GL_MAP1_VERTEX_ATTRIB12_4_NV = 0x866C; + public const int GL_MAP1_VERTEX_ATTRIB13_4_NV = 0x866D; + public const int GL_MAP1_VERTEX_ATTRIB14_4_NV = 0x866E; + public const int GL_MAP1_VERTEX_ATTRIB15_4_NV = 0x866F; + public const int GL_MAP2_VERTEX_ATTRIB0_4_NV = 0x8670; + public const int GL_MAP2_VERTEX_ATTRIB1_4_NV = 0x8671; + public const int GL_MAP2_VERTEX_ATTRIB2_4_NV = 0x8672; + public const int GL_MAP2_VERTEX_ATTRIB3_4_NV = 0x8673; + public const int GL_MAP2_VERTEX_ATTRIB4_4_NV = 0x8674; + public const int GL_MAP2_VERTEX_ATTRIB5_4_NV = 0x8675; + public const int GL_MAP2_VERTEX_ATTRIB6_4_NV = 0x8676; + public const int GL_MAP2_VERTEX_ATTRIB7_4_NV = 0x8677; + public const int GL_MAP2_VERTEX_ATTRIB8_4_NV = 0x8678; + public const int GL_MAP2_VERTEX_ATTRIB9_4_NV = 0x8679; + public const int GL_MAP2_VERTEX_ATTRIB10_4_NV = 0x867A; + public const int GL_MAP2_VERTEX_ATTRIB11_4_NV = 0x867B; + public const int GL_MAP2_VERTEX_ATTRIB12_4_NV = 0x867C; + public const int GL_MAP2_VERTEX_ATTRIB13_4_NV = 0x867D; + public const int GL_MAP2_VERTEX_ATTRIB14_4_NV = 0x867E; + public const int GL_MAP2_VERTEX_ATTRIB15_4_NV = 0x867F; + public const int GL_NV_vertex_program1_1 = 1; + public const int GL_NV_vertex_program2 = 1; + public const int GL_NV_vertex_program2_option = 1; + public const int GL_NV_vertex_program3 = 1; + public const int GL_NV_vertex_program4 = 1; + public const int GL_VERTEX_ATTRIB_ARRAY_INTEGER_NV = 0x88FD; + public const int GL_NV_video_capture = 1; + public const int GL_VIDEO_BUFFER_NV = 0x9020; + public const int GL_VIDEO_BUFFER_BINDING_NV = 0x9021; + public const int GL_FIELD_UPPER_NV = 0x9022; + public const int GL_FIELD_LOWER_NV = 0x9023; + public const int GL_NUM_VIDEO_CAPTURE_STREAMS_NV = 0x9024; + public const int GL_NEXT_VIDEO_CAPTURE_BUFFER_STATUS_NV = 0x9025; + public const int GL_VIDEO_CAPTURE_TO_422_SUPPORTED_NV = 0x9026; + public const int GL_LAST_VIDEO_CAPTURE_STATUS_NV = 0x9027; + public const int GL_VIDEO_BUFFER_PITCH_NV = 0x9028; + public const int GL_VIDEO_COLOR_CONVERSION_MATRIX_NV = 0x9029; + public const int GL_VIDEO_COLOR_CONVERSION_MAX_NV = 0x902A; + public const int GL_VIDEO_COLOR_CONVERSION_MIN_NV = 0x902B; + public const int GL_VIDEO_COLOR_CONVERSION_OFFSET_NV = 0x902C; + public const int GL_VIDEO_BUFFER_INTERNAL_FORMAT_NV = 0x902D; + public const int GL_PARTIAL_SUCCESS_NV = 0x902E; + public const int GL_SUCCESS_NV = 0x902F; + public const int GL_FAILURE_NV = 0x9030; + public const int GL_YCBYCR8_422_NV = 0x9031; + public const int GL_YCBAYCR8A_4224_NV = 0x9032; + public const int GL_Z6Y10Z6CB10Z6Y10Z6CR10_422_NV = 0x9033; + public const int GL_Z6Y10Z6CB10Z6A10Z6Y10Z6CR10Z6A10_4224_NV = 0x9034; + public const int GL_Z4Y12Z4CB12Z4Y12Z4CR12_422_NV = 0x9035; + public const int GL_Z4Y12Z4CB12Z4A12Z4Y12Z4CR12Z4A12_4224_NV = 0x9036; + public const int GL_Z4Y12Z4CB12Z4CR12_444_NV = 0x9037; + public const int GL_VIDEO_CAPTURE_FRAME_WIDTH_NV = 0x9038; + public const int GL_VIDEO_CAPTURE_FRAME_HEIGHT_NV = 0x9039; + public const int GL_VIDEO_CAPTURE_FIELD_UPPER_HEIGHT_NV = 0x903A; + public const int GL_VIDEO_CAPTURE_FIELD_LOWER_HEIGHT_NV = 0x903B; + public const int GL_VIDEO_CAPTURE_SURFACE_ORIGIN_NV = 0x903C; + public const int GL_NV_viewport_array2 = 1; + public const int GL_NV_viewport_swizzle = 1; + public const int GL_VIEWPORT_SWIZZLE_POSITIVE_X_NV = 0x9350; + public const int GL_VIEWPORT_SWIZZLE_NEGATIVE_X_NV = 0x9351; + public const int GL_VIEWPORT_SWIZZLE_POSITIVE_Y_NV = 0x9352; + public const int GL_VIEWPORT_SWIZZLE_NEGATIVE_Y_NV = 0x9353; + public const int GL_VIEWPORT_SWIZZLE_POSITIVE_Z_NV = 0x9354; + public const int GL_VIEWPORT_SWIZZLE_NEGATIVE_Z_NV = 0x9355; + public const int GL_VIEWPORT_SWIZZLE_POSITIVE_W_NV = 0x9356; + public const int GL_VIEWPORT_SWIZZLE_NEGATIVE_W_NV = 0x9357; + public const int GL_VIEWPORT_SWIZZLE_X_NV = 0x9358; + public const int GL_VIEWPORT_SWIZZLE_Y_NV = 0x9359; + public const int GL_VIEWPORT_SWIZZLE_Z_NV = 0x935A; + public const int GL_VIEWPORT_SWIZZLE_W_NV = 0x935B; + public const int GL_OML_interlace = 1; + public const int GL_INTERLACE_OML = 0x8980; + public const int GL_INTERLACE_READ_OML = 0x8981; + public const int GL_OML_resample = 1; + public const int GL_PACK_RESAMPLE_OML = 0x8984; + public const int GL_UNPACK_RESAMPLE_OML = 0x8985; + public const int GL_RESAMPLE_REPLICATE_OML = 0x8986; + public const int GL_RESAMPLE_ZERO_FILL_OML = 0x8987; + public const int GL_RESAMPLE_AVERAGE_OML = 0x8988; + public const int GL_RESAMPLE_DECIMATE_OML = 0x8989; + public const int GL_OML_subsample = 1; + public const int GL_FORMAT_SUBSAMPLE_24_24_OML = 0x8982; + public const int GL_FORMAT_SUBSAMPLE_244_244_OML = 0x8983; + public const int GL_OVR_multiview = 1; + public const int GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR = 0x9630; + public const int GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR = 0x9632; + public const int GL_MAX_VIEWS_OVR = 0x9631; + public const int GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR = 0x9633; + public const int GL_OVR_multiview2 = 1; + public const int GL_PGI_misc_hints = 1; + public const int GL_PREFER_DOUBLEBUFFER_HINT_PGI = 0x1A1F8; + public const int GL_CONSERVE_MEMORY_HINT_PGI = 0x1A1FD; + public const int GL_RECLAIM_MEMORY_HINT_PGI = 0x1A1FE; + public const int GL_NATIVE_GRAPHICS_HANDLE_PGI = 0x1A202; + public const int GL_NATIVE_GRAPHICS_BEGIN_HINT_PGI = 0x1A203; + public const int GL_NATIVE_GRAPHICS_END_HINT_PGI = 0x1A204; + public const int GL_ALWAYS_FAST_HINT_PGI = 0x1A20C; + public const int GL_ALWAYS_SOFT_HINT_PGI = 0x1A20D; + public const int GL_ALLOW_DRAW_OBJ_HINT_PGI = 0x1A20E; + public const int GL_ALLOW_DRAW_WIN_HINT_PGI = 0x1A20F; + public const int GL_ALLOW_DRAW_FRG_HINT_PGI = 0x1A210; + public const int GL_ALLOW_DRAW_MEM_HINT_PGI = 0x1A211; + public const int GL_STRICT_DEPTHFUNC_HINT_PGI = 0x1A216; + public const int GL_STRICT_LIGHTING_HINT_PGI = 0x1A217; + public const int GL_STRICT_SCISSOR_HINT_PGI = 0x1A218; + public const int GL_FULL_STIPPLE_HINT_PGI = 0x1A219; + public const int GL_CLIP_NEAR_HINT_PGI = 0x1A220; + public const int GL_CLIP_FAR_HINT_PGI = 0x1A221; + public const int GL_WIDE_LINE_HINT_PGI = 0x1A222; + public const int GL_BACK_NORMALS_HINT_PGI = 0x1A223; + public const int GL_PGI_vertex_hints = 1; + public const int GL_VERTEX_DATA_HINT_PGI = 0x1A22A; + public const int GL_VERTEX_CONSISTENT_HINT_PGI = 0x1A22B; + public const int GL_MATERIAL_SIDE_HINT_PGI = 0x1A22C; + public const int GL_MAX_VERTEX_HINT_PGI = 0x1A22D; + public const int GL_COLOR3_BIT_PGI = 0x00010000; + public const int GL_COLOR4_BIT_PGI = 0x00020000; + public const int GL_EDGEFLAG_BIT_PGI = 0x00040000; + public const int GL_INDEX_BIT_PGI = 0x00080000; + public const int GL_MAT_AMBIENT_BIT_PGI = 0x00100000; + public const int GL_MAT_AMBIENT_AND_DIFFUSE_BIT_PGI = 0x00200000; + public const int GL_MAT_DIFFUSE_BIT_PGI = 0x00400000; + public const int GL_MAT_EMISSION_BIT_PGI = 0x00800000; + public const int GL_MAT_COLOR_INDEXES_BIT_PGI = 0x01000000; + public const int GL_MAT_SHININESS_BIT_PGI = 0x02000000; + public const int GL_MAT_SPECULAR_BIT_PGI = 0x04000000; + public const int GL_NORMAL_BIT_PGI = 0x08000000; + public const int GL_TEXCOORD1_BIT_PGI = 0x10000000; + public const int GL_TEXCOORD2_BIT_PGI = 0x20000000; + public const int GL_TEXCOORD3_BIT_PGI = 0x40000000; + public const int GL_TEXCOORD4_BIT_PGI = unchecked((int)0x80000000); + public const int GL_VERTEX23_BIT_PGI = 0x00000004; + public const int GL_VERTEX4_BIT_PGI = 0x00000008; + public const int GL_REND_screen_coordinates = 1; + public const int GL_SCREEN_COORDINATES_REND = 0x8490; + public const int GL_INVERTED_SCREEN_W_REND = 0x8491; + public const int GL_S3_s3tc = 1; + public const int GL_RGB_S3TC = 0x83A0; + public const int GL_RGB4_S3TC = 0x83A1; + public const int GL_RGBA_S3TC = 0x83A2; + public const int GL_RGBA4_S3TC = 0x83A3; + public const int GL_RGBA_DXT5_S3TC = 0x83A4; + public const int GL_RGBA4_DXT5_S3TC = 0x83A5; + public const int GL_SGIS_detail_texture = 1; + public const int GL_DETAIL_TEXTURE_2D_SGIS = 0x8095; + public const int GL_DETAIL_TEXTURE_2D_BINDING_SGIS = 0x8096; + public const int GL_LINEAR_DETAIL_SGIS = 0x8097; + public const int GL_LINEAR_DETAIL_ALPHA_SGIS = 0x8098; + public const int GL_LINEAR_DETAIL_COLOR_SGIS = 0x8099; + public const int GL_DETAIL_TEXTURE_LEVEL_SGIS = 0x809A; + public const int GL_DETAIL_TEXTURE_MODE_SGIS = 0x809B; + public const int GL_DETAIL_TEXTURE_FUNC_POINTS_SGIS = 0x809C; + public const int GL_SGIS_fog_function = 1; + public const int GL_FOG_FUNC_SGIS = 0x812A; + public const int GL_FOG_FUNC_POINTS_SGIS = 0x812B; + public const int GL_MAX_FOG_FUNC_POINTS_SGIS = 0x812C; + public const int GL_SGIS_generate_mipmap = 1; + public const int GL_GENERATE_MIPMAP_SGIS = 0x8191; + public const int GL_GENERATE_MIPMAP_HINT_SGIS = 0x8192; + public const int GL_SGIS_multisample = 1; + public const int GL_MULTISAMPLE_SGIS = 0x809D; + public const int GL_SAMPLE_ALPHA_TO_MASK_SGIS = 0x809E; + public const int GL_SAMPLE_ALPHA_TO_ONE_SGIS = 0x809F; + public const int GL_SAMPLE_MASK_SGIS = 0x80A0; + public const int GL_1PASS_SGIS = 0x80A1; + public const int GL_2PASS_0_SGIS = 0x80A2; + public const int GL_2PASS_1_SGIS = 0x80A3; + public const int GL_4PASS_0_SGIS = 0x80A4; + public const int GL_4PASS_1_SGIS = 0x80A5; + public const int GL_4PASS_2_SGIS = 0x80A6; + public const int GL_4PASS_3_SGIS = 0x80A7; + public const int GL_SAMPLE_BUFFERS_SGIS = 0x80A8; + public const int GL_SAMPLES_SGIS = 0x80A9; + public const int GL_SAMPLE_MASK_VALUE_SGIS = 0x80AA; + public const int GL_SAMPLE_MASK_INVERT_SGIS = 0x80AB; + public const int GL_SAMPLE_PATTERN_SGIS = 0x80AC; + public const int GL_SGIS_pixel_texture = 1; + public const int GL_PIXEL_TEXTURE_SGIS = 0x8353; + public const int GL_PIXEL_FRAGMENT_RGB_SOURCE_SGIS = 0x8354; + public const int GL_PIXEL_FRAGMENT_ALPHA_SOURCE_SGIS = 0x8355; + public const int GL_PIXEL_GROUP_COLOR_SGIS = 0x8356; + public const int GL_SGIS_point_line_texgen = 1; + public const int GL_EYE_DISTANCE_TO_POINT_SGIS = 0x81F0; + public const int GL_OBJECT_DISTANCE_TO_POINT_SGIS = 0x81F1; + public const int GL_EYE_DISTANCE_TO_LINE_SGIS = 0x81F2; + public const int GL_OBJECT_DISTANCE_TO_LINE_SGIS = 0x81F3; + public const int GL_EYE_POINT_SGIS = 0x81F4; + public const int GL_OBJECT_POINT_SGIS = 0x81F5; + public const int GL_EYE_LINE_SGIS = 0x81F6; + public const int GL_OBJECT_LINE_SGIS = 0x81F7; + public const int GL_SGIS_point_parameters = 1; + public const int GL_POINT_SIZE_MIN_SGIS = 0x8126; + public const int GL_POINT_SIZE_MAX_SGIS = 0x8127; + public const int GL_POINT_FADE_THRESHOLD_SIZE_SGIS = 0x8128; + public const int GL_DISTANCE_ATTENUATION_SGIS = 0x8129; + public const int GL_SGIS_sharpen_texture = 1; + public const int GL_LINEAR_SHARPEN_SGIS = 0x80AD; + public const int GL_LINEAR_SHARPEN_ALPHA_SGIS = 0x80AE; + public const int GL_LINEAR_SHARPEN_COLOR_SGIS = 0x80AF; + public const int GL_SHARPEN_TEXTURE_FUNC_POINTS_SGIS = 0x80B0; + public const int GL_SGIS_texture4D = 1; + public const int GL_PACK_SKIP_VOLUMES_SGIS = 0x8130; + public const int GL_PACK_IMAGE_DEPTH_SGIS = 0x8131; + public const int GL_UNPACK_SKIP_VOLUMES_SGIS = 0x8132; + public const int GL_UNPACK_IMAGE_DEPTH_SGIS = 0x8133; + public const int GL_TEXTURE_4D_SGIS = 0x8134; + public const int GL_PROXY_TEXTURE_4D_SGIS = 0x8135; + public const int GL_TEXTURE_4DSIZE_SGIS = 0x8136; + public const int GL_TEXTURE_WRAP_Q_SGIS = 0x8137; + public const int GL_MAX_4D_TEXTURE_SIZE_SGIS = 0x8138; + public const int GL_TEXTURE_4D_BINDING_SGIS = 0x814F; + public const int GL_SGIS_texture_border_clamp = 1; + public const int GL_CLAMP_TO_BORDER_SGIS = 0x812D; + public const int GL_SGIS_texture_color_mask = 1; + public const int GL_TEXTURE_COLOR_WRITEMASK_SGIS = 0x81EF; + public const int GL_SGIS_texture_edge_clamp = 1; + public const int GL_CLAMP_TO_EDGE_SGIS = 0x812F; + public const int GL_SGIS_texture_filter4 = 1; + public const int GL_FILTER4_SGIS = 0x8146; + public const int GL_TEXTURE_FILTER4_SIZE_SGIS = 0x8147; + public const int GL_SGIS_texture_lod = 1; + public const int GL_TEXTURE_MIN_LOD_SGIS = 0x813A; + public const int GL_TEXTURE_MAX_LOD_SGIS = 0x813B; + public const int GL_TEXTURE_BASE_LEVEL_SGIS = 0x813C; + public const int GL_TEXTURE_MAX_LEVEL_SGIS = 0x813D; + public const int GL_SGIS_texture_select = 1; + public const int GL_DUAL_ALPHA4_SGIS = 0x8110; + public const int GL_DUAL_ALPHA8_SGIS = 0x8111; + public const int GL_DUAL_ALPHA12_SGIS = 0x8112; + public const int GL_DUAL_ALPHA16_SGIS = 0x8113; + public const int GL_DUAL_LUMINANCE4_SGIS = 0x8114; + public const int GL_DUAL_LUMINANCE8_SGIS = 0x8115; + public const int GL_DUAL_LUMINANCE12_SGIS = 0x8116; + public const int GL_DUAL_LUMINANCE16_SGIS = 0x8117; + public const int GL_DUAL_INTENSITY4_SGIS = 0x8118; + public const int GL_DUAL_INTENSITY8_SGIS = 0x8119; + public const int GL_DUAL_INTENSITY12_SGIS = 0x811A; + public const int GL_DUAL_INTENSITY16_SGIS = 0x811B; + public const int GL_DUAL_LUMINANCE_ALPHA4_SGIS = 0x811C; + public const int GL_DUAL_LUMINANCE_ALPHA8_SGIS = 0x811D; + public const int GL_QUAD_ALPHA4_SGIS = 0x811E; + public const int GL_QUAD_ALPHA8_SGIS = 0x811F; + public const int GL_QUAD_LUMINANCE4_SGIS = 0x8120; + public const int GL_QUAD_LUMINANCE8_SGIS = 0x8121; + public const int GL_QUAD_INTENSITY4_SGIS = 0x8122; + public const int GL_QUAD_INTENSITY8_SGIS = 0x8123; + public const int GL_DUAL_TEXTURE_SELECT_SGIS = 0x8124; + public const int GL_QUAD_TEXTURE_SELECT_SGIS = 0x8125; + public const int GL_SGIX_async = 1; + public const int GL_ASYNC_MARKER_SGIX = 0x8329; + public const int GL_SGIX_async_histogram = 1; + public const int GL_ASYNC_HISTOGRAM_SGIX = 0x832C; + public const int GL_MAX_ASYNC_HISTOGRAM_SGIX = 0x832D; + public const int GL_SGIX_async_pixel = 1; + public const int GL_ASYNC_TEX_IMAGE_SGIX = 0x835C; + public const int GL_ASYNC_DRAW_PIXELS_SGIX = 0x835D; + public const int GL_ASYNC_READ_PIXELS_SGIX = 0x835E; + public const int GL_MAX_ASYNC_TEX_IMAGE_SGIX = 0x835F; + public const int GL_MAX_ASYNC_DRAW_PIXELS_SGIX = 0x8360; + public const int GL_MAX_ASYNC_READ_PIXELS_SGIX = 0x8361; + public const int GL_SGIX_blend_alpha_minmax = 1; + public const int GL_ALPHA_MIN_SGIX = 0x8320; + public const int GL_ALPHA_MAX_SGIX = 0x8321; + public const int GL_SGIX_calligraphic_fragment = 1; + public const int GL_CALLIGRAPHIC_FRAGMENT_SGIX = 0x8183; + public const int GL_SGIX_clipmap = 1; + public const int GL_LINEAR_CLIPMAP_LINEAR_SGIX = 0x8170; + public const int GL_TEXTURE_CLIPMAP_CENTER_SGIX = 0x8171; + public const int GL_TEXTURE_CLIPMAP_FRAME_SGIX = 0x8172; + public const int GL_TEXTURE_CLIPMAP_OFFSET_SGIX = 0x8173; + public const int GL_TEXTURE_CLIPMAP_VIRTUAL_DEPTH_SGIX = 0x8174; + public const int GL_TEXTURE_CLIPMAP_LOD_OFFSET_SGIX = 0x8175; + public const int GL_TEXTURE_CLIPMAP_DEPTH_SGIX = 0x8176; + public const int GL_MAX_CLIPMAP_DEPTH_SGIX = 0x8177; + public const int GL_MAX_CLIPMAP_VIRTUAL_DEPTH_SGIX = 0x8178; + public const int GL_NEAREST_CLIPMAP_NEAREST_SGIX = 0x844D; + public const int GL_NEAREST_CLIPMAP_LINEAR_SGIX = 0x844E; + public const int GL_LINEAR_CLIPMAP_NEAREST_SGIX = 0x844F; + public const int GL_SGIX_convolution_accuracy = 1; + public const int GL_CONVOLUTION_HINT_SGIX = 0x8316; + public const int GL_SGIX_depth_pass_instrument = 1; + public const int GL_SGIX_depth_texture = 1; + public const int GL_DEPTH_COMPONENT16_SGIX = 0x81A5; + public const int GL_DEPTH_COMPONENT24_SGIX = 0x81A6; + public const int GL_DEPTH_COMPONENT32_SGIX = 0x81A7; + public const int GL_SGIX_flush_raster = 1; + public const int GL_SGIX_fog_offset = 1; + public const int GL_FOG_OFFSET_SGIX = 0x8198; + public const int GL_FOG_OFFSET_VALUE_SGIX = 0x8199; + public const int GL_SGIX_fragment_lighting = 1; + public const int GL_FRAGMENT_LIGHTING_SGIX = 0x8400; + public const int GL_FRAGMENT_COLOR_MATERIAL_SGIX = 0x8401; + public const int GL_FRAGMENT_COLOR_MATERIAL_FACE_SGIX = 0x8402; + public const int GL_FRAGMENT_COLOR_MATERIAL_PARAMETER_SGIX = 0x8403; + public const int GL_MAX_FRAGMENT_LIGHTS_SGIX = 0x8404; + public const int GL_MAX_ACTIVE_LIGHTS_SGIX = 0x8405; + public const int GL_CURRENT_RASTER_NORMAL_SGIX = 0x8406; + public const int GL_LIGHT_ENV_MODE_SGIX = 0x8407; + public const int GL_FRAGMENT_LIGHT_MODEL_LOCAL_VIEWER_SGIX = 0x8408; + public const int GL_FRAGMENT_LIGHT_MODEL_TWO_SIDE_SGIX = 0x8409; + public const int GL_FRAGMENT_LIGHT_MODEL_AMBIENT_SGIX = 0x840A; + public const int GL_FRAGMENT_LIGHT_MODEL_NORMAL_INTERPOLATION_SGIX = 0x840B; + public const int GL_FRAGMENT_LIGHT0_SGIX = 0x840C; + public const int GL_FRAGMENT_LIGHT1_SGIX = 0x840D; + public const int GL_FRAGMENT_LIGHT2_SGIX = 0x840E; + public const int GL_FRAGMENT_LIGHT3_SGIX = 0x840F; + public const int GL_FRAGMENT_LIGHT4_SGIX = 0x8410; + public const int GL_FRAGMENT_LIGHT5_SGIX = 0x8411; + public const int GL_FRAGMENT_LIGHT6_SGIX = 0x8412; + public const int GL_FRAGMENT_LIGHT7_SGIX = 0x8413; + public const int GL_SGIX_framezoom = 1; + public const int GL_FRAMEZOOM_SGIX = 0x818B; + public const int GL_FRAMEZOOM_FACTOR_SGIX = 0x818C; + public const int GL_MAX_FRAMEZOOM_FACTOR_SGIX = 0x818D; + public const int GL_SGIX_igloo_interface = 1; + public const int GL_SGIX_instruments = 1; + public const int GL_INSTRUMENT_BUFFER_POINTER_SGIX = 0x8180; + public const int GL_INSTRUMENT_MEASUREMENTS_SGIX = 0x8181; + public const int GL_SGIX_interlace = 1; + public const int GL_INTERLACE_SGIX = 0x8094; + public const int GL_SGIX_ir_instrument1 = 1; + public const int GL_IR_INSTRUMENT1_SGIX = 0x817F; + public const int GL_SGIX_list_priority = 1; + public const int GL_LIST_PRIORITY_SGIX = 0x8182; + public const int GL_SGIX_pixel_texture = 1; + public const int GL_PIXEL_TEX_GEN_SGIX = 0x8139; + public const int GL_PIXEL_TEX_GEN_MODE_SGIX = 0x832B; + public const int GL_SGIX_pixel_tiles = 1; + public const int GL_PIXEL_TILE_BEST_ALIGNMENT_SGIX = 0x813E; + public const int GL_PIXEL_TILE_CACHE_INCREMENT_SGIX = 0x813F; + public const int GL_PIXEL_TILE_WIDTH_SGIX = 0x8140; + public const int GL_PIXEL_TILE_HEIGHT_SGIX = 0x8141; + public const int GL_PIXEL_TILE_GRID_WIDTH_SGIX = 0x8142; + public const int GL_PIXEL_TILE_GRID_HEIGHT_SGIX = 0x8143; + public const int GL_PIXEL_TILE_GRID_DEPTH_SGIX = 0x8144; + public const int GL_PIXEL_TILE_CACHE_SIZE_SGIX = 0x8145; + public const int GL_SGIX_polynomial_ffd = 1; + public const int GL_TEXTURE_DEFORMATION_BIT_SGIX = 0x00000001; + public const int GL_GEOMETRY_DEFORMATION_BIT_SGIX = 0x00000002; + public const int GL_GEOMETRY_DEFORMATION_SGIX = 0x8194; + public const int GL_TEXTURE_DEFORMATION_SGIX = 0x8195; + public const int GL_DEFORMATIONS_MASK_SGIX = 0x8196; + public const int GL_MAX_DEFORMATION_ORDER_SGIX = 0x8197; + public const int GL_SGIX_reference_plane = 1; + public const int GL_REFERENCE_PLANE_SGIX = 0x817D; + public const int GL_REFERENCE_PLANE_EQUATION_SGIX = 0x817E; + public const int GL_SGIX_resample = 1; + public const int GL_PACK_RESAMPLE_SGIX = 0x842E; + public const int GL_UNPACK_RESAMPLE_SGIX = 0x842F; + public const int GL_RESAMPLE_REPLICATE_SGIX = 0x8433; + public const int GL_RESAMPLE_ZERO_FILL_SGIX = 0x8434; + public const int GL_RESAMPLE_DECIMATE_SGIX = 0x8430; + public const int GL_SGIX_scalebias_hint = 1; + public const int GL_SCALEBIAS_HINT_SGIX = 0x8322; + public const int GL_SGIX_shadow = 1; + public const int GL_TEXTURE_COMPARE_SGIX = 0x819A; + public const int GL_TEXTURE_COMPARE_OPERATOR_SGIX = 0x819B; + public const int GL_TEXTURE_LEQUAL_R_SGIX = 0x819C; + public const int GL_TEXTURE_GEQUAL_R_SGIX = 0x819D; + public const int GL_SGIX_shadow_ambient = 1; + public const int GL_SHADOW_AMBIENT_SGIX = 0x80BF; + public const int GL_SGIX_sprite = 1; + public const int GL_SPRITE_SGIX = 0x8148; + public const int GL_SPRITE_MODE_SGIX = 0x8149; + public const int GL_SPRITE_AXIS_SGIX = 0x814A; + public const int GL_SPRITE_TRANSLATION_SGIX = 0x814B; + public const int GL_SPRITE_AXIAL_SGIX = 0x814C; + public const int GL_SPRITE_OBJECT_ALIGNED_SGIX = 0x814D; + public const int GL_SPRITE_EYE_ALIGNED_SGIX = 0x814E; + public const int GL_SGIX_subsample = 1; + public const int GL_PACK_SUBSAMPLE_RATE_SGIX = 0x85A0; + public const int GL_UNPACK_SUBSAMPLE_RATE_SGIX = 0x85A1; + public const int GL_PIXEL_SUBSAMPLE_4444_SGIX = 0x85A2; + public const int GL_PIXEL_SUBSAMPLE_2424_SGIX = 0x85A3; + public const int GL_PIXEL_SUBSAMPLE_4242_SGIX = 0x85A4; + public const int GL_SGIX_tag_sample_buffer = 1; + public const int GL_SGIX_texture_add_env = 1; + public const int GL_TEXTURE_ENV_BIAS_SGIX = 0x80BE; + public const int GL_SGIX_texture_coordinate_clamp = 1; + public const int GL_TEXTURE_MAX_CLAMP_S_SGIX = 0x8369; + public const int GL_TEXTURE_MAX_CLAMP_T_SGIX = 0x836A; + public const int GL_TEXTURE_MAX_CLAMP_R_SGIX = 0x836B; + public const int GL_SGIX_texture_lod_bias = 1; + public const int GL_TEXTURE_LOD_BIAS_S_SGIX = 0x818E; + public const int GL_TEXTURE_LOD_BIAS_T_SGIX = 0x818F; + public const int GL_TEXTURE_LOD_BIAS_R_SGIX = 0x8190; + public const int GL_SGIX_texture_multi_buffer = 1; + public const int GL_TEXTURE_MULTI_BUFFER_HINT_SGIX = 0x812E; + public const int GL_SGIX_texture_scale_bias = 1; + public const int GL_POST_TEXTURE_FILTER_BIAS_SGIX = 0x8179; + public const int GL_POST_TEXTURE_FILTER_SCALE_SGIX = 0x817A; + public const int GL_POST_TEXTURE_FILTER_BIAS_RANGE_SGIX = 0x817B; + public const int GL_POST_TEXTURE_FILTER_SCALE_RANGE_SGIX = 0x817C; + public const int GL_SGIX_vertex_preclip = 1; + public const int GL_VERTEX_PRECLIP_SGIX = 0x83EE; + public const int GL_VERTEX_PRECLIP_HINT_SGIX = 0x83EF; + public const int GL_SGIX_ycrcb = 1; + public const int GL_YCRCB_422_SGIX = 0x81BB; + public const int GL_YCRCB_444_SGIX = 0x81BC; + public const int GL_SGIX_ycrcb_subsample = 1; + public const int GL_SGIX_ycrcba = 1; + public const int GL_YCRCB_SGIX = 0x8318; + public const int GL_YCRCBA_SGIX = 0x8319; + public const int GL_SGI_color_matrix = 1; + public const int GL_COLOR_MATRIX_SGI = 0x80B1; + public const int GL_COLOR_MATRIX_STACK_DEPTH_SGI = 0x80B2; + public const int GL_MAX_COLOR_MATRIX_STACK_DEPTH_SGI = 0x80B3; + public const int GL_POST_COLOR_MATRIX_RED_SCALE_SGI = 0x80B4; + public const int GL_POST_COLOR_MATRIX_GREEN_SCALE_SGI = 0x80B5; + public const int GL_POST_COLOR_MATRIX_BLUE_SCALE_SGI = 0x80B6; + public const int GL_POST_COLOR_MATRIX_ALPHA_SCALE_SGI = 0x80B7; + public const int GL_POST_COLOR_MATRIX_RED_BIAS_SGI = 0x80B8; + public const int GL_POST_COLOR_MATRIX_GREEN_BIAS_SGI = 0x80B9; + public const int GL_POST_COLOR_MATRIX_BLUE_BIAS_SGI = 0x80BA; + public const int GL_POST_COLOR_MATRIX_ALPHA_BIAS_SGI = 0x80BB; + public const int GL_SGI_color_table = 1; + public const int GL_COLOR_TABLE_SGI = 0x80D0; + public const int GL_POST_CONVOLUTION_COLOR_TABLE_SGI = 0x80D1; + public const int GL_POST_COLOR_MATRIX_COLOR_TABLE_SGI = 0x80D2; + public const int GL_PROXY_COLOR_TABLE_SGI = 0x80D3; + public const int GL_PROXY_POST_CONVOLUTION_COLOR_TABLE_SGI = 0x80D4; + public const int GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE_SGI = 0x80D5; + public const int GL_COLOR_TABLE_SCALE_SGI = 0x80D6; + public const int GL_COLOR_TABLE_BIAS_SGI = 0x80D7; + public const int GL_COLOR_TABLE_FORMAT_SGI = 0x80D8; + public const int GL_COLOR_TABLE_WIDTH_SGI = 0x80D9; + public const int GL_COLOR_TABLE_RED_SIZE_SGI = 0x80DA; + public const int GL_COLOR_TABLE_GREEN_SIZE_SGI = 0x80DB; + public const int GL_COLOR_TABLE_BLUE_SIZE_SGI = 0x80DC; + public const int GL_COLOR_TABLE_ALPHA_SIZE_SGI = 0x80DD; + public const int GL_COLOR_TABLE_LUMINANCE_SIZE_SGI = 0x80DE; + public const int GL_COLOR_TABLE_INTENSITY_SIZE_SGI = 0x80DF; + public const int GL_SGI_texture_color_table = 1; + public const int GL_TEXTURE_COLOR_TABLE_SGI = 0x80BC; + public const int GL_PROXY_TEXTURE_COLOR_TABLE_SGI = 0x80BD; + public const int GL_SUNX_constant_data = 1; + public const int GL_UNPACK_CONSTANT_DATA_SUNX = 0x81D5; + public const int GL_TEXTURE_CONSTANT_DATA_SUNX = 0x81D6; + public const int GL_SUN_convolution_border_modes = 1; + public const int GL_WRAP_BORDER_SUN = 0x81D4; + public const int GL_SUN_global_alpha = 1; + public const int GL_GLOBAL_ALPHA_SUN = 0x81D9; + public const int GL_GLOBAL_ALPHA_FACTOR_SUN = 0x81DA; + public const int GL_SUN_mesh_array = 1; + public const int GL_QUAD_MESH_SUN = 0x8614; + public const int GL_TRIANGLE_MESH_SUN = 0x8615; + public const int GL_SUN_slice_accum = 1; + public const int GL_SLICE_ACCUM_SUN = 0x85CC; + public const int GL_SUN_triangle_list = 1; + public const int GL_RESTART_SUN = 0x0001; + public const int GL_REPLACE_MIDDLE_SUN = 0x0002; + public const int GL_REPLACE_OLDEST_SUN = 0x0003; + public const int GL_TRIANGLE_LIST_SUN = 0x81D7; + public const int GL_REPLACEMENT_CODE_SUN = 0x81D8; + public const int GL_REPLACEMENT_CODE_ARRAY_SUN = 0x85C0; + public const int GL_REPLACEMENT_CODE_ARRAY_TYPE_SUN = 0x85C1; + public const int GL_REPLACEMENT_CODE_ARRAY_STRIDE_SUN = 0x85C2; + public const int GL_REPLACEMENT_CODE_ARRAY_POINTER_SUN = 0x85C3; + public const int GL_R1UI_V3F_SUN = 0x85C4; + public const int GL_R1UI_C4UB_V3F_SUN = 0x85C5; + public const int GL_R1UI_C3F_V3F_SUN = 0x85C6; + public const int GL_R1UI_N3F_V3F_SUN = 0x85C7; + public const int GL_R1UI_C4F_N3F_V3F_SUN = 0x85C8; + public const int GL_R1UI_T2F_V3F_SUN = 0x85C9; + public const int GL_R1UI_T2F_N3F_V3F_SUN = 0x85CA; + public const int GL_R1UI_T2F_C4F_N3F_V3F_SUN = 0x85CB; + public const int GL_SUN_vertex = 1; + public const int GL_WIN_phong_shading = 1; + public const int GL_PHONG_WIN = 0x80EA; + public const int GL_PHONG_HINT_WIN = 0x80EB; + public const int GL_WIN_specular_fog = 1; + public const int GL_FOG_SPECULAR_TEXTURE_WIN = 0x80EC; } } diff --git a/src/Avalonia.OpenGL/GlInterface.cs b/src/Avalonia.OpenGL/GlInterface.cs index 30f7d67152..5614ddfb17 100644 --- a/src/Avalonia.OpenGL/GlInterface.cs +++ b/src/Avalonia.OpenGL/GlInterface.cs @@ -6,7 +6,7 @@ namespace Avalonia.OpenGL { public delegate IntPtr GlGetProcAddressDelegate(string procName); - public class GlInterface : GlInterfaceBase + public unsafe class GlInterface : GlInterfaceBase { public string Version { get; } public string Vendor { get; } @@ -70,6 +70,61 @@ namespace Avalonia.OpenGL [GlEntryPoint("glGetIntegerv")] public GlGetIntegerv GetIntegerv { get; } + public delegate void GlGenFramebuffers(int count, int[] res); + [GlEntryPoint("glGenFramebuffers")] + public GlGenFramebuffers GenFramebuffers { get; } + + public delegate void GlBindFramebuffer(int target, int fb); + [GlEntryPoint("glBindFramebuffer")] + public GlBindFramebuffer BindFramebuffer { get; } + + public delegate int GlCheckFramebufferStatus(int target); + [GlEntryPoint("glCheckFramebufferStatus")] + public GlCheckFramebufferStatus CheckFramebufferStatus { get; } + + public delegate void GlGenRenderbuffers(int count, int[] res); + [GlEntryPoint("glGenRenderbuffers")] + public GlGenRenderbuffers GenRenderbuffers { get; } + + public delegate void GlBindRenderbuffer(int target, int fb); + [GlEntryPoint("glBindRenderbuffer")] + public GlBindRenderbuffer BindRenderbuffer { get; } + + public delegate void GlRenderbufferStorage(int target, int internalFormat, int width, int height); + [GlEntryPoint("glRenderbufferStorage")] + public GlRenderbufferStorage RenderbufferStorage { get; } + + public delegate void GlFramebufferRenderbuffer(int target, int attachment, + int renderbufferTarget, int renderbuffer); + [GlEntryPoint("glFramebufferRenderbuffer")] + public GlFramebufferRenderbuffer FramebufferRenderbuffer { get; } + + public delegate void GlGenTextures(int count, int[] res); + [GlEntryPoint("glGenTextures")] + public GlGenTextures GenTextures { get; } + + public delegate void GlBindTexture(int target, int fb); + [GlEntryPoint("glBindTexture")] + public GlBindTexture BindTexture { get; } + + public delegate void GlTexImage2D(int target, int level, int internalFormat, int width, int height, int border, + int format, int type, IntPtr data); + [GlEntryPoint("glTexImage2D")] + public GlTexImage2D TexImage2D { get; } + + public delegate void GlTexParameteri(int target, int name, int value); + [GlEntryPoint("glTexParameteri")] + public GlTexParameteri TexParameteri { get; } + + public delegate void GlFramebufferTexture2D(int target, int attachment, + int texTarget, int texture, int level); + [GlEntryPoint("glFramebufferTexture2D")] + public GlFramebufferTexture2D FramebufferTexture2D { get; } + + public delegate void GlDrawBuffers(int n, int[] bufs); + [GlEntryPoint("glDrawBuffers")] + public GlDrawBuffers DrawBuffers { get; } + // ReSharper restore UnassignedGetOnlyAutoProperty } } diff --git a/src/Avalonia.OpenGL/IGlContext.cs b/src/Avalonia.OpenGL/IGlContext.cs index 04aa1b98e4..d5201c7a50 100644 --- a/src/Avalonia.OpenGL/IGlContext.cs +++ b/src/Avalonia.OpenGL/IGlContext.cs @@ -1,8 +1,10 @@ +using System; + namespace Avalonia.OpenGL { - public interface IGlContext + public interface IGlContext : IDisposable { IGlDisplay Display { get; } - void MakeCurrent(); + IDisposable MakeCurrent(); } } diff --git a/src/Avalonia.OpenGL/IGlDisplay.cs b/src/Avalonia.OpenGL/IGlDisplay.cs index e7568bd5e7..ec1435a7ba 100644 --- a/src/Avalonia.OpenGL/IGlDisplay.cs +++ b/src/Avalonia.OpenGL/IGlDisplay.cs @@ -4,7 +4,6 @@ namespace Avalonia.OpenGL { GlDisplayType Type { get; } GlInterface GlInterface { get; } - void ClearContext(); int SampleCount { get; } int StencilSize { get; } } diff --git a/src/Avalonia.OpenGL/IOpenGlAwarePlatformRenderInterface.cs b/src/Avalonia.OpenGL/IOpenGlAwarePlatformRenderInterface.cs new file mode 100644 index 0000000000..30f83745ad --- /dev/null +++ b/src/Avalonia.OpenGL/IOpenGlAwarePlatformRenderInterface.cs @@ -0,0 +1,9 @@ +using Avalonia.OpenGL.Imaging; + +namespace Avalonia.OpenGL +{ + public interface IOpenGlAwarePlatformRenderInterface + { + IOpenGlTextureBitmapImpl CreateOpenGlTextureBitmap(); + } +} diff --git a/src/Avalonia.OpenGL/IWindowingPlatformGlFeature.cs b/src/Avalonia.OpenGL/IWindowingPlatformGlFeature.cs index 2c4a8b64b3..8a24820704 100644 --- a/src/Avalonia.OpenGL/IWindowingPlatformGlFeature.cs +++ b/src/Avalonia.OpenGL/IWindowingPlatformGlFeature.cs @@ -2,6 +2,7 @@ namespace Avalonia.OpenGL { public interface IWindowingPlatformGlFeature { - IGlContext ImmediateContext { get; } + IGlDisplay Display { get; } + IGlContext CreateContext(); } } diff --git a/src/Avalonia.OpenGL/Imaging/IOpenGlTextureBitmapImpl.cs b/src/Avalonia.OpenGL/Imaging/IOpenGlTextureBitmapImpl.cs new file mode 100644 index 0000000000..e5f3691569 --- /dev/null +++ b/src/Avalonia.OpenGL/Imaging/IOpenGlTextureBitmapImpl.cs @@ -0,0 +1,13 @@ +using System; +using Avalonia.Media.Imaging; +using Avalonia.Platform; + +namespace Avalonia.OpenGL.Imaging +{ + public interface IOpenGlTextureBitmapImpl : IBitmapImpl + { + IDisposable Lock(); + void SetBackBuffer(int textureId, int internalFormat, PixelSize pixelSize, double dpiScaling); + void SetDirty(); + } +} diff --git a/src/Avalonia.OpenGL/Imaging/OpenGlTextureBitmap.cs b/src/Avalonia.OpenGL/Imaging/OpenGlTextureBitmap.cs new file mode 100644 index 0000000000..558eae8fdf --- /dev/null +++ b/src/Avalonia.OpenGL/Imaging/OpenGlTextureBitmap.cs @@ -0,0 +1,46 @@ +using System; +using Avalonia.Media; +using Avalonia.Media.Imaging; +using Avalonia.Platform; +using Avalonia.Threading; + +namespace Avalonia.OpenGL.Imaging +{ + public class OpenGlTextureBitmap : Bitmap, IAffectsRender + { + private IOpenGlTextureBitmapImpl _impl; + static IOpenGlTextureBitmapImpl CreateOrThrow() + { + if (!(AvaloniaLocator.Current.GetService() is IOpenGlAwarePlatformRenderInterface + glAware)) + throw new PlatformNotSupportedException("Rendering platform does not support OpenGL integration"); + return glAware.CreateOpenGlTextureBitmap(); + } + + public OpenGlTextureBitmap() + : base(CreateOrThrow()) + { + _impl = (IOpenGlTextureBitmapImpl)PlatformImpl.Item; + } + + public IDisposable Lock() => _impl.Lock(); + + public void SetTexture(int textureId, int internalFormat, PixelSize size, double dpiScaling) + { + _impl.SetBackBuffer(textureId, internalFormat, size, dpiScaling); + SetIsDirty(); + } + + public void SetIsDirty() + { + if (Dispatcher.UIThread.CheckAccess()) + CallInvalidated(); + else + Dispatcher.UIThread.Post(CallInvalidated); + } + + private void CallInvalidated() => Invalidated?.Invoke(this, EventArgs.Empty); + + public event EventHandler Invalidated; + } +} diff --git a/src/Avalonia.Visuals/Rendering/ManagedDeferredRendererLock.cs b/src/Avalonia.Visuals/Rendering/ManagedDeferredRendererLock.cs index 259874423e..f755edcbd4 100644 --- a/src/Avalonia.Visuals/Rendering/ManagedDeferredRendererLock.cs +++ b/src/Avalonia.Visuals/Rendering/ManagedDeferredRendererLock.cs @@ -3,10 +3,11 @@ using System; using System.Threading; +using Avalonia.Utilities; namespace Avalonia.Rendering { - public class ManagedDeferredRendererLock : IDeferredRendererLock + public class ManagedDeferredRendererLock : DisposableLock, IDeferredRendererLock { private readonly object _lock = new object(); diff --git a/src/Avalonia.X11/Glx/Glx.cs b/src/Avalonia.X11/Glx/Glx.cs index 714a592f2b..d14d52b819 100644 --- a/src/Avalonia.X11/Glx/Glx.cs +++ b/src/Avalonia.X11/Glx/Glx.cs @@ -15,11 +15,30 @@ namespace Avalonia.X11.Glx public GlxMakeContextCurrent MakeContextCurrent { get; } public delegate bool GlxMakeContextCurrent(IntPtr display, IntPtr draw, IntPtr read, IntPtr context); + [GlEntryPoint("glXGetCurrentContext")] + public GlxGetCurrentContext GetCurrentContext { get; } + public delegate IntPtr GlxGetCurrentContext(); + + [GlEntryPoint("glXGetCurrentDisplay")] + public GlxGetCurrentDisplay GetCurrentDisplay { get; } + public delegate IntPtr GlxGetCurrentDisplay(); + + [GlEntryPoint("glXGetCurrentDrawable")] + public GlxGetCurrentDrawable GetCurrentDrawable { get; } + public delegate IntPtr GlxGetCurrentDrawable(); + + [GlEntryPoint("glXGetCurrentReadDrawable")] + public GlxGetCurrentReadDrawable GetCurrentReadDrawable { get; } + public delegate IntPtr GlxGetCurrentReadDrawable(); + [GlEntryPoint("glXCreatePbuffer")] public GlxCreatePbuffer CreatePbuffer { get; } - public delegate IntPtr GlxCreatePbuffer(IntPtr dpy, IntPtr fbc, int[] attrib_list); + [GlEntryPoint("glXDestroyPbuffer")] + public GlxDestroyPbuffer DestroyPbuffer { get; } + public delegate IntPtr GlxDestroyPbuffer(IntPtr dpy, IntPtr fb); + [GlEntryPointAttribute("glXChooseVisual")] public GlxChooseVisual ChooseVisual { get; } public delegate XVisualInfo* GlxChooseVisual(IntPtr dpy, int screen, int[] attribList); diff --git a/src/Avalonia.X11/Glx/GlxContext.cs b/src/Avalonia.X11/Glx/GlxContext.cs index dd95841d57..7770cc4976 100644 --- a/src/Avalonia.X11/Glx/GlxContext.cs +++ b/src/Avalonia.X11/Glx/GlxContext.cs @@ -10,14 +10,17 @@ namespace Avalonia.X11.Glx public GlxInterface Glx { get; } private readonly X11Info _x11; private readonly IntPtr _defaultXid; + private readonly bool _ownsPBuffer; private readonly object _lock = new object(); - public GlxContext(GlxInterface glx, IntPtr handle, GlxDisplay display, X11Info x11, IntPtr defaultXid) + public GlxContext(GlxInterface glx, IntPtr handle, GlxDisplay display, X11Info x11, IntPtr defaultXid, + bool ownsPBuffer) { Handle = handle; Glx = glx; _x11 = x11; _defaultXid = defaultXid; + _ownsPBuffer = ownsPBuffer; Display = display; } @@ -29,13 +32,48 @@ namespace Avalonia.X11.Glx Monitor.Enter(_lock); return Disposable.Create(() => Monitor.Exit(_lock)); } - - public void MakeCurrent() => MakeCurrent(_defaultXid); - public void MakeCurrent(IntPtr xid) + class RestoreContext : IDisposable + { + private GlxInterface _glx; + private IntPtr _defaultDisplay; + private IntPtr _display; + private IntPtr _context; + private IntPtr _read; + private IntPtr _draw; + + public RestoreContext(GlxInterface glx, IntPtr defaultDisplay) + { + _glx = glx; + _defaultDisplay = defaultDisplay; + _display = _glx.GetCurrentDisplay(); + _context = _glx.GetCurrentContext(); + _read = _glx.GetCurrentReadDrawable(); + _draw = _glx.GetCurrentDrawable(); + } + + public void Dispose() + { + var disp = _display == IntPtr.Zero ? _defaultDisplay : _display; + _glx.MakeContextCurrent(disp, _draw, _read, _context); + } + } + + public IDisposable MakeCurrent() => MakeCurrent(_defaultXid); + + public IDisposable MakeCurrent(IntPtr xid) { + var old = new RestoreContext(Glx, _x11.Display); if (!Glx.MakeContextCurrent(_x11.Display, xid, xid, Handle)) throw new OpenGlException("glXMakeContextCurrent failed "); + return old; + } + + public void Dispose() + { + Glx.DestroyContext(_x11.Display, Handle); + if (_ownsPBuffer) + Glx.DestroyPbuffer(_x11.Display, _defaultXid); } } } diff --git a/src/Avalonia.X11/Glx/GlxDisplay.cs b/src/Avalonia.X11/Glx/GlxDisplay.cs index 22eb0792e8..4b78c40737 100644 --- a/src/Avalonia.X11/Glx/GlxDisplay.cs +++ b/src/Avalonia.X11/Glx/GlxDisplay.cs @@ -17,7 +17,6 @@ namespace Avalonia.X11.Glx public int SampleCount { get; } public int StencilSize { get; } - public GlxContext ImmediateContext { get; } public GlxContext DeferredContext { get; } public GlxInterface Glx { get; } = new GlxInterface(); public GlxDisplay(X11Info x11) @@ -81,35 +80,39 @@ namespace Avalonia.X11.Glx })).ToList(); XLib.XFlush(_x11.Display); - - ImmediateContext = CreateContext(pbuffers[0],null); - DeferredContext = CreateContext(pbuffers[1], ImmediateContext); - ImmediateContext.MakeCurrent(); - var err = Glx.GetError(); - - GlInterface = new GlInterface(GlxInterface.SafeGetProcAddress); - if (GlInterface.Version == null) - throw new OpenGlException("GL version string is null, aborting"); - if (GlInterface.Renderer == null) - throw new OpenGlException("GL renderer string is null, aborting"); - if (Environment.GetEnvironmentVariable("AVALONIA_GLX_IGNORE_RENDERER_BLACKLIST") != "1") + DeferredContext = CreateContext(CreatePBuffer(), null, true); + using (DeferredContext.MakeCurrent()) { - var blacklist = AvaloniaLocator.Current.GetService() - ?.GlxRendererBlacklist; - if (blacklist != null) - foreach(var item in blacklist) - if (GlInterface.Renderer.Contains(item)) - throw new OpenGlException($"Renderer '{GlInterface.Renderer}' is blacklisted by '{item}'"); + GlInterface = new GlInterface(GlxInterface.SafeGetProcAddress); + if (GlInterface.Version == null) + throw new OpenGlException("GL version string is null, aborting"); + if (GlInterface.Renderer == null) + throw new OpenGlException("GL renderer string is null, aborting"); + + if (Environment.GetEnvironmentVariable("AVALONIA_GLX_IGNORE_RENDERER_BLACKLIST") != "1") + { + var blacklist = AvaloniaLocator.Current.GetService() + ?.GlxRendererBlacklist; + if (blacklist != null) + foreach (var item in blacklist) + if (GlInterface.Renderer.Contains(item)) + throw new OpenGlException( + $"Renderer '{GlInterface.Renderer}' is blacklisted by '{item}'"); + } } - + + } + + IntPtr CreatePBuffer() + { + return Glx.CreatePbuffer(_x11.Display, _fbconfig, new[] { GLX_PBUFFER_WIDTH, 1, GLX_PBUFFER_HEIGHT, 1, 0 }); } - public void ClearContext() => Glx.MakeContextCurrent(_x11.Display, - IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); - public GlxContext CreateContext(IGlContext share) => CreateContext(IntPtr.Zero, share); - public GlxContext CreateContext(IntPtr defaultXid, IGlContext share) + public GlxContext CreateContext() => CreateContext(DeferredContext); + GlxContext CreateContext(IGlContext share) => CreateContext(CreatePBuffer(), share, true); + GlxContext CreateContext(IntPtr defaultXid, IGlContext share, bool ownsPBuffer) { var sharelist = ((GlxContext)share)?.Handle ?? IntPtr.Zero; IntPtr handle = default; @@ -141,7 +144,7 @@ namespace Avalonia.X11.Glx if (handle == IntPtr.Zero) throw new OpenGlException("Unable to create direct GLX context"); - return new GlxContext(new GlxInterface(), handle, this, _x11, defaultXid); + return new GlxContext(new GlxInterface(), handle, this, _x11, defaultXid, ownsPBuffer); } public void SwapBuffers(IntPtr xid) => Glx.SwapBuffers(_x11.Display, xid); diff --git a/src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs b/src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs index 45a46bd6f5..36ccc0376d 100644 --- a/src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs +++ b/src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs @@ -43,8 +43,8 @@ namespace Avalonia.X11.Glx var l = _context.Lock(); try { - _context.MakeCurrent(_info.Handle); - return new Session(_context, _info, l); + + return new Session(_context, _info, l, _context.MakeCurrent(_info.Handle)); } catch { @@ -58,13 +58,15 @@ namespace Avalonia.X11.Glx private readonly GlxContext _context; private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info; private IDisposable _lock; + private readonly IDisposable _clearContext; public Session(GlxContext context, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info, - IDisposable @lock) + IDisposable @lock, IDisposable clearContext) { _context = context; _info = info; _lock = @lock; + _clearContext = clearContext; } public void Dispose() @@ -73,7 +75,7 @@ namespace Avalonia.X11.Glx _context.Glx.WaitGL(); _context.Display.SwapBuffers(_info.Handle); _context.Glx.WaitX(); - _context.Display.ClearContext(); + _clearContext.Dispose(); _lock.Dispose(); } diff --git a/src/Avalonia.X11/Glx/GlxPlatformFeature.cs b/src/Avalonia.X11/Glx/GlxPlatformFeature.cs index 3dc2e8e41f..0665977681 100644 --- a/src/Avalonia.X11/Glx/GlxPlatformFeature.cs +++ b/src/Avalonia.X11/Glx/GlxPlatformFeature.cs @@ -7,7 +7,9 @@ namespace Avalonia.X11.Glx class GlxGlPlatformFeature : IWindowingPlatformGlFeature { public GlxDisplay Display { get; private set; } - public IGlContext ImmediateContext { get; private set; } + public IGlContext CreateContext() => Display.CreateContext(); + + IGlDisplay IWindowingPlatformGlFeature.Display => Display; public GlxContext DeferredContext { get; private set; } public static bool TryInitialize(X11Info x11) @@ -30,7 +32,6 @@ namespace Avalonia.X11.Glx return new GlxGlPlatformFeature { Display = disp, - ImmediateContext = disp.ImmediateContext, DeferredContext = disp.DeferredContext }; } diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs b/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs index 273265a6dc..5c22b29356 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs @@ -48,7 +48,6 @@ namespace Avalonia.LinuxFramebuffer.Output private drmModeModeInfo _mode; private EglDisplay _eglDisplay; private EglSurface _eglSurface; - private EglContext _immediateContext; private EglContext _deferredContext; private IntPtr _currentBo; private IntPtr _gbmTargetSurface; @@ -129,13 +128,15 @@ namespace Avalonia.LinuxFramebuffer.Output return _eglDisplay.CreateContext(share, _eglDisplay.CreateWindowSurface(offSurf)); } - _immediateContext = CreateContext(null); - _deferredContext = CreateContext(_immediateContext); - - _immediateContext.MakeCurrent(_eglSurface); - _eglDisplay.GlInterface.ClearColor(0, 0, 0, 0); - _eglDisplay.GlInterface.Clear(GlConsts.GL_COLOR_BUFFER_BIT | GlConsts.GL_STENCIL_BUFFER_BIT); - _eglSurface.SwapBuffers(); + _deferredContext = CreateContext(null); + + using (_deferredContext.MakeCurrent(_eglSurface)) + { + _eglDisplay.GlInterface.ClearColor(0, 0, 0, 0); + _eglDisplay.GlInterface.Clear(GlConsts.GL_COLOR_BUFFER_BIT | GlConsts.GL_STENCIL_BUFFER_BIT); + _eglSurface.SwapBuffers(); + } + var bo = gbm_surface_lock_front_buffer(_gbmTargetSurface); var fbId = GetFbIdForBo(bo); var connectorId = connector.Id; @@ -156,6 +157,7 @@ namespace Avalonia.LinuxFramebuffer.Output _eglDisplay.GlInterface.ClearColor(0, 0, 0, 0); _eglDisplay.GlInterface.Clear(GlConsts.GL_COLOR_BUFFER_BIT | GlConsts.GL_STENCIL_BUFFER_BIT); } + } public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget() @@ -179,10 +181,12 @@ namespace Avalonia.LinuxFramebuffer.Output class RenderSession : IGlPlatformSurfaceRenderingSession { private readonly DrmOutput _parent; + private readonly IDisposable _clearContext; - public RenderSession(DrmOutput parent) + public RenderSession(DrmOutput parent, IDisposable clearContext) { _parent = parent; + _clearContext = clearContext; } public void Dispose() @@ -225,7 +229,7 @@ namespace Avalonia.LinuxFramebuffer.Output gbm_surface_release_buffer(_parent._gbmTargetSurface, _parent._currentBo); _parent._currentBo = nextBo; } - _parent._eglDisplay.ClearContext(); + _clearContext.Dispose(); } @@ -238,14 +242,15 @@ namespace Avalonia.LinuxFramebuffer.Output public IGlPlatformSurfaceRenderingSession BeginDraw() { - _parent._deferredContext.MakeCurrent(_parent._eglSurface); - return new RenderSession(_parent); + return new RenderSession(_parent, _parent._deferredContext.MakeCurrent(_parent._eglSurface)); } - - } - IGlContext IWindowingPlatformGlFeature.ImmediateContext => _immediateContext; + public IGlDisplay Display => _eglDisplay; + public IGlContext CreateContext() + { + throw new NotImplementedException(); + } } diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index d06cfa69a7..de0cead33b 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -31,8 +31,10 @@ namespace Avalonia.Skia private double _currentOpacity = 1.0f; private readonly bool _canTextUseLcdRendering; private Matrix _currentTransform; - private GRContext _grContext; private bool _disposed; + private GRContext _grContext; + + public GRContext GrContext => _grContext; /// /// Context create info. /// diff --git a/src/Skia/Avalonia.Skia/ICustomSkiaGpu.cs b/src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs similarity index 68% rename from src/Skia/Avalonia.Skia/ICustomSkiaGpu.cs rename to src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs index 751dd3c1e7..92e649448f 100644 --- a/src/Skia/Avalonia.Skia/ICustomSkiaGpu.cs +++ b/src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System.Collections.Generic; +using Avalonia.OpenGL.Imaging; using SkiaSharp; namespace Avalonia.Skia @@ -9,18 +10,18 @@ namespace Avalonia.Skia /// /// Custom Skia gpu instance. /// - public interface ICustomSkiaGpu + public interface ISkiaGpu { - /// - /// Skia GrContext used. - /// - GRContext GrContext { get; } - /// /// Attempts to create custom render target from given surfaces. /// /// Surfaces. /// Created render target or if it fails. - ICustomSkiaRenderTarget TryCreateRenderTarget(IEnumerable surfaces); + ISkiaGpuRenderTarget TryCreateRenderTarget(IEnumerable surfaces); + } + + public interface IOpenGlAwareSkiaGpu : ISkiaGpu + { + IOpenGlTextureBitmapImpl CreateOpenGlTextureBitmap(); } } diff --git a/src/Skia/Avalonia.Skia/ICustomSkiaRenderSession.cs b/src/Skia/Avalonia.Skia/Gpu/ISkiaGpuRenderSession.cs similarity index 92% rename from src/Skia/Avalonia.Skia/ICustomSkiaRenderSession.cs rename to src/Skia/Avalonia.Skia/Gpu/ISkiaGpuRenderSession.cs index 6a4591921e..994fb36f19 100644 --- a/src/Skia/Avalonia.Skia/ICustomSkiaRenderSession.cs +++ b/src/Skia/Avalonia.Skia/Gpu/ISkiaGpuRenderSession.cs @@ -9,7 +9,7 @@ namespace Avalonia.Skia /// /// Custom render session for Skia render target. /// - public interface ICustomSkiaRenderSession : IDisposable + public interface ISkiaGpuRenderSession : IDisposable { /// /// GrContext used by this session. diff --git a/src/Skia/Avalonia.Skia/ICustomSkiaRenderTarget.cs b/src/Skia/Avalonia.Skia/Gpu/ISkiaGpuRenderTarget.cs similarity index 71% rename from src/Skia/Avalonia.Skia/ICustomSkiaRenderTarget.cs rename to src/Skia/Avalonia.Skia/Gpu/ISkiaGpuRenderTarget.cs index f67b28b77b..5c2d975803 100644 --- a/src/Skia/Avalonia.Skia/ICustomSkiaRenderTarget.cs +++ b/src/Skia/Avalonia.Skia/Gpu/ISkiaGpuRenderTarget.cs @@ -2,18 +2,21 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; +using SkiaSharp; namespace Avalonia.Skia { /// /// Custom Skia render target. /// - public interface ICustomSkiaRenderTarget : IDisposable + public interface ISkiaGpuRenderTarget : IDisposable { /// /// Start rendering to this render target. /// /// - ICustomSkiaRenderSession BeginRendering(); + ISkiaGpuRenderSession BeginRenderingSession(); + + bool IsCorrupted { get; } } } diff --git a/src/Skia/Avalonia.Skia/GlRenderTarget.cs b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs similarity index 55% rename from src/Skia/Avalonia.Skia/GlRenderTarget.cs rename to src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs index 61ccf09e52..69a726f232 100644 --- a/src/Skia/Avalonia.Skia/GlRenderTarget.cs +++ b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs @@ -8,7 +8,7 @@ using static Avalonia.OpenGL.GlConsts; namespace Avalonia.Skia { - internal class GlRenderTarget : IRenderTargetWithCorruptionInfo + internal class GlRenderTarget : ISkiaGpuRenderTarget { private readonly GRContext _grContext; private IGlPlatformSurfaceRenderTarget _surface; @@ -22,22 +22,52 @@ namespace Avalonia.Skia public void Dispose() => _surface.Dispose(); public bool IsCorrupted => (_surface as IGlPlatformSurfaceRenderTargetWithCorruptionInfo)?.IsCorrupted == true; - - public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer) + + class GlGpuSession : ISkiaGpuRenderSession + { + private readonly GRBackendRenderTarget _backendRenderTarget; + private readonly SKSurface _surface; + private readonly IGlPlatformSurfaceRenderingSession _glSession; + + public GlGpuSession(GRContext grContext, + GRBackendRenderTarget backendRenderTarget, + SKSurface surface, + IGlPlatformSurfaceRenderingSession glSession) + { + GrContext = grContext; + _backendRenderTarget = backendRenderTarget; + _surface = surface; + _glSession = glSession; + } + public void Dispose() + { + _surface.Canvas.Flush(); + _surface.Dispose(); + _backendRenderTarget.Dispose(); + GrContext.Flush(); + _glSession.Dispose(); + } + + public GRContext GrContext { get; } + public SKCanvas Canvas => _surface.Canvas; + public double ScaleFactor => _glSession.Scaling; + } + + public ISkiaGpuRenderSession BeginRenderingSession() { - var session = _surface.BeginDraw(); + var glSession = _surface.BeginDraw(); bool success = false; try { - var disp = session.Display; + var disp = glSession.Display; var gl = disp.GlInterface; gl.GetIntegerv(GL_FRAMEBUFFER_BINDING, out var fb); - var size = session.Size; - var scaling = session.Scaling; + var size = glSession.Size; + var scaling = glSession.Scaling; if (size.Width <= 0 || size.Height <= 0 || scaling < 0) { - session.Dispose(); + glSession.Dispose(); throw new InvalidOperationException( $"Can't create drawing context for surface with {size} size and {scaling} scaling"); } @@ -50,40 +80,21 @@ namespace Avalonia.Skia { _grContext.ResetContext(); - GRBackendRenderTarget renderTarget = + var renderTarget = new GRBackendRenderTarget(size.Width, size.Height, disp.SampleCount, disp.StencilSize, new GRGlFramebufferInfo((uint)fb, GRPixelConfig.Rgba8888.ToGlSizedFormat())); var surface = SKSurface.Create(_grContext, renderTarget, GRSurfaceOrigin.BottomLeft, GRPixelConfig.Rgba8888.ToColorType()); - var nfo = new DrawingContextImpl.CreateInfo - { - GrContext = _grContext, - Canvas = surface.Canvas, - Dpi = SkiaPlatform.DefaultDpi * scaling, - VisualBrushRenderer = visualBrushRenderer, - DisableTextLcdRendering = true - }; - - - var ctx = new DrawingContextImpl(nfo, Disposable.Create(() => - { - - surface.Canvas.Flush(); - surface.Dispose(); - renderTarget.Dispose(); - _grContext.Flush(); - session.Dispose(); - })); success = true; - return ctx; + return new GlGpuSession(_grContext, renderTarget, surface, glSession); } } finally { if(!success) - session.Dispose(); + glSession.Dispose(); } } } diff --git a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs new file mode 100644 index 0000000000..3a7eec134e --- /dev/null +++ b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using Avalonia.OpenGL; +using Avalonia.OpenGL.Imaging; +using SkiaSharp; + +namespace Avalonia.Skia +{ + class GlSkiaGpu : IOpenGlAwareSkiaGpu + { + private GRContext _grContext; + + public GlSkiaGpu(IWindowingPlatformGlFeature gl) + { + + var immediateContext = gl.CreateContext(); + using (immediateContext.MakeCurrent()) + { + var display = gl.Display; + using (var iface = display.Type == GlDisplayType.OpenGL2 ? + GRGlInterface.AssembleGlInterface((_, proc) => display.GlInterface.GetProcAddress(proc)) : + GRGlInterface.AssembleGlesInterface((_, proc) => display.GlInterface.GetProcAddress(proc))) + { + _grContext = GRContext.Create(GRBackend.OpenGL, iface); + } + } + } + + public ISkiaGpuRenderTarget TryCreateRenderTarget(IEnumerable surfaces) + { + foreach (var surface in surfaces) + { + if (surface is IGlPlatformSurface glSurface) + { + return new GlRenderTarget(_grContext, glSurface); + } + } + + return null; + } + + public IOpenGlTextureBitmapImpl CreateOpenGlTextureBitmap() => new OpenGlTextureBitmapImpl(); + } +} diff --git a/src/Skia/Avalonia.Skia/Gpu/OpenGlTextureBitmapImpl.cs b/src/Skia/Avalonia.Skia/Gpu/OpenGlTextureBitmapImpl.cs new file mode 100644 index 0000000000..8d007e35f3 --- /dev/null +++ b/src/Skia/Avalonia.Skia/Gpu/OpenGlTextureBitmapImpl.cs @@ -0,0 +1,81 @@ +using System; +using System.IO; +using Avalonia.OpenGL; +using Avalonia.OpenGL.Imaging; +using Avalonia.Skia.Helpers; +using Avalonia.Utilities; +using SkiaSharp; + +namespace Avalonia.Skia +{ + class OpenGlTextureBitmapImpl : IOpenGlTextureBitmapImpl, IDrawableBitmapImpl + { + private DisposableLock _lock = new DisposableLock(); + private int _textureId; + private int _internalFormat; + + public void Dispose() + { + using (Lock()) + { + _textureId = 0; + PixelSize = new PixelSize(1, 1); + Version++; + } + } + + public Vector Dpi { get; private set; } = new Vector(96, 96); + public PixelSize PixelSize { get; private set; } = new PixelSize(1, 1); + public int Version { get; private set; } = 0; + + public void Save(string fileName) => throw new System.NotSupportedException(); + public void Save(Stream stream) => throw new System.NotSupportedException(); + + public void Draw(DrawingContextImpl context, SKRect sourceRect, SKRect destRect, SKPaint paint) + { + // For now silently ignore + if (context.GrContext == null) + return; + + using (Lock()) + { + if (_textureId == 0) + return; + using (var backendTexture = new GRBackendTexture(PixelSize.Width, PixelSize.Height, false, + new GRGlTextureInfo( + GlConsts.GL_TEXTURE_2D, (uint)_textureId, + (uint)_internalFormat))) + using (var surface = SKSurface.Create(context.GrContext, backendTexture, GRSurfaceOrigin.TopLeft, + SKColorType.Rgba8888)) + { + // Again, silently ignore, if something went wrong it's not our fault + if (surface == null) + return; + + using (var snapshot = surface.Snapshot()) + context.Canvas.DrawImage(snapshot, sourceRect, destRect, paint); + } + } + } + + public IDisposable Lock() => _lock.Lock(); + + public void SetBackBuffer(int textureId, int internalFormat, PixelSize pixelSize, double dpiScaling) + { + using (_lock.Lock()) + { + _textureId = textureId; + _internalFormat = internalFormat; + PixelSize = pixelSize; + Dpi = new Vector(96 * dpiScaling, 96 * dpiScaling); + Version++; + } + } + + public void SetDirty() + { + using (_lock.Lock()) + Version++; + } + } +} diff --git a/src/Skia/Avalonia.Skia/CustomRenderTarget.cs b/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs similarity index 69% rename from src/Skia/Avalonia.Skia/CustomRenderTarget.cs rename to src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs index 23a509a2a4..03a6ef24ce 100644 --- a/src/Skia/Avalonia.Skia/CustomRenderTarget.cs +++ b/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs @@ -7,13 +7,13 @@ using Avalonia.Rendering; namespace Avalonia.Skia { /// - /// Adapts to be used within Skia rendering pipeline. + /// Adapts to be used within our rendering pipeline. /// - internal class CustomRenderTarget : IRenderTarget + internal class SkiaGpuRenderTarget : IRenderTargetWithCorruptionInfo { - private readonly ICustomSkiaRenderTarget _renderTarget; + private readonly ISkiaGpuRenderTarget _renderTarget; - public CustomRenderTarget(ICustomSkiaRenderTarget renderTarget) + public SkiaGpuRenderTarget(ISkiaGpuRenderTarget renderTarget) { _renderTarget = renderTarget; } @@ -25,7 +25,7 @@ namespace Avalonia.Skia public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer) { - ICustomSkiaRenderSession session = _renderTarget.BeginRendering(); + var session = _renderTarget.BeginRenderingSession(); var nfo = new DrawingContextImpl.CreateInfo { @@ -38,5 +38,7 @@ namespace Avalonia.Skia return new DrawingContextImpl(nfo, session); } + + public bool IsCorrupted => _renderTarget.IsCorrupted; } } diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index 05c3bbdaa0..b6d6a4e4f1 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -2,11 +2,14 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; +using System.Collections; using System.Collections.Generic; using System.IO; +using System.Linq; using Avalonia.Controls.Platform.Surfaces; using Avalonia.Media; using Avalonia.OpenGL; +using Avalonia.OpenGL.Imaging; using Avalonia.Platform; using SkiaSharp; @@ -15,35 +18,21 @@ namespace Avalonia.Skia /// /// Skia platform render interface. /// - internal class PlatformRenderInterface : IPlatformRenderInterface + internal class PlatformRenderInterface : IPlatformRenderInterface, IOpenGlAwarePlatformRenderInterface { - private readonly ICustomSkiaGpu _customSkiaGpu; + private readonly ISkiaGpu _skiaGpu; - private GRContext GrContext { get; } - - public PlatformRenderInterface(ICustomSkiaGpu customSkiaGpu) + public PlatformRenderInterface(ISkiaGpu skiaGpu) { - if (customSkiaGpu != null) + if (skiaGpu != null) { - _customSkiaGpu = customSkiaGpu; - - GrContext = _customSkiaGpu.GrContext; - + _skiaGpu = skiaGpu; return; } var gl = AvaloniaLocator.Current.GetService(); - if (gl != null) - { - var display = gl.ImmediateContext.Display; - gl.ImmediateContext.MakeCurrent(); - using (var iface = display.Type == GlDisplayType.OpenGL2 - ? GRGlInterface.AssembleGlInterface((_, proc) => display.GlInterface.GetProcAddress(proc)) - : GRGlInterface.AssembleGlesInterface((_, proc) => display.GlInterface.GetProcAddress(proc))) - { - GrContext = GRContext.Create(GRBackend.OpenGL, iface); - } - } + if (gl != null) + _skiaGpu = new GlSkiaGpu(gl); } /// @@ -119,26 +108,18 @@ namespace Avalonia.Skia /// public IRenderTarget CreateRenderTarget(IEnumerable surfaces) { - if (_customSkiaGpu != null) + if (!(surfaces is IList)) + surfaces = surfaces.ToList(); + var gpuRenderTarget = _skiaGpu?.TryCreateRenderTarget(surfaces); + if (gpuRenderTarget != null) { - ICustomSkiaRenderTarget customRenderTarget = _customSkiaGpu.TryCreateRenderTarget(surfaces); - - if (customRenderTarget != null) - { - return new CustomRenderTarget(customRenderTarget); - } + return new SkiaGpuRenderTarget(gpuRenderTarget); } foreach (var surface in surfaces) { - if (surface is IGlPlatformSurface glSurface && GrContext != null) - { - return new GlRenderTarget(GrContext, glSurface); - } if (surface is IFramebufferPlatformSurface framebufferSurface) - { return new FramebufferRenderTarget(framebufferSurface); - } } throw new NotSupportedException( @@ -241,5 +222,15 @@ namespace Avalonia.Skia return new GlyphRunImpl(paint, textBlob); } } + + public IOpenGlTextureBitmapImpl CreateOpenGlTextureBitmap() + { + if (_skiaGpu is IOpenGlAwareSkiaGpu glAware) + return glAware.CreateOpenGlTextureBitmap(); + if (_skiaGpu == null) + throw new PlatformNotSupportedException("GPU acceleration is not available"); + throw new PlatformNotSupportedException( + "Current GPU acceleration backend does not support OpenGL integration"); + } } } diff --git a/src/Skia/Avalonia.Skia/SkiaOptions.cs b/src/Skia/Avalonia.Skia/SkiaOptions.cs index bac1849be8..6e415e9767 100644 --- a/src/Skia/Avalonia.Skia/SkiaOptions.cs +++ b/src/Skia/Avalonia.Skia/SkiaOptions.cs @@ -14,6 +14,6 @@ namespace Avalonia /// /// Custom gpu factory to use. Can be used to customize behavior of Skia renderer. /// - public Func CustomGpuFactory { get; set; } + public Func CustomGpuFactory { get; set; } } } From 021995ed35d9666adfb8e0a95bbc9c6fa8306b59 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 22 Dec 2019 17:06:19 +0300 Subject: [PATCH 007/663] Implemented GlControlBase --- samples/ControlCatalog/ControlCatalog.csproj | 4 +- samples/ControlCatalog/MainView.xaml | 1 + samples/ControlCatalog/Pages/OpenGlPage.xaml | 18 ++ .../ControlCatalog/Pages/OpenGlPage.xaml.cs | 288 ++++++++++++++++++ samples/ControlCatalog/Pages/teapot.bin | Bin 0 -> 15464 bytes src/Avalonia.Native/GlPlatformFeature.cs | 2 +- src/Avalonia.OpenGL/EglDisplay.cs | 4 +- src/Avalonia.OpenGL/GlDisplayType.cs | 6 +- src/Avalonia.OpenGL/GlEntryPointAttribute.cs | 9 +- src/Avalonia.OpenGL/GlInterface.cs | 212 +++++++++++++ src/Avalonia.OpenGL/GlInterfaceBase.cs | 17 +- src/Avalonia.OpenGL/OpenGlControlBase.cs | 149 +++++++++ src/Avalonia.X11/Glx/GlxDisplay.cs | 2 +- .../Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs | 2 +- 14 files changed, 702 insertions(+), 12 deletions(-) create mode 100644 samples/ControlCatalog/Pages/OpenGlPage.xaml create mode 100644 samples/ControlCatalog/Pages/OpenGlPage.xaml.cs create mode 100644 samples/ControlCatalog/Pages/teapot.bin create mode 100644 src/Avalonia.OpenGL/OpenGlControlBase.cs diff --git a/samples/ControlCatalog/ControlCatalog.csproj b/samples/ControlCatalog/ControlCatalog.csproj index 4c2769bcc4..d4c4b64058 100644 --- a/samples/ControlCatalog/ControlCatalog.csproj +++ b/samples/ControlCatalog/ControlCatalog.csproj @@ -1,6 +1,7 @@  - netstandard2.0 + netstandard2.0 + true @@ -17,6 +18,7 @@ + diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index 874560a294..9749c1c4cf 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -14,6 +14,7 @@ + diff --git a/samples/ControlCatalog/Pages/OpenGlPage.xaml b/samples/ControlCatalog/Pages/OpenGlPage.xaml new file mode 100644 index 0000000000..30f8f76240 --- /dev/null +++ b/samples/ControlCatalog/Pages/OpenGlPage.xaml @@ -0,0 +1,18 @@ + + + + + + Yaw + + Pitch + + Roll + + + + + diff --git a/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs b/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs new file mode 100644 index 0000000000..791c49ed8f --- /dev/null +++ b/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs @@ -0,0 +1,288 @@ +using System; +using System.IO; +using System.Linq; +using System.Numerics; +using System.Runtime.InteropServices; +using Avalonia; +using Avalonia.Controls; +using Avalonia.OpenGL; +using static Avalonia.OpenGL.GlConsts; +// ReSharper disable StringLiteralTypo + +namespace ControlCatalog.Pages +{ + public class OpenGlPage : UserControl + { + + } + + public class OpenGlPageControl : OpenGlControlBase + { + private float _yaw; + + public static readonly DirectProperty YawProperty = + AvaloniaProperty.RegisterDirect("Yaw", o => o.Yaw, (o, v) => o.Yaw = v); + + public float Yaw + { + get => _yaw; + set => SetAndRaise(YawProperty, ref _yaw, value); + } + + private float _pitch; + + public static readonly DirectProperty PitchProperty = + AvaloniaProperty.RegisterDirect("Pitch", o => o.Pitch, (o, v) => o.Pitch = v); + + public float Pitch + { + get => _pitch; + set => SetAndRaise(PitchProperty, ref _pitch, value); + } + + + private float _roll; + + public static readonly DirectProperty RollProperty = + AvaloniaProperty.RegisterDirect("Roll", o => o.Roll, (o, v) => o.Roll = v); + + public float Roll + { + get => _roll; + set => SetAndRaise(RollProperty, ref _roll, value); + } + + static OpenGlPageControl() + { + AffectsRender(YawProperty, PitchProperty, RollProperty); + } + + private int _vertexShader; + private int _fragmentShader; + private int _shaderProgram; + private int _vertexBufferObject; + private int _indexBufferObject; + private int _vertexArrayObject; + + private string WithVersion(string shader) => + $"#version {(DisplayType == GlDisplayType.OpenGl ? 120 : 100)}\n" + shader; + + private string WithVersionAndPrecision(string shader, string precision) => + WithVersion(DisplayType == GlDisplayType.OpenGLES ? $"precision {precision}\n{shader}" : shader); + + private string VertexShaderSource => WithVersion(@" + attribute vec3 aPos; + attribute vec3 aNormal; + uniform mat4 uModel; + uniform mat4 uProjection; + uniform mat4 uView; + + varying vec3 FragPos; + varying vec3 VecPos; + varying vec3 Normal; + void main() + { + gl_Position = uProjection * uView * uModel * vec4(aPos, 1.0); + FragPos = vec3(uModel * vec4(aPos, 1.0)); + VecPos = aPos; + Normal = normalize(vec3(uModel * vec4(aNormal, 1.0))); + } +"); + + private string FragmentShaderSource => WithVersionAndPrecision(@" + varying vec3 FragPos; + varying vec3 VecPos; + varying vec3 Normal; + uniform float uMaxY; + uniform float uMinY; + void main() + { + float y = (VecPos.y - uMinY) / (uMaxY - uMinY); + vec3 objectColor = vec3((1 - y), 0.40 + y / 4, y * 0.75+0.25); + //vec3 objectColor = normalize(FragPos); + + + float ambientStrength = 0.3; + vec3 lightColor = vec3(1.0, 1.0, 1.0); + vec3 lightPos = vec3(uMaxY * 2, uMaxY * 2, uMaxY * 2); + vec3 ambient = ambientStrength * lightColor; + + + vec3 norm = normalize(Normal); + vec3 lightDir = normalize(lightPos - FragPos); + + float diff = max(dot(norm, lightDir), 0.0); + vec3 diffuse = diff * lightColor; + + vec3 result = (ambient + diffuse) * objectColor; + gl_FragColor = vec4(result, 1.0); + + } +", "mediump float"); + + [StructLayout(LayoutKind.Sequential, Pack = 4)] + private struct Vertex + { + public Vector3 Position; + public Vector3 Normal; + } + + private readonly Vertex[] _points; + private readonly ushort[] _indices; + private readonly float _minY; + private readonly float _maxY; + + + public OpenGlPageControl() + { + var name = typeof(OpenGlPage).Assembly.GetManifestResourceNames().First(x => x.Contains("teapot.bin")); + using (var sr = new BinaryReader(typeof(OpenGlPage).Assembly.GetManifestResourceStream(name))) + { + var buf = new byte[sr.ReadInt32()]; + sr.Read(buf, 0, buf.Length); + var points = new float[buf.Length / 4]; + Buffer.BlockCopy(buf, 0, points, 0, buf.Length); + buf = new byte[sr.ReadInt32()]; + sr.Read(buf, 0, buf.Length); + _indices = new ushort[buf.Length / 2]; + Buffer.BlockCopy(buf, 0, _indices, 0, buf.Length); + _points = new Vertex[points.Length / 3]; + for (var primitive = 0; primitive < points.Length / 3; primitive++) + { + var srci = primitive * 3; + _points[primitive] = new Vertex + { + Position = new Vector3(points[srci], points[srci + 1], points[srci + 2]) + }; + } + + for (int i = 0; i < _indices.Length; i += 3) + { + Vector3 a = _points[_indices[i]].Position; + Vector3 b = _points[_indices[i + 1]].Position; + Vector3 c = _points[_indices[i + 2]].Position; + var normal = Vector3.Normalize(Vector3.Cross(c - b, a - b)); + + _points[_indices[i]].Normal += normal; + _points[_indices[i + 1]].Normal += normal; + _points[_indices[i + 2]].Normal += normal; + } + + for (int i = 0; i < _points.Length; i++) + { + _points[i].Normal = Vector3.Normalize(_points[i].Normal); + _maxY = Math.Max(_maxY, _points[i].Position.Y); + _minY = Math.Min(_minY, _points[i].Position.Y); + } + } + + } + + private void CheckError(GlInterface gl) + { + int err; + while ((err = gl.GetError()) != GL_NO_ERROR) + Console.WriteLine(err); + } + + protected unsafe override void OnOpenGlInit(GlInterface GL, int fb) + { + // Load the source of the vertex shader and compile it. + _vertexShader = GL.CreateShader(GL_VERTEX_SHADER); + Console.WriteLine(GL.CompileShaderAndGetError(_vertexShader, VertexShaderSource)); + + // Load the source of the fragment shader and compile it. + _fragmentShader = GL.CreateShader(GL_FRAGMENT_SHADER); + Console.WriteLine(GL.CompileShaderAndGetError(_fragmentShader, FragmentShaderSource)); + + // Create the shader program, attach the vertex and fragment shaders and link the program. + _shaderProgram = GL.CreateProgram(); + GL.AttachShader(_shaderProgram, _vertexShader); + GL.AttachShader(_shaderProgram, _fragmentShader); + const int positionLocation = 0; + const int normalLocation = 1; + GL.BindAttribLocationString(_shaderProgram, positionLocation, "aPos"); + GL.BindAttribLocationString(_shaderProgram, normalLocation, "aNormal"); + Console.WriteLine(GL.LinkProgramAndGetError(_shaderProgram)); + CheckError(GL); + + // Create the vertex buffer object (VBO) for the vertex data. + _vertexBufferObject = GL.GenBuffer(); + // Bind the VBO and copy the vertex data into it. + GL.BindBuffer(GL_ARRAY_BUFFER, _vertexBufferObject); + var vertexSize = Marshal.SizeOf(); + fixed (void* pdata = _points) + GL.BufferData(GL_ARRAY_BUFFER, new IntPtr(_points.Length * vertexSize), + new IntPtr(pdata), GL_STATIC_DRAW); + + _indexBufferObject = GL.GenBuffer(); + GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferObject); + fixed (void* pdata = _indices) + GL.BufferData(GL_ELEMENT_ARRAY_BUFFER, new IntPtr(_indices.Length * sizeof(ushort)), new IntPtr(pdata), + GL_STATIC_DRAW); + + _vertexArrayObject = GL.GenVertexArray(); + GL.BindVertexArray(_vertexArrayObject); + + GL.VertexAttribPointer(positionLocation, 3, GL_FLOAT, + 0, vertexSize, IntPtr.Zero); + GL.VertexAttribPointer(normalLocation, 3, GL_FLOAT, + 0, vertexSize, new IntPtr(12)); + GL.EnableVertexAttribArray(positionLocation); + GL.EnableVertexAttribArray(normalLocation); + CheckError(GL); + + } + + protected override void OnOpenGlDeinit(GlInterface GL, int fb) + { + // Unbind everything + GL.BindBuffer(GL_ARRAY_BUFFER, 0); + GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + GL.BindVertexArray(0); + GL.UseProgram(0); + + // Delete all resources. + GL.DeleteBuffer(_vertexBufferObject); + GL.DeleteBuffer(_indexBufferObject); + GL.DeleteVertexArray(_vertexArrayObject); + GL.DeleteProgram(_shaderProgram); + GL.DeleteShader(_fragmentShader); + GL.DeleteShader(_vertexShader); + } + + protected override unsafe void OnOpenGlRender(GlInterface gl, int fb) + { + gl.ClearColor(0, 0, 0, 0); + gl.Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + gl.Enable(GL_DEPTH_TEST); + gl.Viewport(0, 0, (int)Bounds.Width, (int)Bounds.Height); + var GL = gl; + + GL.BindBuffer(GL_ARRAY_BUFFER, _vertexBufferObject); + GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferObject); + GL.BindVertexArray(_vertexArrayObject); + GL.UseProgram(_shaderProgram); + var projection = + Matrix4x4.CreatePerspectiveFieldOfView((float)(Math.PI / 4), (float)(Bounds.Width / Bounds.Height), + 0.01f, 1000); + + + var view = Matrix4x4.CreateLookAt(new Vector3(25, 25, 25), new Vector3(), new Vector3(0, -1, 0)); + var model = Matrix4x4.CreateFromYawPitchRoll(_yaw, _pitch, _roll); + var modelLoc = GL.GetUniformLocationString(_shaderProgram, "uModel"); + var viewLoc = GL.GetUniformLocationString(_shaderProgram, "uView"); + var projectionLoc = GL.GetUniformLocationString(_shaderProgram, "uProjection"); + var maxYLoc = GL.GetUniformLocationString(_shaderProgram, "uMaxY"); + var minYLoc = GL.GetUniformLocationString(_shaderProgram, "uMinY"); + GL.UniformMatrix4fv(modelLoc, 1, false, &model); + GL.UniformMatrix4fv(viewLoc, 1, false, &view); + GL.UniformMatrix4fv(projectionLoc, 1, false, &projection); + GL.Uniform1f(maxYLoc, _maxY); + GL.Uniform1f(minYLoc, _minY); + GL.DrawElements(GL_TRIANGLES, _indices.Length, GL_UNSIGNED_SHORT, IntPtr.Zero); + + CheckError(GL); + } + } +} diff --git a/samples/ControlCatalog/Pages/teapot.bin b/samples/ControlCatalog/Pages/teapot.bin new file mode 100644 index 0000000000000000000000000000000000000000..589eeb912dca4141b6c4d55a767446fe3aad67cf GIT binary patch literal 15464 zcmZ{q37pN<`^V4ujxGBxEsU%!$lk)-@6|6Mg{Y=dO=M6iDxu9(L`oEu8DtAB))pnq z{oWQugvy#~ETsimN*m_?KF{~g+~@xOzwY~Wf6wPRozFSvp67he+!??0hQ`=nwQtO5 z-o|Y2w@`H1ZO#4dkG(Uc5Ep#@sgdbHDbQ%@McGZSl|iGQ+n;+}eJrz0tq8++Jy) zwttJidHt`_KRf#y|3u-qSonDhL_4o7*cEELCP%mLSN#twUEsf$krNcH;gYoBLD3rA z_(fy(@*fC_)+6nS@!KqaQ2M*ICHwF9TizFrnYzxy{=B7YWG=T?LvM(S*X`ATo*&5g zZm(MYu_heT?bZDC(%`=5`Bb~Sx!*CFZSYPurthUu+Tt|?ddvei6t5@H zQIf)VUI!I0K;$>#o}OtNw?TE(p}w4%FFJ-d%w@ z=V5zde0Ank|NVhF|7xzofqDi5^$Z5;84T1j7^r73P}B%K z(RYi!PxKw4?+vZhn0{VltAFa&vi`JTz5K$uzlb)x+Uz~(8#5%M?c@Qz&GMp7yY}2^ zr+gc1jOA?@=9?kqqE5T^ypgU?X3(nDL);4$MZ0$O+5KsDhK!#VjrpxwE%Du$E$`dx zPph&j9JA-Qe(Pfa?QWdCDfdtA?-j+HJL*rrb+!89&8t!-I{m5^;@xmpXkX)od% zlHt}9&Kr)uzR?V7wlU_`=Gu$qnzmym{WF8)I>UL@Gv!ZvY`4ti_Nv8O$7FoBSL-_) z?hy8$y~ZR@W=Zms&%h))441)p`OwzFJ$L=ThqoteRKtRiO4NPJ?Y#O74Wj8A!BC-9;yAooJW(rg-(;qRw{8nChITjIYjo%3SLF zr_8IK!9YEOJY&VrV4$ACKs|#g_m8?O0(CzG>P`sMJ;2#des9rti@s0v9is0It<{)* zt2_Vl&5&t9n|?!mJF868X?G6SmoMNuNPe(k{Y~5lzl(P5*^Afsw#U|>f7?!c@7gKa zwTE}$z99MiYG2^m)o1rxR(nt8YSn9}zy6t5663G&vr9cE;|%EZmLKk|8|QUj?SHi8 z-V4N=x1~aKW4Bu3&2D6)1D0Fy_CNGv;(l}9qVHGg{&Alxs2!E_#^*AoylD4XOwI|p ze~fW!F8Y2IdevEt<{rrl-2S*V$T*2H`!|}z82N!)j~>7EO=AMtE7s=Pi{=XNK)udz zUiD1*EhnFlxn!@(|0LsYSkGQPxkbj5y^`-&;a;id+RyvFPQ0k5*`2D!i|W|wCB7pi zY8T!u&g;Hksr#qkJn>RGW7E$Tujs5w;tku~9TnDBd(|2OwT8f|G1YnkJ-%8S-@B4? zsddsnIj`EQK*H>sE0C+=qiKiA^w41_v7 zSE3WOPV|@hrg+uaj;phsGNwA`DdVd%FJl(X72bh*o#DLd84T1j7^r73P|qNDl=~eg z+$(ig1nRC}p9|y}_nK*}=eMA9&wAWTZ-{p7T^qOb&Eda-rF;4Pte}%<*Dkd)JY&4n z=k;rJ9W7&48$aK_x%;g$&f@*g`R6S>8gb)P+uFg`UbW}`O_%NGcfA8a=f2zhDRWjt z+6YuFL)ru8;&D2l6L1clYS2S;59iLLe0E|OUC@GSKlX#W5(w?Yw z*e9%SsKc86t5==jK%L=~G1Yla8DE|4f1OJ`Ux9kQ0`+_a>iJ4pr+W4S_3Q`gc@NYx z9@Bf9a-XQXLf;Snb|={MD*TMA?7MSi3o6A6BWI#BBW6asxOc2}sB_xJx#Q5hKJ6X% zw!8kB5u1Ju-qL7ZvFvzZ^l(JHe!qB{zZdPs988}{(r(iSCA9mQTLInLm;YAzl6c7; z++Xywvu=!gIgP^hA$RjLx>VSnJ}lv-PgYrer|3pI>wKbp+$`!E>JeG-Q;pk)y50kE z8>=qbW<8?T;C{Ad(#{%Ybzq!pL!I7_cJ57SADP$(l1VuUuUoV7Cf5*CYl!JFO>#Xk ztHw9Uwb4I0ms)2mb>8B=ikakI;Vs^)m`Uyxa~1DZOw}}|Y8q2@jH%kuzqnqElRO(n zoq?3P>sqJOSJgT>W+Z3ep!m|X{g+DZOm_Snv%Q1cbMwqC@e}Rl#$3Bi@8k9yZA?z? zmGPVz=LFI}=t{k6qTRV_PM-nNuD_e9&mJ*94vE*wxh`MMRY5<_)dt4oERMJ;e(K%! zcsWbyO&Mo&sMR^i889MV;;fx`Ib*H)ET8^?H#3rRUH5p-7uSXJX7L>|XNqWDFSF?( z&QB>>znyg_^OmF!uM2y`3zzg2ZPU+X-sz&XSDyj3hL|2xt%oy^9AB+1mNHjzoiRPH z+N+q_E6zaiUd7a2;Vs^)lyjnL8dG(QsoK%MxL%BtJRAI`ReT0?-Hoob{@xzAzc2aW z-l9*9dNY=ndoW^}rAJL>)7aFfwnmJ9cC?D=M(x})-XdBu$c$`WKR03vnntDm?ms+# zY1Cfb#4nNFH)2e?s12I=uGwZ?O&|C7`PTIBl+d)xysi1ZSI4)r8b#CmZ(;*WRq}1Y z#q=4Hk)6FWVrN|*Ev$Qne=_zW-dhshA^eT}pl`Fr@^>57S+!}j@TV4mTc=I$7%iI< z&v$Dxi@Qg?qvd2hHtU|`8f4zYIWZSTy__C_th3;9`ZSS!Fn({^OR{G7Ts)BQ7Ogq- zsx`#a8t|(zlk1^gYW(EdVkvVa*Gd2Xo>%rNPQDP6gC^9s8|P%a?6bKeGg|oM1axI; z-Ge=M+L++U%xHSsr~Tuj4rOpp7WcOWy%KfVo?#1$&X}uL@n?^i-r3Q@4Nv#y%tKG%t>f7H)U{Mo>B8FS|dw zr^AGBJq3f*8j|%gC32#LUq3^CUXya7HfvI-Pk$(y_ICKWY{A{hbERvYTtiH)A!hWL z$@RFOYsKTMwdI?Xxr*1BpE_^RXCD$%d&L|ONP?LT)k z7HM+08T+`_ziQ0DNX;=#+4r~o<4p!jf9Ex3XNoucjPA2+lJ;=@4=4J#HRnYBSodvA z&-=f|S-fp0bA@ZrV;&g$e(`!-d(rrVJFHAuTev?tk@(3f{2SQ+-=CUyR}6~w=d`hV zWE{6Ym+jswwvgf|>ho#&MC)!9y&OFdtS z_7lFVoqE1v>iLSP=PRb}ffTQL_G9XK&sWcQ{=eND`mWITgT52AR%2$Yyk#`sOXm5R z7xj;nFw;c4&(fi6bD!^}{@@N9BVNvBqFsC5pmF@Y>3RR9i>vrHcu}-#?=pLa@4hcB zzM?+ui$uG2_1Poa=9ZW75BJ;{>9(8iK#c#&kW2kmtvbp$xi>uFpTDcGc&jg-?5q8c z#Ot)mckAr4q?Et=!Sdqu$KS`l-RDN!dU9WT-Orl1n)cCt=iM=WcDnz+L3p<~?YzcR zO|(y!Iiwx0F)I@7?)%=R6t6m;ktWmsk#XD_^qB9}anIUx->oN{Hyl5A#kE1v+Vort zKN&!Oz0Po6^&CZxH@P5iYj%4z=kO>Q-|f}Z+{Ycx@5nfAuhg@gabxo<#p`NX zx1{^t7uGTFr}g4>wd+!Ek9b|Z^s|3z)OF%DysST;KJK$P{k3>Q9sYkCoYr2ohJ3Y# zm>yHDC#J_&Yh#||Txy-PC+Agr6;pc^Q+pMty$aM`1*)boRntJ#k$>+iu3ez&6@>K- z?;Ldo675oV(XQ^Ct3NoBK%Mi1HymG``LKUtE_MDB{lj_HGZ<6P zU`#!O{99h}GZ?66Fi>|zzPc-7>VAl+J0Vc_K%nm&eYfcQMBgF$-tcS5Ni!a#A=8K@ zpd_j!u@sa>l_r*f(@@rwHRZrVdBpNiLVV>-1t=xH3Z^2IAuDStnaWU3e3eZVC@;P$ zrYck*t3a#<6;YK;byEW>i?4>M3026d5NknIR8`{XPz_bvoMFy{vruOd&xSgvI>fq= zj!GxigL6=4nEIvxoGHErrXidyzJ{g|)D>SN(-`WJ)i+JdxzIp-=bH1NAz4G>`OpZ} zhwqq>=E z&2?~<_^vb8LwE6AZ!)2$_%h86(2ML^bECNlt`px)rVm_Cc0KWC$V6olZ-E<7H=0{b z7ThGhEOQ(5A?riz3pb0eujvQ3klk+jn`{_>8bBNfgHVHrcfg&fJBfqgE>wSWx48$h z#dnVx0t3Z2#0-Ty#5dI33xmn-HustP;U4kbZ-&7TvLVC=U?^%RaX8$Iy3dR-BjJAW zjWiF!FtTC9hu{J6J!Bq+;bf1PN6je6LFEu1gU3;i6Q6+5sL{kR@FeO{GuDiQQQ{kC zo`T23_mmkAPl#{4c^bx$jWy4h2{2B46U?*l6xmb6i7*~Do;V4fMm=MmGta{W@jY)Q z!?R@15~sjK@l7!=z$CI4%}Zt~v&=j*A7+bhzF7cs$mS4Vg}JD?#Mj^z)I9ULc?0H)?+vpM7LY9< zE`nFZx5zAp*T|NbrDhqtiF%Xx7QBsmoA?eaM=dA53-6(pnib}KSSG&r%}RJnd@Ic= zct?Dz%xZX-Y=y};{LvVP^TkX6D^V+naae^~Mf?C(qw>v%<|BxS?<4au1mgSHtbw@r z)|gM=1F}!eTC)y5Lw!bE4;xS$h@Zno)JEba*o<0hzA#_HI`MsJzJm4Q`^s#A&&9XJ zY=uo^UzlxXJA5g=?PdpjMfMeOCu~7&A$|>8QQOQe^9^hl-#6x4*g>{~_#NyN-*=_} zz9!pk_L#k}54Df@J^X=$#`9DxJkJ7SK)L9&CyV{iy{i1-`)iaKn5H^P-D;3(Nq;-7F#e1Dq1;5V|r z%|GS@oJ5@@o`OPDA(0=k*4je;$5;>2$o?@UY)LpFzLK^SoDyFtTN(_RA(jD;tb{$y zmW7h2lEiXQ3RQ|&9!jH16DvR&)M>V&tpsJoSIJg}a^kCOt3Y}2Rk2l}0$DX%-PVAb zsG7uDa60OAVr@7Bbq4WFI15$Xo^9(u4e`~nb)lB{>e_UuExvSH56&bz+n!_VLmlze zw+)~!SzTg7NJpg;8$mtPIkvHF0`;M;` zE+S^ok6*0dQgQe;3hl|-6FWi&adfno+5gzfZ6~+_bp^3AbU}3?c7-cZR}#C??<#vW zbP~tawmWndM|ax;x{9NR?Mc6DY%jQ49KCFB=uXz1crElm^&nnHzw2!#^b$v=y#acY z^(Nj3*NWptdz0;BZ??C4QqCO;k1RtY5Ca!@`P%-Ku*4lOOf%w+h&)_5ReP-9g8nRFA2Kzaz72oG}BdjA^N8ALTp*|yShV`fo_6z$Z zd@jB(?N_joY$I_CY!crVyA?K*ZL{0$4%ms>N&Fgip>`3!fp1aY62F52)ONeu?tvZR z+hg~_*W%l2_rW*f+h@Or@5pxBAMAeEBfkCiN7zfYm-rLxL+vB}4Bw-Eu)o*?uwQ%! z>_PaE>__4u_(^<+?62@M*JAwa%wVr#OM4d#Y!6{UsE#Z{}<2heR zuM}7^ODqi@$|IJ6G}LKcS+5+FN0ld5fQqP!#7a;ZRhd`?s-ntz)x7FZPJGq98c;!e zHN2WoNqjZET2O_ons>Ta8>)-1ws!{9Age(<6KbMr63>EKsMEc(y*f}^e099Ka0b~K z#B?}QeCb|2IE(BYufEp+8loB!8$n}KV`39H7j-W2JUAa!-@CwT3Jt{9)N2Nf#MjJg z4o$?@+`ACYBfG$B;kAUO;%n)(f@WmRh^?VHsyVR@T!?Dnwe{LTOYybyE`nBMt%w&x zYw=y|T>@>$F7?`b9UudhL5zTp@`+LCi0VkZ4E}>^?_KV7f)3*AD zdtKl%vdg`$-j&cvd{=ti;0m%Uh*v>pRA=JV&;`}i>+bb{E5+Bt>j~Y+x)HB|tHgJW z*9)#D>+N0ZUB|n(cRggHGKn|9ji?)mH$flN&Gfm2_s!m|aJ@Kg^|Igwab$V7!A)d+ z>C=yQU+;FfRUEf_{UM7iigZoj#=<@*YVcu}KM;ybw5inF7BfODtAK8QSd5HIe-or3l91nYs zzzDJt#7AKyY7~8Pc#rZPgNMcOnD;n5LiPyp33wDW+8g6N$$PXn7RI5*5ubwbsPV+7 z;ThBf`aH{ff;SPyiesWT37!(iB=0$Rn(TS{Oy>Q(Hw7k&V~Y0zOd^{^d=Z{Qy+og> zykGKiVTw3%y*zk<>;>X9co8+-o8i68d%8ChW}#*gXTu!S9O7Jf1vQU8^Lfwn7Qjq# zEbv~1+2VNBdkyB2y-uGuc)#u~gazVQ=q-X*$zCNchSyL_=(Cjf5^otS6vr~}O;|*> zi1-#PM!oI5<1L4GQSTDpgB7S1#P?w(Y9(OK8AoSAg+NpDo*?aK0tlwed?`+kHojuTL&MLeN6le)`;&jZ#{fM zw!!<{+X$Obn~0m?3)C0HFX1cHSHvx_74^Bd&D#ze#kbwt0h`6Q!`lg8if^a)HEbc< z=I!#nf$ie^#`_j_knJFT2Rl(ai3RXAYL~a$+XLT-Z;!VZz9svXxDUP)-#+hqC?Na6 z+wc7dKcRji{tUmMejy%!gQ$bVL+~qVzjxR>0zZoHh<6lz7T;0t7#tAaG4D4xM0VKw z-8&9P#CP2L1CEj%CH@J=P{)XW!EdPFy}!MG;JEny@lL=WWPcD(!k^+h>79bV$O`$> zGr!pK6j%0o%# literal 0 HcmV?d00001 diff --git a/src/Avalonia.Native/GlPlatformFeature.cs b/src/Avalonia.Native/GlPlatformFeature.cs index e2231dae65..7ed6eaa266 100644 --- a/src/Avalonia.Native/GlPlatformFeature.cs +++ b/src/Avalonia.Native/GlPlatformFeature.cs @@ -49,7 +49,7 @@ namespace Avalonia.Native }); } - public GlDisplayType Type => GlDisplayType.OpenGL2; + public GlDisplayType Type => GlDisplayType.OpenGl; public GlInterface GlInterface { get; } diff --git a/src/Avalonia.OpenGL/EglDisplay.cs b/src/Avalonia.OpenGL/EglDisplay.cs index 57787243b2..f93bf7280c 100644 --- a/src/Avalonia.OpenGL/EglDisplay.cs +++ b/src/Avalonia.OpenGL/EglDisplay.cs @@ -81,7 +81,7 @@ namespace Avalonia.OpenGL Attributes = new[] {EGL_NONE}, Api = EGL_OPENGL_API, RenderableTypeBit = EGL_OPENGL_BIT, - Type = GlDisplayType.OpenGL2 + Type = GlDisplayType.OpenGl }, new { @@ -92,7 +92,7 @@ namespace Avalonia.OpenGL }, Api = EGL_OPENGL_ES_API, RenderableTypeBit = EGL_OPENGL_ES2_BIT, - Type = GlDisplayType.OpenGLES2 + Type = GlDisplayType.OpenGLES } }) { diff --git a/src/Avalonia.OpenGL/GlDisplayType.cs b/src/Avalonia.OpenGL/GlDisplayType.cs index 2e5178bc37..6ce7600ec6 100644 --- a/src/Avalonia.OpenGL/GlDisplayType.cs +++ b/src/Avalonia.OpenGL/GlDisplayType.cs @@ -2,7 +2,7 @@ namespace Avalonia.OpenGL { public enum GlDisplayType { - OpenGL2, - OpenGLES2 + OpenGl, + OpenGLES } -} \ No newline at end of file +} diff --git a/src/Avalonia.OpenGL/GlEntryPointAttribute.cs b/src/Avalonia.OpenGL/GlEntryPointAttribute.cs index 016c3d0af9..62bf3567e4 100644 --- a/src/Avalonia.OpenGL/GlEntryPointAttribute.cs +++ b/src/Avalonia.OpenGL/GlEntryPointAttribute.cs @@ -5,13 +5,18 @@ namespace Avalonia.OpenGL [AttributeUsage(AttributeTargets.Property)] public class GlEntryPointAttribute : Attribute { - public string EntryPoint { get; } + public string[] EntryPoints { get; } public bool Optional { get; } public GlEntryPointAttribute(string entryPoint, bool optional = false) { - EntryPoint = entryPoint; + EntryPoints = new []{entryPoint}; Optional = optional; } + + public GlEntryPointAttribute(params string[] entryPoints) + { + EntryPoints = entryPoints; + } } } diff --git a/src/Avalonia.OpenGL/GlInterface.cs b/src/Avalonia.OpenGL/GlInterface.cs index 5614ddfb17..33208f8e6c 100644 --- a/src/Avalonia.OpenGL/GlInterface.cs +++ b/src/Avalonia.OpenGL/GlInterface.cs @@ -1,6 +1,8 @@ using System; using System.Runtime.InteropServices; +using System.Text; using Avalonia.Platform.Interop; +using static Avalonia.OpenGL.GlConsts; namespace Avalonia.OpenGL { @@ -74,6 +76,10 @@ namespace Avalonia.OpenGL [GlEntryPoint("glGenFramebuffers")] public GlGenFramebuffers GenFramebuffers { get; } + public delegate void GlDeleteFramebuffers(int count, int[] framebuffers); + [GlEntryPoint("glDeleteFramebuffers")] + public GlDeleteFramebuffers DeleteFramebuffers { get; } + public delegate void GlBindFramebuffer(int target, int fb); [GlEntryPoint("glBindFramebuffer")] public GlBindFramebuffer BindFramebuffer { get; } @@ -106,6 +112,11 @@ namespace Avalonia.OpenGL public delegate void GlBindTexture(int target, int fb); [GlEntryPoint("glBindTexture")] public GlBindTexture BindTexture { get; } + + public delegate void GlDeleteTextures(int count, int[] textures); + [GlEntryPoint("glDeleteTextures")] + public GlDeleteTextures DeleteTextures { get; } + public delegate void GlTexImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, IntPtr data); @@ -125,6 +136,207 @@ namespace Avalonia.OpenGL [GlEntryPoint("glDrawBuffers")] public GlDrawBuffers DrawBuffers { get; } + public delegate int GlCreateShader(int shaderType); + [GlEntryPoint("glCreateShader")] + public GlCreateShader CreateShader { get; } + + public delegate void GlShaderSource(int shader, int count, IntPtr strings, IntPtr lengths); + [GlEntryPoint("glShaderSource")] + public GlShaderSource ShaderSource { get; } + + public void ShaderSourceString(int shader, string source) + { + using (var b = new Utf8Buffer(source)) + { + var ptr = b.DangerousGetHandle(); + var len = new IntPtr(b.ByteLen); + ShaderSource(shader, 1, new IntPtr(&ptr), new IntPtr(&len)); + } + } + + public delegate void GlCompileShader(int shader); + [GlEntryPoint("glCompileShader")] + public GlCompileShader CompileShader { get; } + + public delegate void GlGetShaderiv(int shader, int name, int* parameters); + [GlEntryPoint("glGetShaderiv")] + public GlGetShaderiv GetShaderiv { get; } + + public delegate void GlGetShaderInfoLog(int shader, int maxLength, out int length, void*infoLog); + [GlEntryPoint("glGetShaderInfoLog")] + public GlGetShaderInfoLog GetShaderInfoLog { get; } + + public unsafe string CompileShaderAndGetError(int shader, string source) + { + ShaderSourceString(shader, source); + CompileShader(shader); + int compiled; + GetShaderiv(shader, GL_COMPILE_STATUS, &compiled); + if (compiled != 0) + return null; + int logLength; + GetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength); + if (logLength == 0) + logLength = 4096; + var logData = new byte[logLength]; + int len; + fixed (void* ptr = logData) + GetShaderInfoLog(shader, logLength, out len, ptr); + return Encoding.UTF8.GetString(logData,0, len); + } + + public delegate int GlCreateProgram(); + [GlEntryPoint("glCreateProgram")] + public GlCreateProgram CreateProgram { get; } + + public delegate void GlAttachShader(int program, int shader); + [GlEntryPoint("glAttachShader")] + public GlAttachShader AttachShader { get; } + + public delegate void GlLinkProgram(int program); + [GlEntryPoint("glLinkProgram")] + public GlLinkProgram LinkProgram { get; } + + public delegate void GlGetProgramiv(int program, int name, int* parameters); + [GlEntryPoint("glGetProgramiv")] + public GlGetProgramiv GetProgramiv { get; } + + public delegate void GlGetProgramInfoLog(int program, int maxLength, out int len, void* infoLog); + [GlEntryPoint("glGetProgramInfoLog")] + public GlGetProgramInfoLog GetProgramInfoLog { get; } + + public unsafe string LinkProgramAndGetError(int program) + { + LinkProgram(program); + int compiled; + GetProgramiv(program, GL_LINK_STATUS, &compiled); + if (compiled != 0) + return null; + int logLength; + GetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength); + var logData = new byte[logLength]; + int len; + fixed (void* ptr = logData) + GetProgramInfoLog(program, logLength, out len, ptr); + return Encoding.UTF8.GetString(logData,0, len); + } + + public delegate void GlBindAttribLocation(int program, int index, IntPtr name); + [GlEntryPoint("glBindAttribLocation")] + public GlBindAttribLocation BindAttribLocation { get; } + + public void BindAttribLocationString(int program, int index, string name) + { + using (var b = new Utf8Buffer(name)) + BindAttribLocation(program, index, b.DangerousGetHandle()); + } + + public delegate void GlGenBuffers(int len, int[] rv); + [GlEntryPoint("glGenBuffers")] + public GlGenBuffers GenBuffers { get; } + + public int GenBuffer() + { + var rv = new int[1]; + GenBuffers(1, rv); + return rv[0]; + } + + public delegate void GlBindBuffer(int target, int buffer); + [GlEntryPoint("glBindBuffer")] + public GlBindBuffer BindBuffer { get; } + + public delegate void GlBufferData(int target, IntPtr size, IntPtr data, int usage); + [GlEntryPoint("glBufferData")] + public GlBufferData BufferData { get; } + + public delegate int GlGetAttribLocation(int program, IntPtr name); + [GlEntryPoint("glGetAttribLocation")] + public GlGetAttribLocation GetAttribLocation { get; } + + public int GetAttribLocationString(int program, string name) + { + using (var b = new Utf8Buffer(name)) + return GetAttribLocation(program, b.DangerousGetHandle()); + } + + public delegate void GlVertexAttribPointer(int index, int size, int type, + int normalized, int stride, IntPtr pointer); + [GlEntryPoint("glVertexAttribPointer")] + public GlVertexAttribPointer VertexAttribPointer { get; } + + public delegate void GlEnableVertexAttribArray(int index); + [GlEntryPoint("glEnableVertexAttribArray")] + public GlEnableVertexAttribArray EnableVertexAttribArray { get; } + + public delegate void GlUseProgram(int program); + [GlEntryPoint("glUseProgram")] + public GlUseProgram UseProgram { get; } + + public delegate void GlDrawArrays(int mode, int first, IntPtr count); + [GlEntryPoint("glDrawArrays")] + public GlDrawArrays DrawArrays { get; } + + public delegate void GlDrawElements(int mode, int count, int type, IntPtr indices); + [GlEntryPoint("glDrawElements")] + public GlDrawElements DrawElements { get; } + + + public delegate void GlGenVertexArrays(int n, int[] rv); + [GlEntryPoint("glGenVertexArrays", "glGenVertexArraysOES")] + public GlGenVertexArrays GenVertexArrays { get; } + + public int GenVertexArray() + { + var rv = new int[1]; + GenVertexArrays(1, rv); + return rv[0]; + } + + public delegate void GlBindVertexArray(int array); + [GlEntryPoint("glBindVertexArray", "glBindVertexArrayOES")] + public GlBindVertexArray BindVertexArray { get; } + + public delegate int GlGetUniformLocation(int program, IntPtr name); + [GlEntryPoint("glGetUniformLocation")] + public GlGetUniformLocation GetUniformLocation { get; } + + public int GetUniformLocationString(int program, string name) + { + using (var b = new Utf8Buffer(name)) + return GetUniformLocation(program, b.DangerousGetHandle()); + } + + public delegate void GlUniform1f(int location, float falue); + [GlEntryPoint("glUniform1f")] + public GlUniform1f Uniform1f { get; } + + + public delegate void GlUniformMatrix4fv(int location, int count, bool transpose, void* value); + [GlEntryPoint("glUniformMatrix4fv")] + public GlUniformMatrix4fv UniformMatrix4fv { get; } + + public delegate void GlEnable(int what); + [GlEntryPoint("glEnable")] + public GlEnable Enable { get; } + + public delegate void GlDeleteBuffer(int buffer); + [GlEntryPoint("glDeleteBuffer")] + public GlDeleteBuffer DeleteBuffer { get; } + + public delegate void GlDeleteVertexArray(int array); + [GlEntryPoint("glDeleteVertexArray", "glDeleteVertexArrayOES")] + public GlDeleteVertexArray DeleteVertexArray { get; } + + public delegate void GlDeleteProgram(int program); + [GlEntryPoint("glDeleteProgram")] + public GlDeleteProgram DeleteProgram { get; } + + public delegate void GlDeleteShader(int shader); + [GlEntryPoint("glDeleteShader")] + public GlDeleteShader DeleteShader { get; } + + // ReSharper restore UnassignedGetOnlyAutoProperty } } diff --git a/src/Avalonia.OpenGL/GlInterfaceBase.cs b/src/Avalonia.OpenGL/GlInterfaceBase.cs index 89ec0e77d4..445777b065 100644 --- a/src/Avalonia.OpenGL/GlInterfaceBase.cs +++ b/src/Avalonia.OpenGL/GlInterfaceBase.cs @@ -22,7 +22,22 @@ namespace Avalonia.OpenGL BindingFlags.Instance | BindingFlags.NonPublic); if (field == null) throw new InvalidProgramException($"Expected property {prop.Name} to have {fieldName}"); - var proc = getProcAddress(a.EntryPoint, a.Optional); + IntPtr proc = IntPtr.Zero; + if (a.EntryPoints.Length == 1) + proc = getProcAddress(a.EntryPoints[0], a.Optional); + else + { + foreach (var ep in a.EntryPoints) + { + proc = getProcAddress(ep, true); + + } + + if (proc == IntPtr.Zero && !a.Optional) + throw new OpenGlException("Unable to resolve function by any of aliases " + + string.Join(", ", a.EntryPoints)); + } + if (proc != IntPtr.Zero) field.SetValue(this, Marshal.GetDelegateForFunctionPointer(proc, prop.PropertyType)); } diff --git a/src/Avalonia.OpenGL/OpenGlControlBase.cs b/src/Avalonia.OpenGL/OpenGlControlBase.cs new file mode 100644 index 0000000000..13a60c7574 --- /dev/null +++ b/src/Avalonia.OpenGL/OpenGlControlBase.cs @@ -0,0 +1,149 @@ +using System; +using Avalonia.Controls; +using Avalonia.Media; +using Avalonia.OpenGL.Imaging; +using static Avalonia.OpenGL.GlConsts; + +namespace Avalonia.OpenGL +{ + public abstract class OpenGlControlBase : Control + { + private IGlContext _context; + private int _fb, _texture, _renderBuffer; + private OpenGlTextureBitmap _bitmap; + private Size _oldSize; + protected GlDisplayType DisplayType { get; private set; } + public sealed override void Render(DrawingContext context) + { + if(!EnsureInitialized()) + return; + using (_context.MakeCurrent()) + { + using (_bitmap.Lock()) + { + var gl = _context.Display.GlInterface; + gl.BindFramebuffer(GL_FRAMEBUFFER, _fb); + if (_oldSize != Bounds.Size) + ResizeTexture(gl); + + OnOpenGlRender(gl, _fb); + gl.Flush(); + } + } + + context.DrawImage(_bitmap, 1, new Rect(_bitmap.Size), Bounds); + base.Render(context); + } + + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + if (_context != null) + { + using (_context.MakeCurrent()) + { + OnOpenGlDeinit(_context.Display.GlInterface, _fb); + var gl = _context.Display.GlInterface; + gl.BindTexture(GL_TEXTURE_2D, 0); + gl.BindFramebuffer(GL_FRAMEBUFFER, 0); + gl.DeleteFramebuffers(1, new[] { _fb }); + using (_bitmap.Lock()) + { + _bitmap.SetTexture(0, 0, new PixelSize(1, 1), 1); + gl.DeleteTextures(1, new[] { _texture }); + } + _bitmap.Dispose(); + + _context.Dispose(); + _context = null; + } + } + base.OnDetachedFromVisualTree(e); + } + + bool EnsureInitialized() + { + if (_context != null) + return true; + + var feature = AvaloniaLocator.Current.GetService(); + if (feature == null) + return false; + _context = feature.CreateContext(); + DisplayType = feature.Display.Type; + try + { + _bitmap = new OpenGlTextureBitmap(); + } + catch (PlatformNotSupportedException) + { + _context.Dispose(); + _context = null; + return false; + } + + using (_context.MakeCurrent()) + { + _oldSize = Bounds.Size; + var gl = _context.Display.GlInterface; + var oneArr = new int[1]; + gl.GenFramebuffers(1, oneArr); + _fb = oneArr[0]; + gl.BindFramebuffer(GL_FRAMEBUFFER, _fb); + + gl.GenTextures(1, oneArr); + _texture = oneArr[0]; + gl.BindTexture(GL_TEXTURE_2D, _texture); + ResizeTexture(gl); + + gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0); + gl.DrawBuffers(1, new[] { GL_COLOR_ATTACHMENT0 }); + var status = gl.CheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) + { + //TODO: Cleanup + return false; + } + OnOpenGlInit(_context.Display.GlInterface, _fb); + } + + return true; + } + + void ResizeTexture(GlInterface gl) + { + var size = GetPixelSize(); + gl.TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, + size.Width, size.Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, IntPtr.Zero); + gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + //TODO: destroy the previous one + var oneArr = new int[1]; + gl.GenRenderbuffers(1, oneArr); + _renderBuffer = oneArr[0]; + gl.BindRenderbuffer(GL_RENDERBUFFER, _renderBuffer); + gl.RenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, size.Width, size.Height); + gl.FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _renderBuffer); + using (_bitmap.Lock()) + _bitmap.SetTexture(_texture, GL_RGBA8, size, 1); + } + + //TODO: dpi + PixelSize GetPixelSize() => + new PixelSize(Math.Max(1, (int)Bounds.Width), + Math.Max(1, (int)Bounds.Height)); + + + protected virtual void OnOpenGlInit(GlInterface gl, int fb) + { + + } + + protected virtual void OnOpenGlDeinit(GlInterface gl, int fb) + { + + } + + protected abstract void OnOpenGlRender(GlInterface gl, int fb); + } +} diff --git a/src/Avalonia.X11/Glx/GlxDisplay.cs b/src/Avalonia.X11/Glx/GlxDisplay.cs index 4b78c40737..2522ff35ab 100644 --- a/src/Avalonia.X11/Glx/GlxDisplay.cs +++ b/src/Avalonia.X11/Glx/GlxDisplay.cs @@ -10,7 +10,7 @@ namespace Avalonia.X11.Glx private readonly X11Info _x11; private readonly IntPtr _fbconfig; private readonly XVisualInfo* _visual; - public GlDisplayType Type => GlDisplayType.OpenGL2; + public GlDisplayType Type => GlDisplayType.OpenGl; public GlInterface GlInterface { get; } public XVisualInfo* VisualInfo => _visual; diff --git a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs index 3a7eec134e..76c149e53b 100644 --- a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs +++ b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs @@ -16,7 +16,7 @@ namespace Avalonia.Skia using (immediateContext.MakeCurrent()) { var display = gl.Display; - using (var iface = display.Type == GlDisplayType.OpenGL2 ? + using (var iface = display.Type == GlDisplayType.OpenGl ? GRGlInterface.AssembleGlInterface((_, proc) => display.GlInterface.GetProcAddress(proc)) : GRGlInterface.AssembleGlesInterface((_, proc) => display.GlInterface.GetProcAddress(proc))) { From df2cb9d210d2dda9bfa43a5a5d52447e65b5b4fe Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 22 Dec 2019 20:11:52 +0300 Subject: [PATCH 008/663] Fixed imports --- samples/ControlCatalog/Pages/OpenGlPage.xaml.cs | 5 ++--- src/Avalonia.OpenGL/GlEntryPointAttribute.cs | 2 +- src/Avalonia.OpenGL/GlInterface.cs | 12 ++++++------ 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs b/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs index 791c49ed8f..f2a0d9a03e 100644 --- a/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs +++ b/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs @@ -243,9 +243,8 @@ namespace ControlCatalog.Pages GL.UseProgram(0); // Delete all resources. - GL.DeleteBuffer(_vertexBufferObject); - GL.DeleteBuffer(_indexBufferObject); - GL.DeleteVertexArray(_vertexArrayObject); + GL.DeleteBuffers(2, new[] { _vertexBufferObject, _indexBufferObject }); + GL.DeleteVertexArrays(1, new[] { _vertexArrayObject }); GL.DeleteProgram(_shaderProgram); GL.DeleteShader(_fragmentShader); GL.DeleteShader(_vertexShader); diff --git a/src/Avalonia.OpenGL/GlEntryPointAttribute.cs b/src/Avalonia.OpenGL/GlEntryPointAttribute.cs index 62bf3567e4..c37caa06c1 100644 --- a/src/Avalonia.OpenGL/GlEntryPointAttribute.cs +++ b/src/Avalonia.OpenGL/GlEntryPointAttribute.cs @@ -6,7 +6,7 @@ namespace Avalonia.OpenGL public class GlEntryPointAttribute : Attribute { public string[] EntryPoints { get; } - public bool Optional { get; } + public bool Optional { get; set; } public GlEntryPointAttribute(string entryPoint, bool optional = false) { diff --git a/src/Avalonia.OpenGL/GlInterface.cs b/src/Avalonia.OpenGL/GlInterface.cs index 33208f8e6c..77cbe4cc72 100644 --- a/src/Avalonia.OpenGL/GlInterface.cs +++ b/src/Avalonia.OpenGL/GlInterface.cs @@ -320,13 +320,13 @@ namespace Avalonia.OpenGL [GlEntryPoint("glEnable")] public GlEnable Enable { get; } - public delegate void GlDeleteBuffer(int buffer); - [GlEntryPoint("glDeleteBuffer")] - public GlDeleteBuffer DeleteBuffer { get; } + public delegate void GlDeleteBuffers(int count, int[] buffers); + [GlEntryPoint("glDeleteBuffers")] + public GlDeleteBuffers DeleteBuffers { get; } - public delegate void GlDeleteVertexArray(int array); - [GlEntryPoint("glDeleteVertexArray", "glDeleteVertexArrayOES")] - public GlDeleteVertexArray DeleteVertexArray { get; } + public delegate void GlDeleteVertexArrays(int count, int[] buffers); + [GlEntryPoint("glDeleteVertexArrays", "glDeleteVertexArraysOES")] + public GlDeleteVertexArrays DeleteVertexArrays { get; } public delegate void GlDeleteProgram(int program); [GlEntryPoint("glDeleteProgram")] From 565ac31cba21594b00b60c67780fd1b30c3d39fb Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Mon, 23 Dec 2019 11:49:19 +0300 Subject: [PATCH 009/663] Properly restore the previous EGL context --- src/Avalonia.OpenGL/EglContext.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.OpenGL/EglContext.cs b/src/Avalonia.OpenGL/EglContext.cs index 9acac74cb7..8c3a00c308 100644 --- a/src/Avalonia.OpenGL/EglContext.cs +++ b/src/Avalonia.OpenGL/EglContext.cs @@ -35,21 +35,28 @@ namespace Avalonia.OpenGL private readonly IntPtr _display; private IntPtr _context, _read, _draw; - public RestoreContext(EglInterface egl) + public RestoreContext(EglInterface egl, IntPtr defDisplay) { _egl = egl; _display = _egl.GetCurrentDisplay(); + if (_display == IntPtr.Zero) + _display = defDisplay; _context = _egl.GetCurrentContext(); _read = _egl.GetCurrentSurface(EGL_READ); _draw = _egl.GetCurrentSurface(EGL_DRAW); } - public void Dispose() => _egl.MakeCurrent(_display, _draw, _read, _context); + public void Dispose() + { + _egl.MakeCurrent(_display, _draw, _read, _context); + } + } public IDisposable MakeCurrent() { - var old = new RestoreContext(_egl); + var old = new RestoreContext(_egl, _disp.Handle); + _egl.MakeCurrent(_disp.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); if (!_egl.MakeCurrent(_disp.Handle, IntPtr.Zero, IntPtr.Zero, Context)) throw OpenGlException.GetFormattedException("eglMakeCurrent", _egl); return old; @@ -57,8 +64,9 @@ namespace Avalonia.OpenGL public IDisposable MakeCurrent(EglSurface surface) { - var old = new RestoreContext(_egl); + var old = new RestoreContext(_egl, _disp.Handle); var surf = surface ?? OffscreenSurface; + _egl.MakeCurrent(_disp.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); if (!_egl.MakeCurrent(_disp.Handle, surf.DangerousGetHandle(), surf.DangerousGetHandle(), Context)) throw OpenGlException.GetFormattedException("eglMakeCurrent", _egl); return old; From ffface8f9501d5b3a7a8d9b33a378afc6da06f14 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Mon, 23 Dec 2019 11:50:10 +0300 Subject: [PATCH 010/663] Fixed EGLDisplay initialization --- src/Avalonia.OpenGL/EglDisplay.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.OpenGL/EglDisplay.cs b/src/Avalonia.OpenGL/EglDisplay.cs index f93bf7280c..8a81b2b584 100644 --- a/src/Avalonia.OpenGL/EglDisplay.cs +++ b/src/Avalonia.OpenGL/EglDisplay.cs @@ -121,9 +121,11 @@ namespace Avalonia.OpenGL _contextAttributes = cfg.Attributes; _surfaceType = surfaceType; Type = cfg.Type; + goto Found; } - } + } + Found: if (_contextAttributes == null) throw new OpenGlException("No suitable EGL config was found"); @@ -155,7 +157,6 @@ namespace Avalonia.OpenGL if (surf == IntPtr.Zero) throw OpenGlException.GetFormattedException("eglCreatePBufferSurface", _egl); var rv = new EglContext(this, _egl, ctx, new EglSurface(this, _egl, surf)); - rv.MakeCurrent(null); return rv; } From 633ab61b4d426a1fb906f0575f4774a93263942b Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Mon, 23 Dec 2019 11:50:39 +0300 Subject: [PATCH 011/663] Fixed eglWaitNative usage --- src/Avalonia.OpenGL/EglGlPlatformSurface.cs | 4 ++-- src/Avalonia.OpenGL/EglInterface.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.OpenGL/EglGlPlatformSurface.cs b/src/Avalonia.OpenGL/EglGlPlatformSurface.cs index 2bb2e4e65e..ad5808c22a 100644 --- a/src/Avalonia.OpenGL/EglGlPlatformSurface.cs +++ b/src/Avalonia.OpenGL/EglGlPlatformSurface.cs @@ -61,7 +61,7 @@ namespace Avalonia.OpenGL var restoreContext = _context.MakeCurrent(_glSurface); _display.EglInterface.WaitClient(); _display.EglInterface.WaitGL(); - _display.EglInterface.WaitNative(); + _display.EglInterface.WaitNative(EglConsts.EGL_CORE_NATIVE_ENGINE); return new Session(_display, _context, _glSurface, _info, l, restoreContext); } @@ -101,7 +101,7 @@ namespace Avalonia.OpenGL _glSurface.SwapBuffers(); _display.EglInterface.WaitClient(); _display.EglInterface.WaitGL(); - _display.EglInterface.WaitNative(); + _display.EglInterface.WaitNative(EglConsts.EGL_CORE_NATIVE_ENGINE); _restoreContext.Dispose(); _lock.Dispose(); } diff --git a/src/Avalonia.OpenGL/EglInterface.cs b/src/Avalonia.OpenGL/EglInterface.cs index cdc5f38f33..7597ccc48b 100644 --- a/src/Avalonia.OpenGL/EglInterface.cs +++ b/src/Avalonia.OpenGL/EglInterface.cs @@ -136,9 +136,9 @@ namespace Avalonia.OpenGL [GlEntryPoint("eglWaitClient")] public EglWaitGL WaitClient { get; } - public delegate bool EglWaitNative(); + public delegate bool EglWaitNative(int engine); [GlEntryPoint("eglWaitNative")] - public EglWaitGL WaitNative { get; } + public EglWaitNative WaitNative { get; } public delegate IntPtr EglQueryString(IntPtr display, int i); From 8115941104c121e27fcc08ec5ca832dfc9cc3c93 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Mon, 23 Dec 2019 11:52:55 +0300 Subject: [PATCH 012/663] Make OpenGlControlBase GLES2-compatible --- src/Avalonia.OpenGL/GlInterface.cs | 6 +----- src/Avalonia.OpenGL/OpenGlControlBase.cs | 9 ++++++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.OpenGL/GlInterface.cs b/src/Avalonia.OpenGL/GlInterface.cs index 77cbe4cc72..e6107d2349 100644 --- a/src/Avalonia.OpenGL/GlInterface.cs +++ b/src/Avalonia.OpenGL/GlInterface.cs @@ -131,11 +131,7 @@ namespace Avalonia.OpenGL int texTarget, int texture, int level); [GlEntryPoint("glFramebufferTexture2D")] public GlFramebufferTexture2D FramebufferTexture2D { get; } - - public delegate void GlDrawBuffers(int n, int[] bufs); - [GlEntryPoint("glDrawBuffers")] - public GlDrawBuffers DrawBuffers { get; } - + public delegate int GlCreateShader(int shaderType); [GlEntryPoint("glCreateShader")] public GlCreateShader CreateShader { get; } diff --git a/src/Avalonia.OpenGL/OpenGlControlBase.cs b/src/Avalonia.OpenGL/OpenGlControlBase.cs index 13a60c7574..3a09c0a76f 100644 --- a/src/Avalonia.OpenGL/OpenGlControlBase.cs +++ b/src/Avalonia.OpenGL/OpenGlControlBase.cs @@ -96,7 +96,7 @@ namespace Avalonia.OpenGL ResizeTexture(gl); gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0); - gl.DrawBuffers(1, new[] { GL_COLOR_ATTACHMENT0 }); + var status = gl.CheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { @@ -112,7 +112,8 @@ namespace Avalonia.OpenGL void ResizeTexture(GlInterface gl) { var size = GetPixelSize(); - gl.TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, + gl.TexImage2D(GL_TEXTURE_2D, 0, + DisplayType == GlDisplayType.OpenGLES ? GL_RGBA : GL_RGBA8, size.Width, size.Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, IntPtr.Zero); gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); @@ -122,7 +123,9 @@ namespace Avalonia.OpenGL gl.GenRenderbuffers(1, oneArr); _renderBuffer = oneArr[0]; gl.BindRenderbuffer(GL_RENDERBUFFER, _renderBuffer); - gl.RenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, size.Width, size.Height); + gl.RenderbufferStorage(GL_RENDERBUFFER, + DisplayType == GlDisplayType.OpenGLES ? GL_DEPTH_COMPONENT16 : GL_DEPTH_COMPONENT, + size.Width, size.Height); gl.FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _renderBuffer); using (_bitmap.Lock()) _bitmap.SetTexture(_texture, GL_RGBA8, size, 1); From 731b23374b65450b1edeaaace0c73349c2ca900d Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Mon, 23 Dec 2019 11:53:59 +0300 Subject: [PATCH 013/663] Made OpenGlPage GLES2-compatible --- samples/ControlCatalog/Pages/OpenGlPage.xaml.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs b/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs index f2a0d9a03e..767ee1c589 100644 --- a/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs +++ b/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs @@ -68,7 +68,7 @@ namespace ControlCatalog.Pages $"#version {(DisplayType == GlDisplayType.OpenGl ? 120 : 100)}\n" + shader; private string WithVersionAndPrecision(string shader, string precision) => - WithVersion(DisplayType == GlDisplayType.OpenGLES ? $"precision {precision}\n{shader}" : shader); + WithVersion(DisplayType == GlDisplayType.OpenGLES ? $"precision {precision};\n{shader}" : shader); private string VertexShaderSource => WithVersion(@" attribute vec3 aPos; @@ -98,13 +98,13 @@ namespace ControlCatalog.Pages void main() { float y = (VecPos.y - uMinY) / (uMaxY - uMinY); - vec3 objectColor = vec3((1 - y), 0.40 + y / 4, y * 0.75+0.25); + vec3 objectColor = vec3((1.0 - y), 0.40 + y / 4.0, y * 0.75+0.25); //vec3 objectColor = normalize(FragPos); float ambientStrength = 0.3; vec3 lightColor = vec3(1.0, 1.0, 1.0); - vec3 lightPos = vec3(uMaxY * 2, uMaxY * 2, uMaxY * 2); + vec3 lightPos = vec3(uMaxY * 2.0, uMaxY * 2.0, uMaxY * 2.0); vec3 ambient = ambientStrength * lightColor; @@ -187,6 +187,7 @@ namespace ControlCatalog.Pages protected unsafe override void OnOpenGlInit(GlInterface GL, int fb) { + CheckError(GL); // Load the source of the vertex shader and compile it. _vertexShader = GL.CreateShader(GL_VERTEX_SHADER); Console.WriteLine(GL.CompileShaderAndGetError(_vertexShader, VertexShaderSource)); From adf943cd797a4d110014f25f92bacc5b9dcf7e7d Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Mon, 23 Dec 2019 22:56:31 +0300 Subject: [PATCH 014/663] I don't know anymore what I'm doing and why --- samples/ControlCatalog/Pages/OpenGlPage.xaml | 8 +++ .../ControlCatalog/Pages/OpenGlPage.xaml.cs | 51 +++++++++++++++++-- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/samples/ControlCatalog/Pages/OpenGlPage.xaml b/samples/ControlCatalog/Pages/OpenGlPage.xaml index 30f8f76240..afd8001263 100644 --- a/samples/ControlCatalog/Pages/OpenGlPage.xaml +++ b/samples/ControlCatalog/Pages/OpenGlPage.xaml @@ -12,6 +12,14 @@ Roll + + D + I + S + C + O + + diff --git a/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs b/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs index 767ee1c589..6931e07dfd 100644 --- a/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs +++ b/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.IO; using System.Linq; using System.Numerics; @@ -6,6 +7,7 @@ using System.Runtime.InteropServices; using Avalonia; using Avalonia.Controls; using Avalonia.OpenGL; +using Avalonia.Threading; using static Avalonia.OpenGL.GlConsts; // ReSharper disable StringLiteralTypo @@ -52,9 +54,22 @@ namespace ControlCatalog.Pages set => SetAndRaise(RollProperty, ref _roll, value); } + + private float _disco; + + public static readonly DirectProperty DiscoProperty = + AvaloniaProperty.RegisterDirect("Disco", o => o.Disco, (o, v) => o.Disco = v); + + public float Disco + { + get => _disco; + set => SetAndRaise(DiscoProperty, ref _disco, value); + } + + static OpenGlPageControl() { - AffectsRender(YawProperty, PitchProperty, RollProperty); + AffectsRender(YawProperty, PitchProperty, RollProperty, DiscoProperty); } private int _vertexShader; @@ -80,9 +95,20 @@ namespace ControlCatalog.Pages varying vec3 FragPos; varying vec3 VecPos; varying vec3 Normal; + uniform float uTime; + uniform float uDisco; void main() { - gl_Position = uProjection * uView * uModel * vec4(aPos, 1.0); + float discoScale = sin(uTime*10)/10; + float distortionX = 1 + uDisco * cos(uTime*20)/10; + + float scale = 1 + uDisco * discoScale; + + vec3 scaledPos = aPos; + scaledPos.x = scaledPos.x * distortionX; + + scaledPos *= scale; + gl_Position = uProjection * uView * uModel * vec4(scaledPos, 1.0); FragPos = vec3(uModel * vec4(aPos, 1.0)); VecPos = aPos; Normal = normalize(vec3(uModel * vec4(aNormal, 1.0))); @@ -95,12 +121,22 @@ namespace ControlCatalog.Pages varying vec3 Normal; uniform float uMaxY; uniform float uMinY; + uniform float uTime; + uniform float uDisco; + void main() { float y = (VecPos.y - uMinY) / (uMaxY - uMinY); - vec3 objectColor = vec3((1.0 - y), 0.40 + y / 4.0, y * 0.75+0.25); - //vec3 objectColor = normalize(FragPos); + float c = cos(atan(VecPos.x, VecPos.z)*20.0+uTime*40.0+y*50); + float s = sin(-atan(VecPos.z, VecPos.x)*20.0-uTime*20.0-y*30); + vec3 discoColor = vec3( + 0.5 + abs(0.5-y)*cos(uTime*10), + 0.25 + (smoothstep(0.3, 0.8, y)*(0.5-c/4)), + 0.25 + abs((smoothstep(0.1, 0.4, y)*(0.5-s/4)))); + + vec3 objectColor = vec3((1.0 - y), 0.40 + y / 4.0, y * 0.75+0.25); + objectColor = objectColor * (1-uDisco) + discoColor * uDisco; float ambientStrength = 0.3; vec3 lightColor = vec3(1.0, 1.0, 1.0); @@ -251,6 +287,7 @@ namespace ControlCatalog.Pages GL.DeleteShader(_vertexShader); } + static Stopwatch St = Stopwatch.StartNew(); protected override unsafe void OnOpenGlRender(GlInterface gl, int fb) { gl.ClearColor(0, 0, 0, 0); @@ -275,14 +312,20 @@ namespace ControlCatalog.Pages var projectionLoc = GL.GetUniformLocationString(_shaderProgram, "uProjection"); var maxYLoc = GL.GetUniformLocationString(_shaderProgram, "uMaxY"); var minYLoc = GL.GetUniformLocationString(_shaderProgram, "uMinY"); + var timeLoc = GL.GetUniformLocationString(_shaderProgram, "uTime"); + var discoLoc = GL.GetUniformLocationString(_shaderProgram, "uDisco"); GL.UniformMatrix4fv(modelLoc, 1, false, &model); GL.UniformMatrix4fv(viewLoc, 1, false, &view); GL.UniformMatrix4fv(projectionLoc, 1, false, &projection); GL.Uniform1f(maxYLoc, _maxY); GL.Uniform1f(minYLoc, _minY); + GL.Uniform1f(timeLoc, (float)St.Elapsed.TotalSeconds); + GL.Uniform1f(discoLoc, _disco); GL.DrawElements(GL_TRIANGLES, _indices.Length, GL_UNSIGNED_SHORT, IntPtr.Zero); CheckError(GL); + if (_disco > 0.01) + Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background); } } } From 85f205a94f33384348df13bcc517b5e64b45dc36 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Sun, 12 Jan 2020 20:32:17 +0100 Subject: [PATCH 015/663] Failing unit tests for SelectingItemsControl events --- .../SelectingItemsControlTests_Multiple.cs | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs index 952a00a14e..492e1744f8 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs @@ -683,6 +683,57 @@ namespace Avalonia.Controls.UnitTests.Primitives Assert.Equal(new[] { 0, 1, 2 }, SelectedContainers(target)); } + [Fact] + public void Ctrl_Selecting_Raises_SelectionChanged_Events() + { + var target = new ListBox + { + Template = Template(), + Items = new[] { "Foo", "Bar", "Baz", "Qux" }, + SelectionMode = SelectionMode.Multiple, + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + + SelectionChangedEventArgs receivedArgs = null; + + target.SelectionChanged += (_, args) => receivedArgs = null; + + void VerifyAdded(string selection) + { + Assert.NotNull(receivedArgs); + Assert.Equal(new[] { selection }, receivedArgs.AddedItems); + Assert.Empty(receivedArgs.RemovedItems); + } + + void VerifyRemoved(string selection) + { + Assert.NotNull(receivedArgs); + Assert.Equal(new[] { selection }, receivedArgs.RemovedItems); + Assert.Empty(receivedArgs.AddedItems); + } + + _helper.Click((Interactive)target.Presenter.Panel.Children[1]); + + VerifyAdded("Bar"); + + receivedArgs = null; + _helper.Click((Interactive)target.Presenter.Panel.Children[2], modifiers: InputModifiers.Control); + + VerifyAdded("Baz"); + + receivedArgs = null; + _helper.Click((Interactive)target.Presenter.Panel.Children[3], modifiers: InputModifiers.Control); + + VerifyAdded("Qux"); + + receivedArgs = null; + _helper.Click((Interactive)target.Presenter.Panel.Children[1], modifiers: InputModifiers.Control); + + VerifyRemoved("Bar"); + } + [Fact] public void Ctrl_Selecting_SelectedItem_With_Multiple_Selection_Active_Sets_SelectedItem_To_Next_Selection() { @@ -797,6 +848,52 @@ namespace Avalonia.Controls.UnitTests.Primitives Assert.Equal(new[] { 0, 1, 2, 3, 4, 5 }, SelectedContainers(target)); } + [Fact] + public void Shift_Selecting_Raises_SelectionChanged_Events() + { + var target = new ListBox + { + Template = Template(), + Items = new[] { "Foo", "Bar", "Baz", "Qux" }, + SelectionMode = SelectionMode.Multiple, + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + + SelectionChangedEventArgs receivedArgs = null; + + target.SelectionChanged += (_, args) => receivedArgs = null; + + void VerifyAdded(params string[] selection) + { + Assert.NotNull(receivedArgs); + Assert.Equal(selection, receivedArgs.AddedItems); + Assert.Empty(receivedArgs.RemovedItems); + } + + void VerifyRemoved(string selection) + { + Assert.NotNull(receivedArgs); + Assert.Equal(new[] { selection }, receivedArgs.RemovedItems); + Assert.Empty(receivedArgs.AddedItems); + } + + _helper.Click((Interactive)target.Presenter.Panel.Children[1]); + + VerifyAdded("Bar"); + + receivedArgs = null; + _helper.Click((Interactive)target.Presenter.Panel.Children[3], modifiers: InputModifiers.Shift); + + VerifyAdded("Baz" ,"Qux"); + + receivedArgs = null; + _helper.Click((Interactive)target.Presenter.Panel.Children[2], modifiers: InputModifiers.Shift); + + VerifyRemoved("Qux"); + } + [Fact] public void Duplicate_Items_Are_Added_To_SelectedItems_In_Order() { @@ -845,6 +942,30 @@ namespace Avalonia.Controls.UnitTests.Primitives Assert.Equal("Foo", target.SelectedItem); } + [Fact] + public void SelectAll_Raises_SelectionChanged_Event() + { + var target = new TestSelector + { + Template = Template(), + Items = new[] { "Foo", "Bar", "Baz" }, + SelectionMode = SelectionMode.Multiple, + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + + SelectionChangedEventArgs receivedArgs = null; + + target.SelectionChanged += (_, e) => receivedArgs = e; + + target.SelectAll(); + + Assert.NotNull(receivedArgs); + Assert.Equal(target.Items, receivedArgs.AddedItems); + Assert.Empty(receivedArgs.RemovedItems); + } + [Fact] public void UnselectAll_Clears_SelectedIndex_And_SelectedItem() { From c6fceb84546aa84270c01192dbee05e9fd777975 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Sun, 12 Jan 2020 20:55:08 +0100 Subject: [PATCH 016/663] Verify that removing items raises events as well. --- .../Primitives/SelectingItemsControlTests.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs index d819581000..2943c2cb32 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs @@ -522,10 +522,19 @@ namespace Avalonia.Controls.UnitTests.Primitives Assert.Equal(items[1], target.SelectedItem); Assert.Equal(1, target.SelectedIndex); + SelectionChangedEventArgs receivedArgs = null; + + target.SelectionChanged += (_, args) => receivedArgs = null; + + var removed = items[1]; + items.RemoveAt(1); Assert.Null(target.SelectedItem); Assert.Equal(-1, target.SelectedIndex); + Assert.NotNull(receivedArgs); + Assert.Empty(receivedArgs.AddedItems); + Assert.Equal(new[] { removed }, receivedArgs.RemovedItems); } [Fact] From 41ab586d4617c443c06bd7ab73ba9870db62fb71 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Fri, 17 Jan 2020 20:52:45 +0100 Subject: [PATCH 017/663] Fix unit tests. --- .../Primitives/SelectingItemsControlTests.cs | 2 +- .../Primitives/SelectingItemsControlTests_Multiple.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs index 2943c2cb32..e5bbebdec2 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs @@ -524,7 +524,7 @@ namespace Avalonia.Controls.UnitTests.Primitives SelectionChangedEventArgs receivedArgs = null; - target.SelectionChanged += (_, args) => receivedArgs = null; + target.SelectionChanged += (_, args) => receivedArgs = args; var removed = items[1]; diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs index 492e1744f8..3a8c98983f 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs @@ -698,7 +698,7 @@ namespace Avalonia.Controls.UnitTests.Primitives SelectionChangedEventArgs receivedArgs = null; - target.SelectionChanged += (_, args) => receivedArgs = null; + target.SelectionChanged += (_, args) => receivedArgs = args; void VerifyAdded(string selection) { @@ -863,7 +863,7 @@ namespace Avalonia.Controls.UnitTests.Primitives SelectionChangedEventArgs receivedArgs = null; - target.SelectionChanged += (_, args) => receivedArgs = null; + target.SelectionChanged += (_, args) => receivedArgs = args; void VerifyAdded(params string[] selection) { @@ -957,7 +957,7 @@ namespace Avalonia.Controls.UnitTests.Primitives SelectionChangedEventArgs receivedArgs = null; - target.SelectionChanged += (_, e) => receivedArgs = e; + target.SelectionChanged += (_, args) => receivedArgs = args; target.SelectAll(); From 640a5c5d8bbf25407a890cb8472b84ea215df006 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 14 Jan 2020 12:53:33 +0100 Subject: [PATCH 018/663] Swap order of parameters to event args. As described in #3437 the argument ordering was different to WPF/UWP causing bugs in some code ported from WPF/UWP. Use the same argument ordering as WPF/UWP. Fixes #3437 --- src/Avalonia.Controls/AutoCompleteBox.cs | 2 +- src/Avalonia.Controls/Calendar/DatePicker.cs | 2 +- src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs | 4 ++-- src/Avalonia.Controls/Primitives/SelectingItemsControl.cs | 8 ++++---- .../Repeater/ItemsRepeaterElementIndexChangedEventArgs.cs | 4 ++-- src/Avalonia.Controls/SelectionChangedEventArgs.cs | 6 +++--- src/Avalonia.Controls/TreeView.cs | 4 ++-- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Avalonia.Controls/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox.cs index 6deddef0d0..bf177d64cd 100644 --- a/src/Avalonia.Controls/AutoCompleteBox.cs +++ b/src/Avalonia.Controls/AutoCompleteBox.cs @@ -683,7 +683,7 @@ namespace Avalonia.Controls added.Add(e.NewValue); } - OnSelectionChanged(new SelectionChangedEventArgs(SelectionChangedEvent, added, removed)); + OnSelectionChanged(new SelectionChangedEventArgs(SelectionChangedEvent, removed, added)); } /// diff --git a/src/Avalonia.Controls/Calendar/DatePicker.cs b/src/Avalonia.Controls/Calendar/DatePicker.cs index b4d4fed9fc..445182a6c1 100644 --- a/src/Avalonia.Controls/Calendar/DatePicker.cs +++ b/src/Avalonia.Controls/Calendar/DatePicker.cs @@ -788,7 +788,7 @@ namespace Avalonia.Controls removedItems.Add(removedDate.Value); } - handler(this, new SelectionChangedEventArgs(SelectingItemsControl.SelectionChangedEvent, addedItems, removedItems)); + handler(this, new SelectionChangedEventArgs(SelectingItemsControl.SelectionChangedEvent, removedItems, addedItems)); } } private void OnCalendarClosed(EventArgs e) diff --git a/src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs b/src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs index e6e0cf7c4f..3573c67057 100644 --- a/src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs +++ b/src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs @@ -49,7 +49,7 @@ namespace Avalonia.Controls.Primitives private void InvokeCollectionChanged(System.Collections.IList removedItems, System.Collections.IList addedItems) { - _owner.OnSelectedDatesCollectionChanged(new SelectionChangedEventArgs(null, addedItems, removedItems)); + _owner.OnSelectedDatesCollectionChanged(new SelectionChangedEventArgs(null, removedItems, addedItems)); } /// @@ -119,7 +119,7 @@ namespace Avalonia.Controls.Primitives } } - _owner.OnSelectedDatesCollectionChanged(new SelectionChangedEventArgs(null, _addedItems, _owner.RemovedItems)); + _owner.OnSelectedDatesCollectionChanged(new SelectionChangedEventArgs(null, _owner.RemovedItems, _addedItems)); _owner.RemovedItems.Clear(); _owner.UpdateMonths(); _isRangeAdded = false; diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index 69da211aa4..6bc4e71508 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -939,8 +939,8 @@ namespace Avalonia.Controls.Primitives { var changed = new SelectionChangedEventArgs( SelectionChangedEvent, - added ?? Empty, - removed ?? Empty); + removed ?? Empty, + added ?? Empty); RaiseEvent(changed); } } @@ -1055,8 +1055,8 @@ namespace Avalonia.Controls.Primitives var e = new SelectionChangedEventArgs( SelectionChangedEvent, - added != -1 ? new[] { ElementAt(Items, added) } : Array.Empty(), - removed?.Select(x => ElementAt(Items, x)).ToArray() ?? Array.Empty()); + removed?.Select(x => ElementAt(Items, x)).ToArray() ?? Array.Empty(), + added != -1 ? new[] { ElementAt(Items, added) } : Array.Empty()); RaiseEvent(e); } diff --git a/src/Avalonia.Controls/Repeater/ItemsRepeaterElementIndexChangedEventArgs.cs b/src/Avalonia.Controls/Repeater/ItemsRepeaterElementIndexChangedEventArgs.cs index 7ca68140b2..bf1b80f947 100644 --- a/src/Avalonia.Controls/Repeater/ItemsRepeaterElementIndexChangedEventArgs.cs +++ b/src/Avalonia.Controls/Repeater/ItemsRepeaterElementIndexChangedEventArgs.cs @@ -12,11 +12,11 @@ namespace Avalonia.Controls /// public class ItemsRepeaterElementIndexChangedEventArgs : EventArgs { - internal ItemsRepeaterElementIndexChangedEventArgs(IControl element, int newIndex, int oldIndex) + internal ItemsRepeaterElementIndexChangedEventArgs(IControl element, int oldIndex, int newIndex) { Element = element; - NewIndex = newIndex; OldIndex = oldIndex; + NewIndex = newIndex; } /// diff --git a/src/Avalonia.Controls/SelectionChangedEventArgs.cs b/src/Avalonia.Controls/SelectionChangedEventArgs.cs index 267ddf036e..fb412a64a8 100644 --- a/src/Avalonia.Controls/SelectionChangedEventArgs.cs +++ b/src/Avalonia.Controls/SelectionChangedEventArgs.cs @@ -16,13 +16,13 @@ namespace Avalonia.Controls /// Initializes a new instance of the class. /// /// The event being raised. - /// The items added to the selection. /// The items removed from the selection. - public SelectionChangedEventArgs(RoutedEvent routedEvent, IList addedItems, IList removedItems) + /// The items added to the selection. + public SelectionChangedEventArgs(RoutedEvent routedEvent, IList removedItems, IList addedItems) : base(routedEvent) { - AddedItems = addedItems; RemovedItems = removedItems; + AddedItems = addedItems; } /// diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs index 738d9d0b51..3a7ad97763 100644 --- a/src/Avalonia.Controls/TreeView.cs +++ b/src/Avalonia.Controls/TreeView.cs @@ -324,8 +324,8 @@ namespace Avalonia.Controls { var changed = new SelectionChangedEventArgs( SelectingItemsControl.SelectionChangedEvent, - added ?? Empty, - removed ?? Empty); + removed ?? Empty, + added ?? Empty); RaiseEvent(changed); } } From 6bd7b4f3359b43c2d2e329942643ae98ab245dcd Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 25 Jan 2020 12:09:35 +0100 Subject: [PATCH 019/663] Ported SelectionModel and friends from WinUI. --- src/Avalonia.Controls/IndexPath.cs | 98 ++ src/Avalonia.Controls/IndexRange.cs | 61 + src/Avalonia.Controls/SelectedItems.cs | 55 + src/Avalonia.Controls/SelectionModel.cs | 710 +++++++++ ...electionModelChildrenRequestedEventArgs.cs | 31 + ...SelectionModelSelectionChangedEventArgs.cs | 15 + src/Avalonia.Controls/SelectionNode.cs | 811 ++++++++++ .../Utils/SelectionTreeHelper.cs | 183 +++ .../SelectionModelTests.cs | 1314 +++++++++++++++++ 9 files changed, 3278 insertions(+) create mode 100644 src/Avalonia.Controls/IndexPath.cs create mode 100644 src/Avalonia.Controls/IndexRange.cs create mode 100644 src/Avalonia.Controls/SelectedItems.cs create mode 100644 src/Avalonia.Controls/SelectionModel.cs create mode 100644 src/Avalonia.Controls/SelectionModelChildrenRequestedEventArgs.cs create mode 100644 src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs create mode 100644 src/Avalonia.Controls/SelectionNode.cs create mode 100644 src/Avalonia.Controls/Utils/SelectionTreeHelper.cs create mode 100644 tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs diff --git a/src/Avalonia.Controls/IndexPath.cs b/src/Avalonia.Controls/IndexPath.cs new file mode 100644 index 0000000000..1251a022da --- /dev/null +++ b/src/Avalonia.Controls/IndexPath.cs @@ -0,0 +1,98 @@ +// 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.Text; + +namespace Avalonia.Controls +{ + public sealed class IndexPath : IComparable + { + private readonly List _path = new List(); + + internal IndexPath(int index) + { + _path.Add(index); + } + + internal IndexPath(int groupIndex, int itemIndex) + { + _path.Add(groupIndex); + _path.Add(itemIndex); + } + + internal IndexPath(IEnumerable indices) + { + if (indices != null) + { + _path.AddRange(indices); + } + } + + public int GetSize() => _path.Count; + public int GetAt(int index) => _path[index]; + + public int CompareTo(IndexPath other) + { + var rhsPath = other; + int compareResult = 0; + int lhsCount = _path.Count; + int rhsCount = rhsPath._path.Count; + + 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 (_path[i] < rhsPath._path[i]) + { + compareResult = -1; + break; + } + else if (_path[i] > rhsPath._path[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) + { + var newPath = new List(_path); + newPath.Add(childIndex); + return new IndexPath(newPath); + } + + public override string ToString() + { + return "R." + string.Join(".", _path); + } + + 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 indices) => new IndexPath(indices); + + } +} diff --git a/src/Avalonia.Controls/IndexRange.cs b/src/Avalonia.Controls/IndexRange.cs new file mode 100644 index 0000000000..f3820c087a --- /dev/null +++ b/src/Avalonia.Controls/IndexRange.cs @@ -0,0 +1,61 @@ +// 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.Text; + +namespace Avalonia.Controls +{ + internal readonly struct IndexRange + { + public IndexRange(int begin, int end) + { + // Accept out of order begin/end pairs, just swap them. + if (begin > end) + { + int temp = begin; + begin = end; + end = temp; + } + + Begin = begin; + End = end; + } + + public int Begin { get; } + public int End { get; } + + public bool Contains(int index) + { + return index >= Begin && index <= End; + } + + public bool Split(int splitIndex, out IndexRange before, out IndexRange after) + { + bool afterIsValid; + + before = new IndexRange(Begin, splitIndex); + + if (splitIndex < End) + { + after = new IndexRange(splitIndex + 1, End); + afterIsValid = true; + } + else + { + after = new IndexRange(); + afterIsValid = false; + } + + return afterIsValid; + } + + public bool Intersects(IndexRange other) + { + return (Begin <= other.End) && (End >= other.Begin); + } + } +} diff --git a/src/Avalonia.Controls/SelectedItems.cs b/src/Avalonia.Controls/SelectedItems.cs new file mode 100644 index 0000000000..036788e7b2 --- /dev/null +++ b/src/Avalonia.Controls/SelectedItems.cs @@ -0,0 +1,55 @@ +// 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 SelectedItemInfo = Avalonia.Controls.SelectionModel.SelectedItemInfo; + +namespace Avalonia.Controls +{ + internal class SelectedItems : IReadOnlyList + { + private readonly List _infos; + private readonly Func, int, T> _getAtImpl; + private int _totalCount; + + public SelectedItems( + List infos, + Func, int, T> getAtImpl) + { + _infos = infos; + _getAtImpl = getAtImpl; + + foreach (var info in infos) + { + var node = info.Node; + + if (node != null) + { + _totalCount += node.SelectedCount; + } + else + { + throw new InvalidOperationException("Selection changed after the SelectedIndices/Items property was read."); + } + } + } + + public T this[int index] => _getAtImpl(_infos, index); + + public int Count => _totalCount; + + public IEnumerator GetEnumerator() + { + for (var i = 0; i < _totalCount; ++i) + { + yield return this[i]; + } + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } +} diff --git a/src/Avalonia.Controls/SelectionModel.cs b/src/Avalonia.Controls/SelectionModel.cs new file mode 100644 index 0000000000..6b48e02a21 --- /dev/null +++ b/src/Avalonia.Controls/SelectionModel.cs @@ -0,0 +1,710 @@ +// 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 Avalonia.Controls.Utils; + +namespace Avalonia.Controls +{ + public class SelectionModel : INotifyPropertyChanged, IDisposable + { + private SelectionNode _rootNode; + private bool _singleSelect; + private IReadOnlyList _selectedIndicesCached; + private IReadOnlyList _selectedItemsCached; + private SelectionModelChildrenRequestedEventArgs _childrenRequestedEventArgs; + private SelectionModelSelectionChangedEventArgs _selectionChangedEventArgs; + + public event EventHandler ChildrenRequested; + public event PropertyChangedEventHandler PropertyChanged; + public event EventHandler SelectionChanged; + + public SelectionModel() + { + _rootNode = new SelectionNode(this, null); + SharedLeafNode = new SelectionNode(this, null); + } + + public object Source + { + get => _rootNode.Source; + set + { + ClearSelection(resetAnchor: true, raiseSelectionChanged: false); + _rootNode.Source = value; + OnSelectionChanged(); + RaisePropertyChanged("Source"); + } + } + + public bool SingleSelect + { + get => _singleSelect; + set + { + if (_singleSelect != value) + { + _singleSelect = value; + var selectedIndices = SelectedIndices; + + if (value && selectedIndices != null && selectedIndices.Count > 0) + { + // We want to be single select, so make sure there is only + // one selected item. + var firstSelectionIndexPath = selectedIndices[0]; + ClearSelection(resetAnchor: true, raiseSelectionChanged: false); + SelectWithPathImpl(firstSelectionIndexPath, select: true, raiseSelectionChanged: false); + // Setting SelectedIndex will raise SelectionChanged event. + SelectedIndex = firstSelectionIndexPath; + } + + RaisePropertyChanged("SingleSelect"); + } + } + } + + + public IndexPath AnchorIndex + { + get + { + IndexPath anchor = null; + + if (_rootNode.AnchorIndex >= 0) + { + var path = new List(); + var current = _rootNode; + + while (current?.AnchorIndex >= 0) + { + path.Add(current.AnchorIndex); + current = current.GetAt(current.AnchorIndex, false); + } + + anchor = new IndexPath(path); + } + + return anchor; + } + set + { + if (value != null) + { + SelectionTreeHelper.TraverseIndexPath( + _rootNode, + value, + realizeChildren: true, + (currentNode, path, depth, childIndex) => currentNode.AnchorIndex = path.GetAt(depth)); + } + else + { + _rootNode.AnchorIndex = -1; + } + + RaisePropertyChanged("AnchorIndex"); + } + } + + public IndexPath SelectedIndex + { + get + { + IndexPath selectedIndex = null; + var selectedIndices = SelectedIndices; + + if (selectedIndices?.Count > 0) + { + selectedIndex = selectedIndices[0]; + } + + return selectedIndex; + } + set + { + var isSelected = IsSelectedAt(value); + + if (!isSelected.HasValue || !isSelected.Value) + { + ClearSelection(resetAnchor: true, raiseSelectionChanged: false); + SelectWithPathImpl(value, select: true, raiseSelectionChanged: false); + OnSelectionChanged(); + } + } + } + + public object SelectedItem + { + get + { + object item = null; + var selectedItems = SelectedItems; + + if (selectedItems?.Count > 0) + { + item = selectedItems[0]; + } + + return item; + } + } + + public IReadOnlyList SelectedItems + { + get + { + if (_selectedItemsCached == null) + { + var selectedInfos = new List(); + + if (_rootNode.Source != null) + { + SelectionTreeHelper.Traverse( + _rootNode, + realizeChildren: false, + currentInfo => + { + if (currentInfo.Node.SelectedCount > 0) + { + selectedInfos.Add(new SelectedItemInfo(currentInfo.Node, currentInfo.Path)); + } + }); + } + + // Instead of creating a dumb vector that takes up the space for all the selected items, + // we create a custom VectorView implimentation 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 ( + selectedInfos, + (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 SelectedIndices + { + get + { + if (_selectedIndicesCached == null) + { + var selectedInfos = new List(); + SelectionTreeHelper.Traverse( + _rootNode, + false, + currentInfo => + { + if (currentInfo.Node.SelectedCount > 0) + { + selectedInfos.Add(new SelectedItemInfo(currentInfo.Node, currentInfo.Path)); + } + }); + + // 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( + selectedInfos, + (infos, index) => // callback for GetAt(index) + { + var currentIndex = 0; + IndexPath path = null; + + 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, raiseSelectionChanged: false); + _rootNode?.Dispose(); + _rootNode = null; + SharedLeafNode = null; + _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) => SelectImpl(index, select: true); + + public void Select(int groupIndex, int itemIndex) => SelectWithGroupImpl(groupIndex, itemIndex, select: true); + + public void SelectAt(IndexPath index) => SelectWithPathImpl(index, select: true, raiseSelectionChanged: true); + + public void Deselect(int index) => SelectImpl(index, select: false); + + public void Deselect(int groupIndex, int itemIndex) => SelectWithGroupImpl(groupIndex, itemIndex, select: false); + + public void DeselectAt(IndexPath index) => SelectWithPathImpl(index, select: false, raiseSelectionChanged: true); + + public bool? IsSelected(int index) + { + if (index < 0) + { + throw new ArgumentException("Index must be >= 0", nameof(index)); + } + + var isSelected = _rootNode.IsSelectedWithPartial(index); + return isSelected; + } + + public bool? IsSelected(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, realizeChild: false); + + if (childNode != null) + { + isSelected = childNode.IsSelectedWithPartial(itemIndex); + } + + return isSelected; + } + + public bool? IsSelectedAt(IndexPath index) + { + var path = index; + var isRealized = true; + var node = _rootNode; + + for (int i = 0; i < path.GetSize() - 1; i++) + { + var childIndex = path.GetAt(i); + node = node.GetAt(childIndex, realizeChild: false); + + 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) + { + SelectRangeFromAnchorImpl(index, select: true); + } + + public void SelectRangeFromAnchor(int endGroupIndex, int endItemIndex) + { + SelectRangeFromAnchorWithGroupImpl(endGroupIndex, endItemIndex, select: true); + } + + public void SelectRangeFromAnchorTo(IndexPath index) + { + SelectRangeImpl(AnchorIndex, index, select: true); + } + + public void DeselectRangeFromAnchor(int index) + { + SelectRangeFromAnchorImpl(index, select: false); + } + + public void DeselectRangeFromAnchor(int endGroupIndex, int endItemIndex) + { + SelectRangeFromAnchorWithGroupImpl(endGroupIndex, endItemIndex, false /* select */); + } + + public void DeselectRangeFromAnchorTo(IndexPath index) + { + SelectRangeImpl(AnchorIndex, index, select: false); + } + + public void SelectRange(IndexPath start, IndexPath end) + { + SelectRangeImpl(start, end, select: true); + } + + public void DeselectRange(IndexPath start, IndexPath end) + { + SelectRangeImpl(start, end, select: false); + } + + public void SelectAll() + { + SelectionTreeHelper.Traverse( + _rootNode, + realizeChildren: true, + info => + { + if (info.Node.DataCount > 0) + { + info.Node.SelectAll(); + } + }); + + OnSelectionChanged(); + } + + public void ClearSelection() + { + ClearSelection(resetAnchor: true, raiseSelectionChanged: true); + } + + protected void OnPropertyChanged(string propertyName) + { + RaisePropertyChanged(propertyName); + } + + private void RaisePropertyChanged(string propertyName) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + public void OnSelectionInvalidatedDueToCollectionChange() + { + OnSelectionChanged(); + } + + internal object ResolvePath(object data, SelectionNode sourceNode) + { + object resolved = null; + + // Raise ChildrenRequested event if there is a handler + if (ChildrenRequested != null) + { + if (_childrenRequestedEventArgs == null) + { + _childrenRequestedEventArgs = new SelectionModelChildrenRequestedEventArgs(data, sourceNode); + } + else + { + _childrenRequestedEventArgs.Initialize(data, sourceNode); + } + + 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, null); + } + else + { + // No handlers for ChildrenRequested event. If data is of type ItemsSourceView + // or a type that can be used to create a ItemsSourceView, then we can auto-resolve + // that as the child. If not, then we consider the value as a leaf. This is to + // avoid having to provide the event handler for the most common scenarios. If the + // app dev does not want this default behavior, they can provide the handler to + // override. + if (data is IEnumerable) + { + resolved = data; + } + } + + return resolved; + } + + private void ClearSelection(bool resetAnchor, bool raiseSelectionChanged) + { + SelectionTreeHelper.Traverse( + _rootNode, + realizeChildren: false, + info => info.Node.Clear()); + + if (resetAnchor) + { + AnchorIndex = null; + } + + if (raiseSelectionChanged) + { + OnSelectionChanged(); + } + } + + private void OnSelectionChanged() + { + _selectedIndicesCached = null; + _selectedItemsCached = null; + + // Raise SelectionChanged event + if (SelectionChanged != null) + { + if (_selectionChangedEventArgs == null) + { + _selectionChangedEventArgs = new SelectionModelSelectionChangedEventArgs(); + } + + SelectionChanged(this, _selectionChangedEventArgs); + } + + 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, raiseSelectionChanged: false); + } + + 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, raiseSelectionChanged: false); + } + + var childNode = _rootNode.GetAt(groupIndex, realizeChild: true); + var selected = childNode.Select(itemIndex, select); + + if (selected) + { + AnchorIndex = new IndexPath(groupIndex, itemIndex); + } + + OnSelectionChanged(); + } + + private void SelectWithPathImpl(IndexPath index, bool select, bool raiseSelectionChanged) + { + bool selected = false; + + if (_singleSelect) + { + ClearSelection(resetAnchor: true, raiseSelectionChanged: false); + } + + SelectionTreeHelper.TraverseIndexPath( + _rootNode, + index, + true, + (currentNode, path, depth, childIndex) => + { + if (depth == path.GetSize() - 1) + { + selected = currentNode.Select(childIndex, select); + } + } + ); + + if (selected) + { + AnchorIndex = index; + } + + if (raiseSelectionChanged) + { + OnSelectionChanged(); + } + } + + private void SelectRangeFromAnchorImpl(int index, bool select) + { + int anchorIndex = 0; + var anchor = AnchorIndex; + + if (anchor != null) + { + anchorIndex = anchor.GetAt(0); + } + + bool selected = _rootNode.SelectRange(new IndexRange(anchorIndex, index), select); + + if (selected) + { + 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; + } + + var selected = false; + for (int groupIdx = startGroupIndex; groupIdx <= endGroupIndex; groupIdx++) + { + var groupNode = _rootNode.GetAt(groupIdx, realizeChild: true); + int startIndex = groupIdx == startGroupIndex ? startItemIndex : 0; + int endIndex = groupIdx == endGroupIndex ? endItemIndex : groupNode.DataCount - 1; + selected |= groupNode.SelectRange(new IndexRange(startIndex, endIndex), select); + } + + if (selected) + { + 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.Node.DataCount == 0) + { + // Select only leaf nodes + info.ParentNode.Select(info.Path.GetAt(info.Path.GetSize() - 1), select); + } + }); + + OnSelectionChanged(); + } + + internal class SelectedItemInfo + { + public SelectedItemInfo(SelectionNode node, IndexPath path) + { + Node = node; + Path = path; + } + + public SelectionNode Node { get; } + public IndexPath Path { get; } + } + } +} diff --git a/src/Avalonia.Controls/SelectionModelChildrenRequestedEventArgs.cs b/src/Avalonia.Controls/SelectionModelChildrenRequestedEventArgs.cs new file mode 100644 index 0000000000..c5571b9f74 --- /dev/null +++ b/src/Avalonia.Controls/SelectionModelChildrenRequestedEventArgs.cs @@ -0,0 +1,31 @@ +// 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.Text; + +namespace Avalonia.Controls +{ + public class SelectionModelChildrenRequestedEventArgs : EventArgs + { + private SelectionNode _sourceNode; + + internal SelectionModelChildrenRequestedEventArgs(object source, SelectionNode sourceNode) + { + Initialize(source, sourceNode); + } + + public object Children { get; set; } + public object Source { get; private set; } + public IndexPath SourceIndex => _sourceNode.IndexPath; + + internal void Initialize(object source, SelectionNode sourceNode) + { + Source = source; + _sourceNode = sourceNode; + } + } +} diff --git a/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs b/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs new file mode 100644 index 0000000000..8c9e0343de --- /dev/null +++ b/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs @@ -0,0 +1,15 @@ +// 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.Text; + +namespace Avalonia.Controls +{ + public class SelectionModelSelectionChangedEventArgs : EventArgs + { + } +} diff --git a/src/Avalonia.Controls/SelectionNode.cs b/src/Avalonia.Controls/SelectionNode.cs new file mode 100644 index 0000000000..f5d93681c4 --- /dev/null +++ b/src/Avalonia.Controls/SelectionNode.cs @@ -0,0 +1,811 @@ +// 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; + +namespace Avalonia.Controls +{ + /// + /// Tracks nested selection. + /// + /// + /// 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. + /// + internal class SelectionNode : IDisposable + { + private readonly SelectionModel _manager; + private readonly List _childrenNodes = new List(); + private readonly SelectionNode _parent; + private readonly List _selected = new List(); + private object _source; + private ItemsSourceView _dataSource; + private int _selectedCount; + private List _selectedIndicesCached = new List(); + private bool _selectedIndicesCacheIsValid; + private int _realizedChildrenNodeCount; + + public SelectionNode(SelectionModel manager, SelectionNode parent) + { + _manager = manager; + _parent = parent; + } + + public int AnchorIndex { get; set; } = -1; + + public object Source + { + get => _source; + set + { + if (_source != value) + { + ClearSelection(); + UnhookCollectionChangedHandler(); + + _source = value; + + // Setup ItemsSourceView + var newDataSource = value as ItemsSourceView; + + if (value != null && newDataSource == null) + { + newDataSource = new ItemsSourceView((IEnumerable)value); + } + + _dataSource = newDataSource; + + HookupCollectionChangedHandler(); + OnSelectionChanged(); + } + } + } + + public ItemsSourceView ItemsSourceView => _dataSource; + public int DataCount => _dataSource?.Count ?? 0; + public int ChildrenNodeCount => _childrenNodes.Count; + public int RealizedChildrenNodeCount => _realizedChildrenNodeCount; + + public IndexPath IndexPath + { + get + { + var path = new List(); ; + 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) + { + SelectionNode child = null; + + if (realizeChild) + { + if (_childrenNodes.Count == 0) + { + if (_dataSource != null) + { + for (int i = 0; i < _dataSource.Count; i++) + { + _childrenNodes.Add(null); + } + } + } + + if (_childrenNodes[index] == null) + { + var childData = _dataSource.GetAt(index); + + if (childData != null) + { + var resolvedChild = _manager.ResolvePath(childData, this); + + if (resolvedChild != null) + { + child = new SelectionNode(_manager, parent: this); + child.Source = resolvedChild; + } + else + { + child = _manager.SharedLeafNode; + } + } + else + { + child = _manager.SharedLeafNode; + } + + _childrenNodes[index] = child; + _realizedChildrenNodeCount++; + } + else + { + child = _childrenNodes[index]; + } + } + else + { + if (_childrenNodes.Count > 0) + { + child = _childrenNodes[index]; + } + } + + return child; + } + + public int SelectedCount => _selectedCount; + + 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) + { + var selectionState = SelectionState.NotSelected; + + 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 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 void Dispose() + { + _dataSource?.Dispose(); + UnhookCollectionChangedHandler(); + } + + 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 (_dataSource != null) + { + var size = _dataSource.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 (_dataSource != null) + { + _dataSource.CollectionChanged += OnSourceListChanged; + } + } + + private void UnhookCollectionChangedHandler() + { + if (_dataSource != null) + { + _dataSource.CollectionChanged -= OnSourceListChanged; + } + } + + private bool IsValidIndex(int index) + { + return ItemsSourceView == null || (index >= 0 && index < ItemsSourceView.Count); + } + + private void AddRange(IndexRange addRange, bool raiseOnSelectionChanged) + { + // TODO: Check for duplicates (Task 14107720) + // TODO: Optimize by merging adjacent ranges (Task 14107720) + var oldCount = SelectedCount; + + for (int i = addRange.Begin; i <= addRange.End; i++) + { + if (!IsSelected(i)) + { + _selectedCount++; + } + } + + if (oldCount != _selectedCount) + { + _selected.Add(addRange); + + if (raiseOnSelectionChanged) + { + OnSelectionChanged(); + } + } + } + + private void RemoveRange(IndexRange removeRange, bool raiseOnSelectionChanged) + { + int oldCount = _selectedCount; + + // TODO: Prevent overlap of Ranges in _selected (Task 14107720) + for (int i = removeRange.Begin; i <= removeRange.End; i++) + { + if (IsSelected(i)) + { + _selectedCount--; + } + } + + if (oldCount != _selectedCount) + { + // Build up a both a list of Ranges to remove and ranges to add + var toRemove = new List(); + var toAdd = new List(); + + foreach (var range in _selected) + { + // If this range intersects the remove range, we have to do something + if (removeRange.Intersects(range)) + { + // Intersection with the beginning of the range + // Anything to the left of the point (exclusive) stays + // Anything to the right of the point (inclusive) gets clipped + if (range.Contains(removeRange.Begin - 1)) + { + range.Split(removeRange.Begin - 1, out var before, out _); + toAdd.Add(before); + } + + // Intersection with the end of the range + // Anything to the left of the point (inclusive) gets clipped + // Anything to the right of the point (exclusive) stays + if (range.Contains(removeRange.End)) + { + if (range.Split(removeRange.End, out _, out var after)) + { + toAdd.Add(after); + } + } + + // Remove this Range from the collection + // New ranges will be added for any remaining subsections + toRemove.Add(range); + } + } + + bool change = ((toRemove.Count > 0) || (toAdd.Count > 0)); + + if (change) + { + // Remove tagged ranges + foreach (var remove in toRemove) + { + _selected.Remove(remove); + } + + // Add new ranges + _selected.AddRange(toAdd); + + if (raiseOnSelectionChanged) + { + OnSelectionChanged(); + } + } + } + } + + private void ClearSelection() + { + // Deselect all items + if (_selected.Count > 0) + { + _selected.Clear(); + OnSelectionChanged(); + } + + _selectedCount = 0; + AnchorIndex = -1; + + // This will throw away all the children SelectionNodes + // causing them to be unhooked from their data source. This + // essentially cleans up the tree. + foreach (var child in _childrenNodes) + { + child?.Dispose(); + } + + _childrenNodes.Clear(); + } + + 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; + + switch (args.Action) + { + case NotifyCollectionChangedAction.Add: + { + selectionInvalidated = OnItemsAdded(args.NewStartingIndex, args.NewItems.Count); + break; + } + + case NotifyCollectionChangedAction.Remove: + { + selectionInvalidated = OnItemsRemoved(args.OldStartingIndex, args.OldItems.Count); + break; + } + + case NotifyCollectionChangedAction.Reset: + { + ClearSelection(); + selectionInvalidated = true; + break; + } + + case NotifyCollectionChangedAction.Replace: + { + selectionInvalidated = OnItemsRemoved(args.OldStartingIndex, args.OldItems.Count); + selectionInvalidated |= OnItemsAdded(args.NewStartingIndex, args.NewItems.Count); + break; + } + } + + if (selectionInvalidated) + { + OnSelectionChanged(); + _manager.OnSelectionInvalidatedDueToCollectionChange(); + } + } + + private bool OnItemsAdded(int index, int count) + { + var selectionInvalidated = false; + + // Update ranges for leaf items + var toAdd = new List(); + + 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 = 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 OnItemsRemoved(int index, int count) + { + bool selectionInvalidated = false; + + // Remove the items from the selection for leaf + if (ItemsSourceView.Count > 0) + { + bool isSelected = false; + + for (int i = index; i <= index + count - 1; i++) + { + if (IsSelected(i)) + { + isSelected = true; + break; + } + } + + if (isSelected) + { + RemoveRange(new IndexRange(index, index + count - 1), raiseOnSelectionChanged: false); + selectionInvalidated = true; + } + + 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) + { + _realizedChildrenNodeCount--; + } + _childrenNodes.RemoveAt(index); + } + } + + //Adjust the anchor + if (AnchorIndex >= index) + { + AnchorIndex = AnchorIndex - count; + } + } + else + { + // No more items in the list, clear + ClearSelection(); + _realizedChildrenNodeCount = 0; + selectionInvalidated = true; + } + + // Check if removing a node invalidated an ancestors + // selection state. For example if parent was partially selected before + // removing an item, it could be selected now. + if (!selectionInvalidated) + { + var parent = _parent; + + while (parent != null) + { + var isSelected = parent.IsSelectedWithPartial(); + // If a parent is partially selected, then it will become selected. + // If it is selected or not selected - there is no change. + if (!isSelected.HasValue) + { + selectionInvalidated = true; + break; + } + + parent = parent._parent; + } + } + + return selectionInvalidated; + } + + 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() + { + SelectionState 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. + bool isSelected = false; + selectedCount = 0; + int notSelectedCount = 0; + for (int i = 0; i < ChildrenNodeCount; i++) + { + var child = GetAt(i, realizeChild: false); + + 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; + } + + public enum SelectionState + { + Selected, + NotSelected, + PartiallySelected + } + } +} diff --git a/src/Avalonia.Controls/Utils/SelectionTreeHelper.cs b/src/Avalonia.Controls/Utils/SelectionTreeHelper.cs new file mode 100644 index 0000000000..52e29da0e5 --- /dev/null +++ b/src/Avalonia.Controls/Utils/SelectionTreeHelper.cs @@ -0,0 +1,183 @@ +// 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; + +namespace Avalonia.Controls.Utils +{ + internal static class SelectionTreeHelper + { + public static void TraverseIndexPath( + SelectionNode root, + IndexPath path, + bool realizeChildren, + Action 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); + } + } + } + + public static void Traverse( + SelectionNode root, + bool realizeChildren, + Action nodeAction) + { + var pendingNodes = new List(); + 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--) + { + SelectionNode child = nextNode.Node.GetAt(i, realizeChildren); + 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 nodeAction) + { + var pendingNodes = new List(); + 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, realizeChild: true); + 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, realizeChild: true); + 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) + { + bool isSubset = true; + for (int i = 0; i < subset.GetSize(); i++) + { + isSubset = path.GetAt(i) == subset.GetAt(i); + if (!isSubset) + break; + } + + return isSubset; + } + + private static IndexPath StartPath(IndexPath path, int length) + { + var subPath = new List(); + 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)); + indexPath = indexPath ?? throw new ArgumentNullException(nameof(indexPath)); + + Node = node; + Path = indexPath; + ParentNode = parent; + } + + public TreeWalkNodeInfo(SelectionNode node, IndexPath indexPath) + { + node = node ?? throw new ArgumentNullException(nameof(node)); + indexPath = indexPath ?? throw new ArgumentNullException(nameof(indexPath)); + + Node = node; + Path = indexPath; + ParentNode = null; + } + + public SelectionNode Node { get; } + public IndexPath Path { get; } + public SelectionNode ParentNode { get; } + }; + + } +} diff --git a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs new file mode 100644 index 0000000000..6c3137c636 --- /dev/null +++ b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs @@ -0,0 +1,1314 @@ +// 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.ObjectModel; +using System.Linq; +using Avalonia.Collections; +using Avalonia.Diagnostics; +using Xunit; +using Xunit.Abstractions; + +namespace Avalonia.Controls.UnitTests +{ + public class SelectionModelTests + { + private LogWrapper Log { get; } + + public SelectionModelTests(ITestOutputHelper output) + { + Log = new LogWrapper(output); + } + + [Fact] + public void ValidateOneLevelSingleSelectionNoSource() + { + RunOnUIThread.Execute(() => + { + SelectionModel selectionModel = new SelectionModel() { SingleSelect = true }; + Log.Comment("No source set."); + Select(selectionModel, 4, true); + ValidateSelection(selectionModel, new List() { Path(4) }); + Select(selectionModel, 4, false); + ValidateSelection(selectionModel, new List() { }); + }); + } + + [Fact] + public void ValidateOneLevelSingleSelection() + { + RunOnUIThread.Execute(() => + { + SelectionModel selectionModel = new SelectionModel() { SingleSelect = true }; + Log.Comment("Set the source to 10 items"); + selectionModel.Source = Enumerable.Range(0, 10).ToList(); + Select(selectionModel, 3, true); + ValidateSelection(selectionModel, new List() { Path(3) }, new List() { Path() }); + Select(selectionModel, 3, false); + ValidateSelection(selectionModel, new List() { }); + }); + } + + [Fact] + public void ValidateSelectionChangedEvent() + { + RunOnUIThread.Execute(() => + { + SelectionModel selectionModel = new SelectionModel(); + selectionModel.Source = Enumerable.Range(0, 10).ToList(); + + int selectionChangedFiredCount = 0; + selectionModel.SelectionChanged += delegate (object sender, SelectionModelSelectionChangedEventArgs args) + { + selectionChangedFiredCount++; + ValidateSelection(selectionModel, new List() { Path(4) }, new List() { Path() }); + }; + + Select(selectionModel, 4, true); + ValidateSelection(selectionModel, new List() { Path(4) }, new List() { Path() }); + Assert.Equal(1, selectionChangedFiredCount); + }); + } + + [Fact] + public void ValidateCanSetSelectedIndex() + { + RunOnUIThread.Execute(() => + { + var model = new SelectionModel(); + var ip = IndexPath.CreateFrom(34); + model.SelectedIndex = ip; + Assert.Equal(0, ip.CompareTo(model.SelectedIndex)); + }); + } + + [Fact] + public void ValidateOneLevelMultipleSelection() + { + RunOnUIThread.Execute(() => + { + SelectionModel selectionModel = new SelectionModel(); + selectionModel.Source = Enumerable.Range(0, 10).ToList(); + + Select(selectionModel, 4, true); + ValidateSelection(selectionModel, new List() { Path(4) }, new List() { Path() }); + SelectRangeFromAnchor(selectionModel, 8, true /* select */); + ValidateSelection(selectionModel, + new List() + { + Path(4), + Path(5), + Path(6), + Path(7), + Path(8) + }, + new List() { Path() }); + + ClearSelection(selectionModel); + SetAnchorIndex(selectionModel, 6); + SelectRangeFromAnchor(selectionModel, 3, true /* select */); + ValidateSelection(selectionModel, + new List() + { + Path(3), + Path(4), + Path(5), + Path(6) + }, + new List() { Path() }); + + SetAnchorIndex(selectionModel, 4); + SelectRangeFromAnchor(selectionModel, 5, false /* select */); + ValidateSelection(selectionModel, + new List() + { + Path(3), + Path(6) + }, + new List() { Path() }); + }); + } + + [Fact] + public void ValidateTwoLevelSingleSelection() + { + RunOnUIThread.Execute(() => + { + SelectionModel selectionModel = new SelectionModel(); + Log.Comment("Setting the source"); + selectionModel.Source = CreateNestedData(1 /* levels */ , 2 /* groupsAtLevel */, 2 /* countAtLeaf */); + Select(selectionModel, 1, 1, true); + ValidateSelection(selectionModel, + new List() { Path(1, 1) }, new List() { Path(), Path(1) }); + Select(selectionModel, 1, 1, false); + ValidateSelection(selectionModel, new List() { }); + }); + } + + [Fact] + public void ValidateTwoLevelMultipleSelection() + { + RunOnUIThread.Execute(() => + { + SelectionModel selectionModel = new SelectionModel(); + Log.Comment("Setting the source"); + selectionModel.Source = CreateNestedData(1 /* levels */ , 3 /* groupsAtLevel */, 3 /* countAtLeaf */); + + Select(selectionModel, 1, 2, true); + ValidateSelection(selectionModel, new List() { Path(1, 2) }, new List() { Path(), Path(1) }); + SelectRangeFromAnchor(selectionModel, 2, 2, true /* select */); + ValidateSelection(selectionModel, + new List() + { + Path(1, 2), + Path(2), // Inner node should be selected since everything 2.* is selected + Path(2, 0), + Path(2, 1), + Path(2, 2) + }, + new List() + { + Path(), + Path(1) + }, + 1 /* selectedInnerNodes */); + + ClearSelection(selectionModel); + SetAnchorIndex(selectionModel, 2, 1); + SelectRangeFromAnchor(selectionModel, 0, 1, true /* select */); + ValidateSelection(selectionModel, + new List() + { + Path(0, 1), + Path(0, 2), + Path(1, 0), + Path(1, 1), + Path(1, 2), + Path(1), + Path(2, 0), + Path(2, 1) + }, + new List() + { + Path(), + Path(0), + Path(2), + }, + 1 /* selectedInnerNodes */); + + SetAnchorIndex(selectionModel, 1, 1); + SelectRangeFromAnchor(selectionModel, 2, 0, false /* select */); + ValidateSelection(selectionModel, + new List() + { + Path(0, 1), + Path(0, 2), + Path(1, 0), + Path(2, 1) + }, + new List() + { + Path(), + Path(1), + Path(0), + Path(2), + }, + 0 /* selectedInnerNodes */); + + ClearSelection(selectionModel); + ValidateSelection(selectionModel, new List() { }); + }); + } + + [Fact] + public void ValidateNestedSingleSelection() + { + RunOnUIThread.Execute(() => + { + SelectionModel selectionModel = new SelectionModel() { SingleSelect = true }; + Log.Comment("Setting the source"); + selectionModel.Source = CreateNestedData(3 /* levels */ , 2 /* groupsAtLevel */, 2 /* countAtLeaf */); + var path = Path(1, 0, 1, 1); + Select(selectionModel, path, true); + ValidateSelection(selectionModel, + new List() { path }, + new List() + { + Path(), + Path(1), + Path(1, 0), + Path(1, 0, 1), + }); + Select(selectionModel, Path(0, 0, 1, 0), true); + ValidateSelection(selectionModel, + new List() + { + Path(0, 0, 1, 0) + }, + new List() + { + Path(), + Path(0), + Path(0, 0), + Path(0, 0, 1) + }); + Select(selectionModel, Path(0, 0, 1, 0), false); + ValidateSelection(selectionModel, new List() { }); + }); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void ValidateNestedMultipleSelection(bool handleChildrenRequested) + { + RunOnUIThread.Execute(() => + { + SelectionModel selectionModel = new SelectionModel(); + List sourcePaths = new List(); + + Log.Comment("Setting the source"); + selectionModel.Source = CreateNestedData(3 /* levels */ , 2 /* groupsAtLevel */, 4 /* countAtLeaf */); + if (handleChildrenRequested) + { + selectionModel.ChildrenRequested += (object sender, SelectionModelChildrenRequestedEventArgs args) => + { + Log.Comment("ChildrenRequestedIndexPath:" + args.SourceIndex); + sourcePaths.Add(args.SourceIndex); + args.Children = args.Source is IEnumerable ? args.Source : null; + }; + } + + var startPath = Path(1, 0, 1, 0); + Select(selectionModel, startPath, true); + ValidateSelection(selectionModel, + new List() { startPath }, + new List() + { + Path(), + Path(1), + Path(1, 0), + Path(1, 0, 1) + }); + + var endPath = Path(1, 1, 1, 0); + SelectRangeFromAnchor(selectionModel, endPath, true /* select */); + + if (handleChildrenRequested) + { + // Validate SourceIndices. + var expectedSourceIndices = new List() + { + Path(), + Path(1), + Path(1, 0), + Path(1), + Path(1, 0, 1), + Path(1, 0, 1), + Path(1, 0, 1), + Path(1, 0, 1), + Path(1, 1), + Path(1, 1), + Path(1, 1, 0), + Path(1, 1, 0), + Path(1, 1, 0), + Path(1, 1, 0), + Path(1, 1, 1) + }; + + Assert.Equal(expectedSourceIndices.Count, sourcePaths.Count); + for (int i = 0; i < expectedSourceIndices.Count; i++) + { + Assert.True(AreEqual(expectedSourceIndices[i], sourcePaths[i])); + } + } + + ValidateSelection(selectionModel, + new List() + { + Path(1, 0, 1, 0), + Path(1, 0, 1, 1), + Path(1, 0, 1, 2), + Path(1, 0, 1, 3), + Path(1, 0, 1), + Path(1, 1, 0, 0), + Path(1, 1, 0, 1), + Path(1, 1, 0, 2), + Path(1, 1, 0, 3), + Path(1, 1, 0), + Path(1, 1, 1, 0), + }, + new List() + { + Path(), + Path(1), + Path(1, 0), + Path(1, 1), + Path(1, 1, 1), + }, + 2 /* selectedInnerNodes */); + + ClearSelection(selectionModel); + ValidateSelection(selectionModel, new List() { }); + + startPath = Path(0, 1, 0, 2); + SetAnchorIndex(selectionModel, startPath); + endPath = Path(0, 0, 0, 2); + SelectRangeFromAnchor(selectionModel, endPath, true /* select */); + ValidateSelection(selectionModel, + new List() + { + Path(0, 0, 0, 2), + Path(0, 0, 0, 3), + Path(0, 0, 1, 0), + Path(0, 0, 1, 1), + Path(0, 0, 1, 2), + Path(0, 0, 1, 3), + Path(0, 0, 1), + Path(0, 1, 0, 0), + Path(0, 1, 0, 1), + Path(0, 1, 0, 2), + }, + new List() + { + Path(), + Path(0), + Path(0, 0), + Path(0, 0, 0), + Path(0, 1), + Path(0, 1, 0), + }, + 1 /* selectedInnerNodes */); + + startPath = Path(0, 1, 0, 2); + SetAnchorIndex(selectionModel, startPath); + endPath = Path(0, 0, 0, 2); + SelectRangeFromAnchor(selectionModel, endPath, false /* select */); + ValidateSelection(selectionModel, new List() { }); + }); + } + + [Fact] + public void ValidateInserts() + { + RunOnUIThread.Execute(() => + { + var data = new ObservableCollection(Enumerable.Range(0, 10)); + var selectionModel = new SelectionModel(); + selectionModel.Source = data; + + selectionModel.Select(3); + selectionModel.Select(4); + selectionModel.Select(5); + ValidateSelection(selectionModel, + new List() + { + Path(3), + Path(4), + Path(5), + }, + new List() + { + Path() + }); + + Log.Comment("Insert in selected range: Inserting 3 items at index 4"); + data.Insert(4, 41); + data.Insert(4, 42); + data.Insert(4, 43); + ValidateSelection(selectionModel, + new List() + { + Path(3), + Path(7), + Path(8), + }, + new List() + { + Path() + }); + + Log.Comment("Insert before selected range: Inserting 3 items at index 0"); + data.Insert(0, 100); + data.Insert(0, 101); + data.Insert(0, 102); + ValidateSelection(selectionModel, + new List() + { + Path(6), + Path(10), + Path(11), + }, + new List() + { + Path() + }); + + Log.Comment("Insert after selected range: Inserting 3 items at index 12"); + data.Insert(12, 1000); + data.Insert(12, 1001); + data.Insert(12, 1002); + ValidateSelection(selectionModel, + new List() + { + Path(6), + Path(10), + Path(11), + }, + new List() + { + Path() + }); + }); + } + + [Fact] + public void ValidateGroupInserts() + { + RunOnUIThread.Execute(() => + { + var data = CreateNestedData(1 /* levels */ , 3 /* groupsAtLevel */, 3 /* countAtLeaf */); + var selectionModel = new SelectionModel(); + selectionModel.Source = data; + + selectionModel.Select(1, 1); + ValidateSelection(selectionModel, + new List() + { + Path(1, 1), + }, + new List() + { + Path(), + Path(1), + }); + + Log.Comment("Insert before selected range: Inserting item at group index 0"); + data.Insert(0, 100); + ValidateSelection(selectionModel, + new List() + { + Path(2, 1) + }, + new List() + { + Path(), + Path(2), + }); + + Log.Comment("Insert after selected range: Inserting item at group index 3"); + data.Insert(3, 1000); + ValidateSelection(selectionModel, + new List() + { + Path(2, 1) + }, + new List() + { + Path(), + Path(2), + }); + }); + } + + [Fact] + public void ValidateRemoves() + { + RunOnUIThread.Execute(() => + { + var data = new ObservableCollection(Enumerable.Range(0, 10)); + var selectionModel = new SelectionModel(); + selectionModel.Source = data; + + selectionModel.Select(6); + selectionModel.Select(7); + selectionModel.Select(8); + ValidateSelection(selectionModel, + new List() + { + Path(6), + Path(7), + Path(8) + }, + new List() + { + Path() + }); + + Log.Comment("Remove before selected range: Removing item at index 0"); + data.RemoveAt(0); + ValidateSelection(selectionModel, + new List() + { + Path(5), + Path(6), + Path(7) + }, + new List() + { + Path() + }); + + Log.Comment("Remove from before to middle of selected range: Removing items at index 3, 4, 5"); + data.RemoveAt(3); + data.RemoveAt(3); + data.RemoveAt(3); + ValidateSelection(selectionModel, + new List() + { + Path(3), + Path(4) + }, + new List() + { + Path() + }); + + Log.Comment("Remove after selected range: Removing item at index 5"); + data.RemoveAt(5); + ValidateSelection(selectionModel, + new List() + { + Path(3), + Path(4) + }, + new List() + { + Path() + }); + }); + } + + [Fact] + public void ValidateGroupRemoves() + { + RunOnUIThread.Execute(() => + { + var data = CreateNestedData(1 /* levels */ , 3 /* groupsAtLevel */, 3 /* countAtLeaf */); + var selectionModel = new SelectionModel(); + selectionModel.Source = data; + + selectionModel.Select(1, 1); + selectionModel.Select(1, 2); + ValidateSelection(selectionModel, + new List() + { + Path(1, 1), + Path(1, 2) + }, + new List() + { + Path(), + Path(1), + }); + + Log.Comment("Remove before selected range: Removing item at group index 0"); + data.RemoveAt(0); + ValidateSelection(selectionModel, + new List() + { + Path(0, 1), + Path(0, 2) + }, + new List() + { + Path(), + Path(0), + }); + + Log.Comment("Remove after selected range: Removing item at group index 1"); + data.RemoveAt(1); + ValidateSelection(selectionModel, + new List() + { + Path(0, 1), + Path(0, 2) + }, + new List() + { + Path(), + Path(0), + }); + + Log.Comment("Remove group containing selected items"); + data.RemoveAt(0); + ValidateSelection(selectionModel, new List()); + }); + } + + [Fact] + public void CanReplaceItem() + { + RunOnUIThread.Execute(() => + { + var data = new ObservableCollection(Enumerable.Range(0, 10)); + var selectionModel = new SelectionModel(); + selectionModel.Source = data; + + selectionModel.Select(3); + selectionModel.Select(4); + selectionModel.Select(5); + ValidateSelection(selectionModel, + new List() + { + Path(3), + Path(4), + Path(5), + }, + new List() + { + Path() + }); + + data[3] = 300; + data[4] = 400; + ValidateSelection(selectionModel, + new List() + { + Path(5), + }, + new List() + { + Path() + }); + }); + } + + [Fact] + public void ValidateGroupReplaceLosesSelection() + { + RunOnUIThread.Execute(() => + { + var data = CreateNestedData(1 /* levels */ , 3 /* groupsAtLevel */, 3 /* countAtLeaf */); + var selectionModel = new SelectionModel(); + selectionModel.Source = data; + + selectionModel.Select(1, 1); + ValidateSelection(selectionModel, + new List() + { + Path(1, 1) + }, + new List() + { + Path(), + Path(1) + }); + + data[1] = new ObservableCollection(Enumerable.Range(0, 5)); + ValidateSelection(selectionModel, new List()); + }); + } + + [Fact] + public void ValidateClear() + { + RunOnUIThread.Execute(() => + { + var data = new ObservableCollection(Enumerable.Range(0, 10)); + var selectionModel = new SelectionModel(); + selectionModel.Source = data; + + selectionModel.Select(3); + selectionModel.Select(4); + selectionModel.Select(5); + ValidateSelection(selectionModel, + new List() + { + Path(3), + Path(4), + Path(5), + }, + new List() + { + Path() + }); + + data.Clear(); + ValidateSelection(selectionModel, new List()); + }); + } + + [Fact] + public void ValidateGroupClear() + { + RunOnUIThread.Execute(() => + { + var data = CreateNestedData(1 /* levels */ , 3 /* groupsAtLevel */, 3 /* countAtLeaf */); + var selectionModel = new SelectionModel(); + selectionModel.Source = data; + + selectionModel.Select(1, 1); + ValidateSelection(selectionModel, + new List() + { + Path(1, 1) + }, + new List() + { + Path(), + Path(1) + }); + + (data[1] as IList).Clear(); + ValidateSelection(selectionModel, new List()); + }); + } + + // In some cases the leaf node might get a collection change that affects an ancestors selection + // state. In this case we were not raising selection changed event. For example, if all elements + // in a group are selected and a new item gets inserted - the parent goes from selected to partially + // selected. In that case we need to raise the selection changed event so that the header containers + // can show the correct visual. + [Fact] + public void ValidateEventWhenInnerNodeChangesSelectionState() + { + RunOnUIThread.Execute(() => + { + bool selectionChangedRaised = false; + var data = CreateNestedData(1 /* levels */ , 3 /* groupsAtLevel */, 3 /* countAtLeaf */); + var selectionModel = new SelectionModel(); + selectionModel.Source = data; + selectionModel.SelectionChanged += (sender, args) => { selectionChangedRaised = true; }; + + selectionModel.Select(1, 0); + selectionModel.Select(1, 1); + selectionModel.Select(1, 2); + ValidateSelection(selectionModel, + new List() + { + Path(1, 0), + Path(1, 1), + Path(1, 2), + Path(1) + }, + new List() + { + Path(), + }, + 1 /* selectedInnerNodes */); + + Log.Comment("Inserting 1.0"); + selectionChangedRaised = false; + (data[1] as AvaloniaList).Insert(0, 100); + Assert.True(selectionChangedRaised, "SelectionChanged event was not raised"); + ValidateSelection(selectionModel, + new List() + { + Path(1, 1), + Path(1, 2), + Path(1, 3), + }, + new List() + { + Path(), + Path(1), + }); + + Log.Comment("Removing 1.0"); + selectionChangedRaised = false; + (data[1] as AvaloniaList).RemoveAt(0); + Assert.True(selectionChangedRaised, "SelectionChanged event was not raised"); + ValidateSelection(selectionModel, + new List() + { + Path(1, 0), + Path(1, 1), + Path(1, 2), + Path(1) + }, + new List() + { + Path(), + }, + 1 /* selectedInnerNodes */); + }); + } + + [Fact] + public void ValidatePropertyChangedEventIsRaised() + { + RunOnUIThread.Execute(() => + { + var selectionModel = new SelectionModel(); + Log.Comment("Set the source to 10 items"); + selectionModel.Source = Enumerable.Range(0, 10).ToList(); + + bool selectedIndexChanged = false; + bool selectedIndicesChanged = false; + bool SelectedItemChanged = false; + bool SelectedItemsChanged = false; + bool AnchorIndexChanged = false; + selectionModel.PropertyChanged += (sender, args) => + { + switch (args.PropertyName) + { + case "SelectedIndex": + selectedIndexChanged = true; + break; + case "SelectedIndices": + selectedIndicesChanged = true; + break; + case "SelectedItem": + SelectedItemChanged = true; + break; + case "SelectedItems": + SelectedItemsChanged = true; + break; + case "AnchorIndex": + AnchorIndexChanged = true; + break; + + default: + throw new InvalidOperationException(); + } + }; + + Select(selectionModel, 3, true); + + Assert.True(selectedIndexChanged); + Assert.True(selectedIndicesChanged); + Assert.True(SelectedItemChanged); + Assert.True(SelectedItemsChanged); + Assert.True(AnchorIndexChanged); + }); + } + + [Fact] + public void CanExtendSelectionModelINPC() + { + RunOnUIThread.Execute(() => + { + var selectionModel = new CustomSelectionModel(); + bool intPropertyChanged = false; + selectionModel.PropertyChanged += (sender, args) => + { + if (args.PropertyName == "IntProperty") + { + intPropertyChanged = true; + } + }; + + selectionModel.IntProperty = 5; + Assert.True(intPropertyChanged); + }); + } + + + [Fact] + public void Disposing_Unhooks_CollectionChanged_Handlers() + { + var data = CreateNestedData(2, 2, 2); + var target = new SelectionModel { Source = data }; + + target.SelectAll(); + VerifyCollectionChangedHandlers(1, data); + + target.Dispose(); + + VerifyCollectionChangedHandlers(0, data); + } + + [Fact] + public void Clearing_Selection_Unhooks_CollectionChanged_Handlers() + { + var data = CreateNestedData(2, 2, 2); + var target = new SelectionModel { Source = data }; + + target.SelectAll(); + VerifyCollectionChangedHandlers(1, data); + + target.ClearSelection(); + + // Root subscription not unhooked until SelectionModel is disposed. + Assert.Equal(1, GetSubscriberCount(data)); + + foreach (AvaloniaList i in data) + { + VerifyCollectionChangedHandlers(0, i); + } + } + + [Fact] + public void Should_Not_Treat_Strings_As_Nested_Selections() + { + var data = new[] { "foo", "bar", "baz" }; + var target = new SelectionModel { Source = data }; + + target.SelectAll(); + + Assert.Equal(3, target.SelectedItems.Count); + } + + private int GetSubscriberCount(AvaloniaList list) + { + return ((INotifyCollectionChangedDebug)list).GetCollectionChangedSubscribers()?.Length ?? 0; + } + + private void VerifyCollectionChangedHandlers(int expected, AvaloniaList list) + { + var count = GetSubscriberCount(list); + + Assert.Equal(expected, count); + + foreach (var i in list) + { + if (i is AvaloniaList l) + { + VerifyCollectionChangedHandlers(expected, l); + } + } + } + + private void Select(SelectionModel manager, int index, bool select) + { + Log.Comment((select ? "Selecting " : "DeSelecting ") + index); + if (select) + { + manager.Select(index); + } + else + { + manager.Deselect(index); + } + } + + private void Select(SelectionModel manager, int groupIndex, int itemIndex, bool select) + { + Log.Comment((select ? "Selecting " : "DeSelecting ") + groupIndex + "." + itemIndex); + if (select) + { + manager.Select(groupIndex, itemIndex); + } + else + { + manager.Deselect(groupIndex, itemIndex); + } + } + + private void Select(SelectionModel manager, IndexPath index, bool select) + { + Log.Comment((select ? "Selecting " : "DeSelecting ") + index); + if (select) + { + manager.SelectAt(index); + } + else + { + manager.DeselectAt(index); + } + } + + private void SelectRangeFromAnchor(SelectionModel manager, int index, bool select) + { + Log.Comment("SelectRangeFromAnchor " + index + " select: " + select.ToString()); + if (select) + { + manager.SelectRangeFromAnchor(index); + } + else + { + manager.DeselectRangeFromAnchor(index); + } + } + + private void SelectRangeFromAnchor(SelectionModel manager, int groupIndex, int itemIndex, bool select) + { + Log.Comment("SelectRangeFromAnchor " + groupIndex + "." + itemIndex + " select:" + select.ToString()); + if (select) + { + manager.SelectRangeFromAnchor(groupIndex, itemIndex); + } + else + { + manager.DeselectRangeFromAnchor(groupIndex, itemIndex); + } + } + + private void SelectRangeFromAnchor(SelectionModel manager, IndexPath index, bool select) + { + Log.Comment("SelectRangeFromAnchor " + index + " select: " + select.ToString()); + if (select) + { + manager.SelectRangeFromAnchorTo(index); + } + else + { + manager.DeselectRangeFromAnchorTo(index); + } + } + + private void ClearSelection(SelectionModel manager) + { + Log.Comment("ClearSelection"); + manager.ClearSelection(); + } + + private void SetAnchorIndex(SelectionModel manager, int index) + { + Log.Comment("SetAnchorIndex " + index); + manager.SetAnchorIndex(index); + } + + private void SetAnchorIndex(SelectionModel manager, int groupIndex, int itemIndex) + { + Log.Comment("SetAnchor " + groupIndex + "." + itemIndex); + manager.SetAnchorIndex(groupIndex, itemIndex); + } + + private void SetAnchorIndex(SelectionModel manager, IndexPath index) + { + Log.Comment("SetAnchor " + index); + manager.AnchorIndex = index; + } + + private void ValidateSelection( + SelectionModel selectionModel, + List expectedSelected, + List expectedPartialSelected = null, + int selectedInnerNodes = 0) + { + Log.Comment("Validating Selection..."); + + Log.Comment("Selection contains indices:"); + foreach (var index in selectionModel.SelectedIndices) + { + Log.Comment(" " + index.ToString()); + } + + Log.Comment("Selection contains items:"); + foreach (var item in selectionModel.SelectedItems) + { + Log.Comment(" " + item.ToString()); + } + + if (selectionModel.Source != null) + { + List allIndices = GetIndexPathsInSource(selectionModel.Source); + foreach (var index in allIndices) + { + bool? isSelected = selectionModel.IsSelectedAt(index); + if (Contains(expectedSelected, index)) + { + Assert.True(isSelected.Value, index + " is Selected"); + } + else if (expectedPartialSelected != null && Contains(expectedPartialSelected, index)) + { + Assert.True(isSelected == null, index + " is partially Selected"); + } + else + { + if (isSelected == null) + { + Log.Comment("*************" + index + " is null"); + Assert.True(false, "Expected false but got null");; + } + else + { + Assert.False(isSelected.Value, index + " is not Selected"); + } + } + } + } + else + { + foreach (var index in expectedSelected) + { + Assert.True(selectionModel.IsSelectedAt(index).Value, index + " is Selected"); + } + } + if (expectedSelected.Count > 0) + { + Log.Comment("SelectedIndex is " + selectionModel.SelectedIndex); + Assert.Equal(0, selectionModel.SelectedIndex.CompareTo(expectedSelected[0])); + if (selectionModel.Source != null) + { + Assert.Equal(selectionModel.SelectedItem, GetData(selectionModel, expectedSelected[0])); + } + + int itemsCount = selectionModel.SelectedItems.Count(); + Assert.Equal(selectionModel.Source != null ? expectedSelected.Count - selectedInnerNodes : 0, itemsCount); + int indicesCount = selectionModel.SelectedIndices.Count(); + Assert.Equal(expectedSelected.Count - selectedInnerNodes, indicesCount); + } + + Log.Comment("Validating Selection... done"); + } + + private object GetData(SelectionModel selectionModel, IndexPath indexPath) + { + var data = selectionModel.Source; + for (int i = 0; i < indexPath.GetSize(); i++) + { + var listData = data as IList; + data = listData[indexPath.GetAt(i)]; + } + + return data; + } + + private bool AreEqual(IndexPath a, IndexPath b) + { + if (a.GetSize() != b.GetSize()) + { + return false; + } + + for (int i = 0; i < a.GetSize(); i++) + { + if (a.GetAt(i) != b.GetAt(i)) + { + return false; + } + } + + return true; + } + + private List GetIndexPathsInSource(object source) + { + List paths = new List(); + Traverse(source, (TreeWalkNodeInfo node) => + { + if (!paths.Contains(node.Path)) + { + paths.Add(node.Path); + } + }); + + Log.Comment("All Paths in source.."); + foreach (var path in paths) + { + Log.Comment(path.ToString()); + } + Log.Comment("done."); + + return paths; + } + + private static void Traverse(object root, Action nodeAction) + { + var pendingNodes = new Stack(); + IndexPath current = Path(null); + pendingNodes.Push(new TreeWalkNodeInfo() { Current = root, Path = current }); + + while (pendingNodes.Count > 0) + { + var currentNode = pendingNodes.Pop(); + var currentObject = currentNode.Current as IList; + + if (currentObject != null) + { + for (int i = currentObject.Count - 1; i >= 0; i--) + { + var child = currentObject[i]; + List path = new List(); + for (int idx = 0; idx < currentNode.Path.GetSize(); idx++) + { + path.Add(currentNode.Path.GetAt(idx)); + } + + path.Add(i); + var childPath = IndexPath.CreateFromIndices(path); + if (child != null) + { + pendingNodes.Push(new TreeWalkNodeInfo() { Current = child, Path = childPath }); + } + } + } + + nodeAction(currentNode); + } + } + + private bool Contains(List list, IndexPath index) + { + bool contains = false; + foreach (var item in list) + { + if (item.CompareTo(index) == 0) + { + contains = true; + break; + } + } + + return contains; + } + + public static AvaloniaList CreateNestedData(int levels = 3, int groupsAtLevel = 5, int countAtLeaf = 10) + { + var nextData = 0; + return CreateNestedData(levels, groupsAtLevel, countAtLeaf, ref nextData); + } + + public static AvaloniaList CreateNestedData( + int levels, + int groupsAtLevel, + int countAtLeaf, + ref int nextData) + { + var data = new AvaloniaList(); + if (levels != 0) + { + for (int i = 0; i < groupsAtLevel; i++) + { + data.Add(CreateNestedData(levels - 1, groupsAtLevel, countAtLeaf, ref nextData)); + } + } + else + { + for (int i = 0; i < countAtLeaf; i++) + { + data.Add(nextData++); + } + } + + return data; + } + + static IndexPath Path(params int[] path) + { + return IndexPath.CreateFromIndices(path); + } + + private static int _nextData = 0; + private struct TreeWalkNodeInfo + { + public object Current { get; set; } + + public IndexPath Path { get; set; } + } + + private static class RunOnUIThread + { + public static void Execute(Action a) => a(); + } + + private class LogWrapper + { + private readonly ITestOutputHelper _output; + public LogWrapper(ITestOutputHelper output) => _output = output; + public void Comment(string s) => _output.WriteLine(s); + } + } + + class CustomSelectionModel : SelectionModel + { + public int IntProperty + { + get { return _intProperty; } + set + { + _intProperty = value; + OnPropertyChanged("IntProperty"); + } + } + + private int _intProperty; + } +} From e4c6c858266295ca305f546c89ea02cb44973a1c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 25 Jan 2020 15:09:01 +0100 Subject: [PATCH 020/663] Refactor IndexPath. - #nullable enable - Make it a `readonly struct` - Most of the time it will only hold a single `int`, so optimize for the common case by having an `int _index` field - Make `_path` an array rather than a list as it will be fixed-size - Implement equality - Implement operators --- src/Avalonia.Controls/IndexPath.cs | 115 +++++++++++++++--- src/Avalonia.Controls/SelectionModel.cs | 8 +- .../Utils/SelectionTreeHelper.cs | 2 - .../IndexPathTests.cs | 81 ++++++++++++ 4 files changed, 180 insertions(+), 26 deletions(-) create mode 100644 tests/Avalonia.Controls.UnitTests/IndexPathTests.cs diff --git a/src/Avalonia.Controls/IndexPath.cs b/src/Avalonia.Controls/IndexPath.cs index 1251a022da..32d8c2b051 100644 --- a/src/Avalonia.Controls/IndexPath.cs +++ b/src/Avalonia.Controls/IndexPath.cs @@ -5,42 +5,68 @@ using System; using System.Collections.Generic; -using System.Text; +using System.Linq; + +#nullable enable namespace Avalonia.Controls { - public sealed class IndexPath : IComparable + public readonly struct IndexPath : IComparable, IEquatable { - private readonly List _path = new List(); + public static readonly IndexPath Unselected = default; + + private readonly int _index; + private readonly int[]? _path; - internal IndexPath(int index) + public IndexPath(int index) { - _path.Add(index); + _index = index + 1; + _path = null; } - internal IndexPath(int groupIndex, int itemIndex) + public IndexPath(int groupIndex, int itemIndex) { - _path.Add(groupIndex); - _path.Add(itemIndex); + _index = 0; + _path = new[] { groupIndex, itemIndex }; } - internal IndexPath(IEnumerable indices) + public IndexPath(IEnumerable? indices) { if (indices != null) { - _path.AddRange(indices); + _index = 0; + _path = indices.ToArray(); + } + else + { + _index = 0; + _path = null; } } - public int GetSize() => _path.Count; - public int GetAt(int index) => _path[index]; + 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) + { + return _path?[index] ?? (_index - 1); + } public int CompareTo(IndexPath other) { var rhsPath = other; int compareResult = 0; - int lhsCount = _path.Count; - int rhsCount = rhsPath._path.Count; + int lhsCount = GetSize(); + int rhsCount = rhsPath.GetSize(); if (lhsCount == 0 || rhsCount == 0) { @@ -52,12 +78,12 @@ namespace Avalonia.Controls // both paths are non-empty, but can be of different size for (int i = 0; i < Math.Min(lhsCount, rhsCount); i++) { - if (_path[i] < rhsPath._path[i]) + if (GetAt(i) < rhsPath.GetAt(i)) { compareResult = -1; break; } - else if (_path[i] > rhsPath._path[i]) + else if (GetAt(i) > rhsPath.GetAt(i)) { compareResult = 1; break; @@ -78,14 +104,34 @@ namespace Avalonia.Controls public IndexPath CloneWithChildIndex(int childIndex) { - var newPath = new List(_path); - newPath.Add(childIndex); - return new IndexPath(newPath); + if (_path != null) + { + return new IndexPath(_path, childIndex); + } + else if (_index != 0) + { + return new IndexPath(_index - 1, childIndex); + } + else + { + return new IndexPath(childIndex); + } } public override string ToString() { - return "R." + string.Join(".", _path); + 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); @@ -94,5 +140,34 @@ namespace Avalonia.Controls public static IndexPath CreateFromIndices(IList 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; } } } diff --git a/src/Avalonia.Controls/SelectionModel.cs b/src/Avalonia.Controls/SelectionModel.cs index 6b48e02a21..ea3a09d4e7 100644 --- a/src/Avalonia.Controls/SelectionModel.cs +++ b/src/Avalonia.Controls/SelectionModel.cs @@ -72,7 +72,7 @@ namespace Avalonia.Controls { get { - IndexPath anchor = null; + IndexPath anchor = default; if (_rootNode.AnchorIndex >= 0) { @@ -113,7 +113,7 @@ namespace Avalonia.Controls { get { - IndexPath selectedIndex = null; + IndexPath selectedIndex = default; var selectedIndices = SelectedIndices; if (selectedIndices?.Count > 0) @@ -248,7 +248,7 @@ namespace Avalonia.Controls (infos, index) => // callback for GetAt(index) { var currentIndex = 0; - IndexPath path = null; + IndexPath path = default; foreach (var info in infos) { @@ -505,7 +505,7 @@ namespace Avalonia.Controls if (resetAnchor) { - AnchorIndex = null; + AnchorIndex = default; } if (raiseSelectionChanged) diff --git a/src/Avalonia.Controls/Utils/SelectionTreeHelper.cs b/src/Avalonia.Controls/Utils/SelectionTreeHelper.cs index 52e29da0e5..38b1dde5d7 100644 --- a/src/Avalonia.Controls/Utils/SelectionTreeHelper.cs +++ b/src/Avalonia.Controls/Utils/SelectionTreeHelper.cs @@ -157,7 +157,6 @@ namespace Avalonia.Controls.Utils public TreeWalkNodeInfo(SelectionNode node, IndexPath indexPath, SelectionNode parent) { node = node ?? throw new ArgumentNullException(nameof(node)); - indexPath = indexPath ?? throw new ArgumentNullException(nameof(indexPath)); Node = node; Path = indexPath; @@ -167,7 +166,6 @@ namespace Avalonia.Controls.Utils public TreeWalkNodeInfo(SelectionNode node, IndexPath indexPath) { node = node ?? throw new ArgumentNullException(nameof(node)); - indexPath = indexPath ?? throw new ArgumentNullException(nameof(indexPath)); Node = node; Path = indexPath; diff --git a/tests/Avalonia.Controls.UnitTests/IndexPathTests.cs b/tests/Avalonia.Controls.UnitTests/IndexPathTests.cs new file mode 100644 index 0000000000..190e92ed5e --- /dev/null +++ b/tests/Avalonia.Controls.UnitTests/IndexPathTests.cs @@ -0,0 +1,81 @@ +using Xunit; + +namespace Avalonia.Controls.UnitTests +{ + public class IndexPathTests + { + [Fact] + public void Simple_Index() + { + var a = new IndexPath(1); + + Assert.Equal(1, a.GetSize()); + Assert.Equal(1, a.GetAt(0)); + } + + [Fact] + public void Equal_Paths() + { + var a = new IndexPath(1); + var b = new IndexPath(1); + + Assert.True(a == b); + Assert.False(a != b); + Assert.True(a.Equals(b)); + Assert.Equal(0, a.CompareTo(b)); + Assert.Equal(a.GetHashCode(), b.GetHashCode()); + } + + [Fact] + public void Unequal_Paths() + { + var a = new IndexPath(1); + var b = new IndexPath(2); + + Assert.False(a == b); + Assert.True(a != b); + Assert.False(a.Equals(b)); + Assert.Equal(-1, a.CompareTo(b)); + Assert.NotEqual(a.GetHashCode(), b.GetHashCode()); + } + + [Fact] + public void Equal_Null_Path() + { + var a = new IndexPath(null); + var b = new IndexPath(null); + + Assert.True(a == b); + Assert.False(a != b); + Assert.True(a.Equals(b)); + Assert.Equal(0, a.CompareTo(b)); + Assert.Equal(a.GetHashCode(), b.GetHashCode()); + } + + [Fact] + public void Unequal_Null_Path() + { + var a = new IndexPath(null); + var b = new IndexPath(2); + + Assert.False(a == b); + Assert.True(a != b); + Assert.False(a.Equals(b)); + Assert.Equal(-1, a.CompareTo(b)); + Assert.NotEqual(a.GetHashCode(), b.GetHashCode()); + } + + [Fact] + public void Default_Is_Null_Path() + { + var a = new IndexPath(null); + var b = default(IndexPath); + + Assert.True(a == b); + Assert.False(a != b); + Assert.True(a.Equals(b)); + Assert.Equal(0, a.CompareTo(b)); + Assert.Equal(a.GetHashCode(), b.GetHashCode()); + } + } +} From d615cdebcb7491f984589936fe5e3d775315001e Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 25 Jan 2020 19:32:43 +0100 Subject: [PATCH 021/663] Add nullability annotations to SelectionModel etc. And ran a few of VS' lightbulb suggestions. --- src/Avalonia.Controls/IndexRange.cs | 4 +- src/Avalonia.Controls/SelectedItems.cs | 9 +- src/Avalonia.Controls/SelectionModel.cs | 56 ++++++------ ...electionModelChildrenRequestedEventArgs.cs | 44 ++++++++-- ...SelectionModelSelectionChangedEventArgs.cs | 4 +- src/Avalonia.Controls/SelectionNode.cs | 87 ++++++++++--------- .../Utils/SelectionTreeHelper.cs | 10 ++- 7 files changed, 122 insertions(+), 92 deletions(-) diff --git a/src/Avalonia.Controls/IndexRange.cs b/src/Avalonia.Controls/IndexRange.cs index f3820c087a..b1a112ab39 100644 --- a/src/Avalonia.Controls/IndexRange.cs +++ b/src/Avalonia.Controls/IndexRange.cs @@ -3,9 +3,7 @@ // // Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation. -using System; -using System.Collections.Generic; -using System.Text; +#nullable enable namespace Avalonia.Controls { diff --git a/src/Avalonia.Controls/SelectedItems.cs b/src/Avalonia.Controls/SelectedItems.cs index 036788e7b2..af43742670 100644 --- a/src/Avalonia.Controls/SelectedItems.cs +++ b/src/Avalonia.Controls/SelectedItems.cs @@ -8,13 +8,14 @@ using System.Collections; using System.Collections.Generic; using SelectedItemInfo = Avalonia.Controls.SelectionModel.SelectedItemInfo; +#nullable enable + namespace Avalonia.Controls { internal class SelectedItems : IReadOnlyList { private readonly List _infos; private readonly Func, int, T> _getAtImpl; - private int _totalCount; public SelectedItems( List infos, @@ -29,7 +30,7 @@ namespace Avalonia.Controls if (node != null) { - _totalCount += node.SelectedCount; + Count += node.SelectedCount; } else { @@ -40,11 +41,11 @@ namespace Avalonia.Controls public T this[int index] => _getAtImpl(_infos, index); - public int Count => _totalCount; + public int Count { get; } public IEnumerator GetEnumerator() { - for (var i = 0; i < _totalCount; ++i) + for (var i = 0; i < Count; ++i) { yield return this[i]; } diff --git a/src/Avalonia.Controls/SelectionModel.cs b/src/Avalonia.Controls/SelectionModel.cs index ea3a09d4e7..34d5f78434 100644 --- a/src/Avalonia.Controls/SelectionModel.cs +++ b/src/Avalonia.Controls/SelectionModel.cs @@ -8,20 +8,22 @@ using System.Collections.Generic; using System.ComponentModel; using Avalonia.Controls.Utils; +#nullable enable + namespace Avalonia.Controls { public class SelectionModel : INotifyPropertyChanged, IDisposable { - private SelectionNode _rootNode; + private readonly SelectionNode _rootNode; private bool _singleSelect; - private IReadOnlyList _selectedIndicesCached; - private IReadOnlyList _selectedItemsCached; - private SelectionModelChildrenRequestedEventArgs _childrenRequestedEventArgs; - private SelectionModelSelectionChangedEventArgs _selectionChangedEventArgs; + private IReadOnlyList? _selectedIndicesCached; + private IReadOnlyList? _selectedItemsCached; + private SelectionModelChildrenRequestedEventArgs? _childrenRequestedEventArgs; + private SelectionModelSelectionChangedEventArgs? _selectionChangedEventArgs; - public event EventHandler ChildrenRequested; - public event PropertyChangedEventHandler PropertyChanged; - public event EventHandler SelectionChanged; + public event EventHandler? ChildrenRequested; + public event PropertyChangedEventHandler? PropertyChanged; + public event EventHandler? SelectionChanged; public SelectionModel() { @@ -29,9 +31,9 @@ namespace Avalonia.Controls SharedLeafNode = new SelectionNode(this, null); } - public object Source + public object? Source { - get => _rootNode.Source; + get => _rootNode?.Source; set { ClearSelection(resetAnchor: true, raiseSelectionChanged: false); @@ -74,10 +76,10 @@ namespace Avalonia.Controls { IndexPath anchor = default; - if (_rootNode.AnchorIndex >= 0) + if (_rootNode?.AnchorIndex >= 0) { var path = new List(); - var current = _rootNode; + SelectionNode? current = _rootNode; while (current?.AnchorIndex >= 0) { @@ -136,11 +138,11 @@ namespace Avalonia.Controls } } - public object SelectedItem + public object? SelectedItem { get { - object item = null; + object? item = null; var selectedItems = SelectedItems; if (selectedItems?.Count > 0) @@ -152,7 +154,7 @@ namespace Avalonia.Controls } } - public IReadOnlyList SelectedItems + public IReadOnlyList SelectedItems { get { @@ -179,12 +181,12 @@ namespace Avalonia.Controls // 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 ( + var selectedItems = new SelectedItems ( selectedInfos, (infos, index) => { var currentIndex = 0; - object item = null; + object? item = null; foreach (var info in infos) { @@ -197,7 +199,7 @@ namespace Avalonia.Controls if (index >= currentIndex && index < currentIndex + currentCount) { var targetIndex = node.SelectedIndices[index - currentIndex]; - item = node.ItemsSourceView.GetAt(targetIndex); + item = node.ItemsSourceView!.GetAt(targetIndex); break; } @@ -289,8 +291,6 @@ namespace Avalonia.Controls { ClearSelection(resetAnchor: false, raiseSelectionChanged: false); _rootNode?.Dispose(); - _rootNode = null; - SharedLeafNode = null; _selectedIndicesCached = null; _selectedItemsCached = null; } @@ -349,7 +349,7 @@ namespace Avalonia.Controls { var path = index; var isRealized = true; - var node = _rootNode; + SelectionNode? node = _rootNode; for (int i = 0; i < path.GetSize() - 1; i++) { @@ -370,11 +370,11 @@ namespace Avalonia.Controls var size = path.GetSize(); if (size == 0) { - isSelected = SelectionNode.ConvertToNullableBool(node.EvaluateIsSelectedBasedOnChildrenNodes()); + isSelected = SelectionNode.ConvertToNullableBool(node!.EvaluateIsSelectedBasedOnChildrenNodes()); } else { - isSelected = node.IsSelectedWithPartial(path.GetAt(size - 1)); + isSelected = node!.IsSelectedWithPartial(path.GetAt(size - 1)); } } @@ -457,9 +457,9 @@ namespace Avalonia.Controls OnSelectionChanged(); } - internal object ResolvePath(object data, SelectionNode sourceNode) + internal object? ResolvePath(object data, SelectionNode sourceNode) { - object resolved = null; + object? resolved = null; // Raise ChildrenRequested event if there is a handler if (ChildrenRequested != null) @@ -565,7 +565,7 @@ namespace Avalonia.Controls } var childNode = _rootNode.GetAt(groupIndex, realizeChild: true); - var selected = childNode.Select(itemIndex, select); + var selected = childNode!.Select(itemIndex, select); if (selected) { @@ -653,7 +653,7 @@ namespace Avalonia.Controls var selected = false; for (int groupIdx = startGroupIndex; groupIdx <= endGroupIndex; groupIdx++) { - var groupNode = _rootNode.GetAt(groupIdx, realizeChild: true); + var groupNode = _rootNode.GetAt(groupIdx, realizeChild: true)!; int startIndex = groupIdx == startGroupIndex ? startItemIndex : 0; int endIndex = groupIdx == endGroupIndex ? endItemIndex : groupNode.DataCount - 1; selected |= groupNode.SelectRange(new IndexRange(startIndex, endIndex), select); @@ -688,7 +688,7 @@ namespace Avalonia.Controls if (info.Node.DataCount == 0) { // Select only leaf nodes - info.ParentNode.Select(info.Path.GetAt(info.Path.GetSize() - 1), select); + info.ParentNode!.Select(info.Path.GetAt(info.Path.GetSize() - 1), select); } }); diff --git a/src/Avalonia.Controls/SelectionModelChildrenRequestedEventArgs.cs b/src/Avalonia.Controls/SelectionModelChildrenRequestedEventArgs.cs index c5571b9f74..aa5a9b5cad 100644 --- a/src/Avalonia.Controls/SelectionModelChildrenRequestedEventArgs.cs +++ b/src/Avalonia.Controls/SelectionModelChildrenRequestedEventArgs.cs @@ -4,27 +4,53 @@ // Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation. using System; -using System.Collections.Generic; -using System.Text; + +#nullable enable namespace Avalonia.Controls { public class SelectionModelChildrenRequestedEventArgs : EventArgs { - private SelectionNode _sourceNode; + private object? _source; + private SelectionNode? _sourceNode; internal SelectionModelChildrenRequestedEventArgs(object source, SelectionNode sourceNode) { - Initialize(source, sourceNode); + _source = source; + _sourceNode = sourceNode; } - public object Children { get; set; } - public object Source { get; private set; } - public IndexPath SourceIndex => _sourceNode.IndexPath; + public object? Children { get; set; } + + public object Source + { + get + { + if (_source == null) + { + throw new ObjectDisposedException(nameof(SelectionModelChildrenRequestedEventArgs)); + } + + return _source; + } + } + + public IndexPath SourceIndex + { + get + { + if (_sourceNode == null) + { + throw new ObjectDisposedException(nameof(SelectionModelChildrenRequestedEventArgs)); + } + + return _sourceNode.IndexPath; + } + } - internal void Initialize(object source, SelectionNode sourceNode) + internal void Initialize(object? source, SelectionNode? sourceNode) { - Source = source; + _source = source; _sourceNode = sourceNode; } } diff --git a/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs b/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs index 8c9e0343de..c8edc1f8ae 100644 --- a/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs +++ b/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs @@ -4,8 +4,8 @@ // Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation. using System; -using System.Collections.Generic; -using System.Text; + +#nullable enable namespace Avalonia.Controls { diff --git a/src/Avalonia.Controls/SelectionNode.cs b/src/Avalonia.Controls/SelectionNode.cs index f5d93681c4..363eb35b94 100644 --- a/src/Avalonia.Controls/SelectionNode.cs +++ b/src/Avalonia.Controls/SelectionNode.cs @@ -8,6 +8,8 @@ using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; +#nullable enable + namespace Avalonia.Controls { /// @@ -22,17 +24,14 @@ namespace Avalonia.Controls internal class SelectionNode : IDisposable { private readonly SelectionModel _manager; - private readonly List _childrenNodes = new List(); - private readonly SelectionNode _parent; + private readonly List _childrenNodes = new List(); + private readonly SelectionNode? _parent; private readonly List _selected = new List(); - private object _source; - private ItemsSourceView _dataSource; - private int _selectedCount; - private List _selectedIndicesCached = new List(); + private readonly List _selectedIndicesCached = new List(); + private object? _source; private bool _selectedIndicesCacheIsValid; - private int _realizedChildrenNodeCount; - public SelectionNode(SelectionModel manager, SelectionNode parent) + public SelectionNode(SelectionModel manager, SelectionNode? parent) { _manager = manager; _parent = parent; @@ -40,7 +39,7 @@ namespace Avalonia.Controls public int AnchorIndex { get; set; } = -1; - public object Source + public object? Source { get => _source; set @@ -60,7 +59,7 @@ namespace Avalonia.Controls newDataSource = new ItemsSourceView((IEnumerable)value); } - _dataSource = newDataSource; + ItemsSourceView = newDataSource; HookupCollectionChangedHandler(); OnSelectionChanged(); @@ -68,10 +67,10 @@ namespace Avalonia.Controls } } - public ItemsSourceView ItemsSourceView => _dataSource; - public int DataCount => _dataSource?.Count ?? 0; + public ItemsSourceView? ItemsSourceView { get; private set; } + public int DataCount => ItemsSourceView?.Count ?? 0; public int ChildrenNodeCount => _childrenNodes.Count; - public int RealizedChildrenNodeCount => _realizedChildrenNodeCount; + public int RealizedChildrenNodeCount { get; private set; } public IndexPath IndexPath { @@ -101,17 +100,22 @@ namespace Avalonia.Controls // 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) + public SelectionNode? GetAt(int index, bool realizeChild) { - SelectionNode child = null; + SelectionNode? child = null; if (realizeChild) { + if (ItemsSourceView == null || index < 0 || index >= ItemsSourceView.Count) + { + throw new IndexOutOfRangeException(); + } + if (_childrenNodes.Count == 0) { - if (_dataSource != null) + if (ItemsSourceView != null) { - for (int i = 0; i < _dataSource.Count; i++) + for (int i = 0; i < ItemsSourceView.Count; i++) { _childrenNodes.Add(null); } @@ -120,7 +124,7 @@ namespace Avalonia.Controls if (_childrenNodes[index] == null) { - var childData = _dataSource.GetAt(index); + var childData = ItemsSourceView!.GetAt(index); if (childData != null) { @@ -142,7 +146,7 @@ namespace Avalonia.Controls } _childrenNodes[index] = child; - _realizedChildrenNodeCount++; + RealizedChildrenNodeCount++; } else { @@ -160,7 +164,7 @@ namespace Avalonia.Controls return child; } - public int SelectedCount => _selectedCount; + public int SelectedCount { get; private set; } public bool IsSelected(int index) { @@ -205,7 +209,7 @@ namespace Avalonia.Controls // Null -> Some descendents are selected and some are not public bool? IsSelectedWithPartial(int index) { - var selectionState = SelectionState.NotSelected; + SelectionState selectionState; if (_childrenNodes.Count == 0 || // no nodes realized _childrenNodes.Count <= index || // target node is not realized @@ -221,7 +225,7 @@ namespace Avalonia.Controls // 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(); + selectionState = targetNode!.EvaluateIsSelectedBasedOnChildrenNodes(); } return ConvertToNullableBool(selectionState); @@ -274,7 +278,7 @@ namespace Avalonia.Controls public void Dispose() { - _dataSource?.Dispose(); + ItemsSourceView?.Dispose(); UnhookCollectionChangedHandler(); } @@ -290,9 +294,9 @@ namespace Avalonia.Controls public void SelectAll() { - if (_dataSource != null) + if (ItemsSourceView != null) { - var size = _dataSource.Count; + var size = ItemsSourceView.Count; if (size > 0) { @@ -324,17 +328,17 @@ namespace Avalonia.Controls private void HookupCollectionChangedHandler() { - if (_dataSource != null) + if (ItemsSourceView != null) { - _dataSource.CollectionChanged += OnSourceListChanged; + ItemsSourceView.CollectionChanged += OnSourceListChanged; } } private void UnhookCollectionChangedHandler() { - if (_dataSource != null) + if (ItemsSourceView != null) { - _dataSource.CollectionChanged -= OnSourceListChanged; + ItemsSourceView.CollectionChanged -= OnSourceListChanged; } } @@ -353,11 +357,11 @@ namespace Avalonia.Controls { if (!IsSelected(i)) { - _selectedCount++; + SelectedCount++; } } - if (oldCount != _selectedCount) + if (oldCount != SelectedCount) { _selected.Add(addRange); @@ -370,18 +374,18 @@ namespace Avalonia.Controls private void RemoveRange(IndexRange removeRange, bool raiseOnSelectionChanged) { - int oldCount = _selectedCount; + int oldCount = SelectedCount; // TODO: Prevent overlap of Ranges in _selected (Task 14107720) for (int i = removeRange.Begin; i <= removeRange.End; i++) { if (IsSelected(i)) { - _selectedCount--; + SelectedCount--; } } - if (oldCount != _selectedCount) + if (oldCount != SelectedCount) { // Build up a both a list of Ranges to remove and ranges to add var toRemove = new List(); @@ -448,7 +452,7 @@ namespace Avalonia.Controls OnSelectionChanged(); } - _selectedCount = 0; + SelectedCount = 0; AnchorIndex = -1; // This will throw away all the children SelectionNodes @@ -576,7 +580,7 @@ namespace Avalonia.Controls // Adjust the anchor if (AnchorIndex >= index) { - AnchorIndex = AnchorIndex + count; + AnchorIndex += count; } // Check if adding a node invalidated an ancestors @@ -610,7 +614,7 @@ namespace Avalonia.Controls bool selectionInvalidated = false; // Remove the items from the selection for leaf - if (ItemsSourceView.Count > 0) + if (ItemsSourceView!.Count > 0) { bool isSelected = false; @@ -650,7 +654,7 @@ namespace Avalonia.Controls { if (_childrenNodes[index] != null) { - _realizedChildrenNodeCount--; + RealizedChildrenNodeCount--; } _childrenNodes.RemoveAt(index); } @@ -659,14 +663,14 @@ namespace Avalonia.Controls //Adjust the anchor if (AnchorIndex >= index) { - AnchorIndex = AnchorIndex - count; + AnchorIndex -= count; } } else { // No more items in the list, clear ClearSelection(); - _realizedChildrenNodeCount = 0; + RealizedChildrenNodeCount = 0; selectionInvalidated = true; } @@ -719,7 +723,7 @@ namespace Avalonia.Controls public SelectionState EvaluateIsSelectedBasedOnChildrenNodes() { - SelectionState selectionState = SelectionState.NotSelected; + var selectionState = SelectionState.NotSelected; int realizedChildrenNodeCount = RealizedChildrenNodeCount; int selectedCount = SelectedCount; @@ -739,7 +743,6 @@ namespace Avalonia.Controls { // There are child nodes, walk them individually and evaluate based on each child // being selected/not selected or partially selected. - bool isSelected = false; selectedCount = 0; int notSelectedCount = 0; for (int i = 0; i < ChildrenNodeCount; i++) diff --git a/src/Avalonia.Controls/Utils/SelectionTreeHelper.cs b/src/Avalonia.Controls/Utils/SelectionTreeHelper.cs index 38b1dde5d7..93102a7b5b 100644 --- a/src/Avalonia.Controls/Utils/SelectionTreeHelper.cs +++ b/src/Avalonia.Controls/Utils/SelectionTreeHelper.cs @@ -7,6 +7,8 @@ using System; using System.Collections.Generic; using System.Linq; +#nullable enable + namespace Avalonia.Controls.Utils { internal static class SelectionTreeHelper @@ -26,7 +28,7 @@ namespace Avalonia.Controls.Utils if (depth < path.GetSize() - 1) { - node = node.GetAt(childIndex, realizeChildren); + node = node.GetAt(childIndex, realizeChildren)!; } } } @@ -48,7 +50,7 @@ namespace Avalonia.Controls.Utils int count = realizeChildren ? nextNode.Node.DataCount : nextNode.Node.ChildrenNodeCount; for (int i = count - 1; i >= 0; i--) { - SelectionNode child = nextNode.Node.GetAt(i, realizeChildren); + var child = nextNode.Node.GetAt(i, realizeChildren); var childPath = nextNode.Path.CloneWithChildIndex(i); if (child != null) { @@ -154,7 +156,7 @@ namespace Avalonia.Controls.Utils public struct TreeWalkNodeInfo { - public TreeWalkNodeInfo(SelectionNode node, IndexPath indexPath, SelectionNode parent) + public TreeWalkNodeInfo(SelectionNode node, IndexPath indexPath, SelectionNode? parent) { node = node ?? throw new ArgumentNullException(nameof(node)); @@ -174,7 +176,7 @@ namespace Avalonia.Controls.Utils public SelectionNode Node { get; } public IndexPath Path { get; } - public SelectionNode ParentNode { get; } + public SelectionNode? ParentNode { get; } }; } From 8640393a26f5d4ff786c56ee2ea092591e5b530e Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 26 Jan 2020 09:59:48 +0100 Subject: [PATCH 022/663] Add `IndexRange` list add/remove methods. Add or remove index ranges from a list of index ranges, merging and splitting ranges as required. --- src/Avalonia.Controls/IndexRange.cs | 173 +++++++++- .../IndexRangeTests.cs | 307 ++++++++++++++++++ 2 files changed, 474 insertions(+), 6 deletions(-) create mode 100644 tests/Avalonia.Controls.UnitTests/IndexRangeTests.cs diff --git a/src/Avalonia.Controls/IndexRange.cs b/src/Avalonia.Controls/IndexRange.cs index b1a112ab39..124f1e0500 100644 --- a/src/Avalonia.Controls/IndexRange.cs +++ b/src/Avalonia.Controls/IndexRange.cs @@ -3,12 +3,17 @@ // // Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation. +using System; +using System.Collections.Generic; + #nullable enable namespace Avalonia.Controls { - internal readonly struct IndexRange + internal readonly struct IndexRange : IEquatable { + private static readonly IndexRange s_invalid = new IndexRange(int.MinValue, int.MinValue); + public IndexRange(int begin, int end) { // Accept out of order begin/end pairs, just swap them. @@ -25,11 +30,9 @@ namespace Avalonia.Controls public int Begin { get; } public int End { get; } + public int Count => (End - Begin) + 1; - public bool Contains(int index) - { - return index >= Begin && index <= End; - } + public bool Contains(int index) => index >= Begin && index <= End; public bool Split(int splitIndex, out IndexRange before, out IndexRange after) { @@ -54,6 +57,164 @@ namespace Avalonia.Controls public bool Intersects(IndexRange other) { return (Begin <= other.End) && (End >= other.Begin); - } + } + + public bool Adjacent(IndexRange other) + { + return Begin == other.End + 1 || End == other.Begin - 1; + } + + public override bool Equals(object? obj) + { + return obj is IndexRange range && Equals(range); + } + + public bool Equals(IndexRange other) + { + return Begin == other.Begin && End == other.End; + } + + public override int GetHashCode() + { + var hashCode = 1903003160; + hashCode = hashCode * -1521134295 + Begin.GetHashCode(); + hashCode = hashCode * -1521134295 + End.GetHashCode(); + return hashCode; + } + + public override string ToString() => $"[{Begin}..{End}]"; + + public static bool operator ==(IndexRange left, IndexRange right) => left.Equals(right); + public static bool operator !=(IndexRange left, IndexRange right) => !(left == right); + + public static int Add( + IList ranges, + IndexRange range, + IList? added = null) + { + var result = 0; + + for (var i = 0; i < ranges.Count && range != s_invalid; ++i) + { + var existing = ranges[i]; + + if (range.Intersects(existing) || range.Adjacent(existing)) + { + if (range.Begin < existing.Begin) + { + var add = new IndexRange(range.Begin, existing.Begin - 1); + ranges[i] = new IndexRange(range.Begin, existing.End); + added?.Add(add); + result += add.Count; + } + + range = range.End <= existing.End ? + s_invalid : + new IndexRange(existing.End + 1, range.End); + } + else if (range.End < existing.Begin) + { + ranges.Insert(i, range); + added?.Add(range); + result += range.Count; + range = s_invalid; + } + } + + if (range != s_invalid) + { + ranges.Add(range); + added?.Add(range); + result += range.Count; + } + + MergeRanges(ranges); + return result; + } + + public static int Remove( + IList ranges, + IndexRange range, + IList? removed = null) + { + var result = 0; + + for (var i = 0; i < ranges.Count; ++i) + { + var existing = ranges[i]; + + if (range.Intersects(existing)) + { + if (range.Begin <= existing.Begin && range.End >= existing.End) + { + ranges.RemoveAt(i--); + removed?.Add(existing); + result += existing.Count; + } + else if (range.Begin > existing.Begin && range.End >= existing.End) + { + ranges[i] = new IndexRange(existing.Begin, range.Begin - 1); + removed?.Add(new IndexRange(range.Begin, existing.End)); + result += existing.End - (range.Begin - 1); + } + else if (range.Begin > existing.Begin && range.End < existing.End) + { + ranges[i] = new IndexRange(existing.Begin, range.Begin - 1); + ranges.Insert(++i, new IndexRange(range.End + 1, existing.End)); + removed?.Add(range); + result += range.Count; + } + else if (range.End <= existing.End) + { + var remove = new IndexRange(existing.Begin, range.End); + ranges[i] = new IndexRange(range.End + 1, existing.End); + removed?.Add(remove); + result += remove.Count; + } + } + } + + return result; + } + + public static IEnumerable Subtract( + IndexRange lhs, + IEnumerable rhs) + { + var result = new List { lhs }; + + foreach (var range in rhs) + { + Remove(result, range); + } + + return result; + } + + public static IEnumerable EnumerateIndices(IEnumerable ranges) + { + foreach (var range in ranges) + { + for (var i = range.Begin; i <= range.End; ++i) + { + yield return i; + } + } + } + + private static void MergeRanges(IList ranges) + { + for (var i = ranges.Count - 2; i >= 0; --i) + { + var r = ranges[i]; + var r1 = ranges[i + 1]; + + if (r.Intersects(r1) || r.End == r1.Begin - 1) + { + ranges[i] = new IndexRange(r.Begin, r1.End); + ranges.RemoveAt(i + 1); + } + } + } } } diff --git a/tests/Avalonia.Controls.UnitTests/IndexRangeTests.cs b/tests/Avalonia.Controls.UnitTests/IndexRangeTests.cs new file mode 100644 index 0000000000..e0f46d9fa9 --- /dev/null +++ b/tests/Avalonia.Controls.UnitTests/IndexRangeTests.cs @@ -0,0 +1,307 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace Avalonia.Controls.UnitTests +{ + public class IndexRangeTests + { + [Fact] + public void Add_Should_Add_Range_To_Empty_List() + { + var ranges = new List(); + var selected = new List(); + var result = IndexRange.Add(ranges, new IndexRange(0, 4), selected); + + Assert.Equal(5, result); + Assert.Equal(new[] { new IndexRange(0, 4) }, ranges); + Assert.Equal(new[] { new IndexRange(0, 4) }, selected); + } + + [Fact] + public void Add_Should_Add_Non_Intersecting_Range_At_End() + { + var ranges = new List { new IndexRange(0, 4) }; + var selected = new List(); + var result = IndexRange.Add(ranges, new IndexRange(8, 10), selected); + + Assert.Equal(3, result); + Assert.Equal(new[] { new IndexRange(0, 4), new IndexRange(8, 10) }, ranges); + Assert.Equal(new[] { new IndexRange(8, 10) }, selected); + } + + [Fact] + public void Add_Should_Add_Non_Intersecting_Range_At_Beginning() + { + var ranges = new List { new IndexRange(8, 10) }; + var selected = new List(); + var result = IndexRange.Add(ranges, new IndexRange(0, 4), selected); + + Assert.Equal(5, result); + Assert.Equal(new[] { new IndexRange(0, 4), new IndexRange(8, 10) }, ranges); + Assert.Equal(new[] { new IndexRange(0, 4) }, selected); + } + + [Fact] + public void Add_Should_Add_Non_Intersecting_Range_In_Middle() + { + var ranges = new List { new IndexRange(0, 4), new IndexRange(14, 16) }; + var selected = new List(); + var result = IndexRange.Add(ranges, new IndexRange(8, 10), selected); + + Assert.Equal(3, result); + Assert.Equal(new[] { new IndexRange(0, 4), new IndexRange(8, 10), new IndexRange(14, 16) }, ranges); + Assert.Equal(new[] { new IndexRange(8, 10) }, selected); + } + + [Fact] + public void Add_Should_Add_Intersecting_Range_Start() + { + var ranges = new List { new IndexRange(8, 10) }; + var selected = new List(); + var result = IndexRange.Add(ranges, new IndexRange(6, 9), selected); + + Assert.Equal(2, result); + Assert.Equal(new[] { new IndexRange(6, 10) }, ranges); + Assert.Equal(new[] { new IndexRange(6, 7) }, selected); + } + + [Fact] + public void Add_Should_Add_Intersecting_Range_End() + { + var ranges = new List { new IndexRange(8, 10) }; + var selected = new List(); + var result = IndexRange.Add(ranges, new IndexRange(9, 12), selected); + + Assert.Equal(2, result); + Assert.Equal(new[] { new IndexRange(8, 12) }, ranges); + Assert.Equal(new[] { new IndexRange(11, 12) }, selected); + } + + [Fact] + public void Add_Should_Add_Intersecting_Range_Both() + { + var ranges = new List { new IndexRange(8, 10) }; + var selected = new List(); + var result = IndexRange.Add(ranges, new IndexRange(6, 12), selected); + + Assert.Equal(4, result); + Assert.Equal(new[] { new IndexRange(6, 12) }, ranges); + Assert.Equal(new[] { new IndexRange(6, 7), new IndexRange(11, 12) }, selected); + } + + [Fact] + public void Add_Should_Join_Two_Intersecting_Ranges() + { + var ranges = new List { new IndexRange(8, 10), new IndexRange(12, 14) }; + var selected = new List(); + var result = IndexRange.Add(ranges, new IndexRange(8, 14), selected); + + Assert.Equal(1, result); + Assert.Equal(new[] { new IndexRange(8, 14) }, ranges); + Assert.Equal(new[] { new IndexRange(11, 11) }, selected); + } + + [Fact] + public void Add_Should_Join_Two_Intersecting_Ranges_And_Add_Ranges() + { + var ranges = new List { new IndexRange(8, 10), new IndexRange(12, 14) }; + var selected = new List(); + var result = IndexRange.Add(ranges, new IndexRange(6, 18), selected); + + Assert.Equal(7, result); + Assert.Equal(new[] { new IndexRange(6, 18) }, ranges); + Assert.Equal(new[] { new IndexRange(6, 7), new IndexRange(11, 11), new IndexRange(15, 18) }, selected); + } + + [Fact] + public void Add_Should_Not_Add_Already_Selected_Range() + { + var ranges = new List { new IndexRange(8, 10) }; + var selected = new List(); + var result = IndexRange.Add(ranges, new IndexRange(9, 10), selected); + + Assert.Equal(0, result); + Assert.Equal(new[] { new IndexRange(8, 10) }, ranges); + Assert.Empty(selected); + } + + [Fact] + public void Remove_Should_Remove_Entire_Range() + { + var ranges = new List { new IndexRange(8, 10) }; + var deselected = new List(); + var result = IndexRange.Remove(ranges, new IndexRange(8, 10), deselected); + + Assert.Equal(3, result); + Assert.Empty(ranges); + Assert.Equal(new[] { new IndexRange(8, 10) }, deselected); + } + + [Fact] + public void Remove_Should_Remove_Start_Of_Range() + { + var ranges = new List { new IndexRange(8, 12) }; + var deselected = new List(); + var result = IndexRange.Remove(ranges, new IndexRange(8, 10), deselected); + + Assert.Equal(3, result); + Assert.Equal(new[] { new IndexRange(11, 12) }, ranges); + Assert.Equal(new[] { new IndexRange(8, 10) }, deselected); + } + + [Fact] + public void Remove_Should_Remove_End_Of_Range() + { + var ranges = new List { new IndexRange(8, 12) }; + var deselected = new List(); + var result = IndexRange.Remove(ranges, new IndexRange(10, 12), deselected); + + Assert.Equal(3, result); + Assert.Equal(new[] { new IndexRange(8, 9) }, ranges); + Assert.Equal(new[] { new IndexRange(10, 12) }, deselected); + } + + [Fact] + public void Remove_Should_Remove_Overlapping_End_Of_Range() + { + var ranges = new List { new IndexRange(8, 12) }; + var deselected = new List(); + var result = IndexRange.Remove(ranges, new IndexRange(10, 14), deselected); + + Assert.Equal(3, result); + Assert.Equal(new[] { new IndexRange(8, 9) }, ranges); + Assert.Equal(new[] { new IndexRange(10, 12) }, deselected); + } + + [Fact] + public void Remove_Should_Remove_Middle_Of_Range() + { + var ranges = new List { new IndexRange(10, 20) }; + var deselected = new List(); + var result = IndexRange.Remove(ranges, new IndexRange(12, 16), deselected); + + Assert.Equal(5, result); + Assert.Equal(new[] { new IndexRange(10, 11), new IndexRange(17, 20) }, ranges); + Assert.Equal(new[] { new IndexRange(12, 16) }, deselected); + } + + [Fact] + public void Remove_Should_Remove_Multiple_Ranges() + { + var ranges = new List { new IndexRange(8, 10), new IndexRange(12, 14), new IndexRange(16, 18) }; + var deselected = new List(); + var result = IndexRange.Remove(ranges, new IndexRange(6, 15), deselected); + + Assert.Equal(6, result); + Assert.Equal(new[] { new IndexRange(16, 18) }, ranges); + Assert.Equal(new[] { new IndexRange(8, 10), new IndexRange(12, 14) }, deselected); + } + + [Fact] + public void Remove_Should_Remove_Multiple_And_Partial_Ranges_1() + { + var ranges = new List { new IndexRange(8, 10), new IndexRange(12, 14), new IndexRange(16, 18) }; + var deselected = new List(); + var result = IndexRange.Remove(ranges, new IndexRange(9, 15), deselected); + + Assert.Equal(5, result); + Assert.Equal(new[] { new IndexRange(8, 8), new IndexRange(16, 18) }, ranges); + Assert.Equal(new[] { new IndexRange(9, 10), new IndexRange(12, 14) }, deselected); + } + + [Fact] + public void Remove_Should_Remove_Multiple_And_Partial_Ranges_2() + { + var ranges = new List { new IndexRange(8, 10), new IndexRange(12, 14), new IndexRange(16, 18) }; + var deselected = new List(); + var result = IndexRange.Remove(ranges, new IndexRange(8, 13), deselected); + + Assert.Equal(5, result); + Assert.Equal(new[] { new IndexRange(14, 14), new IndexRange(16, 18) }, ranges); + Assert.Equal(new[] { new IndexRange(8, 10), new IndexRange(12, 13) }, deselected); + } + + [Fact] + public void Remove_Should_Remove_Multiple_And_Partial_Ranges_3() + { + var ranges = new List { new IndexRange(8, 10), new IndexRange(12, 14), new IndexRange(16, 18) }; + var deselected = new List(); + var result = IndexRange.Remove(ranges, new IndexRange(9, 13), deselected); + + Assert.Equal(4, result); + Assert.Equal(new[] { new IndexRange(8, 8), new IndexRange(14, 14), new IndexRange(16, 18) }, ranges); + Assert.Equal(new[] { new IndexRange(9, 10), new IndexRange(12, 13) }, deselected); + } + + [Fact] + public void Remove_Should_Do_Nothing_For_Unselected_Range() + { + var ranges = new List { new IndexRange(8, 10) }; + var deselected = new List(); + var result = IndexRange.Remove(ranges, new IndexRange(2, 4), deselected); + + Assert.Equal(0, result); + Assert.Equal(new[] { new IndexRange(8, 10) }, ranges); + Assert.Empty(deselected); + } + + [Fact] + public void Stress_Test() + { + const int iterations = 100; + var random = new Random(0); + var selection = new List(); + var expected = new List(); + + IndexRange Generate() + { + var start = random.Next(100); + return new IndexRange(start, start + random.Next(20)); + } + + for (var i = 0; i < iterations; ++i) + { + var toAdd = random.Next(5); + + for (var j = 0; j < toAdd; ++j) + { + var range = Generate(); + IndexRange.Add(selection, range); + + for (var k = range.Begin; k <= range.End; ++k) + { + if (!expected.Contains(k)) + { + expected.Add(k); + } + } + + var actual = IndexRange.EnumerateIndices(selection).ToList(); + expected.Sort(); + Assert.Equal(expected, actual); + } + + var toRemove = random.Next(5); + + for (var j = 0; j < toRemove; ++j) + { + var range = Generate(); + IndexRange.Remove(selection, range); + + for (var k = range.Begin; k <= range.End; ++k) + { + expected.Remove(k); + } + + var actual = IndexRange.EnumerateIndices(selection).ToList(); + Assert.Equal(expected, actual); + } + + selection.Clear(); + expected.Clear(); + } + } + } +} From 88794773229e5c9fe93f29672c41b9cbac601a89 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 26 Jan 2020 11:47:51 +0100 Subject: [PATCH 023/663] Added SelectionModel changed args. `SelectionModel` as ported from WinUI has no information about what changed in a `SelectionChanged` event. This adds that information along with unit tests. --- src/Avalonia.Controls/SelectionModel.cs | 191 +++++--- .../SelectionModelChangeSet.cs | 144 ++++++ ...SelectionModelSelectionChangedEventArgs.cs | 45 ++ src/Avalonia.Controls/SelectionNode.cs | 150 +++---- .../SelectionModelTests.cs | 416 ++++++++++++++++++ 5 files changed, 803 insertions(+), 143 deletions(-) create mode 100644 src/Avalonia.Controls/SelectionModelChangeSet.cs diff --git a/src/Avalonia.Controls/SelectionModel.cs b/src/Avalonia.Controls/SelectionModel.cs index 34d5f78434..5e2fb32243 100644 --- a/src/Avalonia.Controls/SelectionModel.cs +++ b/src/Avalonia.Controls/SelectionModel.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Linq; using Avalonia.Controls.Utils; #nullable enable @@ -19,7 +20,6 @@ namespace Avalonia.Controls private IReadOnlyList? _selectedIndicesCached; private IReadOnlyList? _selectedItemsCached; private SelectionModelChildrenRequestedEventArgs? _childrenRequestedEventArgs; - private SelectionModelSelectionChangedEventArgs? _selectionChangedEventArgs; public event EventHandler? ChildrenRequested; public event PropertyChangedEventHandler? PropertyChanged; @@ -36,9 +36,12 @@ namespace Avalonia.Controls get => _rootNode?.Source; set { - ClearSelection(resetAnchor: true, raiseSelectionChanged: false); + using (var operation = new Operation(this)) + { + ClearSelection(resetAnchor: true); + } + _rootNode.Source = value; - OnSelectionChanged(); RaisePropertyChanged("Source"); } } @@ -55,12 +58,13 @@ namespace Avalonia.Controls 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, raiseSelectionChanged: false); - SelectWithPathImpl(firstSelectionIndexPath, select: true, raiseSelectionChanged: false); - // Setting SelectedIndex will raise SelectionChanged event. + ClearSelection(resetAnchor: true); + SelectWithPathImpl(firstSelectionIndexPath, select: true); SelectedIndex = firstSelectionIndexPath; } @@ -131,9 +135,9 @@ namespace Avalonia.Controls if (!isSelected.HasValue || !isSelected.Value) { - ClearSelection(resetAnchor: true, raiseSelectionChanged: false); - SelectWithPathImpl(value, select: true, raiseSelectionChanged: false); - OnSelectionChanged(); + using var operation = new Operation(this); + ClearSelection(resetAnchor: true); + SelectWithPathImpl(value, select: true); } } } @@ -289,7 +293,7 @@ namespace Avalonia.Controls public void Dispose() { - ClearSelection(resetAnchor: false, raiseSelectionChanged: false); + ClearSelection(resetAnchor: false); _rootNode?.Dispose(); _selectedIndicesCached = null; _selectedItemsCached = null; @@ -299,17 +303,41 @@ namespace Avalonia.Controls public void SetAnchorIndex(int groupIndex, int index) => AnchorIndex = new IndexPath(groupIndex, index); - public void Select(int index) => SelectImpl(index, select: true); + public void Select(int index) + { + using var operation = new Operation(this); + SelectImpl(index, select: true); + } - public void Select(int groupIndex, int itemIndex) => SelectWithGroupImpl(groupIndex, itemIndex, 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) => SelectWithPathImpl(index, select: true, raiseSelectionChanged: true); + public void SelectAt(IndexPath index) + { + using var operation = new Operation(this); + SelectWithPathImpl(index, select: true); + } - public void Deselect(int index) => SelectImpl(index, select: false); + public void Deselect(int index) + { + using var operation = new Operation(this); + SelectImpl(index, select: false); + } - public void Deselect(int groupIndex, int itemIndex) => SelectWithGroupImpl(groupIndex, itemIndex, 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) => SelectWithPathImpl(index, select: false, raiseSelectionChanged: true); + public void DeselectAt(IndexPath index) + { + using var operation = new Operation(this); + SelectWithPathImpl(index, select: false); + } public bool? IsSelected(int index) { @@ -383,46 +411,56 @@ namespace Avalonia.Controls 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, @@ -433,13 +471,12 @@ namespace Avalonia.Controls info.Node.SelectAll(); } }); - - OnSelectionChanged(); } public void ClearSelection() { - ClearSelection(resetAnchor: true, raiseSelectionChanged: true); + using var operation = new Operation(this); + ClearSelection(resetAnchor: true); } protected void OnPropertyChanged(string propertyName) @@ -452,9 +489,15 @@ namespace Avalonia.Controls PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } - public void OnSelectionInvalidatedDueToCollectionChange() + public void OnSelectionInvalidatedDueToCollectionChange( + IEnumerable? removedItems) { - OnSelectionChanged(); + var e = new SelectionModelSelectionChangedEventArgs( + Enumerable.Empty(), + Enumerable.Empty(), + removedItems ?? Enumerable.Empty(), + Enumerable.Empty()); + OnSelectionChanged(e); } internal object? ResolvePath(object data, SelectionNode sourceNode) @@ -496,7 +539,7 @@ namespace Avalonia.Controls return resolved; } - private void ClearSelection(bool resetAnchor, bool raiseSelectionChanged) + private void ClearSelection(bool resetAnchor) { SelectionTreeHelper.Traverse( _rootNode, @@ -507,27 +550,17 @@ namespace Avalonia.Controls { AnchorIndex = default; } - - if (raiseSelectionChanged) - { - OnSelectionChanged(); - } } - private void OnSelectionChanged() + private void OnSelectionChanged(SelectionModelSelectionChangedEventArgs? e = null) { _selectedIndicesCached = null; _selectedItemsCached = null; // Raise SelectionChanged event - if (SelectionChanged != null) + if (e != null) { - if (_selectionChangedEventArgs == null) - { - _selectionChangedEventArgs = new SelectionModelSelectionChangedEventArgs(); - } - - SelectionChanged(this, _selectionChangedEventArgs); + SelectionChanged?.Invoke(this, e); } RaisePropertyChanged(nameof(SelectedIndex)); @@ -544,7 +577,7 @@ namespace Avalonia.Controls { if (_singleSelect) { - ClearSelection(resetAnchor: true, raiseSelectionChanged: false); + ClearSelection(resetAnchor: true); } var selected = _rootNode.Select(index, select); @@ -553,15 +586,13 @@ namespace Avalonia.Controls { AnchorIndex = new IndexPath(index); } - - OnSelectionChanged(); } private void SelectWithGroupImpl(int groupIndex, int itemIndex, bool select) { if (_singleSelect) { - ClearSelection(resetAnchor: true, raiseSelectionChanged: false); + ClearSelection(resetAnchor: true); } var childNode = _rootNode.GetAt(groupIndex, realizeChild: true); @@ -571,17 +602,15 @@ namespace Avalonia.Controls { AnchorIndex = new IndexPath(groupIndex, itemIndex); } - - OnSelectionChanged(); } - private void SelectWithPathImpl(IndexPath index, bool select, bool raiseSelectionChanged) + private void SelectWithPathImpl(IndexPath index, bool select) { bool selected = false; if (_singleSelect) { - ClearSelection(resetAnchor: true, raiseSelectionChanged: false); + ClearSelection(resetAnchor: true); } SelectionTreeHelper.TraverseIndexPath( @@ -601,11 +630,6 @@ namespace Avalonia.Controls { AnchorIndex = index; } - - if (raiseSelectionChanged) - { - OnSelectionChanged(); - } } private void SelectRangeFromAnchorImpl(int index, bool select) @@ -618,12 +642,7 @@ namespace Avalonia.Controls anchorIndex = anchor.GetAt(0); } - bool selected = _rootNode.SelectRange(new IndexRange(anchorIndex, index), select); - - if (selected) - { - OnSelectionChanged(); - } + _rootNode.SelectRange(new IndexRange(anchorIndex, index), select); } private void SelectRangeFromAnchorWithGroupImpl(int endGroupIndex, int endItemIndex, bool select) @@ -650,18 +669,12 @@ namespace Avalonia.Controls endItemIndex = temp; } - var selected = false; for (int groupIdx = startGroupIndex; groupIdx <= endGroupIndex; groupIdx++) { var groupNode = _rootNode.GetAt(groupIdx, realizeChild: true)!; int startIndex = groupIdx == startGroupIndex ? startItemIndex : 0; int endIndex = groupIdx == endGroupIndex ? endItemIndex : groupNode.DataCount - 1; - selected |= groupNode.SelectRange(new IndexRange(startIndex, endIndex), select); - } - - if (selected) - { - OnSelectionChanged(); + groupNode.SelectRange(new IndexRange(startIndex, endIndex), select); } } @@ -691,8 +704,55 @@ namespace Avalonia.Controls info.ParentNode!.Select(info.Path.GetAt(info.Path.GetSize() - 1), select); } }); + } + + private void BeginOperation() + { + if (SelectionChanged != null) + { + _rootNode.BeginOperation(); + } + } + + private void EndOperation() + { + static IEnumerable? Concat(IEnumerable? a, IEnumerable b) + { + return a == null ? b : a.Concat(b); + } - OnSelectionChanged(); + SelectionModelSelectionChangedEventArgs? e = null; + + if (SelectionChanged != null) + { + IEnumerable? selectedIndices = null; + IEnumerable? deselectedIndices = null; + IEnumerable? selectedItems = null; + IEnumerable? deselectedItems = null; + + foreach (var changes in _rootNode.EndOperation()) + { + if (changes.HasChanges) + { + selectedIndices = Concat(selectedIndices, changes.SelectedIndices); + deselectedIndices = Concat(deselectedIndices, changes.DeselectedIndices); + selectedItems = Concat(selectedItems, changes.SelectedItems); + deselectedItems = Concat(deselectedItems, changes.DeselectedItems); + } + } + + if (selectedIndices != null || deselectedIndices != null || + selectedItems != null || deselectedItems != null) + { + e = new SelectionModelSelectionChangedEventArgs( + deselectedIndices ?? Enumerable.Empty(), + selectedIndices ?? Enumerable.Empty(), + deselectedItems ?? Enumerable.Empty(), + selectedItems ?? Enumerable.Empty()); + } + } + + OnSelectionChanged(e); } internal class SelectedItemInfo @@ -706,5 +766,12 @@ namespace Avalonia.Controls public SelectionNode Node { get; } public IndexPath Path { get; } } + + private struct Operation : IDisposable + { + private readonly SelectionModel _manager; + public Operation(SelectionModel manager) => (_manager = manager).BeginOperation(); + public void Dispose() => _manager.EndOperation(); + } } } diff --git a/src/Avalonia.Controls/SelectionModelChangeSet.cs b/src/Avalonia.Controls/SelectionModelChangeSet.cs new file mode 100644 index 0000000000..989136ac8d --- /dev/null +++ b/src/Avalonia.Controls/SelectionModelChangeSet.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +#nullable enable + +namespace Avalonia.Controls +{ + internal class SelectionModelChangeSet + { + private SelectionNode _owner; + private List? _selected; + private List? _deselected; + + public SelectionModelChangeSet(SelectionNode owner) => _owner = owner; + + public bool IsTracking { get; private set; } + public bool HasChanges => _selected?.Count > 0 || _deselected?.Count > 0; + public IEnumerable SelectedIndices => EnumerateIndices(_selected); + public IEnumerable DeselectedIndices => EnumerateIndices(_deselected); + public IEnumerable SelectedItems => EnumerateItems(_selected); + public IEnumerable DeselectedItems => EnumerateItems(_deselected); + + public void BeginOperation() + { + if (IsTracking) + { + throw new AvaloniaInternalException("SelectionModel change operation already in progress."); + } + + IsTracking = true; + _selected?.Clear(); + _deselected?.Clear(); + } + + public void EndOperation() => IsTracking = false; + + public void Selected(IndexRange range) + { + if (!IsTracking) + { + return; + } + + Add(range, ref _selected, _deselected); + } + + public void Selected(IEnumerable ranges) + { + if (!IsTracking) + { + return; + } + + foreach (var range in ranges) + { + Selected(range); + } + } + + public void Deselected(IndexRange range) + { + if (!IsTracking) + { + return; + } + + Add(range, ref _deselected, _selected); + } + + public void Deselected(IEnumerable ranges) + { + if (!IsTracking) + { + return; + } + + foreach (var range in ranges) + { + Deselected(range); + } + } + + private static void Add( + IndexRange range, + ref List? add, + List? remove) + { + if (remove != null) + { + var removed = new List(); + IndexRange.Remove(remove, range, removed); + var selected = IndexRange.Subtract(range, removed); + + if (selected.Any()) + { + add ??= new List(); + + foreach (var r in selected) + { + IndexRange.Add(add, r); + } + } + } + else + { + add ??= new List(); + IndexRange.Add(add, range); + } + } + + private IEnumerable EnumerateIndices(IEnumerable? ranges) + { + var path = _owner.IndexPath; + + if (ranges != null) + { + foreach (var range in ranges) + { + for (var i = range.Begin; i <= range.End; ++i) + { + yield return path.CloneWithChildIndex(i); + } + } + } + } + + private IEnumerable EnumerateItems(IEnumerable? ranges) + { + var items = _owner.ItemsSourceView; + + if (ranges != null && items != null) + { + foreach (var range in ranges) + { + for (var i = range.Begin; i <= range.End; ++i) + { + yield return items.GetAt(i); + } + } + } + } + } +} diff --git a/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs b/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs index c8edc1f8ae..4976bf1827 100644 --- a/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs +++ b/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs @@ -4,6 +4,7 @@ // Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation. using System; +using System.Collections.Generic; #nullable enable @@ -11,5 +12,49 @@ namespace Avalonia.Controls { public class SelectionModelSelectionChangedEventArgs : EventArgs { + private readonly IEnumerable _selectedIndicesSource; + private readonly IEnumerable _deselectedIndicesSource; + private readonly IEnumerable _selectedItemsSource; + private readonly IEnumerable _deselectedItemsSource; + private List? _selectedIndices; + private List? _deselectedIndices; + private List? _selectedItems; + private List? _deselectedItems; + + public SelectionModelSelectionChangedEventArgs( + IEnumerable deselectedIndices, + IEnumerable selectedIndices, + IEnumerable deselectedItems, + IEnumerable selectedItems) + { + _selectedIndicesSource = selectedIndices; + _deselectedIndicesSource = deselectedIndices; + _selectedItemsSource = selectedItems; + _deselectedItemsSource = deselectedItems; + } + + /// + /// Gets the indices of the items that were added to the selection. + /// + public IReadOnlyList SelectedIndices => + _selectedIndices ?? (_selectedIndices = new List(_selectedIndicesSource)); + + /// + /// Gets the indices of the items that were removed from the selection. + /// + public IReadOnlyList DeselectedIndices => + _deselectedIndices ?? (_deselectedIndices = new List(_deselectedIndicesSource)); + + /// + /// Gets the items that were added to the selection. + /// + public IReadOnlyList SelectedItems => + _selectedItems ?? (_selectedItems = new List(_selectedItemsSource)); + + /// + /// Gets the items that were removed from the selection. + /// + public IReadOnlyList DeselectedItems => + _deselectedItems ?? (_deselectedItems = new List(_deselectedItemsSource)); } } diff --git a/src/Avalonia.Controls/SelectionNode.cs b/src/Avalonia.Controls/SelectionNode.cs index 363eb35b94..d462a51228 100644 --- a/src/Avalonia.Controls/SelectionNode.cs +++ b/src/Avalonia.Controls/SelectionNode.cs @@ -7,6 +7,7 @@ using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; +using System.Linq; #nullable enable @@ -28,6 +29,7 @@ namespace Avalonia.Controls private readonly SelectionNode? _parent; private readonly List _selected = new List(); private readonly List _selectedIndicesCached = new List(); + private SelectionModelChangeSet? _changes; private object? _source; private bool _selectedIndicesCacheIsValid; @@ -134,6 +136,11 @@ namespace Avalonia.Controls { child = new SelectionNode(_manager, parent: this); child.Source = resolvedChild; + + if (_changes?.IsTracking == true) + { + child.BeginOperation(); + } } else { @@ -276,12 +283,50 @@ namespace Avalonia.Controls } } + public IEnumerable SelectedItems + { + get => SelectedIndices.Select(x => ItemsSourceView!.GetAt(x)); + } + public void Dispose() { ItemsSourceView?.Dispose(); UnhookCollectionChangedHandler(); } + public void BeginOperation() + { + _changes ??= new SelectionModelChangeSet(this); + _changes.BeginOperation(); + + for (var i = 0; i < _childrenNodes.Count; ++i) + { + _childrenNodes[i]?.BeginOperation(); + } + } + + public IEnumerable EndOperation() + { + if (_changes != null) + { + _changes.EndOperation(); + yield return _changes; + + for (var i = 0; i < _childrenNodes.Count; ++i) + { + var child = _childrenNodes[i]; + + if (child != null) + { + foreach (var changes in child.EndOperation()) + { + yield return changes; + } + } + } + } + } + public bool Select(int index, bool select) { return Select(index, select, raiseOnSelectionChanged: true); @@ -349,21 +394,13 @@ namespace Avalonia.Controls private void AddRange(IndexRange addRange, bool raiseOnSelectionChanged) { - // TODO: Check for duplicates (Task 14107720) - // TODO: Optimize by merging adjacent ranges (Task 14107720) - var oldCount = SelectedCount; + var selected = new List(); - for (int i = addRange.Begin; i <= addRange.End; i++) - { - if (!IsSelected(i)) - { - SelectedCount++; - } - } + SelectedCount += IndexRange.Add(_selected, addRange, selected); - if (oldCount != SelectedCount) + if (selected.Count > 0) { - _selected.Add(addRange); + _changes?.Selected(selected); if (raiseOnSelectionChanged) { @@ -374,71 +411,17 @@ namespace Avalonia.Controls private void RemoveRange(IndexRange removeRange, bool raiseOnSelectionChanged) { - int oldCount = SelectedCount; + var removed = new List(); - // TODO: Prevent overlap of Ranges in _selected (Task 14107720) - for (int i = removeRange.Begin; i <= removeRange.End; i++) - { - if (IsSelected(i)) - { - SelectedCount--; - } - } + SelectedCount -= IndexRange.Remove(_selected, removeRange, removed); - if (oldCount != SelectedCount) + if (removed.Count > 0) { - // Build up a both a list of Ranges to remove and ranges to add - var toRemove = new List(); - var toAdd = new List(); - - foreach (var range in _selected) - { - // If this range intersects the remove range, we have to do something - if (removeRange.Intersects(range)) - { - // Intersection with the beginning of the range - // Anything to the left of the point (exclusive) stays - // Anything to the right of the point (inclusive) gets clipped - if (range.Contains(removeRange.Begin - 1)) - { - range.Split(removeRange.Begin - 1, out var before, out _); - toAdd.Add(before); - } + _changes?.Deselected(removed); - // Intersection with the end of the range - // Anything to the left of the point (inclusive) gets clipped - // Anything to the right of the point (exclusive) stays - if (range.Contains(removeRange.End)) - { - if (range.Split(removeRange.End, out _, out var after)) - { - toAdd.Add(after); - } - } - - // Remove this Range from the collection - // New ranges will be added for any remaining subsections - toRemove.Add(range); - } - } - - bool change = ((toRemove.Count > 0) || (toAdd.Count > 0)); - - if (change) + if (raiseOnSelectionChanged) { - // Remove tagged ranges - foreach (var remove in toRemove) - { - _selected.Remove(remove); - } - - // Add new ranges - _selected.AddRange(toAdd); - - if (raiseOnSelectionChanged) - { - OnSelectionChanged(); - } + OnSelectionChanged(); } } } @@ -448,6 +431,7 @@ namespace Avalonia.Controls // Deselect all items if (_selected.Count > 0) { + _changes?.Deselected(_selected); _selected.Clear(); OnSelectionChanged(); } @@ -496,6 +480,7 @@ namespace Avalonia.Controls private void OnSourceListChanged(object dataSource, NotifyCollectionChangedEventArgs args) { bool selectionInvalidated = false; + IList? removed = null; switch (args.Action) { @@ -507,7 +492,7 @@ namespace Avalonia.Controls case NotifyCollectionChangedAction.Remove: { - selectionInvalidated = OnItemsRemoved(args.OldStartingIndex, args.OldItems.Count); + (selectionInvalidated, removed) = OnItemsRemoved(args.OldStartingIndex, args.OldItems); break; } @@ -520,7 +505,7 @@ namespace Avalonia.Controls case NotifyCollectionChangedAction.Replace: { - selectionInvalidated = OnItemsRemoved(args.OldStartingIndex, args.OldItems.Count); + (selectionInvalidated, removed) = OnItemsRemoved(args.OldStartingIndex, args.OldItems); selectionInvalidated |= OnItemsAdded(args.NewStartingIndex, args.NewItems.Count); break; } @@ -529,7 +514,7 @@ namespace Avalonia.Controls if (selectionInvalidated) { OnSelectionChanged(); - _manager.OnSelectionInvalidatedDueToCollectionChange(); + _manager.OnSelectionInvalidatedDueToCollectionChange(removed); } } @@ -609,21 +594,23 @@ namespace Avalonia.Controls return selectionInvalidated; } - private bool OnItemsRemoved(int index, int count) + private (bool, IList) OnItemsRemoved(int index, IList items) { - bool selectionInvalidated = false; + var selectionInvalidated = false; + var removed = new List(); + var count = items.Count; // Remove the items from the selection for leaf if (ItemsSourceView!.Count > 0) { bool isSelected = false; - for (int i = index; i <= index + count - 1; i++) + for (int i = 0; i <= count - 1; i++) { - if (IsSelected(i)) + if (IsSelected(index + i)) { isSelected = true; - break; + removed.Add(items[i]); } } @@ -654,6 +641,7 @@ namespace Avalonia.Controls { if (_childrenNodes[index] != null) { + removed.AddRange(_childrenNodes[index]!.SelectedItems); RealizedChildrenNodeCount--; } _childrenNodes.RemoveAt(index); @@ -696,7 +684,7 @@ namespace Avalonia.Controls } } - return selectionInvalidated; + return (selectionInvalidated, removed); } private void OnSelectionChanged() diff --git a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs index 6c3137c636..3e908681e6 100644 --- a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs @@ -898,6 +898,422 @@ namespace Avalonia.Controls.UnitTests }); } + [Fact] + public void Selecting_Item_Raises_SelectionChanged() + { + var target = new SelectionModel(); + var raised = 0; + + target.Source = Enumerable.Range(0, 10).ToList(); + target.SelectionChanged += (s, e) => + { + Assert.Empty(e.DeselectedIndices); + Assert.Empty(e.DeselectedItems); + Assert.Equal(new[] { new IndexPath(4) }, e.SelectedIndices); + Assert.Equal(new object[] { 4 }, e.SelectedItems); + ++raised; + }; + + target.Select(4); + + Assert.Equal(1, raised); + } + + [Fact] + public void Selecting_Already_Selected_Item_Doesnt_Raise_SelectionChanged() + { + var target = new SelectionModel(); + var raised = 0; + + target.Source = Enumerable.Range(0, 10).ToList(); + target.Select(4); + target.SelectionChanged += (s, e) => ++raised; + target.Select(4); + + Assert.Equal(0, raised); + } + + [Fact] + public void SingleSelecting_Item_Raises_SelectionChanged() + { + var target = new SelectionModel { SingleSelect = true }; + var raised = 0; + + target.Source = Enumerable.Range(0, 10).ToList(); + target.Select(3); + + target.SelectionChanged += (s, e) => + { + Assert.Equal(new[] { new IndexPath(3) }, e.DeselectedIndices); + Assert.Equal(new object[] { 3 }, e.DeselectedItems); + Assert.Equal(new[] { new IndexPath(4) }, e.SelectedIndices); + Assert.Equal(new object[] { 4 }, e.SelectedItems); + ++raised; + }; + + target.Select(4); + + Assert.Equal(1, raised); + } + + [Fact] + public void SingleSelecting_Already_Selected_Item_Doesnt_Raise_SelectionChanged() + { + var target = new SelectionModel { SingleSelect = true }; + var raised = 0; + + target.Source = Enumerable.Range(0, 10).ToList(); + target.Select(4); + target.SelectionChanged += (s, e) => ++raised; + target.Select(4); + + Assert.Equal(0, raised); + } + + [Fact] + public void Selecting_Item_With_Group_Raises_SelectionChanged() + { + var target = new SelectionModel(); + var raised = 0; + + target.Source = CreateNestedData(1, 2, 3); + target.SelectionChanged += (s, e) => + { + Assert.Empty(e.DeselectedIndices); + Assert.Empty(e.DeselectedItems); + Assert.Equal(new[] { new IndexPath(1, 1) }, e.SelectedIndices); + Assert.Equal(new object[] { 4 }, e.SelectedItems); + ++raised; + }; + + target.Select(1, 1); + + Assert.Equal(1, raised); + } + + [Fact] + public void SelectAt_Raises_SelectionChanged() + { + var target = new SelectionModel(); + var raised = 0; + + target.Source = CreateNestedData(1, 2, 3); + target.SelectionChanged += (s, e) => + { + Assert.Empty(e.DeselectedIndices); + Assert.Empty(e.DeselectedItems); + Assert.Equal(new[] { new IndexPath(1, 1) }, e.SelectedIndices); + Assert.Equal(new object[] { 4 }, e.SelectedItems); + ++raised; + }; + + target.SelectAt(new IndexPath(1, 1)); + + Assert.Equal(1, raised); + } + + [Fact] + public void SelectAll_Raises_SelectionChanged() + { + var target = new SelectionModel { SingleSelect = true }; + var raised = 0; + + target.Source = Enumerable.Range(0, 10).ToList(); + target.SelectionChanged += (s, e) => + { + var expected = Enumerable.Range(0, 10); + Assert.Empty(e.DeselectedIndices); + Assert.Empty(e.DeselectedItems); + Assert.Equal(expected.Select(x => new IndexPath(x)), e.SelectedIndices); + Assert.Equal(expected, e.SelectedItems.Cast()); + ++raised; + }; + + target.SelectAll(); + + Assert.Equal(1, raised); + } + + [Fact] + public void SelectAll_With_Already_Selected_Items_Raises_SelectionChanged() + { + var target = new SelectionModel { SingleSelect = true }; + var raised = 0; + + target.Source = Enumerable.Range(0, 10).ToList(); + target.Select(4); + + target.SelectionChanged += (s, e) => + { + var expected = Enumerable.Range(0, 10).Except(new[] { 4 }); + Assert.Empty(e.DeselectedIndices); + Assert.Empty(e.DeselectedItems); + Assert.Equal(expected.Select(x => new IndexPath(x)), e.SelectedIndices); + Assert.Equal(expected, e.SelectedItems.Cast()); + ++raised; + }; + + target.SelectAll(); + + Assert.Equal(1, raised); + } + + [Fact] + public void SelectRangeFromAnchor_Raises_SelectionChanged() + { + var target = new SelectionModel(); + var raised = 0; + + target.Source = Enumerable.Range(0, 10).ToList(); + target.SelectionChanged += (s, e) => + { + var expected = Enumerable.Range(4, 3); + Assert.Empty(e.DeselectedIndices); + Assert.Empty(e.DeselectedItems); + Assert.Equal(expected.Select(x => new IndexPath(x)), e.SelectedIndices); + Assert.Equal(expected, e.SelectedItems.Cast()); + ++raised; + }; + + target.AnchorIndex = new IndexPath(4); + target.SelectRangeFromAnchor(6); + + Assert.Equal(1, raised); + } + + [Fact] + public void SelectRangeFromAnchor_With_Group_Raises_SelectionChanged() + { + var target = new SelectionModel(); + var raised = 0; + + target.Source = CreateNestedData(1, 2, 10); + target.SelectionChanged += (s, e) => + { + var expected = Enumerable.Range(11, 6); + Assert.Empty(e.DeselectedIndices); + Assert.Empty(e.DeselectedItems); + Assert.Equal(expected.Select(x => new IndexPath(x / 10, x % 10)), e.SelectedIndices); + Assert.Equal(expected, e.SelectedItems.Cast()); + ++raised; + }; + + target.AnchorIndex = new IndexPath(1, 1); + target.SelectRangeFromAnchor(1, 6); + + Assert.Equal(1, raised); + } + + [Fact] + public void SelectRangeFromAnchorTo_Raises_SelectionChanged() + { + var target = new SelectionModel(); + var raised = 0; + + target.Source = CreateNestedData(1, 2, 10); + target.SelectionChanged += (s, e) => + { + var expected = Enumerable.Range(11, 6); + Assert.Empty(e.DeselectedIndices); + Assert.Empty(e.DeselectedItems); + Assert.Equal(expected.Select(x => new IndexPath(x / 10, x % 10)), e.SelectedIndices); + Assert.Equal(expected, e.SelectedItems.Cast()); + ++raised; + }; + + target.AnchorIndex = new IndexPath(1, 1); + target.SelectRangeFromAnchorTo(new IndexPath(1, 6)); + + Assert.Equal(1, raised); + } + + [Fact] + public void ClearSelection_Raises_SelectionChanged() + { + var target = new SelectionModel(); + var raised = 0; + + target.Source = Enumerable.Range(0, 10).ToList(); + target.Select(4); + target.Select(5); + + target.SelectionChanged += (s, e) => + { + var expected = Enumerable.Range(4, 2); + Assert.Equal(expected.Select(x => new IndexPath(x)), e.DeselectedIndices); + Assert.Equal(expected, e.DeselectedItems.Cast()); + Assert.Empty(e.SelectedIndices); + Assert.Empty(e.SelectedItems); + ++raised; + }; + + target.ClearSelection(); + + Assert.Equal(1, raised); + } + + [Fact] + public void Changing_Source_Raises_SelectionChanged() + { + var target = new SelectionModel(); + var raised = 0; + + target.Source = Enumerable.Range(0, 10).ToList(); + target.Select(4); + target.Select(5); + + target.SelectionChanged += (s, e) => + { + var expected = Enumerable.Range(4, 2); + Assert.Equal(expected.Select(x => new IndexPath(x)), e.DeselectedIndices); + Assert.Equal(expected, e.DeselectedItems.Cast()); + Assert.Empty(e.SelectedIndices); + Assert.Empty(e.SelectedItems); + ++raised; + }; + + target.Source = Enumerable.Range(20, 10).ToList(); + + Assert.Equal(1, raised); + } + + [Fact] + public void Setting_SelectedIndex_Raises_SelectionChanged() + { + var target = new SelectionModel(); + var raised = 0; + + target.Source = Enumerable.Range(0, 10).ToList(); + target.Select(4); + target.Select(5); + + target.SelectionChanged += (s, e) => + { + Assert.Equal(new[] { new IndexPath(4), new IndexPath(5) }, e.DeselectedIndices); + Assert.Equal(new object[] { 4, 5 }, e.DeselectedItems); + Assert.Equal(new[] { new IndexPath(6) }, e.SelectedIndices); + Assert.Equal(new object[] { 6 }, e.SelectedItems); + ++raised; + }; + + target.SelectedIndex = new IndexPath(6); + + Assert.Equal(1, raised); + } + + [Fact] + public void Removing_Selected_Item_Raises_SelectionChanged() + { + var target = new SelectionModel(); + var data = new ObservableCollection(Enumerable.Range(0, 10)); + var raised = 0; + + target.Source = data; + target.Select(4); + target.Select(5); + + target.SelectionChanged += (s, e) => + { + Assert.Empty(e.DeselectedIndices); + Assert.Equal(new object[] { 4 }, e.DeselectedItems); + Assert.Empty(e.SelectedIndices); + Assert.Empty(e.SelectedItems); + ++raised; + }; + + data.Remove(4); + + Assert.Equal(1, raised); + } + + [Fact] + public void Removing_Selected_Child_Item_Raises_SelectionChanged() + { + var target = new SelectionModel(); + var data = CreateNestedData(1, 2, 3); + var raised = 0; + + target.Source = data; + target.SelectRange(new IndexPath(0), new IndexPath(1, 1)); + + target.SelectionChanged += (s, e) => + { + Assert.Empty(e.DeselectedIndices); + Assert.Equal(new object[] { 1}, e.DeselectedItems); + Assert.Empty(e.SelectedIndices); + Assert.Empty(e.SelectedItems); + ++raised; + }; + + ((AvaloniaList)data[0]).RemoveAt(1); + + Assert.Equal(1, raised); + } + + [Fact] + public void Removing_Selected_Item_With_Children_Raises_SelectionChanged() + { + var target = new SelectionModel(); + var data = CreateNestedData(1, 2, 3); + var raised = 0; + + target.Source = data; + target.SelectRange(new IndexPath(0), new IndexPath(1, 1)); + + target.SelectionChanged += (s, e) => + { + Assert.Empty(e.DeselectedIndices); + Assert.Equal(new object[] { 0, 1, 2 }, e.DeselectedItems); + Assert.Empty(e.SelectedIndices); + Assert.Empty(e.SelectedItems); + ++raised; + }; + + data.RemoveAt(0); + + Assert.Equal(1, raised); + } + + [Fact] + public void Removing_Unselected_Item_Before_Selected_Item_Raises_SelectionChanged() + { + var target = new SelectionModel(); + var data = new ObservableCollection(Enumerable.Range(0, 10)); + var raised = 0; + + target.Source = data; + target.Select(8); + + target.SelectionChanged += (s, e) => + { + Assert.Empty(e.DeselectedIndices); + Assert.Empty(e.DeselectedItems); + Assert.Empty(e.SelectedIndices); + Assert.Empty(e.SelectedItems); + ++raised; + }; + + data.Remove(6); + + Assert.Equal(1, raised); + } + + [Fact] + public void Removing_Unselected_Item_After_Selected_Item_Doesnt_Raise_SelectionChanged() + { + var target = new SelectionModel(); + var data = new ObservableCollection(Enumerable.Range(0, 10)); + var raised = 0; + + target.Source = data; + target.Select(4); + + target.SelectionChanged += (s, e) => ++raised; + + data.Remove(6); + + Assert.Equal(0, raised); + } [Fact] public void Disposing_Unhooks_CollectionChanged_Handlers() From 2336f85d02b2b8909d0b7a0ebe3b740c6239eb88 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 29 Jan 2020 21:48:14 +0100 Subject: [PATCH 024/663] Added some failing tests. That demonstrate some problems with the `SelectionModel` change notifications found so far. --- .../SelectionModelTests.cs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs index 3e908681e6..b9149bf42b 100644 --- a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs @@ -1360,6 +1360,38 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(3, target.SelectedItems.Count); } + [Fact] + public void Not_Enumerating_Changes_Does_Not_Prevent_Further_Operations() + { + var data = new[] { "foo", "bar", "baz" }; + var target = new SelectionModel { Source = data }; + + target.SelectionChanged += (s, e) => { }; + + target.SelectAll(); + target.ClearSelection(); + } + + [Fact] + public void Can_Change_Selection_From_SelectionChanged() + { + var data = new[] { "foo", "bar", "baz" }; + var target = new SelectionModel { Source = data }; + var raised = 0; + + target.SelectionChanged += (s, e) => + { + if (raised++ == 0) + { + target.ClearSelection(); + } + }; + + target.SelectAll(); + + Assert.Equal(2, raised); + } + private int GetSubscriberCount(AvaloniaList list) { return ((INotifyCollectionChangedDebug)list).GetCollectionChangedSubscribers()?.Length ?? 0; From ea968c49c2c6495016df139b53e77278d5234f98 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 29 Jan 2020 21:48:43 +0100 Subject: [PATCH 025/663] Refactor of SelectionModel change notifications. To address issues found. --- src/Avalonia.Controls/SelectionModel.cs | 53 ++----- .../SelectionModelChangeSet.cs | 140 +++++------------- ...SelectionModelSelectionChangedEventArgs.cs | 80 ++++++---- src/Avalonia.Controls/SelectionNode.cs | 60 +++++--- .../SelectionNodeOperation.cs | 80 ++++++++++ 5 files changed, 215 insertions(+), 198 deletions(-) create mode 100644 src/Avalonia.Controls/SelectionNodeOperation.cs diff --git a/src/Avalonia.Controls/SelectionModel.cs b/src/Avalonia.Controls/SelectionModel.cs index 5e2fb32243..49f2c8d2f6 100644 --- a/src/Avalonia.Controls/SelectionModel.cs +++ b/src/Avalonia.Controls/SelectionModel.cs @@ -490,13 +490,9 @@ namespace Avalonia.Controls } public void OnSelectionInvalidatedDueToCollectionChange( - IEnumerable? removedItems) + IReadOnlyList? removedItems) { - var e = new SelectionModelSelectionChangedEventArgs( - Enumerable.Empty(), - Enumerable.Empty(), - removedItems ?? Enumerable.Empty(), - Enumerable.Empty()); + var e = new SelectionModelSelectionChangedEventArgs(null, null, removedItems, null); OnSelectionChanged(e); } @@ -706,50 +702,19 @@ namespace Avalonia.Controls }); } - private void BeginOperation() - { - if (SelectionChanged != null) - { - _rootNode.BeginOperation(); - } - } + private void BeginOperation() => _rootNode.BeginOperation(); private void EndOperation() { - static IEnumerable? Concat(IEnumerable? a, IEnumerable b) - { - return a == null ? b : a.Concat(b); - } + var changes = new List(); + _rootNode.EndOperation(changes); SelectionModelSelectionChangedEventArgs? e = null; - - if (SelectionChanged != null) + + if (changes.Count > 0) { - IEnumerable? selectedIndices = null; - IEnumerable? deselectedIndices = null; - IEnumerable? selectedItems = null; - IEnumerable? deselectedItems = null; - - foreach (var changes in _rootNode.EndOperation()) - { - if (changes.HasChanges) - { - selectedIndices = Concat(selectedIndices, changes.SelectedIndices); - deselectedIndices = Concat(deselectedIndices, changes.DeselectedIndices); - selectedItems = Concat(selectedItems, changes.SelectedItems); - deselectedItems = Concat(deselectedItems, changes.DeselectedItems); - } - } - - if (selectedIndices != null || deselectedIndices != null || - selectedItems != null || deselectedItems != null) - { - e = new SelectionModelSelectionChangedEventArgs( - deselectedIndices ?? Enumerable.Empty(), - selectedIndices ?? Enumerable.Empty(), - deselectedItems ?? Enumerable.Empty(), - selectedItems ?? Enumerable.Empty()); - } + var changeSet = new SelectionModelChangeSet(changes); + e = changeSet.CreateEventArgs(); } OnSelectionChanged(e); diff --git a/src/Avalonia.Controls/SelectionModelChangeSet.cs b/src/Avalonia.Controls/SelectionModelChangeSet.cs index 989136ac8d..b195117ae6 100644 --- a/src/Avalonia.Controls/SelectionModelChangeSet.cs +++ b/src/Avalonia.Controls/SelectionModelChangeSet.cs @@ -1,144 +1,80 @@ using System; using System.Collections.Generic; -using System.Linq; - -#nullable enable namespace Avalonia.Controls { internal class SelectionModelChangeSet { - private SelectionNode _owner; - private List? _selected; - private List? _deselected; - - public SelectionModelChangeSet(SelectionNode owner) => _owner = owner; - - public bool IsTracking { get; private set; } - public bool HasChanges => _selected?.Count > 0 || _deselected?.Count > 0; - public IEnumerable SelectedIndices => EnumerateIndices(_selected); - public IEnumerable DeselectedIndices => EnumerateIndices(_deselected); - public IEnumerable SelectedItems => EnumerateItems(_selected); - public IEnumerable DeselectedItems => EnumerateItems(_deselected); + private List _changes; - public void BeginOperation() + public SelectionModelChangeSet(List changes) { - if (IsTracking) - { - throw new AvaloniaInternalException("SelectionModel change operation already in progress."); - } - - IsTracking = true; - _selected?.Clear(); - _deselected?.Clear(); + _changes = changes; } - public void EndOperation() => IsTracking = false; - - public void Selected(IndexRange range) + public SelectionModelSelectionChangedEventArgs CreateEventArgs() { - if (!IsTracking) - { - return; - } - - Add(range, ref _selected, _deselected); + return new SelectionModelSelectionChangedEventArgs( + CreateIndices(x => x.DeselectedRanges), + CreateIndices(x => x.SelectedRanges), + CreateItems(x => x.DeselectedRanges), + CreateItems(x => x.SelectedRanges)); } - public void Selected(IEnumerable ranges) + private IReadOnlyList CreateIndices(Func?> selector) { - if (!IsTracking) + if (_changes == null) { - return; + return Array.Empty(); } - foreach (var range in ranges) - { - Selected(range); - } - } - - public void Deselected(IndexRange range) - { - if (!IsTracking) - { - return; - } - - Add(range, ref _deselected, _selected); - } + var result = new List(); - public void Deselected(IEnumerable ranges) - { - if (!IsTracking) + foreach (var i in _changes) { - return; - } + var ranges = selector(i); - foreach (var range in ranges) - { - Deselected(range); - } - } - - private static void Add( - IndexRange range, - ref List? add, - List? remove) - { - if (remove != null) - { - var removed = new List(); - IndexRange.Remove(remove, range, removed); - var selected = IndexRange.Subtract(range, removed); - - if (selected.Any()) + if (ranges != null) { - add ??= new List(); - - foreach (var r in selected) + foreach (var j in ranges) { - IndexRange.Add(add, r); + for (var k = j.Begin; k <= j.End; ++k) + { + result.Add(i.Path.CloneWithChildIndex(k)); + } } } } - else - { - add ??= new List(); - IndexRange.Add(add, range); - } + + return result; } - private IEnumerable EnumerateIndices(IEnumerable? ranges) + private IReadOnlyList CreateItems(Func?> selector) { - var path = _owner.IndexPath; - - if (ranges != null) + if (_changes == null) { - foreach (var range in ranges) - { - for (var i = range.Begin; i <= range.End; ++i) - { - yield return path.CloneWithChildIndex(i); - } - } + return Array.Empty(); } - } - private IEnumerable EnumerateItems(IEnumerable? ranges) - { - var items = _owner.ItemsSourceView; + var result = new List(); - if (ranges != null && items != null) + foreach (var i in _changes) { - foreach (var range in ranges) + var ranges = selector(i); + + if (ranges != null && i.Items != null) { - for (var i = range.Begin; i <= range.End; ++i) + foreach (var j in ranges) { - yield return items.GetAt(i); + for (var k = j.Begin; k <= j.End; ++k) + { + result.Add(i.Items.GetAt(k)); + } } } } + + return result; } } } diff --git a/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs b/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs index 4976bf1827..ae98f6a1ce 100644 --- a/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs +++ b/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs @@ -12,49 +12,73 @@ namespace Avalonia.Controls { public class SelectionModelSelectionChangedEventArgs : EventArgs { - private readonly IEnumerable _selectedIndicesSource; - private readonly IEnumerable _deselectedIndicesSource; - private readonly IEnumerable _selectedItemsSource; - private readonly IEnumerable _deselectedItemsSource; - private List? _selectedIndices; - private List? _deselectedIndices; - private List? _selectedItems; - private List? _deselectedItems; + private readonly IEnumerable? _deselectedIndicesSource; + private readonly IEnumerable? _selectedIndicesSource; + private readonly IEnumerable? _deselectedItemsSource; + private readonly IEnumerable? _selectedItemsSource; + private IReadOnlyList? _deselectedIndices; + private IReadOnlyList? _selectedIndices; + private IReadOnlyList? _deselectedItems; + private IReadOnlyList? _selectedItems; public SelectionModelSelectionChangedEventArgs( - IEnumerable deselectedIndices, - IEnumerable selectedIndices, - IEnumerable deselectedItems, - IEnumerable selectedItems) + IReadOnlyList? deselectedIndices, + IReadOnlyList? selectedIndices, + IReadOnlyList? deselectedItems, + IReadOnlyList? selectedItems) { - _selectedIndicesSource = selectedIndices; - _deselectedIndicesSource = deselectedIndices; - _selectedItemsSource = selectedItems; - _deselectedItemsSource = deselectedItems; + _deselectedIndices = deselectedIndices ?? Array.Empty(); + _selectedIndices = selectedIndices ?? Array.Empty(); + _deselectedItems = deselectedItems ?? Array.Empty(); + _selectedItems= selectedItems ?? Array.Empty(); } - /// - /// Gets the indices of the items that were added to the selection. - /// - public IReadOnlyList SelectedIndices => - _selectedIndices ?? (_selectedIndices = new List(_selectedIndicesSource)); + public SelectionModelSelectionChangedEventArgs( + IEnumerable? deselectedIndices, + IEnumerable? selectedIndices, + IEnumerable? deselectedItems, + IEnumerable? selectedItems) + { + static void Set(IEnumerable? source, ref IEnumerable? sourceField, ref IReadOnlyList? field) + { + if (source != null) + { + sourceField = source; + } + else + { + field = Array.Empty(); + } + } + + Set(deselectedIndices, ref _deselectedIndicesSource, ref _deselectedIndices); + Set(selectedIndices, ref _selectedIndicesSource, ref _selectedIndices); + Set(deselectedItems, ref _deselectedItemsSource, ref _deselectedItems); + Set(selectedItems, ref _selectedItemsSource, ref _selectedItems); + } /// /// Gets the indices of the items that were removed from the selection. /// - public IReadOnlyList DeselectedIndices => - _deselectedIndices ?? (_deselectedIndices = new List(_deselectedIndicesSource)); + public IReadOnlyList DeselectedIndices + => _deselectedIndices ??= new List(_deselectedIndicesSource); /// - /// Gets the items that were added to the selection. + /// Gets the indices of the items that were added to the selection. /// - public IReadOnlyList SelectedItems => - _selectedItems ?? (_selectedItems = new List(_selectedItemsSource)); + public IReadOnlyList SelectedIndices + => _selectedIndices ??= new List(_selectedIndicesSource); /// /// Gets the items that were removed from the selection. /// - public IReadOnlyList DeselectedItems => - _deselectedItems ?? (_deselectedItems = new List(_deselectedItemsSource)); + public IReadOnlyList DeselectedItems + => _deselectedItems ??= new List(_deselectedItemsSource); + + /// + /// Gets the items that were added to the selection. + /// + public IReadOnlyList SelectedItems + => _selectedItems ??= new List(_selectedItemsSource); } } diff --git a/src/Avalonia.Controls/SelectionNode.cs b/src/Avalonia.Controls/SelectionNode.cs index d462a51228..85db801100 100644 --- a/src/Avalonia.Controls/SelectionNode.cs +++ b/src/Avalonia.Controls/SelectionNode.cs @@ -29,7 +29,7 @@ namespace Avalonia.Controls private readonly SelectionNode? _parent; private readonly List _selected = new List(); private readonly List _selectedIndicesCached = new List(); - private SelectionModelChangeSet? _changes; + private SelectionNodeOperation? _operation; private object? _source; private bool _selectedIndicesCacheIsValid; @@ -137,7 +137,7 @@ namespace Avalonia.Controls child = new SelectionNode(_manager, parent: this); child.Source = resolvedChild; - if (_changes?.IsTracking == true) + if (_operation != null) { child.BeginOperation(); } @@ -296,33 +296,45 @@ namespace Avalonia.Controls public void BeginOperation() { - _changes ??= new SelectionModelChangeSet(this); - _changes.BeginOperation(); + if (_operation != null) + { + throw new AvaloniaInternalException("Selection operation already in progress."); + } + + _operation = new SelectionNodeOperation(this); for (var i = 0; i < _childrenNodes.Count; ++i) { - _childrenNodes[i]?.BeginOperation(); + var child = _childrenNodes[i]; + + if (child != null && child != _manager.SharedLeafNode) + { + child.BeginOperation(); + } } } - public IEnumerable EndOperation() + public void EndOperation(List changes) { - if (_changes != null) + if (_operation == null) { - _changes.EndOperation(); - yield return _changes; + throw new AvaloniaInternalException("No selection operation in progress."); + } - for (var i = 0; i < _childrenNodes.Count; ++i) - { - var child = _childrenNodes[i]; + if (_operation.HasChanges) + { + changes.Add(_operation); + } - if (child != null) - { - foreach (var changes in child.EndOperation()) - { - yield return changes; - } - } + _operation = null; + + for (var i = 0; i < _childrenNodes.Count; ++i) + { + var child = _childrenNodes[i]; + + if (child != null && child != _manager.SharedLeafNode) + { + child.EndOperation(changes); } } } @@ -400,7 +412,7 @@ namespace Avalonia.Controls if (selected.Count > 0) { - _changes?.Selected(selected); + _operation?.Selected(selected); if (raiseOnSelectionChanged) { @@ -417,7 +429,7 @@ namespace Avalonia.Controls if (removed.Count > 0) { - _changes?.Deselected(removed); + _operation?.Deselected(removed); if (raiseOnSelectionChanged) { @@ -431,7 +443,7 @@ namespace Avalonia.Controls // Deselect all items if (_selected.Count > 0) { - _changes?.Deselected(_selected); + _operation?.Deselected(_selected); _selected.Clear(); OnSelectionChanged(); } @@ -480,7 +492,7 @@ namespace Avalonia.Controls private void OnSourceListChanged(object dataSource, NotifyCollectionChangedEventArgs args) { bool selectionInvalidated = false; - IList? removed = null; + List? removed = null; switch (args.Action) { @@ -594,7 +606,7 @@ namespace Avalonia.Controls return selectionInvalidated; } - private (bool, IList) OnItemsRemoved(int index, IList items) + private (bool, List) OnItemsRemoved(int index, IList items) { var selectionInvalidated = false; var removed = new List(); diff --git a/src/Avalonia.Controls/SelectionNodeOperation.cs b/src/Avalonia.Controls/SelectionNodeOperation.cs new file mode 100644 index 0000000000..04b8554f7c --- /dev/null +++ b/src/Avalonia.Controls/SelectionNodeOperation.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +#nullable enable + +namespace Avalonia.Controls +{ + internal class SelectionNodeOperation + { + private readonly SelectionNode _owner; + private List? _selected; + private List? _deselected; + + public SelectionNodeOperation(SelectionNode owner) + { + _owner = owner; + } + + public bool HasChanges => _selected?.Count > 0 || _deselected?.Count > 0; + public List? SelectedRanges => _selected; + public List? DeselectedRanges => _deselected; + public IndexPath Path => _owner.IndexPath; + public ItemsSourceView? Items => _owner.ItemsSourceView; + + public void Selected(IndexRange range) + { + Add(range, ref _selected, _deselected); + } + + public void Selected(IEnumerable ranges) + { + foreach (var range in ranges) + { + Selected(range); + } + } + + public void Deselected(IndexRange range) + { + Add(range, ref _deselected, _selected); + } + + public void Deselected(IEnumerable ranges) + { + foreach (var range in ranges) + { + Deselected(range); + } + } + + private static void Add( + IndexRange range, + ref List? add, + List? remove) + { + if (remove != null) + { + var removed = new List(); + IndexRange.Remove(remove, range, removed); + var selected = IndexRange.Subtract(range, removed); + + if (selected.Any()) + { + add ??= new List(); + + foreach (var r in selected) + { + IndexRange.Add(add, r); + } + } + } + else + { + add ??= new List(); + IndexRange.Add(add, range); + } + } + } +} From 27e11150b78f0b62071de74f48bf3248d167e120 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 30 Jan 2020 11:52:13 +0100 Subject: [PATCH 026/663] Make comparing IndexPath with null do something useful. --- src/Avalonia.Controls/IndexPath.cs | 7 +++++++ .../Avalonia.Controls.UnitTests/IndexPathTests.cs | 14 ++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/Avalonia.Controls/IndexPath.cs b/src/Avalonia.Controls/IndexPath.cs index 32d8c2b051..6c5aaf7ad1 100644 --- a/src/Avalonia.Controls/IndexPath.cs +++ b/src/Avalonia.Controls/IndexPath.cs @@ -58,6 +58,11 @@ namespace Avalonia.Controls public int GetAt(int index) { + if (index >= GetSize()) + { + throw new IndexOutOfRangeException(); + } + return _path?[index] ?? (_index - 1); } @@ -169,5 +174,7 @@ namespace Avalonia.Controls 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; } } } diff --git a/tests/Avalonia.Controls.UnitTests/IndexPathTests.cs b/tests/Avalonia.Controls.UnitTests/IndexPathTests.cs index 190e92ed5e..1e4aa0a2b8 100644 --- a/tests/Avalonia.Controls.UnitTests/IndexPathTests.cs +++ b/tests/Avalonia.Controls.UnitTests/IndexPathTests.cs @@ -77,5 +77,19 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(0, a.CompareTo(b)); Assert.Equal(a.GetHashCode(), b.GetHashCode()); } + + [Fact] + public void Null_Equality() + { + var a = new IndexPath(null); + var b = new IndexPath(1); + + // Implementing operator == on a struct automatically implements an operator which + // accepts null, so make sure this does something useful. + Assert.True(a == null); + Assert.False(a != null); + Assert.False(b == null); + Assert.True(b != null); + } } } From 307b751f680f79b2951b428fae4327fab87867bb Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 31 Jan 2020 10:17:56 +0100 Subject: [PATCH 027/663] Lazily create the selected/deselected lists. --- .../SelectionModelChangeSet.cs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/Avalonia.Controls/SelectionModelChangeSet.cs b/src/Avalonia.Controls/SelectionModelChangeSet.cs index b195117ae6..e927afb9b9 100644 --- a/src/Avalonia.Controls/SelectionModelChangeSet.cs +++ b/src/Avalonia.Controls/SelectionModelChangeSet.cs @@ -21,15 +21,13 @@ namespace Avalonia.Controls CreateItems(x => x.SelectedRanges)); } - private IReadOnlyList CreateIndices(Func?> selector) + private IEnumerable CreateIndices(Func?> selector) { if (_changes == null) { - return Array.Empty(); + yield break; } - var result = new List(); - foreach (var i in _changes) { var ranges = selector(i); @@ -40,20 +38,18 @@ namespace Avalonia.Controls { for (var k = j.Begin; k <= j.End; ++k) { - result.Add(i.Path.CloneWithChildIndex(k)); + yield return i.Path.CloneWithChildIndex(k); } } } } - - return result; } - private IReadOnlyList CreateItems(Func?> selector) + private IEnumerable CreateItems(Func?> selector) { if (_changes == null) { - return Array.Empty(); + yield break; } var result = new List(); @@ -68,13 +64,11 @@ namespace Avalonia.Controls { for (var k = j.Begin; k <= j.End; ++k) { - result.Add(i.Items.GetAt(k)); + yield return i.Items.GetAt(k); } } } } - - return result; } } } From 3a7d9f0800d36fd6d00f1f9ee514686f00d40714 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 1 Feb 2020 00:48:57 +0100 Subject: [PATCH 028/663] Use SelectedItems for change event args. --- src/Avalonia.Controls/IndexRange.cs | 12 ++ src/Avalonia.Controls/SelectedItems.cs | 37 ++--- src/Avalonia.Controls/SelectionModel.cs | 14 +- .../SelectionModelChangeSet.cs | 149 ++++++++++++++---- ...SelectionModelSelectionChangedEventArgs.cs | 53 +------ .../SelectionNodeOperation.cs | 32 +++- 6 files changed, 193 insertions(+), 104 deletions(-) diff --git a/src/Avalonia.Controls/IndexRange.cs b/src/Avalonia.Controls/IndexRange.cs index 124f1e0500..1dc161c699 100644 --- a/src/Avalonia.Controls/IndexRange.cs +++ b/src/Avalonia.Controls/IndexRange.cs @@ -202,6 +202,18 @@ namespace Avalonia.Controls } } + public static int GetCount(IEnumerable ranges) + { + var result = 0; + + foreach (var range in ranges) + { + result += (range.End - range.Begin) + 1; + } + + return result; + } + private static void MergeRanges(IList ranges) { for (var i = ranges.Count - 2; i >= 0; --i) diff --git a/src/Avalonia.Controls/SelectedItems.cs b/src/Avalonia.Controls/SelectedItems.cs index af43742670..a3acb48765 100644 --- a/src/Avalonia.Controls/SelectedItems.cs +++ b/src/Avalonia.Controls/SelectedItems.cs @@ -6,44 +6,37 @@ using System; using System.Collections; using System.Collections.Generic; -using SelectedItemInfo = Avalonia.Controls.SelectionModel.SelectedItemInfo; #nullable enable namespace Avalonia.Controls { - internal class SelectedItems : IReadOnlyList + public interface ISelectedItemInfo { - private readonly List _infos; - private readonly Func, int, T> _getAtImpl; + public IndexPath Path { get; } + } + + internal class SelectedItems : IReadOnlyList + where Tinfo : ISelectedItemInfo + { + private readonly List _infos; + private readonly Func, int, TValue> _getAtImpl; public SelectedItems( - List infos, - Func, int, T> getAtImpl) + List infos, + int count, + Func, int, TValue> getAtImpl) { _infos = infos; _getAtImpl = getAtImpl; - - foreach (var info in infos) - { - var node = info.Node; - - if (node != null) - { - Count += node.SelectedCount; - } - else - { - throw new InvalidOperationException("Selection changed after the SelectedIndices/Items property was read."); - } - } + Count = count; } - public T this[int index] => _getAtImpl(_infos, index); + public TValue this[int index] => _getAtImpl(_infos, index); public int Count { get; } - public IEnumerator GetEnumerator() + public IEnumerator GetEnumerator() { for (var i = 0; i < Count; ++i) { diff --git a/src/Avalonia.Controls/SelectionModel.cs b/src/Avalonia.Controls/SelectionModel.cs index 49f2c8d2f6..c8d2c5cc9e 100644 --- a/src/Avalonia.Controls/SelectionModel.cs +++ b/src/Avalonia.Controls/SelectionModel.cs @@ -165,6 +165,7 @@ namespace Avalonia.Controls if (_selectedItemsCached == null) { var selectedInfos = new List(); + var count = 0; if (_rootNode.Source != null) { @@ -176,6 +177,7 @@ namespace Avalonia.Controls if (currentInfo.Node.SelectedCount > 0) { selectedInfos.Add(new SelectedItemInfo(currentInfo.Node, currentInfo.Path)); + count += currentInfo.Node.SelectedCount; } }); } @@ -185,8 +187,9 @@ namespace Avalonia.Controls // 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 ( + var selectedItems = new SelectedItems ( selectedInfos, + count, (infos, index) => { var currentIndex = 0; @@ -233,6 +236,8 @@ namespace Avalonia.Controls if (_selectedIndicesCached == null) { var selectedInfos = new List(); + var count = 0; + SelectionTreeHelper.Traverse( _rootNode, false, @@ -241,6 +246,7 @@ namespace Avalonia.Controls if (currentInfo.Node.SelectedCount > 0) { selectedInfos.Add(new SelectedItemInfo(currentInfo.Node, currentInfo.Path)); + count += currentInfo.Node.SelectedCount; } }); @@ -249,8 +255,9 @@ namespace Avalonia.Controls // 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( + var indices = new SelectedItems( selectedInfos, + count, (infos, index) => // callback for GetAt(index) { var currentIndex = 0; @@ -720,7 +727,7 @@ namespace Avalonia.Controls OnSelectionChanged(e); } - internal class SelectedItemInfo + internal class SelectedItemInfo : ISelectedItemInfo { public SelectedItemInfo(SelectionNode node, IndexPath path) { @@ -730,6 +737,7 @@ namespace Avalonia.Controls public SelectionNode Node { get; } public IndexPath Path { get; } + public int Count => Node.SelectedCount; } private struct Operation : IDisposable diff --git a/src/Avalonia.Controls/SelectionModelChangeSet.cs b/src/Avalonia.Controls/SelectionModelChangeSet.cs index e927afb9b9..c0228a1cbc 100644 --- a/src/Avalonia.Controls/SelectionModelChangeSet.cs +++ b/src/Avalonia.Controls/SelectionModelChangeSet.cs @@ -14,61 +14,144 @@ namespace Avalonia.Controls public SelectionModelSelectionChangedEventArgs CreateEventArgs() { + var deselectedCount = 0; + var selectedCount = 0; + + foreach (var change in _changes) + { + deselectedCount += change.DeselectedCount; + selectedCount += change.SelectedCount; + } + + var deselectedIndices = new SelectedItems( + _changes, + deselectedCount, + GetDeselectedIndexAt); + var selectedIndices = new SelectedItems( + _changes, + selectedCount, + GetSelectedIndexAt); + var deselectedItems = new SelectedItems( + _changes, + deselectedCount, + GetDeselectedItemAt); + var selectedItems = new SelectedItems( + _changes, + selectedCount, + GetSelectedItemAt); + return new SelectionModelSelectionChangedEventArgs( - CreateIndices(x => x.DeselectedRanges), - CreateIndices(x => x.SelectedRanges), - CreateItems(x => x.DeselectedRanges), - CreateItems(x => x.SelectedRanges)); + deselectedIndices, + selectedIndices, + deselectedItems, + selectedItems); } - private IEnumerable CreateIndices(Func?> selector) + private IndexPath GetDeselectedIndexAt( + List infos, + int index) { - if (_changes == null) - { - yield break; - } + static int GetCount(SelectionNodeOperation info) => info.DeselectedCount; + static List GetRanges(SelectionNodeOperation info) => info.DeselectedRanges; + return GetIndexAt(infos, index, GetCount, GetRanges); + } - foreach (var i in _changes) + private IndexPath GetSelectedIndexAt( + List infos, + int index) + { + static int GetCount(SelectionNodeOperation info) => info.SelectedCount; + static List GetRanges(SelectionNodeOperation info) => info.SelectedRanges; + return GetIndexAt(infos, index, GetCount, GetRanges); + } + + private object GetDeselectedItemAt( + List infos, + int index) + { + static int GetCount(SelectionNodeOperation info) => info.DeselectedCount; + static List GetRanges(SelectionNodeOperation info) => info.DeselectedRanges; + return GetItemAt(infos, index, GetCount, GetRanges); + } + + private object GetSelectedItemAt( + List infos, + int index) + { + static int GetCount(SelectionNodeOperation info) => info.SelectedCount; + static List GetRanges(SelectionNodeOperation info) => info.SelectedRanges; + return GetItemAt(infos, index, GetCount, GetRanges); + } + + private IndexPath GetIndexAt( + List infos, + int index, + Func getCount, + Func> getRanges) + { + var currentIndex = 0; + IndexPath path = default; + + foreach (var info in infos) { - var ranges = selector(i); + var currentCount = getCount(info); - if (ranges != null) + if (index >= currentIndex && index < currentIndex + currentCount) { - foreach (var j in ranges) - { - for (var k = j.Begin; k <= j.End; ++k) - { - yield return i.Path.CloneWithChildIndex(k); - } - } + int targetIndex = GetIndexAt(getRanges(info), index - currentIndex); + path = info.Path.CloneWithChildIndex(targetIndex); + break; } + + currentIndex += currentCount; } + + return path; } - private IEnumerable CreateItems(Func?> selector) + private object GetItemAt( + List infos, + int index, + Func getCount, + Func> getRanges) { - if (_changes == null) + var currentIndex = 0; + object item = null; + + foreach (var info in infos) { - yield break; + var currentCount = getCount(info); + + if (index >= currentIndex && index < currentIndex + currentCount) + { + int targetIndex = GetIndexAt(getRanges(info), index - currentIndex); + item = info.Items.GetAt(targetIndex); + break; + } + + currentIndex += currentCount; } - var result = new List(); + return item; + } - foreach (var i in _changes) + private int GetIndexAt(List ranges, int index) + { + var currentIndex = 0; + + foreach (var range in ranges) { - var ranges = selector(i); + var currentCount = (range.End - range.Begin) + 1; - if (ranges != null && i.Items != null) + if (index >= currentIndex && index < currentIndex + currentCount) { - foreach (var j in ranges) - { - for (var k = j.Begin; k <= j.End; ++k) - { - yield return i.Items.GetAt(k); - } - } + return range.Begin + (index - currentIndex); } + + currentIndex += currentCount; } + + throw new IndexOutOfRangeException(); } } } diff --git a/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs b/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs index ae98f6a1ce..4e64ee6e6f 100644 --- a/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs +++ b/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs @@ -12,73 +12,36 @@ namespace Avalonia.Controls { public class SelectionModelSelectionChangedEventArgs : EventArgs { - private readonly IEnumerable? _deselectedIndicesSource; - private readonly IEnumerable? _selectedIndicesSource; - private readonly IEnumerable? _deselectedItemsSource; - private readonly IEnumerable? _selectedItemsSource; - private IReadOnlyList? _deselectedIndices; - private IReadOnlyList? _selectedIndices; - private IReadOnlyList? _deselectedItems; - private IReadOnlyList? _selectedItems; - public SelectionModelSelectionChangedEventArgs( IReadOnlyList? deselectedIndices, IReadOnlyList? selectedIndices, IReadOnlyList? deselectedItems, IReadOnlyList? selectedItems) { - _deselectedIndices = deselectedIndices ?? Array.Empty(); - _selectedIndices = selectedIndices ?? Array.Empty(); - _deselectedItems = deselectedItems ?? Array.Empty(); - _selectedItems= selectedItems ?? Array.Empty(); - } - - public SelectionModelSelectionChangedEventArgs( - IEnumerable? deselectedIndices, - IEnumerable? selectedIndices, - IEnumerable? deselectedItems, - IEnumerable? selectedItems) - { - static void Set(IEnumerable? source, ref IEnumerable? sourceField, ref IReadOnlyList? field) - { - if (source != null) - { - sourceField = source; - } - else - { - field = Array.Empty(); - } - } - - Set(deselectedIndices, ref _deselectedIndicesSource, ref _deselectedIndices); - Set(selectedIndices, ref _selectedIndicesSource, ref _selectedIndices); - Set(deselectedItems, ref _deselectedItemsSource, ref _deselectedItems); - Set(selectedItems, ref _selectedItemsSource, ref _selectedItems); + DeselectedIndices = deselectedIndices ?? Array.Empty(); + SelectedIndices = selectedIndices ?? Array.Empty(); + DeselectedItems = deselectedItems ?? Array.Empty(); + SelectedItems= selectedItems ?? Array.Empty(); } /// /// Gets the indices of the items that were removed from the selection. /// - public IReadOnlyList DeselectedIndices - => _deselectedIndices ??= new List(_deselectedIndicesSource); + public IReadOnlyList DeselectedIndices { get; } /// /// Gets the indices of the items that were added to the selection. /// - public IReadOnlyList SelectedIndices - => _selectedIndices ??= new List(_selectedIndicesSource); + public IReadOnlyList SelectedIndices { get; } /// /// Gets the items that were removed from the selection. /// - public IReadOnlyList DeselectedItems - => _deselectedItems ??= new List(_deselectedItemsSource); + public IReadOnlyList DeselectedItems { get; } /// /// Gets the items that were added to the selection. /// - public IReadOnlyList SelectedItems - => _selectedItems ??= new List(_selectedItemsSource); + public IReadOnlyList SelectedItems { get; } } } diff --git a/src/Avalonia.Controls/SelectionNodeOperation.cs b/src/Avalonia.Controls/SelectionNodeOperation.cs index 04b8554f7c..9622a52f00 100644 --- a/src/Avalonia.Controls/SelectionNodeOperation.cs +++ b/src/Avalonia.Controls/SelectionNodeOperation.cs @@ -6,11 +6,13 @@ using System.Linq; namespace Avalonia.Controls { - internal class SelectionNodeOperation + internal class SelectionNodeOperation : ISelectedItemInfo { private readonly SelectionNode _owner; private List? _selected; private List? _deselected; + private int _selectedCount = -1; + private int _deselectedCount = -1; public SelectionNodeOperation(SelectionNode owner) { @@ -23,9 +25,36 @@ namespace Avalonia.Controls 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 ranges) @@ -39,6 +68,7 @@ namespace Avalonia.Controls public void Deselected(IndexRange range) { Add(range, ref _deselected, _selected); + _deselectedCount = -1; } public void Deselected(IEnumerable ranges) From 3f6e982be88403f790d38a0d83e5f064c32dda0f Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 1 Feb 2020 23:43:24 +0100 Subject: [PATCH 029/663] Added SelectionModel.RetainSelectionOnReset. --- .../Repeater/ItemsSourceView.cs | 2 + src/Avalonia.Controls/SelectionModel.cs | 7 +- src/Avalonia.Controls/SelectionNode.cs | 104 +++++++++++++-- .../SelectionModelTests.cs | 120 ++++++++++++++++++ 4 files changed, 224 insertions(+), 9 deletions(-) diff --git a/src/Avalonia.Controls/Repeater/ItemsSourceView.cs b/src/Avalonia.Controls/Repeater/ItemsSourceView.cs index 02ead7ef36..ecf8abc13f 100644 --- a/src/Avalonia.Controls/Repeater/ItemsSourceView.cs +++ b/src/Avalonia.Controls/Repeater/ItemsSourceView.cs @@ -96,6 +96,8 @@ namespace Avalonia.Controls /// the item. public object GetAt(int index) => _inner[index]; + public int IndexOf(object item) => _inner.IndexOf(item); + /// /// Retrieves the index of the item that has the specified unique identifier (key). /// diff --git a/src/Avalonia.Controls/SelectionModel.cs b/src/Avalonia.Controls/SelectionModel.cs index c8d2c5cc9e..e5f79fa40f 100644 --- a/src/Avalonia.Controls/SelectionModel.cs +++ b/src/Avalonia.Controls/SelectionModel.cs @@ -73,6 +73,11 @@ namespace Avalonia.Controls } } + public bool RetainSelectionOnReset + { + get => _rootNode.RetainSelectionOnReset; + set => _rootNode.RetainSelectionOnReset = value; + } public IndexPath AnchorIndex { @@ -497,7 +502,7 @@ namespace Avalonia.Controls } public void OnSelectionInvalidatedDueToCollectionChange( - IReadOnlyList? removedItems) + IReadOnlyList? removedItems) { var e = new SelectionModelSelectionChangedEventArgs(null, null, removedItems, null); OnSelectionChanged(e); diff --git a/src/Avalonia.Controls/SelectionNode.cs b/src/Avalonia.Controls/SelectionNode.cs index 85db801100..a8ad634d35 100644 --- a/src/Avalonia.Controls/SelectionNode.cs +++ b/src/Avalonia.Controls/SelectionNode.cs @@ -32,6 +32,8 @@ namespace Avalonia.Controls private SelectionNodeOperation? _operation; private object? _source; private bool _selectedIndicesCacheIsValid; + private bool _retainSelectionOnReset; + private List? _selectedItems; public SelectionNode(SelectionModel manager, SelectionNode? parent) { @@ -41,6 +43,40 @@ namespace Avalonia.Controls public int AnchorIndex { get; set; } = -1; + public bool RetainSelectionOnReset + { + get => _retainSelectionOnReset; + set + { + if (_retainSelectionOnReset != value) + { + _retainSelectionOnReset = value; + + if (_retainSelectionOnReset) + { + _selectedItems = new List(); + + foreach (var i in SelectedIndices) + { + _selectedItems.Add(ItemsSourceView!.GetAt(i)); + } + } + else + { + _selectedItems = null; + } + + foreach (var child in _childrenNodes) + { + if (child != null) + { + child.RetainSelectionOnReset = value; + } + } + } + } + } + public object? Source { get => _source; @@ -414,6 +450,14 @@ namespace Avalonia.Controls { _operation?.Selected(selected); + if (_selectedItems != null) + { + for (var i = addRange.Begin; i <= addRange.End; ++i) + { + _selectedItems.Add(ItemsSourceView!.GetAt(i)); + } + } + if (raiseOnSelectionChanged) { OnSelectionChanged(); @@ -431,6 +475,14 @@ namespace Avalonia.Controls { _operation?.Deselected(removed); + if (_selectedItems != null) + { + for (var i = removeRange.Begin; i <= removeRange.End; ++i) + { + _selectedItems.Remove(ItemsSourceView!.GetAt(i)); + } + } + if (raiseOnSelectionChanged) { OnSelectionChanged(); @@ -448,6 +500,7 @@ namespace Avalonia.Controls OnSelectionChanged(); } + _selectedItems?.Clear(); SelectedCount = 0; AnchorIndex = -1; @@ -492,7 +545,7 @@ namespace Avalonia.Controls private void OnSourceListChanged(object dataSource, NotifyCollectionChangedEventArgs args) { bool selectionInvalidated = false; - List? removed = null; + List? removed = null; switch (args.Action) { @@ -509,11 +562,19 @@ namespace Avalonia.Controls } case NotifyCollectionChangedAction.Reset: - { - ClearSelection(); - selectionInvalidated = true; - break; - } + { + if (_selectedItems == null) + { + ClearSelection(); + } + else + { + removed = RecreateSelectionFromSelectedItems(); + } + + selectionInvalidated = true; + break; + } case NotifyCollectionChangedAction.Replace: { @@ -606,10 +667,10 @@ namespace Avalonia.Controls return selectionInvalidated; } - private (bool, List) OnItemsRemoved(int index, IList items) + private (bool, List) OnItemsRemoved(int index, IList items) { var selectionInvalidated = false; - var removed = new List(); + var removed = new List(); var count = items.Count; // Remove the items from the selection for leaf @@ -804,6 +865,33 @@ namespace Avalonia.Controls return selectionState; } + private List RecreateSelectionFromSelectedItems() + { + var removed = new List(); + + _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, diff --git a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs index b9149bf42b..b3a5e0959f 100644 --- a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs @@ -7,6 +7,7 @@ using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Collections.Specialized; using System.Linq; using Avalonia.Collections; using Avalonia.Diagnostics; @@ -1392,6 +1393,107 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(2, raised); } + [Fact] + public void RetainSelectionOnReset_Retains_Selection_On_Reset() + { + var data = new ResettingList { "foo", "bar", "baz" }; + var target = new SelectionModel { Source = data, RetainSelectionOnReset = true }; + + target.SelectRange(new IndexPath(1), new IndexPath(2)); + data.Reset(); + + Assert.Equal(new[] { new IndexPath(1), new IndexPath(2) }, target.SelectedIndices); + Assert.Equal(new[] { "bar", "baz" }, target.SelectedItems); + } + + [Fact] + public void RetainSelectionOnReset_Retains_Correct_Selection_After_Remove() + { + var data = new ResettingList { "foo", "bar", "baz" }; + var target = new SelectionModel { Source = data, RetainSelectionOnReset = true }; + + target.SelectRange(new IndexPath(1), new IndexPath(2)); + target.Deselect(2); + data.Reset(); + + Assert.Equal(new[] { new IndexPath(1) }, target.SelectedIndices); + Assert.Equal(new[] { "bar", }, target.SelectedItems); + } + + [Fact] + public void RetainSelectionOnReset_Retains_No_Selection_After_Clear() + { + var data = new ResettingList { "foo", "bar", "baz" }; + var target = new SelectionModel { Source = data, RetainSelectionOnReset = true }; + + target.SelectRange(new IndexPath(1), new IndexPath(2)); + target.ClearSelection(); + data.Reset(); + + Assert.Empty(target.SelectedIndices); + Assert.Empty(target.SelectedItems); + } + + [Fact] + public void RetainSelectionOnReset_Retains_Correct_Selection_After_Two_Resets() + { + var data = new ResettingList { "foo", "bar", "baz" }; + var target = new SelectionModel { Source = data, RetainSelectionOnReset = true }; + + target.SelectRange(new IndexPath(1), new IndexPath(2)); + data.Reset(new[] { "foo", "bar" }); + data.Reset(new[] { "foo", "bar", "baz" }); + + Assert.Equal(new[] { new IndexPath(1) }, target.SelectedIndices); + Assert.Equal(new[] { "bar", }, target.SelectedItems); + } + + [Fact] + public void RetainSelectionOnReset_Raises_Empty_SelectionChanged_On_Reset_With_No_Changes() + { + var data = new ResettingList { "foo", "bar", "baz" }; + var target = new SelectionModel { Source = data, RetainSelectionOnReset = true }; + var raised = 0; + + target.SelectRange(new IndexPath(1), new IndexPath(2)); + + target.SelectionChanged += (s, e) => + { + Assert.Empty(e.DeselectedIndices); + Assert.Empty(e.DeselectedItems); + Assert.Empty(e.SelectedIndices); + Assert.Empty(e.SelectedItems); + ++raised; + }; + + data.Reset(); + + Assert.Equal(1, raised); + } + + [Fact] + public void RetainSelectionOnReset_Raises_SelectionChanged_On_Reset_With_Removed_Items() + { + var data = new ResettingList { "foo", "bar", "baz" }; + var target = new SelectionModel { Source = data, RetainSelectionOnReset = true }; + var raised = 0; + + target.SelectRange(new IndexPath(1), new IndexPath(2)); + + target.SelectionChanged += (s, e) => + { + Assert.Empty(e.DeselectedIndices); + Assert.Equal(new[] { "bar" }, e.DeselectedItems); + Assert.Empty(e.SelectedIndices); + Assert.Empty(e.SelectedItems); + ++raised; + }; + + data.Reset(new[] { "foo", "baz" }); + + Assert.Equal(1, raised); + } + private int GetSubscriberCount(AvaloniaList list) { return ((INotifyCollectionChangedDebug)list).GetCollectionChangedSubscribers()?.Length ?? 0; @@ -1743,6 +1845,24 @@ namespace Avalonia.Controls.UnitTests public LogWrapper(ITestOutputHelper output) => _output = output; public void Comment(string s) => _output.WriteLine(s); } + + private class ResettingList : List, INotifyCollectionChanged + { + public event NotifyCollectionChangedEventHandler CollectionChanged; + + public void Reset(IEnumerable items = null) + { + if (items != null) + { + Clear(); + AddRange(items); + } + + CollectionChanged?.Invoke( + this, + new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } + } } class CustomSelectionModel : SelectionModel From fcaa250c72cbbb691911456a1be0691663e41c63 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 3 Feb 2020 13:52:43 +0100 Subject: [PATCH 030/663] Ported fix and test from WinUI. https://github.com/microsoft/microsoft-ui-xaml/pull/1922 --- .../Utils/SelectionTreeHelper.cs | 18 +++++++---- .../SelectionModelTests.cs | 31 +++++++++++++++++++ 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Controls/Utils/SelectionTreeHelper.cs b/src/Avalonia.Controls/Utils/SelectionTreeHelper.cs index 93102a7b5b..430ecabbb8 100644 --- a/src/Avalonia.Controls/Utils/SelectionTreeHelper.cs +++ b/src/Avalonia.Controls/Utils/SelectionTreeHelper.cs @@ -132,15 +132,21 @@ namespace Avalonia.Controls.Utils private static bool IsSubSet(IndexPath path, IndexPath subset) { - bool isSubset = true; - for (int i = 0; i < subset.GetSize(); i++) + var subsetSize = subset.GetSize(); + if (path.GetSize() < subsetSize) { - isSubset = path.GetAt(i) == subset.GetAt(i); - if (!isSubset) - break; + return false; + } + + for (int i = 0; i < subsetSize; i++) + { + if (path.GetAt(i) != subset.GetAt(i)) + { + return false; + } } - return isSubset; + return true; } private static IndexPath StartPath(IndexPath path, int length) diff --git a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs index 6c3137c636..208d85d8fd 100644 --- a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs @@ -898,6 +898,37 @@ namespace Avalonia.Controls.UnitTests }); } + [Fact] + public void SelectRangeRegressionTest() + { + RunOnUIThread.Execute(() => + { + var selectionModel = new SelectionModel() + { + Source = CreateNestedData(1, 2, 3) + }; + + // length of start smaller than end used to cause an out of range error. + selectionModel.SelectRange(IndexPath.CreateFrom(0), IndexPath.CreateFrom(1, 1)); + + ValidateSelection(selectionModel, + new List() + { + Path(0, 0), + Path(0, 1), + Path(0, 2), + Path(0), + Path(1, 0), + Path(1, 1) + }, + new List() + { + Path(), + Path(1) + }, + 1 /* selectedInnerNodes */); + }); + } [Fact] public void Disposing_Unhooks_CollectionChanged_Handlers() From bc4eefcf1b0e90d7d93cb9ffd09607a3e5d78fbe Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 26 Jan 2020 09:59:48 +0100 Subject: [PATCH 031/663] Add `IndexRange` list add/remove methods. Add or remove index ranges from a list of index ranges, merging and splitting ranges as required. --- src/Avalonia.Controls/IndexRange.cs | 173 +++++++++- .../IndexRangeTests.cs | 307 ++++++++++++++++++ 2 files changed, 474 insertions(+), 6 deletions(-) create mode 100644 tests/Avalonia.Controls.UnitTests/IndexRangeTests.cs diff --git a/src/Avalonia.Controls/IndexRange.cs b/src/Avalonia.Controls/IndexRange.cs index b1a112ab39..124f1e0500 100644 --- a/src/Avalonia.Controls/IndexRange.cs +++ b/src/Avalonia.Controls/IndexRange.cs @@ -3,12 +3,17 @@ // // Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation. +using System; +using System.Collections.Generic; + #nullable enable namespace Avalonia.Controls { - internal readonly struct IndexRange + internal readonly struct IndexRange : IEquatable { + private static readonly IndexRange s_invalid = new IndexRange(int.MinValue, int.MinValue); + public IndexRange(int begin, int end) { // Accept out of order begin/end pairs, just swap them. @@ -25,11 +30,9 @@ namespace Avalonia.Controls public int Begin { get; } public int End { get; } + public int Count => (End - Begin) + 1; - public bool Contains(int index) - { - return index >= Begin && index <= End; - } + public bool Contains(int index) => index >= Begin && index <= End; public bool Split(int splitIndex, out IndexRange before, out IndexRange after) { @@ -54,6 +57,164 @@ namespace Avalonia.Controls public bool Intersects(IndexRange other) { return (Begin <= other.End) && (End >= other.Begin); - } + } + + public bool Adjacent(IndexRange other) + { + return Begin == other.End + 1 || End == other.Begin - 1; + } + + public override bool Equals(object? obj) + { + return obj is IndexRange range && Equals(range); + } + + public bool Equals(IndexRange other) + { + return Begin == other.Begin && End == other.End; + } + + public override int GetHashCode() + { + var hashCode = 1903003160; + hashCode = hashCode * -1521134295 + Begin.GetHashCode(); + hashCode = hashCode * -1521134295 + End.GetHashCode(); + return hashCode; + } + + public override string ToString() => $"[{Begin}..{End}]"; + + public static bool operator ==(IndexRange left, IndexRange right) => left.Equals(right); + public static bool operator !=(IndexRange left, IndexRange right) => !(left == right); + + public static int Add( + IList ranges, + IndexRange range, + IList? added = null) + { + var result = 0; + + for (var i = 0; i < ranges.Count && range != s_invalid; ++i) + { + var existing = ranges[i]; + + if (range.Intersects(existing) || range.Adjacent(existing)) + { + if (range.Begin < existing.Begin) + { + var add = new IndexRange(range.Begin, existing.Begin - 1); + ranges[i] = new IndexRange(range.Begin, existing.End); + added?.Add(add); + result += add.Count; + } + + range = range.End <= existing.End ? + s_invalid : + new IndexRange(existing.End + 1, range.End); + } + else if (range.End < existing.Begin) + { + ranges.Insert(i, range); + added?.Add(range); + result += range.Count; + range = s_invalid; + } + } + + if (range != s_invalid) + { + ranges.Add(range); + added?.Add(range); + result += range.Count; + } + + MergeRanges(ranges); + return result; + } + + public static int Remove( + IList ranges, + IndexRange range, + IList? removed = null) + { + var result = 0; + + for (var i = 0; i < ranges.Count; ++i) + { + var existing = ranges[i]; + + if (range.Intersects(existing)) + { + if (range.Begin <= existing.Begin && range.End >= existing.End) + { + ranges.RemoveAt(i--); + removed?.Add(existing); + result += existing.Count; + } + else if (range.Begin > existing.Begin && range.End >= existing.End) + { + ranges[i] = new IndexRange(existing.Begin, range.Begin - 1); + removed?.Add(new IndexRange(range.Begin, existing.End)); + result += existing.End - (range.Begin - 1); + } + else if (range.Begin > existing.Begin && range.End < existing.End) + { + ranges[i] = new IndexRange(existing.Begin, range.Begin - 1); + ranges.Insert(++i, new IndexRange(range.End + 1, existing.End)); + removed?.Add(range); + result += range.Count; + } + else if (range.End <= existing.End) + { + var remove = new IndexRange(existing.Begin, range.End); + ranges[i] = new IndexRange(range.End + 1, existing.End); + removed?.Add(remove); + result += remove.Count; + } + } + } + + return result; + } + + public static IEnumerable Subtract( + IndexRange lhs, + IEnumerable rhs) + { + var result = new List { lhs }; + + foreach (var range in rhs) + { + Remove(result, range); + } + + return result; + } + + public static IEnumerable EnumerateIndices(IEnumerable ranges) + { + foreach (var range in ranges) + { + for (var i = range.Begin; i <= range.End; ++i) + { + yield return i; + } + } + } + + private static void MergeRanges(IList ranges) + { + for (var i = ranges.Count - 2; i >= 0; --i) + { + var r = ranges[i]; + var r1 = ranges[i + 1]; + + if (r.Intersects(r1) || r.End == r1.Begin - 1) + { + ranges[i] = new IndexRange(r.Begin, r1.End); + ranges.RemoveAt(i + 1); + } + } + } } } diff --git a/tests/Avalonia.Controls.UnitTests/IndexRangeTests.cs b/tests/Avalonia.Controls.UnitTests/IndexRangeTests.cs new file mode 100644 index 0000000000..e0f46d9fa9 --- /dev/null +++ b/tests/Avalonia.Controls.UnitTests/IndexRangeTests.cs @@ -0,0 +1,307 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace Avalonia.Controls.UnitTests +{ + public class IndexRangeTests + { + [Fact] + public void Add_Should_Add_Range_To_Empty_List() + { + var ranges = new List(); + var selected = new List(); + var result = IndexRange.Add(ranges, new IndexRange(0, 4), selected); + + Assert.Equal(5, result); + Assert.Equal(new[] { new IndexRange(0, 4) }, ranges); + Assert.Equal(new[] { new IndexRange(0, 4) }, selected); + } + + [Fact] + public void Add_Should_Add_Non_Intersecting_Range_At_End() + { + var ranges = new List { new IndexRange(0, 4) }; + var selected = new List(); + var result = IndexRange.Add(ranges, new IndexRange(8, 10), selected); + + Assert.Equal(3, result); + Assert.Equal(new[] { new IndexRange(0, 4), new IndexRange(8, 10) }, ranges); + Assert.Equal(new[] { new IndexRange(8, 10) }, selected); + } + + [Fact] + public void Add_Should_Add_Non_Intersecting_Range_At_Beginning() + { + var ranges = new List { new IndexRange(8, 10) }; + var selected = new List(); + var result = IndexRange.Add(ranges, new IndexRange(0, 4), selected); + + Assert.Equal(5, result); + Assert.Equal(new[] { new IndexRange(0, 4), new IndexRange(8, 10) }, ranges); + Assert.Equal(new[] { new IndexRange(0, 4) }, selected); + } + + [Fact] + public void Add_Should_Add_Non_Intersecting_Range_In_Middle() + { + var ranges = new List { new IndexRange(0, 4), new IndexRange(14, 16) }; + var selected = new List(); + var result = IndexRange.Add(ranges, new IndexRange(8, 10), selected); + + Assert.Equal(3, result); + Assert.Equal(new[] { new IndexRange(0, 4), new IndexRange(8, 10), new IndexRange(14, 16) }, ranges); + Assert.Equal(new[] { new IndexRange(8, 10) }, selected); + } + + [Fact] + public void Add_Should_Add_Intersecting_Range_Start() + { + var ranges = new List { new IndexRange(8, 10) }; + var selected = new List(); + var result = IndexRange.Add(ranges, new IndexRange(6, 9), selected); + + Assert.Equal(2, result); + Assert.Equal(new[] { new IndexRange(6, 10) }, ranges); + Assert.Equal(new[] { new IndexRange(6, 7) }, selected); + } + + [Fact] + public void Add_Should_Add_Intersecting_Range_End() + { + var ranges = new List { new IndexRange(8, 10) }; + var selected = new List(); + var result = IndexRange.Add(ranges, new IndexRange(9, 12), selected); + + Assert.Equal(2, result); + Assert.Equal(new[] { new IndexRange(8, 12) }, ranges); + Assert.Equal(new[] { new IndexRange(11, 12) }, selected); + } + + [Fact] + public void Add_Should_Add_Intersecting_Range_Both() + { + var ranges = new List { new IndexRange(8, 10) }; + var selected = new List(); + var result = IndexRange.Add(ranges, new IndexRange(6, 12), selected); + + Assert.Equal(4, result); + Assert.Equal(new[] { new IndexRange(6, 12) }, ranges); + Assert.Equal(new[] { new IndexRange(6, 7), new IndexRange(11, 12) }, selected); + } + + [Fact] + public void Add_Should_Join_Two_Intersecting_Ranges() + { + var ranges = new List { new IndexRange(8, 10), new IndexRange(12, 14) }; + var selected = new List(); + var result = IndexRange.Add(ranges, new IndexRange(8, 14), selected); + + Assert.Equal(1, result); + Assert.Equal(new[] { new IndexRange(8, 14) }, ranges); + Assert.Equal(new[] { new IndexRange(11, 11) }, selected); + } + + [Fact] + public void Add_Should_Join_Two_Intersecting_Ranges_And_Add_Ranges() + { + var ranges = new List { new IndexRange(8, 10), new IndexRange(12, 14) }; + var selected = new List(); + var result = IndexRange.Add(ranges, new IndexRange(6, 18), selected); + + Assert.Equal(7, result); + Assert.Equal(new[] { new IndexRange(6, 18) }, ranges); + Assert.Equal(new[] { new IndexRange(6, 7), new IndexRange(11, 11), new IndexRange(15, 18) }, selected); + } + + [Fact] + public void Add_Should_Not_Add_Already_Selected_Range() + { + var ranges = new List { new IndexRange(8, 10) }; + var selected = new List(); + var result = IndexRange.Add(ranges, new IndexRange(9, 10), selected); + + Assert.Equal(0, result); + Assert.Equal(new[] { new IndexRange(8, 10) }, ranges); + Assert.Empty(selected); + } + + [Fact] + public void Remove_Should_Remove_Entire_Range() + { + var ranges = new List { new IndexRange(8, 10) }; + var deselected = new List(); + var result = IndexRange.Remove(ranges, new IndexRange(8, 10), deselected); + + Assert.Equal(3, result); + Assert.Empty(ranges); + Assert.Equal(new[] { new IndexRange(8, 10) }, deselected); + } + + [Fact] + public void Remove_Should_Remove_Start_Of_Range() + { + var ranges = new List { new IndexRange(8, 12) }; + var deselected = new List(); + var result = IndexRange.Remove(ranges, new IndexRange(8, 10), deselected); + + Assert.Equal(3, result); + Assert.Equal(new[] { new IndexRange(11, 12) }, ranges); + Assert.Equal(new[] { new IndexRange(8, 10) }, deselected); + } + + [Fact] + public void Remove_Should_Remove_End_Of_Range() + { + var ranges = new List { new IndexRange(8, 12) }; + var deselected = new List(); + var result = IndexRange.Remove(ranges, new IndexRange(10, 12), deselected); + + Assert.Equal(3, result); + Assert.Equal(new[] { new IndexRange(8, 9) }, ranges); + Assert.Equal(new[] { new IndexRange(10, 12) }, deselected); + } + + [Fact] + public void Remove_Should_Remove_Overlapping_End_Of_Range() + { + var ranges = new List { new IndexRange(8, 12) }; + var deselected = new List(); + var result = IndexRange.Remove(ranges, new IndexRange(10, 14), deselected); + + Assert.Equal(3, result); + Assert.Equal(new[] { new IndexRange(8, 9) }, ranges); + Assert.Equal(new[] { new IndexRange(10, 12) }, deselected); + } + + [Fact] + public void Remove_Should_Remove_Middle_Of_Range() + { + var ranges = new List { new IndexRange(10, 20) }; + var deselected = new List(); + var result = IndexRange.Remove(ranges, new IndexRange(12, 16), deselected); + + Assert.Equal(5, result); + Assert.Equal(new[] { new IndexRange(10, 11), new IndexRange(17, 20) }, ranges); + Assert.Equal(new[] { new IndexRange(12, 16) }, deselected); + } + + [Fact] + public void Remove_Should_Remove_Multiple_Ranges() + { + var ranges = new List { new IndexRange(8, 10), new IndexRange(12, 14), new IndexRange(16, 18) }; + var deselected = new List(); + var result = IndexRange.Remove(ranges, new IndexRange(6, 15), deselected); + + Assert.Equal(6, result); + Assert.Equal(new[] { new IndexRange(16, 18) }, ranges); + Assert.Equal(new[] { new IndexRange(8, 10), new IndexRange(12, 14) }, deselected); + } + + [Fact] + public void Remove_Should_Remove_Multiple_And_Partial_Ranges_1() + { + var ranges = new List { new IndexRange(8, 10), new IndexRange(12, 14), new IndexRange(16, 18) }; + var deselected = new List(); + var result = IndexRange.Remove(ranges, new IndexRange(9, 15), deselected); + + Assert.Equal(5, result); + Assert.Equal(new[] { new IndexRange(8, 8), new IndexRange(16, 18) }, ranges); + Assert.Equal(new[] { new IndexRange(9, 10), new IndexRange(12, 14) }, deselected); + } + + [Fact] + public void Remove_Should_Remove_Multiple_And_Partial_Ranges_2() + { + var ranges = new List { new IndexRange(8, 10), new IndexRange(12, 14), new IndexRange(16, 18) }; + var deselected = new List(); + var result = IndexRange.Remove(ranges, new IndexRange(8, 13), deselected); + + Assert.Equal(5, result); + Assert.Equal(new[] { new IndexRange(14, 14), new IndexRange(16, 18) }, ranges); + Assert.Equal(new[] { new IndexRange(8, 10), new IndexRange(12, 13) }, deselected); + } + + [Fact] + public void Remove_Should_Remove_Multiple_And_Partial_Ranges_3() + { + var ranges = new List { new IndexRange(8, 10), new IndexRange(12, 14), new IndexRange(16, 18) }; + var deselected = new List(); + var result = IndexRange.Remove(ranges, new IndexRange(9, 13), deselected); + + Assert.Equal(4, result); + Assert.Equal(new[] { new IndexRange(8, 8), new IndexRange(14, 14), new IndexRange(16, 18) }, ranges); + Assert.Equal(new[] { new IndexRange(9, 10), new IndexRange(12, 13) }, deselected); + } + + [Fact] + public void Remove_Should_Do_Nothing_For_Unselected_Range() + { + var ranges = new List { new IndexRange(8, 10) }; + var deselected = new List(); + var result = IndexRange.Remove(ranges, new IndexRange(2, 4), deselected); + + Assert.Equal(0, result); + Assert.Equal(new[] { new IndexRange(8, 10) }, ranges); + Assert.Empty(deselected); + } + + [Fact] + public void Stress_Test() + { + const int iterations = 100; + var random = new Random(0); + var selection = new List(); + var expected = new List(); + + IndexRange Generate() + { + var start = random.Next(100); + return new IndexRange(start, start + random.Next(20)); + } + + for (var i = 0; i < iterations; ++i) + { + var toAdd = random.Next(5); + + for (var j = 0; j < toAdd; ++j) + { + var range = Generate(); + IndexRange.Add(selection, range); + + for (var k = range.Begin; k <= range.End; ++k) + { + if (!expected.Contains(k)) + { + expected.Add(k); + } + } + + var actual = IndexRange.EnumerateIndices(selection).ToList(); + expected.Sort(); + Assert.Equal(expected, actual); + } + + var toRemove = random.Next(5); + + for (var j = 0; j < toRemove; ++j) + { + var range = Generate(); + IndexRange.Remove(selection, range); + + for (var k = range.Begin; k <= range.End; ++k) + { + expected.Remove(k); + } + + var actual = IndexRange.EnumerateIndices(selection).ToList(); + Assert.Equal(expected, actual); + } + + selection.Clear(); + expected.Clear(); + } + } + } +} From 7548dc9c2edaf687ebe1d1bfe9371386ef1d5df9 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 26 Jan 2020 11:47:51 +0100 Subject: [PATCH 032/663] Added SelectionModel changed args. `SelectionModel` as ported from WinUI has no information about what changed in a `SelectionChanged` event. This adds that information along with unit tests. --- src/Avalonia.Controls/SelectionModel.cs | 191 +++++--- .../SelectionModelChangeSet.cs | 144 ++++++ ...SelectionModelSelectionChangedEventArgs.cs | 45 ++ src/Avalonia.Controls/SelectionNode.cs | 150 +++---- .../SelectionModelTests.cs | 417 ++++++++++++++++++ 5 files changed, 804 insertions(+), 143 deletions(-) create mode 100644 src/Avalonia.Controls/SelectionModelChangeSet.cs diff --git a/src/Avalonia.Controls/SelectionModel.cs b/src/Avalonia.Controls/SelectionModel.cs index 34d5f78434..5e2fb32243 100644 --- a/src/Avalonia.Controls/SelectionModel.cs +++ b/src/Avalonia.Controls/SelectionModel.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Linq; using Avalonia.Controls.Utils; #nullable enable @@ -19,7 +20,6 @@ namespace Avalonia.Controls private IReadOnlyList? _selectedIndicesCached; private IReadOnlyList? _selectedItemsCached; private SelectionModelChildrenRequestedEventArgs? _childrenRequestedEventArgs; - private SelectionModelSelectionChangedEventArgs? _selectionChangedEventArgs; public event EventHandler? ChildrenRequested; public event PropertyChangedEventHandler? PropertyChanged; @@ -36,9 +36,12 @@ namespace Avalonia.Controls get => _rootNode?.Source; set { - ClearSelection(resetAnchor: true, raiseSelectionChanged: false); + using (var operation = new Operation(this)) + { + ClearSelection(resetAnchor: true); + } + _rootNode.Source = value; - OnSelectionChanged(); RaisePropertyChanged("Source"); } } @@ -55,12 +58,13 @@ namespace Avalonia.Controls 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, raiseSelectionChanged: false); - SelectWithPathImpl(firstSelectionIndexPath, select: true, raiseSelectionChanged: false); - // Setting SelectedIndex will raise SelectionChanged event. + ClearSelection(resetAnchor: true); + SelectWithPathImpl(firstSelectionIndexPath, select: true); SelectedIndex = firstSelectionIndexPath; } @@ -131,9 +135,9 @@ namespace Avalonia.Controls if (!isSelected.HasValue || !isSelected.Value) { - ClearSelection(resetAnchor: true, raiseSelectionChanged: false); - SelectWithPathImpl(value, select: true, raiseSelectionChanged: false); - OnSelectionChanged(); + using var operation = new Operation(this); + ClearSelection(resetAnchor: true); + SelectWithPathImpl(value, select: true); } } } @@ -289,7 +293,7 @@ namespace Avalonia.Controls public void Dispose() { - ClearSelection(resetAnchor: false, raiseSelectionChanged: false); + ClearSelection(resetAnchor: false); _rootNode?.Dispose(); _selectedIndicesCached = null; _selectedItemsCached = null; @@ -299,17 +303,41 @@ namespace Avalonia.Controls public void SetAnchorIndex(int groupIndex, int index) => AnchorIndex = new IndexPath(groupIndex, index); - public void Select(int index) => SelectImpl(index, select: true); + public void Select(int index) + { + using var operation = new Operation(this); + SelectImpl(index, select: true); + } - public void Select(int groupIndex, int itemIndex) => SelectWithGroupImpl(groupIndex, itemIndex, 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) => SelectWithPathImpl(index, select: true, raiseSelectionChanged: true); + public void SelectAt(IndexPath index) + { + using var operation = new Operation(this); + SelectWithPathImpl(index, select: true); + } - public void Deselect(int index) => SelectImpl(index, select: false); + public void Deselect(int index) + { + using var operation = new Operation(this); + SelectImpl(index, select: false); + } - public void Deselect(int groupIndex, int itemIndex) => SelectWithGroupImpl(groupIndex, itemIndex, 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) => SelectWithPathImpl(index, select: false, raiseSelectionChanged: true); + public void DeselectAt(IndexPath index) + { + using var operation = new Operation(this); + SelectWithPathImpl(index, select: false); + } public bool? IsSelected(int index) { @@ -383,46 +411,56 @@ namespace Avalonia.Controls 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, @@ -433,13 +471,12 @@ namespace Avalonia.Controls info.Node.SelectAll(); } }); - - OnSelectionChanged(); } public void ClearSelection() { - ClearSelection(resetAnchor: true, raiseSelectionChanged: true); + using var operation = new Operation(this); + ClearSelection(resetAnchor: true); } protected void OnPropertyChanged(string propertyName) @@ -452,9 +489,15 @@ namespace Avalonia.Controls PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } - public void OnSelectionInvalidatedDueToCollectionChange() + public void OnSelectionInvalidatedDueToCollectionChange( + IEnumerable? removedItems) { - OnSelectionChanged(); + var e = new SelectionModelSelectionChangedEventArgs( + Enumerable.Empty(), + Enumerable.Empty(), + removedItems ?? Enumerable.Empty(), + Enumerable.Empty()); + OnSelectionChanged(e); } internal object? ResolvePath(object data, SelectionNode sourceNode) @@ -496,7 +539,7 @@ namespace Avalonia.Controls return resolved; } - private void ClearSelection(bool resetAnchor, bool raiseSelectionChanged) + private void ClearSelection(bool resetAnchor) { SelectionTreeHelper.Traverse( _rootNode, @@ -507,27 +550,17 @@ namespace Avalonia.Controls { AnchorIndex = default; } - - if (raiseSelectionChanged) - { - OnSelectionChanged(); - } } - private void OnSelectionChanged() + private void OnSelectionChanged(SelectionModelSelectionChangedEventArgs? e = null) { _selectedIndicesCached = null; _selectedItemsCached = null; // Raise SelectionChanged event - if (SelectionChanged != null) + if (e != null) { - if (_selectionChangedEventArgs == null) - { - _selectionChangedEventArgs = new SelectionModelSelectionChangedEventArgs(); - } - - SelectionChanged(this, _selectionChangedEventArgs); + SelectionChanged?.Invoke(this, e); } RaisePropertyChanged(nameof(SelectedIndex)); @@ -544,7 +577,7 @@ namespace Avalonia.Controls { if (_singleSelect) { - ClearSelection(resetAnchor: true, raiseSelectionChanged: false); + ClearSelection(resetAnchor: true); } var selected = _rootNode.Select(index, select); @@ -553,15 +586,13 @@ namespace Avalonia.Controls { AnchorIndex = new IndexPath(index); } - - OnSelectionChanged(); } private void SelectWithGroupImpl(int groupIndex, int itemIndex, bool select) { if (_singleSelect) { - ClearSelection(resetAnchor: true, raiseSelectionChanged: false); + ClearSelection(resetAnchor: true); } var childNode = _rootNode.GetAt(groupIndex, realizeChild: true); @@ -571,17 +602,15 @@ namespace Avalonia.Controls { AnchorIndex = new IndexPath(groupIndex, itemIndex); } - - OnSelectionChanged(); } - private void SelectWithPathImpl(IndexPath index, bool select, bool raiseSelectionChanged) + private void SelectWithPathImpl(IndexPath index, bool select) { bool selected = false; if (_singleSelect) { - ClearSelection(resetAnchor: true, raiseSelectionChanged: false); + ClearSelection(resetAnchor: true); } SelectionTreeHelper.TraverseIndexPath( @@ -601,11 +630,6 @@ namespace Avalonia.Controls { AnchorIndex = index; } - - if (raiseSelectionChanged) - { - OnSelectionChanged(); - } } private void SelectRangeFromAnchorImpl(int index, bool select) @@ -618,12 +642,7 @@ namespace Avalonia.Controls anchorIndex = anchor.GetAt(0); } - bool selected = _rootNode.SelectRange(new IndexRange(anchorIndex, index), select); - - if (selected) - { - OnSelectionChanged(); - } + _rootNode.SelectRange(new IndexRange(anchorIndex, index), select); } private void SelectRangeFromAnchorWithGroupImpl(int endGroupIndex, int endItemIndex, bool select) @@ -650,18 +669,12 @@ namespace Avalonia.Controls endItemIndex = temp; } - var selected = false; for (int groupIdx = startGroupIndex; groupIdx <= endGroupIndex; groupIdx++) { var groupNode = _rootNode.GetAt(groupIdx, realizeChild: true)!; int startIndex = groupIdx == startGroupIndex ? startItemIndex : 0; int endIndex = groupIdx == endGroupIndex ? endItemIndex : groupNode.DataCount - 1; - selected |= groupNode.SelectRange(new IndexRange(startIndex, endIndex), select); - } - - if (selected) - { - OnSelectionChanged(); + groupNode.SelectRange(new IndexRange(startIndex, endIndex), select); } } @@ -691,8 +704,55 @@ namespace Avalonia.Controls info.ParentNode!.Select(info.Path.GetAt(info.Path.GetSize() - 1), select); } }); + } + + private void BeginOperation() + { + if (SelectionChanged != null) + { + _rootNode.BeginOperation(); + } + } + + private void EndOperation() + { + static IEnumerable? Concat(IEnumerable? a, IEnumerable b) + { + return a == null ? b : a.Concat(b); + } - OnSelectionChanged(); + SelectionModelSelectionChangedEventArgs? e = null; + + if (SelectionChanged != null) + { + IEnumerable? selectedIndices = null; + IEnumerable? deselectedIndices = null; + IEnumerable? selectedItems = null; + IEnumerable? deselectedItems = null; + + foreach (var changes in _rootNode.EndOperation()) + { + if (changes.HasChanges) + { + selectedIndices = Concat(selectedIndices, changes.SelectedIndices); + deselectedIndices = Concat(deselectedIndices, changes.DeselectedIndices); + selectedItems = Concat(selectedItems, changes.SelectedItems); + deselectedItems = Concat(deselectedItems, changes.DeselectedItems); + } + } + + if (selectedIndices != null || deselectedIndices != null || + selectedItems != null || deselectedItems != null) + { + e = new SelectionModelSelectionChangedEventArgs( + deselectedIndices ?? Enumerable.Empty(), + selectedIndices ?? Enumerable.Empty(), + deselectedItems ?? Enumerable.Empty(), + selectedItems ?? Enumerable.Empty()); + } + } + + OnSelectionChanged(e); } internal class SelectedItemInfo @@ -706,5 +766,12 @@ namespace Avalonia.Controls public SelectionNode Node { get; } public IndexPath Path { get; } } + + private struct Operation : IDisposable + { + private readonly SelectionModel _manager; + public Operation(SelectionModel manager) => (_manager = manager).BeginOperation(); + public void Dispose() => _manager.EndOperation(); + } } } diff --git a/src/Avalonia.Controls/SelectionModelChangeSet.cs b/src/Avalonia.Controls/SelectionModelChangeSet.cs new file mode 100644 index 0000000000..989136ac8d --- /dev/null +++ b/src/Avalonia.Controls/SelectionModelChangeSet.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +#nullable enable + +namespace Avalonia.Controls +{ + internal class SelectionModelChangeSet + { + private SelectionNode _owner; + private List? _selected; + private List? _deselected; + + public SelectionModelChangeSet(SelectionNode owner) => _owner = owner; + + public bool IsTracking { get; private set; } + public bool HasChanges => _selected?.Count > 0 || _deselected?.Count > 0; + public IEnumerable SelectedIndices => EnumerateIndices(_selected); + public IEnumerable DeselectedIndices => EnumerateIndices(_deselected); + public IEnumerable SelectedItems => EnumerateItems(_selected); + public IEnumerable DeselectedItems => EnumerateItems(_deselected); + + public void BeginOperation() + { + if (IsTracking) + { + throw new AvaloniaInternalException("SelectionModel change operation already in progress."); + } + + IsTracking = true; + _selected?.Clear(); + _deselected?.Clear(); + } + + public void EndOperation() => IsTracking = false; + + public void Selected(IndexRange range) + { + if (!IsTracking) + { + return; + } + + Add(range, ref _selected, _deselected); + } + + public void Selected(IEnumerable ranges) + { + if (!IsTracking) + { + return; + } + + foreach (var range in ranges) + { + Selected(range); + } + } + + public void Deselected(IndexRange range) + { + if (!IsTracking) + { + return; + } + + Add(range, ref _deselected, _selected); + } + + public void Deselected(IEnumerable ranges) + { + if (!IsTracking) + { + return; + } + + foreach (var range in ranges) + { + Deselected(range); + } + } + + private static void Add( + IndexRange range, + ref List? add, + List? remove) + { + if (remove != null) + { + var removed = new List(); + IndexRange.Remove(remove, range, removed); + var selected = IndexRange.Subtract(range, removed); + + if (selected.Any()) + { + add ??= new List(); + + foreach (var r in selected) + { + IndexRange.Add(add, r); + } + } + } + else + { + add ??= new List(); + IndexRange.Add(add, range); + } + } + + private IEnumerable EnumerateIndices(IEnumerable? ranges) + { + var path = _owner.IndexPath; + + if (ranges != null) + { + foreach (var range in ranges) + { + for (var i = range.Begin; i <= range.End; ++i) + { + yield return path.CloneWithChildIndex(i); + } + } + } + } + + private IEnumerable EnumerateItems(IEnumerable? ranges) + { + var items = _owner.ItemsSourceView; + + if (ranges != null && items != null) + { + foreach (var range in ranges) + { + for (var i = range.Begin; i <= range.End; ++i) + { + yield return items.GetAt(i); + } + } + } + } + } +} diff --git a/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs b/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs index c8edc1f8ae..4976bf1827 100644 --- a/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs +++ b/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs @@ -4,6 +4,7 @@ // Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation. using System; +using System.Collections.Generic; #nullable enable @@ -11,5 +12,49 @@ namespace Avalonia.Controls { public class SelectionModelSelectionChangedEventArgs : EventArgs { + private readonly IEnumerable _selectedIndicesSource; + private readonly IEnumerable _deselectedIndicesSource; + private readonly IEnumerable _selectedItemsSource; + private readonly IEnumerable _deselectedItemsSource; + private List? _selectedIndices; + private List? _deselectedIndices; + private List? _selectedItems; + private List? _deselectedItems; + + public SelectionModelSelectionChangedEventArgs( + IEnumerable deselectedIndices, + IEnumerable selectedIndices, + IEnumerable deselectedItems, + IEnumerable selectedItems) + { + _selectedIndicesSource = selectedIndices; + _deselectedIndicesSource = deselectedIndices; + _selectedItemsSource = selectedItems; + _deselectedItemsSource = deselectedItems; + } + + /// + /// Gets the indices of the items that were added to the selection. + /// + public IReadOnlyList SelectedIndices => + _selectedIndices ?? (_selectedIndices = new List(_selectedIndicesSource)); + + /// + /// Gets the indices of the items that were removed from the selection. + /// + public IReadOnlyList DeselectedIndices => + _deselectedIndices ?? (_deselectedIndices = new List(_deselectedIndicesSource)); + + /// + /// Gets the items that were added to the selection. + /// + public IReadOnlyList SelectedItems => + _selectedItems ?? (_selectedItems = new List(_selectedItemsSource)); + + /// + /// Gets the items that were removed from the selection. + /// + public IReadOnlyList DeselectedItems => + _deselectedItems ?? (_deselectedItems = new List(_deselectedItemsSource)); } } diff --git a/src/Avalonia.Controls/SelectionNode.cs b/src/Avalonia.Controls/SelectionNode.cs index 363eb35b94..d462a51228 100644 --- a/src/Avalonia.Controls/SelectionNode.cs +++ b/src/Avalonia.Controls/SelectionNode.cs @@ -7,6 +7,7 @@ using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; +using System.Linq; #nullable enable @@ -28,6 +29,7 @@ namespace Avalonia.Controls private readonly SelectionNode? _parent; private readonly List _selected = new List(); private readonly List _selectedIndicesCached = new List(); + private SelectionModelChangeSet? _changes; private object? _source; private bool _selectedIndicesCacheIsValid; @@ -134,6 +136,11 @@ namespace Avalonia.Controls { child = new SelectionNode(_manager, parent: this); child.Source = resolvedChild; + + if (_changes?.IsTracking == true) + { + child.BeginOperation(); + } } else { @@ -276,12 +283,50 @@ namespace Avalonia.Controls } } + public IEnumerable SelectedItems + { + get => SelectedIndices.Select(x => ItemsSourceView!.GetAt(x)); + } + public void Dispose() { ItemsSourceView?.Dispose(); UnhookCollectionChangedHandler(); } + public void BeginOperation() + { + _changes ??= new SelectionModelChangeSet(this); + _changes.BeginOperation(); + + for (var i = 0; i < _childrenNodes.Count; ++i) + { + _childrenNodes[i]?.BeginOperation(); + } + } + + public IEnumerable EndOperation() + { + if (_changes != null) + { + _changes.EndOperation(); + yield return _changes; + + for (var i = 0; i < _childrenNodes.Count; ++i) + { + var child = _childrenNodes[i]; + + if (child != null) + { + foreach (var changes in child.EndOperation()) + { + yield return changes; + } + } + } + } + } + public bool Select(int index, bool select) { return Select(index, select, raiseOnSelectionChanged: true); @@ -349,21 +394,13 @@ namespace Avalonia.Controls private void AddRange(IndexRange addRange, bool raiseOnSelectionChanged) { - // TODO: Check for duplicates (Task 14107720) - // TODO: Optimize by merging adjacent ranges (Task 14107720) - var oldCount = SelectedCount; + var selected = new List(); - for (int i = addRange.Begin; i <= addRange.End; i++) - { - if (!IsSelected(i)) - { - SelectedCount++; - } - } + SelectedCount += IndexRange.Add(_selected, addRange, selected); - if (oldCount != SelectedCount) + if (selected.Count > 0) { - _selected.Add(addRange); + _changes?.Selected(selected); if (raiseOnSelectionChanged) { @@ -374,71 +411,17 @@ namespace Avalonia.Controls private void RemoveRange(IndexRange removeRange, bool raiseOnSelectionChanged) { - int oldCount = SelectedCount; + var removed = new List(); - // TODO: Prevent overlap of Ranges in _selected (Task 14107720) - for (int i = removeRange.Begin; i <= removeRange.End; i++) - { - if (IsSelected(i)) - { - SelectedCount--; - } - } + SelectedCount -= IndexRange.Remove(_selected, removeRange, removed); - if (oldCount != SelectedCount) + if (removed.Count > 0) { - // Build up a both a list of Ranges to remove and ranges to add - var toRemove = new List(); - var toAdd = new List(); - - foreach (var range in _selected) - { - // If this range intersects the remove range, we have to do something - if (removeRange.Intersects(range)) - { - // Intersection with the beginning of the range - // Anything to the left of the point (exclusive) stays - // Anything to the right of the point (inclusive) gets clipped - if (range.Contains(removeRange.Begin - 1)) - { - range.Split(removeRange.Begin - 1, out var before, out _); - toAdd.Add(before); - } + _changes?.Deselected(removed); - // Intersection with the end of the range - // Anything to the left of the point (inclusive) gets clipped - // Anything to the right of the point (exclusive) stays - if (range.Contains(removeRange.End)) - { - if (range.Split(removeRange.End, out _, out var after)) - { - toAdd.Add(after); - } - } - - // Remove this Range from the collection - // New ranges will be added for any remaining subsections - toRemove.Add(range); - } - } - - bool change = ((toRemove.Count > 0) || (toAdd.Count > 0)); - - if (change) + if (raiseOnSelectionChanged) { - // Remove tagged ranges - foreach (var remove in toRemove) - { - _selected.Remove(remove); - } - - // Add new ranges - _selected.AddRange(toAdd); - - if (raiseOnSelectionChanged) - { - OnSelectionChanged(); - } + OnSelectionChanged(); } } } @@ -448,6 +431,7 @@ namespace Avalonia.Controls // Deselect all items if (_selected.Count > 0) { + _changes?.Deselected(_selected); _selected.Clear(); OnSelectionChanged(); } @@ -496,6 +480,7 @@ namespace Avalonia.Controls private void OnSourceListChanged(object dataSource, NotifyCollectionChangedEventArgs args) { bool selectionInvalidated = false; + IList? removed = null; switch (args.Action) { @@ -507,7 +492,7 @@ namespace Avalonia.Controls case NotifyCollectionChangedAction.Remove: { - selectionInvalidated = OnItemsRemoved(args.OldStartingIndex, args.OldItems.Count); + (selectionInvalidated, removed) = OnItemsRemoved(args.OldStartingIndex, args.OldItems); break; } @@ -520,7 +505,7 @@ namespace Avalonia.Controls case NotifyCollectionChangedAction.Replace: { - selectionInvalidated = OnItemsRemoved(args.OldStartingIndex, args.OldItems.Count); + (selectionInvalidated, removed) = OnItemsRemoved(args.OldStartingIndex, args.OldItems); selectionInvalidated |= OnItemsAdded(args.NewStartingIndex, args.NewItems.Count); break; } @@ -529,7 +514,7 @@ namespace Avalonia.Controls if (selectionInvalidated) { OnSelectionChanged(); - _manager.OnSelectionInvalidatedDueToCollectionChange(); + _manager.OnSelectionInvalidatedDueToCollectionChange(removed); } } @@ -609,21 +594,23 @@ namespace Avalonia.Controls return selectionInvalidated; } - private bool OnItemsRemoved(int index, int count) + private (bool, IList) OnItemsRemoved(int index, IList items) { - bool selectionInvalidated = false; + var selectionInvalidated = false; + var removed = new List(); + var count = items.Count; // Remove the items from the selection for leaf if (ItemsSourceView!.Count > 0) { bool isSelected = false; - for (int i = index; i <= index + count - 1; i++) + for (int i = 0; i <= count - 1; i++) { - if (IsSelected(i)) + if (IsSelected(index + i)) { isSelected = true; - break; + removed.Add(items[i]); } } @@ -654,6 +641,7 @@ namespace Avalonia.Controls { if (_childrenNodes[index] != null) { + removed.AddRange(_childrenNodes[index]!.SelectedItems); RealizedChildrenNodeCount--; } _childrenNodes.RemoveAt(index); @@ -696,7 +684,7 @@ namespace Avalonia.Controls } } - return selectionInvalidated; + return (selectionInvalidated, removed); } private void OnSelectionChanged() diff --git a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs index 208d85d8fd..1cca809c1d 100644 --- a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs @@ -930,6 +930,423 @@ namespace Avalonia.Controls.UnitTests }); } + [Fact] + public void Selecting_Item_Raises_SelectionChanged() + { + var target = new SelectionModel(); + var raised = 0; + + target.Source = Enumerable.Range(0, 10).ToList(); + target.SelectionChanged += (s, e) => + { + Assert.Empty(e.DeselectedIndices); + Assert.Empty(e.DeselectedItems); + Assert.Equal(new[] { new IndexPath(4) }, e.SelectedIndices); + Assert.Equal(new object[] { 4 }, e.SelectedItems); + ++raised; + }; + + target.Select(4); + + Assert.Equal(1, raised); + } + + [Fact] + public void Selecting_Already_Selected_Item_Doesnt_Raise_SelectionChanged() + { + var target = new SelectionModel(); + var raised = 0; + + target.Source = Enumerable.Range(0, 10).ToList(); + target.Select(4); + target.SelectionChanged += (s, e) => ++raised; + target.Select(4); + + Assert.Equal(0, raised); + } + + [Fact] + public void SingleSelecting_Item_Raises_SelectionChanged() + { + var target = new SelectionModel { SingleSelect = true }; + var raised = 0; + + target.Source = Enumerable.Range(0, 10).ToList(); + target.Select(3); + + target.SelectionChanged += (s, e) => + { + Assert.Equal(new[] { new IndexPath(3) }, e.DeselectedIndices); + Assert.Equal(new object[] { 3 }, e.DeselectedItems); + Assert.Equal(new[] { new IndexPath(4) }, e.SelectedIndices); + Assert.Equal(new object[] { 4 }, e.SelectedItems); + ++raised; + }; + + target.Select(4); + + Assert.Equal(1, raised); + } + + [Fact] + public void SingleSelecting_Already_Selected_Item_Doesnt_Raise_SelectionChanged() + { + var target = new SelectionModel { SingleSelect = true }; + var raised = 0; + + target.Source = Enumerable.Range(0, 10).ToList(); + target.Select(4); + target.SelectionChanged += (s, e) => ++raised; + target.Select(4); + + Assert.Equal(0, raised); + } + + [Fact] + public void Selecting_Item_With_Group_Raises_SelectionChanged() + { + var target = new SelectionModel(); + var raised = 0; + + target.Source = CreateNestedData(1, 2, 3); + target.SelectionChanged += (s, e) => + { + Assert.Empty(e.DeselectedIndices); + Assert.Empty(e.DeselectedItems); + Assert.Equal(new[] { new IndexPath(1, 1) }, e.SelectedIndices); + Assert.Equal(new object[] { 4 }, e.SelectedItems); + ++raised; + }; + + target.Select(1, 1); + + Assert.Equal(1, raised); + } + + [Fact] + public void SelectAt_Raises_SelectionChanged() + { + var target = new SelectionModel(); + var raised = 0; + + target.Source = CreateNestedData(1, 2, 3); + target.SelectionChanged += (s, e) => + { + Assert.Empty(e.DeselectedIndices); + Assert.Empty(e.DeselectedItems); + Assert.Equal(new[] { new IndexPath(1, 1) }, e.SelectedIndices); + Assert.Equal(new object[] { 4 }, e.SelectedItems); + ++raised; + }; + + target.SelectAt(new IndexPath(1, 1)); + + Assert.Equal(1, raised); + } + + [Fact] + public void SelectAll_Raises_SelectionChanged() + { + var target = new SelectionModel { SingleSelect = true }; + var raised = 0; + + target.Source = Enumerable.Range(0, 10).ToList(); + target.SelectionChanged += (s, e) => + { + var expected = Enumerable.Range(0, 10); + Assert.Empty(e.DeselectedIndices); + Assert.Empty(e.DeselectedItems); + Assert.Equal(expected.Select(x => new IndexPath(x)), e.SelectedIndices); + Assert.Equal(expected, e.SelectedItems.Cast()); + ++raised; + }; + + target.SelectAll(); + + Assert.Equal(1, raised); + } + + [Fact] + public void SelectAll_With_Already_Selected_Items_Raises_SelectionChanged() + { + var target = new SelectionModel { SingleSelect = true }; + var raised = 0; + + target.Source = Enumerable.Range(0, 10).ToList(); + target.Select(4); + + target.SelectionChanged += (s, e) => + { + var expected = Enumerable.Range(0, 10).Except(new[] { 4 }); + Assert.Empty(e.DeselectedIndices); + Assert.Empty(e.DeselectedItems); + Assert.Equal(expected.Select(x => new IndexPath(x)), e.SelectedIndices); + Assert.Equal(expected, e.SelectedItems.Cast()); + ++raised; + }; + + target.SelectAll(); + + Assert.Equal(1, raised); + } + + [Fact] + public void SelectRangeFromAnchor_Raises_SelectionChanged() + { + var target = new SelectionModel(); + var raised = 0; + + target.Source = Enumerable.Range(0, 10).ToList(); + target.SelectionChanged += (s, e) => + { + var expected = Enumerable.Range(4, 3); + Assert.Empty(e.DeselectedIndices); + Assert.Empty(e.DeselectedItems); + Assert.Equal(expected.Select(x => new IndexPath(x)), e.SelectedIndices); + Assert.Equal(expected, e.SelectedItems.Cast()); + ++raised; + }; + + target.AnchorIndex = new IndexPath(4); + target.SelectRangeFromAnchor(6); + + Assert.Equal(1, raised); + } + + [Fact] + public void SelectRangeFromAnchor_With_Group_Raises_SelectionChanged() + { + var target = new SelectionModel(); + var raised = 0; + + target.Source = CreateNestedData(1, 2, 10); + target.SelectionChanged += (s, e) => + { + var expected = Enumerable.Range(11, 6); + Assert.Empty(e.DeselectedIndices); + Assert.Empty(e.DeselectedItems); + Assert.Equal(expected.Select(x => new IndexPath(x / 10, x % 10)), e.SelectedIndices); + Assert.Equal(expected, e.SelectedItems.Cast()); + ++raised; + }; + + target.AnchorIndex = new IndexPath(1, 1); + target.SelectRangeFromAnchor(1, 6); + + Assert.Equal(1, raised); + } + + [Fact] + public void SelectRangeFromAnchorTo_Raises_SelectionChanged() + { + var target = new SelectionModel(); + var raised = 0; + + target.Source = CreateNestedData(1, 2, 10); + target.SelectionChanged += (s, e) => + { + var expected = Enumerable.Range(11, 6); + Assert.Empty(e.DeselectedIndices); + Assert.Empty(e.DeselectedItems); + Assert.Equal(expected.Select(x => new IndexPath(x / 10, x % 10)), e.SelectedIndices); + Assert.Equal(expected, e.SelectedItems.Cast()); + ++raised; + }; + + target.AnchorIndex = new IndexPath(1, 1); + target.SelectRangeFromAnchorTo(new IndexPath(1, 6)); + + Assert.Equal(1, raised); + } + + [Fact] + public void ClearSelection_Raises_SelectionChanged() + { + var target = new SelectionModel(); + var raised = 0; + + target.Source = Enumerable.Range(0, 10).ToList(); + target.Select(4); + target.Select(5); + + target.SelectionChanged += (s, e) => + { + var expected = Enumerable.Range(4, 2); + Assert.Equal(expected.Select(x => new IndexPath(x)), e.DeselectedIndices); + Assert.Equal(expected, e.DeselectedItems.Cast()); + Assert.Empty(e.SelectedIndices); + Assert.Empty(e.SelectedItems); + ++raised; + }; + + target.ClearSelection(); + + Assert.Equal(1, raised); + } + + [Fact] + public void Changing_Source_Raises_SelectionChanged() + { + var target = new SelectionModel(); + var raised = 0; + + target.Source = Enumerable.Range(0, 10).ToList(); + target.Select(4); + target.Select(5); + + target.SelectionChanged += (s, e) => + { + var expected = Enumerable.Range(4, 2); + Assert.Equal(expected.Select(x => new IndexPath(x)), e.DeselectedIndices); + Assert.Equal(expected, e.DeselectedItems.Cast()); + Assert.Empty(e.SelectedIndices); + Assert.Empty(e.SelectedItems); + ++raised; + }; + + target.Source = Enumerable.Range(20, 10).ToList(); + + Assert.Equal(1, raised); + } + + [Fact] + public void Setting_SelectedIndex_Raises_SelectionChanged() + { + var target = new SelectionModel(); + var raised = 0; + + target.Source = Enumerable.Range(0, 10).ToList(); + target.Select(4); + target.Select(5); + + target.SelectionChanged += (s, e) => + { + Assert.Equal(new[] { new IndexPath(4), new IndexPath(5) }, e.DeselectedIndices); + Assert.Equal(new object[] { 4, 5 }, e.DeselectedItems); + Assert.Equal(new[] { new IndexPath(6) }, e.SelectedIndices); + Assert.Equal(new object[] { 6 }, e.SelectedItems); + ++raised; + }; + + target.SelectedIndex = new IndexPath(6); + + Assert.Equal(1, raised); + } + + [Fact] + public void Removing_Selected_Item_Raises_SelectionChanged() + { + var target = new SelectionModel(); + var data = new ObservableCollection(Enumerable.Range(0, 10)); + var raised = 0; + + target.Source = data; + target.Select(4); + target.Select(5); + + target.SelectionChanged += (s, e) => + { + Assert.Empty(e.DeselectedIndices); + Assert.Equal(new object[] { 4 }, e.DeselectedItems); + Assert.Empty(e.SelectedIndices); + Assert.Empty(e.SelectedItems); + ++raised; + }; + + data.Remove(4); + + Assert.Equal(1, raised); + } + + [Fact] + public void Removing_Selected_Child_Item_Raises_SelectionChanged() + { + var target = new SelectionModel(); + var data = CreateNestedData(1, 2, 3); + var raised = 0; + + target.Source = data; + target.SelectRange(new IndexPath(0), new IndexPath(1, 1)); + + target.SelectionChanged += (s, e) => + { + Assert.Empty(e.DeselectedIndices); + Assert.Equal(new object[] { 1}, e.DeselectedItems); + Assert.Empty(e.SelectedIndices); + Assert.Empty(e.SelectedItems); + ++raised; + }; + + ((AvaloniaList)data[0]).RemoveAt(1); + + Assert.Equal(1, raised); + } + + [Fact] + public void Removing_Selected_Item_With_Children_Raises_SelectionChanged() + { + var target = new SelectionModel(); + var data = CreateNestedData(1, 2, 3); + var raised = 0; + + target.Source = data; + target.SelectRange(new IndexPath(0), new IndexPath(1, 1)); + + target.SelectionChanged += (s, e) => + { + Assert.Empty(e.DeselectedIndices); + Assert.Equal(new object[] { 0, 1, 2 }, e.DeselectedItems); + Assert.Empty(e.SelectedIndices); + Assert.Empty(e.SelectedItems); + ++raised; + }; + + data.RemoveAt(0); + + Assert.Equal(1, raised); + } + + [Fact] + public void Removing_Unselected_Item_Before_Selected_Item_Raises_SelectionChanged() + { + var target = new SelectionModel(); + var data = new ObservableCollection(Enumerable.Range(0, 10)); + var raised = 0; + + target.Source = data; + target.Select(8); + + target.SelectionChanged += (s, e) => + { + Assert.Empty(e.DeselectedIndices); + Assert.Empty(e.DeselectedItems); + Assert.Empty(e.SelectedIndices); + Assert.Empty(e.SelectedItems); + ++raised; + }; + + data.Remove(6); + + Assert.Equal(1, raised); + } + + [Fact] + public void Removing_Unselected_Item_After_Selected_Item_Doesnt_Raise_SelectionChanged() + { + var target = new SelectionModel(); + var data = new ObservableCollection(Enumerable.Range(0, 10)); + var raised = 0; + + target.Source = data; + target.Select(4); + + target.SelectionChanged += (s, e) => ++raised; + + data.Remove(6); + + Assert.Equal(0, raised); + } + [Fact] public void Disposing_Unhooks_CollectionChanged_Handlers() { From e2132fedf93ef1bdf446f766853b130aaeb9d21c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 29 Jan 2020 21:48:14 +0100 Subject: [PATCH 033/663] Added some failing tests. That demonstrate some problems with the `SelectionModel` change notifications found so far. --- .../SelectionModelTests.cs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs index 1cca809c1d..de9fa4f11f 100644 --- a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs @@ -1392,6 +1392,38 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(3, target.SelectedItems.Count); } + [Fact] + public void Not_Enumerating_Changes_Does_Not_Prevent_Further_Operations() + { + var data = new[] { "foo", "bar", "baz" }; + var target = new SelectionModel { Source = data }; + + target.SelectionChanged += (s, e) => { }; + + target.SelectAll(); + target.ClearSelection(); + } + + [Fact] + public void Can_Change_Selection_From_SelectionChanged() + { + var data = new[] { "foo", "bar", "baz" }; + var target = new SelectionModel { Source = data }; + var raised = 0; + + target.SelectionChanged += (s, e) => + { + if (raised++ == 0) + { + target.ClearSelection(); + } + }; + + target.SelectAll(); + + Assert.Equal(2, raised); + } + private int GetSubscriberCount(AvaloniaList list) { return ((INotifyCollectionChangedDebug)list).GetCollectionChangedSubscribers()?.Length ?? 0; From 859aba1043855105e66ff9f89f6960513744e6a0 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 29 Jan 2020 21:48:43 +0100 Subject: [PATCH 034/663] Refactor of SelectionModel change notifications. To address issues found. --- src/Avalonia.Controls/SelectionModel.cs | 53 ++----- .../SelectionModelChangeSet.cs | 140 +++++------------- ...SelectionModelSelectionChangedEventArgs.cs | 80 ++++++---- src/Avalonia.Controls/SelectionNode.cs | 60 +++++--- .../SelectionNodeOperation.cs | 80 ++++++++++ 5 files changed, 215 insertions(+), 198 deletions(-) create mode 100644 src/Avalonia.Controls/SelectionNodeOperation.cs diff --git a/src/Avalonia.Controls/SelectionModel.cs b/src/Avalonia.Controls/SelectionModel.cs index 5e2fb32243..49f2c8d2f6 100644 --- a/src/Avalonia.Controls/SelectionModel.cs +++ b/src/Avalonia.Controls/SelectionModel.cs @@ -490,13 +490,9 @@ namespace Avalonia.Controls } public void OnSelectionInvalidatedDueToCollectionChange( - IEnumerable? removedItems) + IReadOnlyList? removedItems) { - var e = new SelectionModelSelectionChangedEventArgs( - Enumerable.Empty(), - Enumerable.Empty(), - removedItems ?? Enumerable.Empty(), - Enumerable.Empty()); + var e = new SelectionModelSelectionChangedEventArgs(null, null, removedItems, null); OnSelectionChanged(e); } @@ -706,50 +702,19 @@ namespace Avalonia.Controls }); } - private void BeginOperation() - { - if (SelectionChanged != null) - { - _rootNode.BeginOperation(); - } - } + private void BeginOperation() => _rootNode.BeginOperation(); private void EndOperation() { - static IEnumerable? Concat(IEnumerable? a, IEnumerable b) - { - return a == null ? b : a.Concat(b); - } + var changes = new List(); + _rootNode.EndOperation(changes); SelectionModelSelectionChangedEventArgs? e = null; - - if (SelectionChanged != null) + + if (changes.Count > 0) { - IEnumerable? selectedIndices = null; - IEnumerable? deselectedIndices = null; - IEnumerable? selectedItems = null; - IEnumerable? deselectedItems = null; - - foreach (var changes in _rootNode.EndOperation()) - { - if (changes.HasChanges) - { - selectedIndices = Concat(selectedIndices, changes.SelectedIndices); - deselectedIndices = Concat(deselectedIndices, changes.DeselectedIndices); - selectedItems = Concat(selectedItems, changes.SelectedItems); - deselectedItems = Concat(deselectedItems, changes.DeselectedItems); - } - } - - if (selectedIndices != null || deselectedIndices != null || - selectedItems != null || deselectedItems != null) - { - e = new SelectionModelSelectionChangedEventArgs( - deselectedIndices ?? Enumerable.Empty(), - selectedIndices ?? Enumerable.Empty(), - deselectedItems ?? Enumerable.Empty(), - selectedItems ?? Enumerable.Empty()); - } + var changeSet = new SelectionModelChangeSet(changes); + e = changeSet.CreateEventArgs(); } OnSelectionChanged(e); diff --git a/src/Avalonia.Controls/SelectionModelChangeSet.cs b/src/Avalonia.Controls/SelectionModelChangeSet.cs index 989136ac8d..b195117ae6 100644 --- a/src/Avalonia.Controls/SelectionModelChangeSet.cs +++ b/src/Avalonia.Controls/SelectionModelChangeSet.cs @@ -1,144 +1,80 @@ using System; using System.Collections.Generic; -using System.Linq; - -#nullable enable namespace Avalonia.Controls { internal class SelectionModelChangeSet { - private SelectionNode _owner; - private List? _selected; - private List? _deselected; - - public SelectionModelChangeSet(SelectionNode owner) => _owner = owner; - - public bool IsTracking { get; private set; } - public bool HasChanges => _selected?.Count > 0 || _deselected?.Count > 0; - public IEnumerable SelectedIndices => EnumerateIndices(_selected); - public IEnumerable DeselectedIndices => EnumerateIndices(_deselected); - public IEnumerable SelectedItems => EnumerateItems(_selected); - public IEnumerable DeselectedItems => EnumerateItems(_deselected); + private List _changes; - public void BeginOperation() + public SelectionModelChangeSet(List changes) { - if (IsTracking) - { - throw new AvaloniaInternalException("SelectionModel change operation already in progress."); - } - - IsTracking = true; - _selected?.Clear(); - _deselected?.Clear(); + _changes = changes; } - public void EndOperation() => IsTracking = false; - - public void Selected(IndexRange range) + public SelectionModelSelectionChangedEventArgs CreateEventArgs() { - if (!IsTracking) - { - return; - } - - Add(range, ref _selected, _deselected); + return new SelectionModelSelectionChangedEventArgs( + CreateIndices(x => x.DeselectedRanges), + CreateIndices(x => x.SelectedRanges), + CreateItems(x => x.DeselectedRanges), + CreateItems(x => x.SelectedRanges)); } - public void Selected(IEnumerable ranges) + private IReadOnlyList CreateIndices(Func?> selector) { - if (!IsTracking) + if (_changes == null) { - return; + return Array.Empty(); } - foreach (var range in ranges) - { - Selected(range); - } - } - - public void Deselected(IndexRange range) - { - if (!IsTracking) - { - return; - } - - Add(range, ref _deselected, _selected); - } + var result = new List(); - public void Deselected(IEnumerable ranges) - { - if (!IsTracking) + foreach (var i in _changes) { - return; - } + var ranges = selector(i); - foreach (var range in ranges) - { - Deselected(range); - } - } - - private static void Add( - IndexRange range, - ref List? add, - List? remove) - { - if (remove != null) - { - var removed = new List(); - IndexRange.Remove(remove, range, removed); - var selected = IndexRange.Subtract(range, removed); - - if (selected.Any()) + if (ranges != null) { - add ??= new List(); - - foreach (var r in selected) + foreach (var j in ranges) { - IndexRange.Add(add, r); + for (var k = j.Begin; k <= j.End; ++k) + { + result.Add(i.Path.CloneWithChildIndex(k)); + } } } } - else - { - add ??= new List(); - IndexRange.Add(add, range); - } + + return result; } - private IEnumerable EnumerateIndices(IEnumerable? ranges) + private IReadOnlyList CreateItems(Func?> selector) { - var path = _owner.IndexPath; - - if (ranges != null) + if (_changes == null) { - foreach (var range in ranges) - { - for (var i = range.Begin; i <= range.End; ++i) - { - yield return path.CloneWithChildIndex(i); - } - } + return Array.Empty(); } - } - private IEnumerable EnumerateItems(IEnumerable? ranges) - { - var items = _owner.ItemsSourceView; + var result = new List(); - if (ranges != null && items != null) + foreach (var i in _changes) { - foreach (var range in ranges) + var ranges = selector(i); + + if (ranges != null && i.Items != null) { - for (var i = range.Begin; i <= range.End; ++i) + foreach (var j in ranges) { - yield return items.GetAt(i); + for (var k = j.Begin; k <= j.End; ++k) + { + result.Add(i.Items.GetAt(k)); + } } } } + + return result; } } } diff --git a/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs b/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs index 4976bf1827..ae98f6a1ce 100644 --- a/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs +++ b/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs @@ -12,49 +12,73 @@ namespace Avalonia.Controls { public class SelectionModelSelectionChangedEventArgs : EventArgs { - private readonly IEnumerable _selectedIndicesSource; - private readonly IEnumerable _deselectedIndicesSource; - private readonly IEnumerable _selectedItemsSource; - private readonly IEnumerable _deselectedItemsSource; - private List? _selectedIndices; - private List? _deselectedIndices; - private List? _selectedItems; - private List? _deselectedItems; + private readonly IEnumerable? _deselectedIndicesSource; + private readonly IEnumerable? _selectedIndicesSource; + private readonly IEnumerable? _deselectedItemsSource; + private readonly IEnumerable? _selectedItemsSource; + private IReadOnlyList? _deselectedIndices; + private IReadOnlyList? _selectedIndices; + private IReadOnlyList? _deselectedItems; + private IReadOnlyList? _selectedItems; public SelectionModelSelectionChangedEventArgs( - IEnumerable deselectedIndices, - IEnumerable selectedIndices, - IEnumerable deselectedItems, - IEnumerable selectedItems) + IReadOnlyList? deselectedIndices, + IReadOnlyList? selectedIndices, + IReadOnlyList? deselectedItems, + IReadOnlyList? selectedItems) { - _selectedIndicesSource = selectedIndices; - _deselectedIndicesSource = deselectedIndices; - _selectedItemsSource = selectedItems; - _deselectedItemsSource = deselectedItems; + _deselectedIndices = deselectedIndices ?? Array.Empty(); + _selectedIndices = selectedIndices ?? Array.Empty(); + _deselectedItems = deselectedItems ?? Array.Empty(); + _selectedItems= selectedItems ?? Array.Empty(); } - /// - /// Gets the indices of the items that were added to the selection. - /// - public IReadOnlyList SelectedIndices => - _selectedIndices ?? (_selectedIndices = new List(_selectedIndicesSource)); + public SelectionModelSelectionChangedEventArgs( + IEnumerable? deselectedIndices, + IEnumerable? selectedIndices, + IEnumerable? deselectedItems, + IEnumerable? selectedItems) + { + static void Set(IEnumerable? source, ref IEnumerable? sourceField, ref IReadOnlyList? field) + { + if (source != null) + { + sourceField = source; + } + else + { + field = Array.Empty(); + } + } + + Set(deselectedIndices, ref _deselectedIndicesSource, ref _deselectedIndices); + Set(selectedIndices, ref _selectedIndicesSource, ref _selectedIndices); + Set(deselectedItems, ref _deselectedItemsSource, ref _deselectedItems); + Set(selectedItems, ref _selectedItemsSource, ref _selectedItems); + } /// /// Gets the indices of the items that were removed from the selection. /// - public IReadOnlyList DeselectedIndices => - _deselectedIndices ?? (_deselectedIndices = new List(_deselectedIndicesSource)); + public IReadOnlyList DeselectedIndices + => _deselectedIndices ??= new List(_deselectedIndicesSource); /// - /// Gets the items that were added to the selection. + /// Gets the indices of the items that were added to the selection. /// - public IReadOnlyList SelectedItems => - _selectedItems ?? (_selectedItems = new List(_selectedItemsSource)); + public IReadOnlyList SelectedIndices + => _selectedIndices ??= new List(_selectedIndicesSource); /// /// Gets the items that were removed from the selection. /// - public IReadOnlyList DeselectedItems => - _deselectedItems ?? (_deselectedItems = new List(_deselectedItemsSource)); + public IReadOnlyList DeselectedItems + => _deselectedItems ??= new List(_deselectedItemsSource); + + /// + /// Gets the items that were added to the selection. + /// + public IReadOnlyList SelectedItems + => _selectedItems ??= new List(_selectedItemsSource); } } diff --git a/src/Avalonia.Controls/SelectionNode.cs b/src/Avalonia.Controls/SelectionNode.cs index d462a51228..85db801100 100644 --- a/src/Avalonia.Controls/SelectionNode.cs +++ b/src/Avalonia.Controls/SelectionNode.cs @@ -29,7 +29,7 @@ namespace Avalonia.Controls private readonly SelectionNode? _parent; private readonly List _selected = new List(); private readonly List _selectedIndicesCached = new List(); - private SelectionModelChangeSet? _changes; + private SelectionNodeOperation? _operation; private object? _source; private bool _selectedIndicesCacheIsValid; @@ -137,7 +137,7 @@ namespace Avalonia.Controls child = new SelectionNode(_manager, parent: this); child.Source = resolvedChild; - if (_changes?.IsTracking == true) + if (_operation != null) { child.BeginOperation(); } @@ -296,33 +296,45 @@ namespace Avalonia.Controls public void BeginOperation() { - _changes ??= new SelectionModelChangeSet(this); - _changes.BeginOperation(); + if (_operation != null) + { + throw new AvaloniaInternalException("Selection operation already in progress."); + } + + _operation = new SelectionNodeOperation(this); for (var i = 0; i < _childrenNodes.Count; ++i) { - _childrenNodes[i]?.BeginOperation(); + var child = _childrenNodes[i]; + + if (child != null && child != _manager.SharedLeafNode) + { + child.BeginOperation(); + } } } - public IEnumerable EndOperation() + public void EndOperation(List changes) { - if (_changes != null) + if (_operation == null) { - _changes.EndOperation(); - yield return _changes; + throw new AvaloniaInternalException("No selection operation in progress."); + } - for (var i = 0; i < _childrenNodes.Count; ++i) - { - var child = _childrenNodes[i]; + if (_operation.HasChanges) + { + changes.Add(_operation); + } - if (child != null) - { - foreach (var changes in child.EndOperation()) - { - yield return changes; - } - } + _operation = null; + + for (var i = 0; i < _childrenNodes.Count; ++i) + { + var child = _childrenNodes[i]; + + if (child != null && child != _manager.SharedLeafNode) + { + child.EndOperation(changes); } } } @@ -400,7 +412,7 @@ namespace Avalonia.Controls if (selected.Count > 0) { - _changes?.Selected(selected); + _operation?.Selected(selected); if (raiseOnSelectionChanged) { @@ -417,7 +429,7 @@ namespace Avalonia.Controls if (removed.Count > 0) { - _changes?.Deselected(removed); + _operation?.Deselected(removed); if (raiseOnSelectionChanged) { @@ -431,7 +443,7 @@ namespace Avalonia.Controls // Deselect all items if (_selected.Count > 0) { - _changes?.Deselected(_selected); + _operation?.Deselected(_selected); _selected.Clear(); OnSelectionChanged(); } @@ -480,7 +492,7 @@ namespace Avalonia.Controls private void OnSourceListChanged(object dataSource, NotifyCollectionChangedEventArgs args) { bool selectionInvalidated = false; - IList? removed = null; + List? removed = null; switch (args.Action) { @@ -594,7 +606,7 @@ namespace Avalonia.Controls return selectionInvalidated; } - private (bool, IList) OnItemsRemoved(int index, IList items) + private (bool, List) OnItemsRemoved(int index, IList items) { var selectionInvalidated = false; var removed = new List(); diff --git a/src/Avalonia.Controls/SelectionNodeOperation.cs b/src/Avalonia.Controls/SelectionNodeOperation.cs new file mode 100644 index 0000000000..04b8554f7c --- /dev/null +++ b/src/Avalonia.Controls/SelectionNodeOperation.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +#nullable enable + +namespace Avalonia.Controls +{ + internal class SelectionNodeOperation + { + private readonly SelectionNode _owner; + private List? _selected; + private List? _deselected; + + public SelectionNodeOperation(SelectionNode owner) + { + _owner = owner; + } + + public bool HasChanges => _selected?.Count > 0 || _deselected?.Count > 0; + public List? SelectedRanges => _selected; + public List? DeselectedRanges => _deselected; + public IndexPath Path => _owner.IndexPath; + public ItemsSourceView? Items => _owner.ItemsSourceView; + + public void Selected(IndexRange range) + { + Add(range, ref _selected, _deselected); + } + + public void Selected(IEnumerable ranges) + { + foreach (var range in ranges) + { + Selected(range); + } + } + + public void Deselected(IndexRange range) + { + Add(range, ref _deselected, _selected); + } + + public void Deselected(IEnumerable ranges) + { + foreach (var range in ranges) + { + Deselected(range); + } + } + + private static void Add( + IndexRange range, + ref List? add, + List? remove) + { + if (remove != null) + { + var removed = new List(); + IndexRange.Remove(remove, range, removed); + var selected = IndexRange.Subtract(range, removed); + + if (selected.Any()) + { + add ??= new List(); + + foreach (var r in selected) + { + IndexRange.Add(add, r); + } + } + } + else + { + add ??= new List(); + IndexRange.Add(add, range); + } + } + } +} From b120e5282e2c70c4f9100704cd9c883c5388437f Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 31 Jan 2020 10:17:56 +0100 Subject: [PATCH 035/663] Lazily create the selected/deselected lists. --- .../SelectionModelChangeSet.cs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/Avalonia.Controls/SelectionModelChangeSet.cs b/src/Avalonia.Controls/SelectionModelChangeSet.cs index b195117ae6..e927afb9b9 100644 --- a/src/Avalonia.Controls/SelectionModelChangeSet.cs +++ b/src/Avalonia.Controls/SelectionModelChangeSet.cs @@ -21,15 +21,13 @@ namespace Avalonia.Controls CreateItems(x => x.SelectedRanges)); } - private IReadOnlyList CreateIndices(Func?> selector) + private IEnumerable CreateIndices(Func?> selector) { if (_changes == null) { - return Array.Empty(); + yield break; } - var result = new List(); - foreach (var i in _changes) { var ranges = selector(i); @@ -40,20 +38,18 @@ namespace Avalonia.Controls { for (var k = j.Begin; k <= j.End; ++k) { - result.Add(i.Path.CloneWithChildIndex(k)); + yield return i.Path.CloneWithChildIndex(k); } } } } - - return result; } - private IReadOnlyList CreateItems(Func?> selector) + private IEnumerable CreateItems(Func?> selector) { if (_changes == null) { - return Array.Empty(); + yield break; } var result = new List(); @@ -68,13 +64,11 @@ namespace Avalonia.Controls { for (var k = j.Begin; k <= j.End; ++k) { - result.Add(i.Items.GetAt(k)); + yield return i.Items.GetAt(k); } } } } - - return result; } } } From 9073234f725cdbef28e713353fd52e430030bb92 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 1 Feb 2020 00:48:57 +0100 Subject: [PATCH 036/663] Use SelectedItems for change event args. --- src/Avalonia.Controls/IndexRange.cs | 12 ++ src/Avalonia.Controls/SelectedItems.cs | 37 ++--- src/Avalonia.Controls/SelectionModel.cs | 14 +- .../SelectionModelChangeSet.cs | 149 ++++++++++++++---- ...SelectionModelSelectionChangedEventArgs.cs | 53 +------ .../SelectionNodeOperation.cs | 32 +++- 6 files changed, 193 insertions(+), 104 deletions(-) diff --git a/src/Avalonia.Controls/IndexRange.cs b/src/Avalonia.Controls/IndexRange.cs index 124f1e0500..1dc161c699 100644 --- a/src/Avalonia.Controls/IndexRange.cs +++ b/src/Avalonia.Controls/IndexRange.cs @@ -202,6 +202,18 @@ namespace Avalonia.Controls } } + public static int GetCount(IEnumerable ranges) + { + var result = 0; + + foreach (var range in ranges) + { + result += (range.End - range.Begin) + 1; + } + + return result; + } + private static void MergeRanges(IList ranges) { for (var i = ranges.Count - 2; i >= 0; --i) diff --git a/src/Avalonia.Controls/SelectedItems.cs b/src/Avalonia.Controls/SelectedItems.cs index af43742670..a3acb48765 100644 --- a/src/Avalonia.Controls/SelectedItems.cs +++ b/src/Avalonia.Controls/SelectedItems.cs @@ -6,44 +6,37 @@ using System; using System.Collections; using System.Collections.Generic; -using SelectedItemInfo = Avalonia.Controls.SelectionModel.SelectedItemInfo; #nullable enable namespace Avalonia.Controls { - internal class SelectedItems : IReadOnlyList + public interface ISelectedItemInfo { - private readonly List _infos; - private readonly Func, int, T> _getAtImpl; + public IndexPath Path { get; } + } + + internal class SelectedItems : IReadOnlyList + where Tinfo : ISelectedItemInfo + { + private readonly List _infos; + private readonly Func, int, TValue> _getAtImpl; public SelectedItems( - List infos, - Func, int, T> getAtImpl) + List infos, + int count, + Func, int, TValue> getAtImpl) { _infos = infos; _getAtImpl = getAtImpl; - - foreach (var info in infos) - { - var node = info.Node; - - if (node != null) - { - Count += node.SelectedCount; - } - else - { - throw new InvalidOperationException("Selection changed after the SelectedIndices/Items property was read."); - } - } + Count = count; } - public T this[int index] => _getAtImpl(_infos, index); + public TValue this[int index] => _getAtImpl(_infos, index); public int Count { get; } - public IEnumerator GetEnumerator() + public IEnumerator GetEnumerator() { for (var i = 0; i < Count; ++i) { diff --git a/src/Avalonia.Controls/SelectionModel.cs b/src/Avalonia.Controls/SelectionModel.cs index 49f2c8d2f6..c8d2c5cc9e 100644 --- a/src/Avalonia.Controls/SelectionModel.cs +++ b/src/Avalonia.Controls/SelectionModel.cs @@ -165,6 +165,7 @@ namespace Avalonia.Controls if (_selectedItemsCached == null) { var selectedInfos = new List(); + var count = 0; if (_rootNode.Source != null) { @@ -176,6 +177,7 @@ namespace Avalonia.Controls if (currentInfo.Node.SelectedCount > 0) { selectedInfos.Add(new SelectedItemInfo(currentInfo.Node, currentInfo.Path)); + count += currentInfo.Node.SelectedCount; } }); } @@ -185,8 +187,9 @@ namespace Avalonia.Controls // 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 ( + var selectedItems = new SelectedItems ( selectedInfos, + count, (infos, index) => { var currentIndex = 0; @@ -233,6 +236,8 @@ namespace Avalonia.Controls if (_selectedIndicesCached == null) { var selectedInfos = new List(); + var count = 0; + SelectionTreeHelper.Traverse( _rootNode, false, @@ -241,6 +246,7 @@ namespace Avalonia.Controls if (currentInfo.Node.SelectedCount > 0) { selectedInfos.Add(new SelectedItemInfo(currentInfo.Node, currentInfo.Path)); + count += currentInfo.Node.SelectedCount; } }); @@ -249,8 +255,9 @@ namespace Avalonia.Controls // 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( + var indices = new SelectedItems( selectedInfos, + count, (infos, index) => // callback for GetAt(index) { var currentIndex = 0; @@ -720,7 +727,7 @@ namespace Avalonia.Controls OnSelectionChanged(e); } - internal class SelectedItemInfo + internal class SelectedItemInfo : ISelectedItemInfo { public SelectedItemInfo(SelectionNode node, IndexPath path) { @@ -730,6 +737,7 @@ namespace Avalonia.Controls public SelectionNode Node { get; } public IndexPath Path { get; } + public int Count => Node.SelectedCount; } private struct Operation : IDisposable diff --git a/src/Avalonia.Controls/SelectionModelChangeSet.cs b/src/Avalonia.Controls/SelectionModelChangeSet.cs index e927afb9b9..c0228a1cbc 100644 --- a/src/Avalonia.Controls/SelectionModelChangeSet.cs +++ b/src/Avalonia.Controls/SelectionModelChangeSet.cs @@ -14,61 +14,144 @@ namespace Avalonia.Controls public SelectionModelSelectionChangedEventArgs CreateEventArgs() { + var deselectedCount = 0; + var selectedCount = 0; + + foreach (var change in _changes) + { + deselectedCount += change.DeselectedCount; + selectedCount += change.SelectedCount; + } + + var deselectedIndices = new SelectedItems( + _changes, + deselectedCount, + GetDeselectedIndexAt); + var selectedIndices = new SelectedItems( + _changes, + selectedCount, + GetSelectedIndexAt); + var deselectedItems = new SelectedItems( + _changes, + deselectedCount, + GetDeselectedItemAt); + var selectedItems = new SelectedItems( + _changes, + selectedCount, + GetSelectedItemAt); + return new SelectionModelSelectionChangedEventArgs( - CreateIndices(x => x.DeselectedRanges), - CreateIndices(x => x.SelectedRanges), - CreateItems(x => x.DeselectedRanges), - CreateItems(x => x.SelectedRanges)); + deselectedIndices, + selectedIndices, + deselectedItems, + selectedItems); } - private IEnumerable CreateIndices(Func?> selector) + private IndexPath GetDeselectedIndexAt( + List infos, + int index) { - if (_changes == null) - { - yield break; - } + static int GetCount(SelectionNodeOperation info) => info.DeselectedCount; + static List GetRanges(SelectionNodeOperation info) => info.DeselectedRanges; + return GetIndexAt(infos, index, GetCount, GetRanges); + } - foreach (var i in _changes) + private IndexPath GetSelectedIndexAt( + List infos, + int index) + { + static int GetCount(SelectionNodeOperation info) => info.SelectedCount; + static List GetRanges(SelectionNodeOperation info) => info.SelectedRanges; + return GetIndexAt(infos, index, GetCount, GetRanges); + } + + private object GetDeselectedItemAt( + List infos, + int index) + { + static int GetCount(SelectionNodeOperation info) => info.DeselectedCount; + static List GetRanges(SelectionNodeOperation info) => info.DeselectedRanges; + return GetItemAt(infos, index, GetCount, GetRanges); + } + + private object GetSelectedItemAt( + List infos, + int index) + { + static int GetCount(SelectionNodeOperation info) => info.SelectedCount; + static List GetRanges(SelectionNodeOperation info) => info.SelectedRanges; + return GetItemAt(infos, index, GetCount, GetRanges); + } + + private IndexPath GetIndexAt( + List infos, + int index, + Func getCount, + Func> getRanges) + { + var currentIndex = 0; + IndexPath path = default; + + foreach (var info in infos) { - var ranges = selector(i); + var currentCount = getCount(info); - if (ranges != null) + if (index >= currentIndex && index < currentIndex + currentCount) { - foreach (var j in ranges) - { - for (var k = j.Begin; k <= j.End; ++k) - { - yield return i.Path.CloneWithChildIndex(k); - } - } + int targetIndex = GetIndexAt(getRanges(info), index - currentIndex); + path = info.Path.CloneWithChildIndex(targetIndex); + break; } + + currentIndex += currentCount; } + + return path; } - private IEnumerable CreateItems(Func?> selector) + private object GetItemAt( + List infos, + int index, + Func getCount, + Func> getRanges) { - if (_changes == null) + var currentIndex = 0; + object item = null; + + foreach (var info in infos) { - yield break; + var currentCount = getCount(info); + + if (index >= currentIndex && index < currentIndex + currentCount) + { + int targetIndex = GetIndexAt(getRanges(info), index - currentIndex); + item = info.Items.GetAt(targetIndex); + break; + } + + currentIndex += currentCount; } - var result = new List(); + return item; + } - foreach (var i in _changes) + private int GetIndexAt(List ranges, int index) + { + var currentIndex = 0; + + foreach (var range in ranges) { - var ranges = selector(i); + var currentCount = (range.End - range.Begin) + 1; - if (ranges != null && i.Items != null) + if (index >= currentIndex && index < currentIndex + currentCount) { - foreach (var j in ranges) - { - for (var k = j.Begin; k <= j.End; ++k) - { - yield return i.Items.GetAt(k); - } - } + return range.Begin + (index - currentIndex); } + + currentIndex += currentCount; } + + throw new IndexOutOfRangeException(); } } } diff --git a/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs b/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs index ae98f6a1ce..4e64ee6e6f 100644 --- a/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs +++ b/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs @@ -12,73 +12,36 @@ namespace Avalonia.Controls { public class SelectionModelSelectionChangedEventArgs : EventArgs { - private readonly IEnumerable? _deselectedIndicesSource; - private readonly IEnumerable? _selectedIndicesSource; - private readonly IEnumerable? _deselectedItemsSource; - private readonly IEnumerable? _selectedItemsSource; - private IReadOnlyList? _deselectedIndices; - private IReadOnlyList? _selectedIndices; - private IReadOnlyList? _deselectedItems; - private IReadOnlyList? _selectedItems; - public SelectionModelSelectionChangedEventArgs( IReadOnlyList? deselectedIndices, IReadOnlyList? selectedIndices, IReadOnlyList? deselectedItems, IReadOnlyList? selectedItems) { - _deselectedIndices = deselectedIndices ?? Array.Empty(); - _selectedIndices = selectedIndices ?? Array.Empty(); - _deselectedItems = deselectedItems ?? Array.Empty(); - _selectedItems= selectedItems ?? Array.Empty(); - } - - public SelectionModelSelectionChangedEventArgs( - IEnumerable? deselectedIndices, - IEnumerable? selectedIndices, - IEnumerable? deselectedItems, - IEnumerable? selectedItems) - { - static void Set(IEnumerable? source, ref IEnumerable? sourceField, ref IReadOnlyList? field) - { - if (source != null) - { - sourceField = source; - } - else - { - field = Array.Empty(); - } - } - - Set(deselectedIndices, ref _deselectedIndicesSource, ref _deselectedIndices); - Set(selectedIndices, ref _selectedIndicesSource, ref _selectedIndices); - Set(deselectedItems, ref _deselectedItemsSource, ref _deselectedItems); - Set(selectedItems, ref _selectedItemsSource, ref _selectedItems); + DeselectedIndices = deselectedIndices ?? Array.Empty(); + SelectedIndices = selectedIndices ?? Array.Empty(); + DeselectedItems = deselectedItems ?? Array.Empty(); + SelectedItems= selectedItems ?? Array.Empty(); } /// /// Gets the indices of the items that were removed from the selection. /// - public IReadOnlyList DeselectedIndices - => _deselectedIndices ??= new List(_deselectedIndicesSource); + public IReadOnlyList DeselectedIndices { get; } /// /// Gets the indices of the items that were added to the selection. /// - public IReadOnlyList SelectedIndices - => _selectedIndices ??= new List(_selectedIndicesSource); + public IReadOnlyList SelectedIndices { get; } /// /// Gets the items that were removed from the selection. /// - public IReadOnlyList DeselectedItems - => _deselectedItems ??= new List(_deselectedItemsSource); + public IReadOnlyList DeselectedItems { get; } /// /// Gets the items that were added to the selection. /// - public IReadOnlyList SelectedItems - => _selectedItems ??= new List(_selectedItemsSource); + public IReadOnlyList SelectedItems { get; } } } diff --git a/src/Avalonia.Controls/SelectionNodeOperation.cs b/src/Avalonia.Controls/SelectionNodeOperation.cs index 04b8554f7c..9622a52f00 100644 --- a/src/Avalonia.Controls/SelectionNodeOperation.cs +++ b/src/Avalonia.Controls/SelectionNodeOperation.cs @@ -6,11 +6,13 @@ using System.Linq; namespace Avalonia.Controls { - internal class SelectionNodeOperation + internal class SelectionNodeOperation : ISelectedItemInfo { private readonly SelectionNode _owner; private List? _selected; private List? _deselected; + private int _selectedCount = -1; + private int _deselectedCount = -1; public SelectionNodeOperation(SelectionNode owner) { @@ -23,9 +25,36 @@ namespace Avalonia.Controls 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 ranges) @@ -39,6 +68,7 @@ namespace Avalonia.Controls public void Deselected(IndexRange range) { Add(range, ref _deselected, _selected); + _deselectedCount = -1; } public void Deselected(IEnumerable ranges) From 01a194520122b0f8bf688f21925f31f953ff3b8a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 3 Feb 2020 14:11:39 +0100 Subject: [PATCH 037/663] Add nullable annotations. --- .../SelectionModelChangeSet.cs | 47 ++++++++++--------- ...SelectionModelSelectionChangedEventArgs.cs | 12 ++--- 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/src/Avalonia.Controls/SelectionModelChangeSet.cs b/src/Avalonia.Controls/SelectionModelChangeSet.cs index c0228a1cbc..57bf369585 100644 --- a/src/Avalonia.Controls/SelectionModelChangeSet.cs +++ b/src/Avalonia.Controls/SelectionModelChangeSet.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +#nullable enable + namespace Avalonia.Controls { internal class SelectionModelChangeSet @@ -31,11 +33,11 @@ namespace Avalonia.Controls _changes, selectedCount, GetSelectedIndexAt); - var deselectedItems = new SelectedItems( + var deselectedItems = new SelectedItems( _changes, deselectedCount, GetDeselectedItemAt); - var selectedItems = new SelectedItems( + var selectedItems = new SelectedItems( _changes, selectedCount, GetSelectedItemAt); @@ -52,7 +54,7 @@ namespace Avalonia.Controls int index) { static int GetCount(SelectionNodeOperation info) => info.DeselectedCount; - static List GetRanges(SelectionNodeOperation info) => info.DeselectedRanges; + static List? GetRanges(SelectionNodeOperation info) => info.DeselectedRanges; return GetIndexAt(infos, index, GetCount, GetRanges); } @@ -61,25 +63,25 @@ namespace Avalonia.Controls int index) { static int GetCount(SelectionNodeOperation info) => info.SelectedCount; - static List GetRanges(SelectionNodeOperation info) => info.SelectedRanges; + static List? GetRanges(SelectionNodeOperation info) => info.SelectedRanges; return GetIndexAt(infos, index, GetCount, GetRanges); } - private object GetDeselectedItemAt( + private object? GetDeselectedItemAt( List infos, int index) { static int GetCount(SelectionNodeOperation info) => info.DeselectedCount; - static List GetRanges(SelectionNodeOperation info) => info.DeselectedRanges; + static List? GetRanges(SelectionNodeOperation info) => info.DeselectedRanges; return GetItemAt(infos, index, GetCount, GetRanges); } - private object GetSelectedItemAt( + private object? GetSelectedItemAt( List infos, int index) { static int GetCount(SelectionNodeOperation info) => info.SelectedCount; - static List GetRanges(SelectionNodeOperation info) => info.SelectedRanges; + static List? GetRanges(SelectionNodeOperation info) => info.SelectedRanges; return GetItemAt(infos, index, GetCount, GetRanges); } @@ -87,7 +89,7 @@ namespace Avalonia.Controls List infos, int index, Func getCount, - Func> getRanges) + Func?> getRanges) { var currentIndex = 0; IndexPath path = default; @@ -109,14 +111,14 @@ namespace Avalonia.Controls return path; } - private object GetItemAt( + private object? GetItemAt( List infos, int index, Func getCount, - Func> getRanges) + Func?> getRanges) { var currentIndex = 0; - object item = null; + object? item = null; foreach (var info in infos) { @@ -125,7 +127,7 @@ namespace Avalonia.Controls if (index >= currentIndex && index < currentIndex + currentCount) { int targetIndex = GetIndexAt(getRanges(info), index - currentIndex); - item = info.Items.GetAt(targetIndex); + item = info.Items?.GetAt(targetIndex); break; } @@ -135,20 +137,23 @@ namespace Avalonia.Controls return item; } - private int GetIndexAt(List ranges, int index) + private int GetIndexAt(List? ranges, int index) { var currentIndex = 0; - foreach (var range in ranges) + if (ranges != null) { - var currentCount = (range.End - range.Begin) + 1; - - if (index >= currentIndex && index < currentIndex + currentCount) + foreach (var range in ranges) { - return range.Begin + (index - currentIndex); - } + var currentCount = (range.End - range.Begin) + 1; - currentIndex += currentCount; + if (index >= currentIndex && index < currentIndex + currentCount) + { + return range.Begin + (index - currentIndex); + } + + currentIndex += currentCount; + } } throw new IndexOutOfRangeException(); diff --git a/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs b/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs index 4e64ee6e6f..5e2efdf331 100644 --- a/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs +++ b/src/Avalonia.Controls/SelectionModelSelectionChangedEventArgs.cs @@ -15,13 +15,13 @@ namespace Avalonia.Controls public SelectionModelSelectionChangedEventArgs( IReadOnlyList? deselectedIndices, IReadOnlyList? selectedIndices, - IReadOnlyList? deselectedItems, - IReadOnlyList? selectedItems) + IReadOnlyList? deselectedItems, + IReadOnlyList? selectedItems) { DeselectedIndices = deselectedIndices ?? Array.Empty(); SelectedIndices = selectedIndices ?? Array.Empty(); - DeselectedItems = deselectedItems ?? Array.Empty(); - SelectedItems= selectedItems ?? Array.Empty(); + DeselectedItems = deselectedItems ?? Array.Empty(); + SelectedItems= selectedItems ?? Array.Empty(); } /// @@ -37,11 +37,11 @@ namespace Avalonia.Controls /// /// Gets the items that were removed from the selection. /// - public IReadOnlyList DeselectedItems { get; } + public IReadOnlyList DeselectedItems { get; } /// /// Gets the items that were added to the selection. /// - public IReadOnlyList SelectedItems { get; } + public IReadOnlyList SelectedItems { get; } } } From 04f8516c32e4e2c70b8a0f4af5f0543d58bb56a6 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 3 Feb 2020 14:36:24 +0100 Subject: [PATCH 038/663] Handle null SelectionModel.Source. --- src/Avalonia.Controls/SelectionModel.cs | 20 ++++++++- .../SelectionModelChangeSet.cs | 28 ++++++++----- src/Avalonia.Controls/SelectionNode.cs | 7 +++- .../SelectionModelTests.cs | 42 +++++++++++++++++++ 4 files changed, 83 insertions(+), 14 deletions(-) diff --git a/src/Avalonia.Controls/SelectionModel.cs b/src/Avalonia.Controls/SelectionModel.cs index c8d2c5cc9e..796d108f60 100644 --- a/src/Avalonia.Controls/SelectionModel.cs +++ b/src/Avalonia.Controls/SelectionModel.cs @@ -36,13 +36,29 @@ namespace Avalonia.Controls get => _rootNode?.Source; set { - using (var operation = new Operation(this)) + var wasNull = _rootNode.Source == null; + + if (_rootNode.Source != null) { - ClearSelection(resetAnchor: true); + using (var operation = new Operation(this)) + { + ClearSelection(resetAnchor: true); + } } _rootNode.Source = value; + RaisePropertyChanged("Source"); + + if (wasNull) + { + var e = new SelectionModelSelectionChangedEventArgs( + null, + SelectedIndices, + null, + SelectedItems); + OnSelectionChanged(e); + } } } diff --git a/src/Avalonia.Controls/SelectionModelChangeSet.cs b/src/Avalonia.Controls/SelectionModelChangeSet.cs index 57bf369585..bff84eca92 100644 --- a/src/Avalonia.Controls/SelectionModelChangeSet.cs +++ b/src/Avalonia.Controls/SelectionModelChangeSet.cs @@ -16,30 +16,38 @@ namespace Avalonia.Controls public SelectionModelSelectionChangedEventArgs CreateEventArgs() { - var deselectedCount = 0; - var selectedCount = 0; + var deselectedIndexCount = 0; + var selectedIndexCount = 0; + var deselectedItemCount = 0; + var selectedItemCount = 0; foreach (var change in _changes) { - deselectedCount += change.DeselectedCount; - selectedCount += change.SelectedCount; + deselectedIndexCount += change.DeselectedCount; + selectedIndexCount += change.SelectedCount; + + if (change.Items != null) + { + deselectedItemCount += change.DeselectedCount; + selectedItemCount += change.SelectedCount; + } } var deselectedIndices = new SelectedItems( _changes, - deselectedCount, + deselectedIndexCount, GetDeselectedIndexAt); var selectedIndices = new SelectedItems( _changes, - selectedCount, + selectedIndexCount, GetSelectedIndexAt); var deselectedItems = new SelectedItems( _changes, - deselectedCount, + deselectedItemCount, GetDeselectedItemAt); var selectedItems = new SelectedItems( _changes, - selectedCount, + selectedItemCount, GetSelectedItemAt); return new SelectionModelSelectionChangedEventArgs( @@ -71,7 +79,7 @@ namespace Avalonia.Controls List infos, int index) { - static int GetCount(SelectionNodeOperation info) => info.DeselectedCount; + static int GetCount(SelectionNodeOperation info) => info.Items != null ? info.DeselectedCount : 0; static List? GetRanges(SelectionNodeOperation info) => info.DeselectedRanges; return GetItemAt(infos, index, GetCount, GetRanges); } @@ -80,7 +88,7 @@ namespace Avalonia.Controls List infos, int index) { - static int GetCount(SelectionNodeOperation info) => info.SelectedCount; + static int GetCount(SelectionNodeOperation info) => info.Items != null ? info.SelectedCount : 0; static List? GetRanges(SelectionNodeOperation info) => info.SelectedRanges; return GetItemAt(infos, index, GetCount, GetRanges); } diff --git a/src/Avalonia.Controls/SelectionNode.cs b/src/Avalonia.Controls/SelectionNode.cs index 85db801100..04144e1ed0 100644 --- a/src/Avalonia.Controls/SelectionNode.cs +++ b/src/Avalonia.Controls/SelectionNode.cs @@ -48,8 +48,11 @@ namespace Avalonia.Controls { if (_source != value) { - ClearSelection(); - UnhookCollectionChangedHandler(); + if (_source != null) + { + ClearSelection(); + UnhookCollectionChangedHandler(); + } _source = value; diff --git a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs index de9fa4f11f..1950da2818 100644 --- a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs @@ -1424,6 +1424,48 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(2, raised); } + [Fact] + public void Raises_SelectionChanged_With_No_Source() + { + var target = new SelectionModel(); + var raised = 0; + + target.SelectionChanged += (s, e) => + { + Assert.Empty(e.DeselectedIndices); + Assert.Empty(e.DeselectedItems); + Assert.Equal(new[] { new IndexPath(1) }, e.SelectedIndices); + Assert.Empty(e.SelectedItems); + ++raised; + }; + + target.Select(1); + + Assert.Equal(new[] { new IndexPath(1) }, target.SelectedIndices); + Assert.Empty(target.SelectedItems); + } + + [Fact] + public void Raises_SelectionChanged_With_Items_After_Source_Is_Set() + { + var target = new SelectionModel(); + var raised = 0; + + target.Select(1); + target.SelectionChanged += (s, e) => + { + Assert.Empty(e.DeselectedIndices); + Assert.Empty(e.DeselectedItems); + Assert.Equal(new[] { new IndexPath(1) }, e.SelectedIndices); + Assert.Equal(new[] { "bar" }, e.SelectedItems); + ++raised; + }; + + target.Source = new[] { "foo", "bar", "baz" }; + + Assert.Equal(1, raised); + } + private int GetSubscriberCount(AvaloniaList list) { return ((INotifyCollectionChangedDebug)list).GetCollectionChangedSubscribers()?.Length ?? 0; From 4de9fac5c107f75f6a8dc51ff0325a0fa18b6ce1 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 4 Feb 2020 10:23:36 +0100 Subject: [PATCH 039/663] Don't reset selection if source hasn't changed. Also remove some `?.` operators that aren't needed. --- src/Avalonia.Controls/SelectionModel.cs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Controls/SelectionModel.cs b/src/Avalonia.Controls/SelectionModel.cs index 34d5f78434..a501946365 100644 --- a/src/Avalonia.Controls/SelectionModel.cs +++ b/src/Avalonia.Controls/SelectionModel.cs @@ -33,13 +33,16 @@ namespace Avalonia.Controls public object? Source { - get => _rootNode?.Source; + get => _rootNode.Source; set { - ClearSelection(resetAnchor: true, raiseSelectionChanged: false); - _rootNode.Source = value; - OnSelectionChanged(); - RaisePropertyChanged("Source"); + if (_rootNode.Source != value) + { + ClearSelection(resetAnchor: true, raiseSelectionChanged: false); + _rootNode.Source = value; + OnSelectionChanged(); + RaisePropertyChanged("Source"); + } } } @@ -76,7 +79,7 @@ namespace Avalonia.Controls { IndexPath anchor = default; - if (_rootNode?.AnchorIndex >= 0) + if (_rootNode.AnchorIndex >= 0) { var path = new List(); SelectionNode? current = _rootNode; @@ -290,7 +293,7 @@ namespace Avalonia.Controls public void Dispose() { ClearSelection(resetAnchor: false, raiseSelectionChanged: false); - _rootNode?.Dispose(); + _rootNode.Dispose(); _selectedIndicesCached = null; _selectedItemsCached = null; } From c0f34694a62e5d957156d8cd3d2f8d1c21152dbd Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 4 Feb 2020 22:55:49 +0100 Subject: [PATCH 040/663] Fixed out of bounds in SelectionNode. --- src/Avalonia.Controls/SelectionNode.cs | 11 ++++- .../SelectionModelTests.cs | 41 ++++++++++++++++++- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/SelectionNode.cs b/src/Avalonia.Controls/SelectionNode.cs index ea1a1ca664..e108d0450c 100644 --- a/src/Avalonia.Controls/SelectionNode.cs +++ b/src/Avalonia.Controls/SelectionNode.cs @@ -692,8 +692,17 @@ namespace Avalonia.Controls if (isSelected) { - RemoveRange(new IndexRange(index, index + count - 1), raiseOnSelectionChanged: false); + 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++) diff --git a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs index 9ce0816f7d..81b6127eb6 100644 --- a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs @@ -1481,7 +1481,7 @@ namespace Avalonia.Controls.UnitTests } [Fact] - public void RetainSelectionOnReset_Retains_Correct_Selection_After_Remove() + public void RetainSelectionOnReset_Retains_Correct_Selection_After_Deselect() { var data = new ResettingList { "foo", "bar", "baz" }; var target = new SelectionModel { Source = data, RetainSelectionOnReset = true }; @@ -1491,7 +1491,35 @@ namespace Avalonia.Controls.UnitTests data.Reset(); Assert.Equal(new[] { new IndexPath(1) }, target.SelectedIndices); - Assert.Equal(new[] { "bar", }, target.SelectedItems); + Assert.Equal(new[] { "bar" }, target.SelectedItems); + } + + [Fact] + public void RetainSelectionOnReset_Retains_Correct_Selection_After_Remove_1() + { + var data = new ResettingList { "foo", "bar", "baz" }; + var target = new SelectionModel { Source = data, RetainSelectionOnReset = true }; + + target.SelectRange(new IndexPath(1), new IndexPath(2)); + data.RemoveAt(2); + data.Reset(new[] { "foo", "bar", "baz" }); + + Assert.Equal(new[] { new IndexPath(1) }, target.SelectedIndices); + Assert.Equal(new[] { "bar" }, target.SelectedItems); + } + + [Fact] + public void RetainSelectionOnReset_Retains_Correct_Selection_After_Remove_2() + { + var data = new ResettingList { "foo", "bar", "baz" }; + var target = new SelectionModel { Source = data, RetainSelectionOnReset = true }; + + target.SelectRange(new IndexPath(1), new IndexPath(2)); + data.RemoveAt(0); + data.Reset(new[] { "foo", "bar", "baz" }); + + Assert.Equal(new[] { new IndexPath(1), new IndexPath(2) }, target.SelectedIndices); + Assert.Equal(new[] { "bar", "baz" }, target.SelectedItems); } [Fact] @@ -1925,6 +1953,15 @@ namespace Avalonia.Controls.UnitTests { public event NotifyCollectionChangedEventHandler CollectionChanged; + public new void RemoveAt(int index) + { + var item = this[index]; + base.RemoveAt(index); + CollectionChanged?.Invoke( + this, + new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new[] { item }, index)); + } + public void Reset(IEnumerable items = null) { if (items != null) From 4dd0ec6e4ee1947b2463a0b142c4717c7d887303 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 5 Feb 2020 10:38:25 +0100 Subject: [PATCH 041/663] Fix bad merge. --- src/Avalonia.Controls/SelectionModel.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Avalonia.Controls/SelectionModel.cs b/src/Avalonia.Controls/SelectionModel.cs index 5a2b1cfbde..2fc449de87 100644 --- a/src/Avalonia.Controls/SelectionModel.cs +++ b/src/Avalonia.Controls/SelectionModel.cs @@ -60,10 +60,6 @@ namespace Avalonia.Controls null, SelectedItems); OnSelectionChanged(e); - ClearSelection(resetAnchor: true); - _rootNode.Source = value; - OnSelectionChanged(); - RaisePropertyChanged("Source"); } } } From ee459635a885f4a0094b357b528a459c9ff17b29 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 5 Feb 2020 10:43:12 +0100 Subject: [PATCH 042/663] Handle RetainSelectionOnReset w/ null source. --- src/Avalonia.Controls/SelectionNode.cs | 24 +++++++--- .../SelectionModelTests.cs | 45 +++++++++++++++++++ 2 files changed, 62 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Controls/SelectionNode.cs b/src/Avalonia.Controls/SelectionNode.cs index e108d0450c..fea6707b43 100644 --- a/src/Avalonia.Controls/SelectionNode.cs +++ b/src/Avalonia.Controls/SelectionNode.cs @@ -55,11 +55,7 @@ namespace Avalonia.Controls if (_retainSelectionOnReset) { _selectedItems = new List(); - - foreach (var i in SelectedIndices) - { - _selectedItems.Add(ItemsSourceView!.GetAt(i)); - } + PopulateSelectedItemsFromSelectedIndices(); } else { @@ -94,7 +90,7 @@ namespace Avalonia.Controls // Setup ItemsSourceView var newDataSource = value as ItemsSourceView; - + if (value != null && newDataSource == null) { newDataSource = new ItemsSourceView((IEnumerable)value); @@ -102,6 +98,7 @@ namespace Avalonia.Controls ItemsSourceView = newDataSource; + PopulateSelectedItemsFromSelectedIndices(); HookupCollectionChangedHandler(); OnSelectionChanged(); } @@ -453,7 +450,7 @@ namespace Avalonia.Controls { _operation?.Selected(selected); - if (_selectedItems != null) + if (_selectedItems != null && ItemsSourceView != null) { for (var i = addRange.Begin; i <= addRange.End; ++i) { @@ -877,6 +874,19 @@ namespace Avalonia.Controls return selectionState; } + private void PopulateSelectedItemsFromSelectedIndices() + { + if (_selectedItems != null) + { + _selectedItems.Clear(); + + foreach (var i in SelectedIndices) + { + _selectedItems.Add(ItemsSourceView!.GetAt(i)); + } + } + } + private List RecreateSelectionFromSelectedItems() { var removed = new List(); diff --git a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs index 81b6127eb6..9574843f99 100644 --- a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs @@ -1596,6 +1596,51 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(1, raised); } + [Fact] + public void RetainSelectionOnReset_Handles_Null_Source() + { + var data = new ResettingList { "foo", "bar", "baz" }; + var target = new SelectionModel { RetainSelectionOnReset = true }; + var raised = 0; + + target.SelectionChanged += (s, e) => + { + if (raised == 0) + { + Assert.Empty(e.DeselectedIndices); + Assert.Empty(e.DeselectedItems); + Assert.Equal(new[] { new IndexPath(1) }, e.SelectedIndices); + Assert.Empty(e.SelectedItems); + } + else if (raised == 1) + { + Assert.Empty(e.DeselectedIndices); + Assert.Empty(e.DeselectedItems); + Assert.Equal(new[] { new IndexPath(1) }, e.SelectedIndices); + Assert.Equal(new[] { "bar" }, e.SelectedItems); + } + else if (raised == 3) + { + Assert.Empty(e.DeselectedIndices); + Assert.Empty(e.DeselectedItems); + Assert.Empty(e.SelectedIndices); + Assert.Empty(e.SelectedItems); + } + + ++raised; + }; + + target.Select(1); + Assert.Equal(1, raised); + + target.Source = data; + Assert.Equal(2, raised); + Assert.Equal(new[] { new IndexPath(1) }, target.SelectedIndices); + + data.Reset(new[] { "qux", "foo", "bar", "baz" }); + Assert.Equal(3, raised); + Assert.Equal(new[] { new IndexPath(2) }, target.SelectedIndices); + } private int GetSubscriberCount(AvaloniaList list) { From 0a608d47dc2bc79b3b9ab70b1a9ad6e1ca509130 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 30 Jan 2020 21:12:07 +0100 Subject: [PATCH 043/663] Added SelectionMode.AutoSelect. --- src/Avalonia.Controls/SelectionModel.cs | 87 ++++++-- src/Avalonia.Controls/SelectionNode.cs | 3 +- .../SelectionModelTests.cs | 203 +++++++++++++++++- 3 files changed, 276 insertions(+), 17 deletions(-) diff --git a/src/Avalonia.Controls/SelectionModel.cs b/src/Avalonia.Controls/SelectionModel.cs index 2fc449de87..e0ca3e8827 100644 --- a/src/Avalonia.Controls/SelectionModel.cs +++ b/src/Avalonia.Controls/SelectionModel.cs @@ -17,6 +17,8 @@ namespace Avalonia.Controls { private readonly SelectionNode _rootNode; private bool _singleSelect; + private bool _autoSelect; + private int _operationCount; private IReadOnlyList? _selectedIndicesCached; private IReadOnlyList? _selectedItemsCached; private SelectionModelChildrenRequestedEventArgs? _childrenRequestedEventArgs; @@ -38,21 +40,25 @@ namespace Avalonia.Controls { if (_rootNode.Source != value) { - var wasNull = _rootNode.Source == null; + var raiseChanged = _rootNode.Source == null && SelectedIndices.Count > 0; if (_rootNode.Source != null) { - using (var operation = new Operation(this)) + if (_rootNode.Source != null) { - ClearSelection(resetAnchor: true); + using (var operation = new Operation(this)) + { + ClearSelection(resetAnchor: true); + } } } _rootNode.Source = value; + ApplyAutoSelect(); RaisePropertyChanged("Source"); - if (wasNull) + if (raiseChanged) { var e = new SelectionModelSelectionChangedEventArgs( null, @@ -92,12 +98,25 @@ namespace Avalonia.Controls } } - public bool RetainSelectionOnReset + public bool RetainSelectionOnReset { get => _rootNode.RetainSelectionOnReset; set => _rootNode.RetainSelectionOnReset = value; } + public bool AutoSelect + { + get => _autoSelect; + set + { + if (_autoSelect != value) + { + _autoSelect = value; + ApplyAutoSelect(); + } + } + } + public IndexPath AnchorIndex { get @@ -356,18 +375,21 @@ namespace Avalonia.Controls { using var operation = new Operation(this); SelectImpl(index, select: false); + ApplyAutoSelect(); } public void Deselect(int groupIndex, int itemIndex) { using var operation = new Operation(this); SelectWithGroupImpl(groupIndex, itemIndex, select: false); + ApplyAutoSelect(); } public void DeselectAt(IndexPath index) { using var operation = new Operation(this); SelectWithPathImpl(index, select: false); + ApplyAutoSelect(); } public bool? IsSelected(int index) @@ -508,6 +530,7 @@ namespace Avalonia.Controls { using var operation = new Operation(this); ClearSelection(resetAnchor: true); + ApplyAutoSelect(); } protected void OnPropertyChanged(string propertyName) @@ -521,10 +544,18 @@ namespace Avalonia.Controls } public void OnSelectionInvalidatedDueToCollectionChange( - IReadOnlyList? removedItems) + bool selectionInvalidated, + IReadOnlyList? removedItems) { - var e = new SelectionModelSelectionChangedEventArgs(null, null, removedItems, null); + SelectionModelSelectionChangedEventArgs? e = null; + + if (selectionInvalidated) + { + e = new SelectionModelSelectionChangedEventArgs(null, null, removedItems, null); + } + OnSelectionChanged(e); + ApplyAutoSelect(); } internal object? ResolvePath(object data, SelectionNode sourceNode) @@ -733,24 +764,52 @@ namespace Avalonia.Controls }); } - private void BeginOperation() => _rootNode.BeginOperation(); + private void BeginOperation() + { + if (_operationCount++ == 0) + { + _rootNode.BeginOperation(); + } + } private void EndOperation() { - var changes = new List(); - _rootNode.EndOperation(changes); + if (_operationCount == 0) + { + throw new AvaloniaInternalException("No selection operation in progress."); + } SelectionModelSelectionChangedEventArgs? e = null; - - if (changes.Count > 0) + + if (--_operationCount == 0) { - var changeSet = new SelectionModelChangeSet(changes); - e = changeSet.CreateEventArgs(); + var changes = new List(); + _rootNode.EndOperation(changes); + + if (changes.Count > 0) + { + var changeSet = new SelectionModelChangeSet(changes); + e = changeSet.CreateEventArgs(); + } } OnSelectionChanged(e); } + private void ApplyAutoSelect() + { + if (AutoSelect) + { + _selectedIndicesCached = null; + + if (SelectedIndex == default && _rootNode.ItemsSourceView?.Count > 0) + { + using var operation = new Operation(this); + SelectImpl(0, true); + } + } + } + internal class SelectedItemInfo : ISelectedItemInfo { public SelectedItemInfo(SelectionNode node, IndexPath path) diff --git a/src/Avalonia.Controls/SelectionNode.cs b/src/Avalonia.Controls/SelectionNode.cs index fea6707b43..4b4a12a7e6 100644 --- a/src/Avalonia.Controls/SelectionNode.cs +++ b/src/Avalonia.Controls/SelectionNode.cs @@ -587,8 +587,9 @@ namespace Avalonia.Controls if (selectionInvalidated) { OnSelectionChanged(); - _manager.OnSelectionInvalidatedDueToCollectionChange(removed); } + + _manager.OnSelectionInvalidatedDueToCollectionChange(selectionInvalidated, removed); } private bool OnItemsAdded(int index, int count) diff --git a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs index 9574843f99..1d2cb9b9ef 100644 --- a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs @@ -1569,8 +1569,6 @@ namespace Avalonia.Controls.UnitTests }; data.Reset(); - - Assert.Equal(1, raised); } [Fact] @@ -1642,6 +1640,207 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(new[] { new IndexPath(2) }, target.SelectedIndices); } + [Fact] + public void AutoSelect_Selects_When_Enabled() + { + var data = new[] { "foo", "bar", "baz" }; + var target = new SelectionModel { Source = data }; + var raised = 0; + + target.SelectionChanged += (s, e) => + { + Assert.Empty(e.DeselectedIndices); + Assert.Empty(e.DeselectedItems); + Assert.Equal(new[] { new IndexPath(0) }, e.SelectedIndices); + Assert.Equal(new[] { "foo" }, e.SelectedItems); + ++raised; + }; + + target.AutoSelect = true; + + Assert.Equal(new IndexPath(0), target.SelectedIndex); + Assert.Equal(1, raised); + } + + [Fact] + public void AutoSelect_Selects_When_Source_Assigned() + { + var data = new[] { "foo", "bar", "baz" }; + var target = new SelectionModel { AutoSelect = true }; + var raised = 0; + + target.SelectionChanged += (s, e) => + { + Assert.Empty(e.DeselectedIndices); + Assert.Empty(e.DeselectedItems); + Assert.Equal(new[] { new IndexPath(0) }, e.SelectedIndices); + Assert.Equal(new[] { "foo" }, e.SelectedItems); + ++raised; + }; + + target.Source = data; + + Assert.Equal(new IndexPath(0), target.SelectedIndex); + Assert.Equal(1, raised); + } + + [Fact] + public void AutoSelect_Selects_When_New_Source_Assigned_And_Old_Source_Has_Selection() + { + var data = new[] { "foo", "bar", "baz" }; + var target = new SelectionModel { AutoSelect = true, Source = data }; + var raised = 0; + + target.SelectionChanged += (s, e) => + { + if (raised == 0) + { + Assert.Equal(new[] { new IndexPath(0) }, e.DeselectedIndices); + Assert.Equal(new[] { "foo" }, e.DeselectedItems); + Assert.Empty(e.SelectedIndices); + Assert.Empty(e.SelectedItems); + } + else + { + Assert.Empty(e.DeselectedIndices); + Assert.Empty(e.DeselectedItems); + Assert.Equal(new[] { new IndexPath(0) }, e.SelectedIndices); + Assert.Equal(new[] { "newfoo" }, e.SelectedItems); + } + ++raised; + }; + + target.Source = new[] { "newfoo" }; + + Assert.Equal(new IndexPath(0), target.SelectedIndex); + Assert.Equal(2, raised); + } + + [Fact] + public void AutoSelect_Selects_When_First_Item_Added() + { + var data = new ObservableCollection(); + var target = new SelectionModel { AutoSelect = true , Source = data }; + var raised = 0; + + target.SelectionChanged += (s, e) => + { + Assert.Empty(e.DeselectedIndices); + Assert.Empty(e.DeselectedItems); + Assert.Equal(new[] { new IndexPath(0) }, e.SelectedIndices); + Assert.Equal(new[] { "foo" }, e.SelectedItems); + ++raised; + }; + + data.Add("foo"); + + Assert.Equal(new IndexPath(0), target.SelectedIndex); + Assert.Equal(1, raised); + } + + [Fact] + public void AutoSelect_Selects_When_Selected_Item_Removed() + { + var data = new ObservableCollection { "foo", "bar", "baz" }; + var target = new SelectionModel { AutoSelect = true, Source = data }; + var raised = 0; + + target.SelectedIndex = new IndexPath(2); + + target.SelectionChanged += (s, e) => + { + if (raised == 0) + { + Assert.Empty(e.DeselectedIndices); + Assert.Equal(new[] { "baz" }, e.DeselectedItems); + Assert.Empty(e.SelectedIndices); + Assert.Empty(e.SelectedItems); + } + else + { + Assert.Empty(e.DeselectedIndices); + Assert.Empty(e.DeselectedItems); + Assert.Equal(new[] { new IndexPath(0) }, e.SelectedIndices); + Assert.Equal(new[] { "foo" }, e.SelectedItems); + } + + ++raised; + }; + + data.RemoveAt(2); + + Assert.Equal(new IndexPath(0), target.SelectedIndex); + Assert.Equal(2, raised); + } + + [Fact] + public void AutoSelect_Selects_On_Deselection() + { + var data = new[] { "foo", "bar", "baz" }; + var target = new SelectionModel { AutoSelect = true, Source = data }; + var raised = 0; + + target.SelectedIndex = new IndexPath(2); + + target.SelectionChanged += (s, e) => + { + Assert.Equal(new[] { new IndexPath(2) }, e.DeselectedIndices); + Assert.Equal(new[] { "baz" }, e.DeselectedItems); + Assert.Equal(new[] { new IndexPath(0) }, e.SelectedIndices); + Assert.Equal(new[] { "foo" }, e.SelectedItems); + ++raised; + }; + + target.Deselect(2); + + Assert.Equal(new IndexPath(0), target.SelectedIndex); + Assert.Equal(1, raised); + } + + [Fact] + public void AutoSelect_Selects_On_ClearSelection() + { + var data = new[] { "foo", "bar", "baz" }; + var target = new SelectionModel { AutoSelect = true, Source = data }; + var raised = 0; + + target.SelectedIndex = new IndexPath(2); + + target.SelectionChanged += (s, e) => + { + Assert.Equal(new[] { new IndexPath(2) }, e.DeselectedIndices); + Assert.Equal(new[] { "baz" }, e.DeselectedItems); + Assert.Equal(new[] { new IndexPath(0) }, e.SelectedIndices); + Assert.Equal(new[] { "foo" }, e.SelectedItems); + ++raised; + }; + + target.ClearSelection(); + + Assert.Equal(new IndexPath(0), target.SelectedIndex); + Assert.Equal(1, raised); + } + + [Fact] + public void AutoSelect_Overrides_Deselecting_First_Item() + { + var data = new[] { "foo", "bar", "baz" }; + var target = new SelectionModel { AutoSelect = true, Source = data }; + var raised = 0; + + target.Select(0); + + target.SelectionChanged += (s, e) => + { + ++raised; + }; + + target.Deselect(0); + + Assert.Equal(new IndexPath(0), target.SelectedIndex); + Assert.Equal(0, raised); + } + private int GetSubscriberCount(AvaloniaList list) { return ((INotifyCollectionChangedDebug)list).GetCollectionChangedSubscribers()?.Length ?? 0; From 44cf7f24db15c9f94c33f3ce0bf00d7516f59e55 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 5 Feb 2020 12:44:42 +0100 Subject: [PATCH 044/663] Expose API for batch updates. --- src/Avalonia.Controls/SelectionModel.cs | 31 ++++++++++++++----- .../SelectionModelTests.cs | 27 ++++++++++++++++ 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Controls/SelectionModel.cs b/src/Avalonia.Controls/SelectionModel.cs index c0ae67ff91..bc650907d9 100644 --- a/src/Avalonia.Controls/SelectionModel.cs +++ b/src/Avalonia.Controls/SelectionModel.cs @@ -17,6 +17,7 @@ namespace Avalonia.Controls { private readonly SelectionNode _rootNode; private bool _singleSelect; + private int _operationCount; private IReadOnlyList? _selectedIndicesCached; private IReadOnlyList? _selectedItemsCached; private SelectionModelChildrenRequestedEventArgs? _childrenRequestedEventArgs; @@ -509,6 +510,8 @@ namespace Avalonia.Controls ClearSelection(resetAnchor: true); } + public IDisposable Update() => new Operation(this); + protected void OnPropertyChanged(string propertyName) { RaisePropertyChanged(propertyName); @@ -732,19 +735,33 @@ namespace Avalonia.Controls }); } - private void BeginOperation() => _rootNode.BeginOperation(); + private void BeginOperation() + { + if (_operationCount++ == 0) + { + _rootNode.BeginOperation(); + } + } private void EndOperation() { - var changes = new List(); - _rootNode.EndOperation(changes); + if (_operationCount == 0) + { + throw new AvaloniaInternalException("No selection operation in progress."); + } SelectionModelSelectionChangedEventArgs? e = null; - - if (changes.Count > 0) + + if (--_operationCount == 0) { - var changeSet = new SelectionModelChangeSet(changes); - e = changeSet.CreateEventArgs(); + var changes = new List(); + _rootNode.EndOperation(changes); + + if (changes.Count > 0) + { + var changeSet = new SelectionModelChangeSet(changes); + e = changeSet.CreateEventArgs(); + } } OnSelectionChanged(e); diff --git a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs index 1950da2818..c76dc890a9 100644 --- a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs @@ -1466,6 +1466,33 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(1, raised); } + [Fact] + public void Can_Batch_Update() + { + var target = new SelectionModel(); + var raised = 0; + + target.Source = Enumerable.Range(0, 10).ToList(); + target.Select(1); + + target.SelectionChanged += (s, e) => + { + Assert.Equal(new[] { new IndexPath(1) }, e.DeselectedIndices); + Assert.Equal(new object[] { 1 }, e.DeselectedItems); + Assert.Equal(new[] { new IndexPath(4) }, e.SelectedIndices); + Assert.Equal(new object[] { 4 }, e.SelectedItems); + ++raised; + }; + + using (target.Update()) + { + target.Deselect(1); + target.Select(4); + } + + Assert.Equal(1, raised); + } + private int GetSubscriberCount(AvaloniaList list) { return ((INotifyCollectionChangedDebug)list).GetCollectionChangedSubscribers()?.Length ?? 0; From c8f0bec40d0d98c1e4b0036ef76e644b9d1877ff Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 5 Feb 2020 14:02:27 +0100 Subject: [PATCH 045/663] Added ISelectionModel. --- src/Avalonia.Controls/ISelectionModel.cs | 47 ++++++++++++++++++++++++ src/Avalonia.Controls/SelectionModel.cs | 21 +++++------ 2 files changed, 57 insertions(+), 11 deletions(-) create mode 100644 src/Avalonia.Controls/ISelectionModel.cs diff --git a/src/Avalonia.Controls/ISelectionModel.cs b/src/Avalonia.Controls/ISelectionModel.cs new file mode 100644 index 0000000000..aed21315bb --- /dev/null +++ b/src/Avalonia.Controls/ISelectionModel.cs @@ -0,0 +1,47 @@ +// 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; + +namespace Avalonia.Controls +{ + public interface ISelectionModel + { + IndexPath AnchorIndex { get; set; } + IndexPath SelectedIndex { get; set; } + IReadOnlyList SelectedIndices { get; } + object SelectedItem { get; } + IReadOnlyList SelectedItems { get; } + bool SingleSelect { get; set; } + object Source { get; set; } + + event EventHandler ChildrenRequested; + event EventHandler SelectionChanged; + + void ClearSelection(); + void Deselect(int index); + void Deselect(int groupIndex, int itemIndex); + void DeselectAt(IndexPath index); + void DeselectRange(IndexPath start, IndexPath end); + void DeselectRangeFromAnchor(int index); + void DeselectRangeFromAnchor(int endGroupIndex, int endItemIndex); + void DeselectRangeFromAnchorTo(IndexPath index); + void Dispose(); + bool? IsSelected(int index); + bool? IsSelected(int groupIndex, int itemIndex); + bool? IsSelectedAt(IndexPath index); + void Select(int index); + void Select(int groupIndex, int itemIndex); + void SelectAll(); + void SelectAt(IndexPath index); + void SelectRange(IndexPath start, IndexPath end); + void SelectRangeFromAnchor(int index); + void SelectRangeFromAnchor(int endGroupIndex, int endItemIndex); + void SelectRangeFromAnchorTo(IndexPath index); + void SetAnchorIndex(int index); + void SetAnchorIndex(int groupIndex, int index); + } +} diff --git a/src/Avalonia.Controls/SelectionModel.cs b/src/Avalonia.Controls/SelectionModel.cs index a501946365..ce8e53c994 100644 --- a/src/Avalonia.Controls/SelectionModel.cs +++ b/src/Avalonia.Controls/SelectionModel.cs @@ -12,7 +12,7 @@ using Avalonia.Controls.Utils; namespace Avalonia.Controls { - public class SelectionModel : INotifyPropertyChanged, IDisposable + public class SelectionModel : ISelectionModel, INotifyPropertyChanged, IDisposable { private readonly SelectionNode _rootNode; private bool _singleSelect; @@ -72,7 +72,6 @@ namespace Avalonia.Controls } } - public IndexPath AnchorIndex { get @@ -184,7 +183,7 @@ namespace Avalonia.Controls // 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 ( + var selectedItems = new SelectedItems( selectedInfos, (infos, index) => { @@ -284,7 +283,7 @@ namespace Avalonia.Controls _selectedIndicesCached = indices; } - return _selectedIndicesCached; + return _selectedIndicesCached; } } @@ -455,7 +454,7 @@ namespace Avalonia.Controls PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } - public void OnSelectionInvalidatedDueToCollectionChange() + internal void OnSelectionInvalidatedDueToCollectionChange() { OnSelectionChanged(); } @@ -535,7 +534,7 @@ namespace Avalonia.Controls RaisePropertyChanged(nameof(SelectedIndex)); RaisePropertyChanged(nameof(SelectedIndices)); - + if (_rootNode.Source != null) { RaisePropertyChanged(nameof(SelectedItem)); @@ -551,7 +550,7 @@ namespace Avalonia.Controls } var selected = _rootNode.Select(index, select); - + if (selected) { AnchorIndex = new IndexPath(index); @@ -569,7 +568,7 @@ namespace Avalonia.Controls var childNode = _rootNode.GetAt(groupIndex, realizeChild: true); var selected = childNode!.Select(itemIndex, select); - + if (selected) { AnchorIndex = new IndexPath(groupIndex, itemIndex); @@ -581,7 +580,7 @@ namespace Avalonia.Controls private void SelectWithPathImpl(IndexPath index, bool select, bool raiseSelectionChanged) { bool selected = false; - + if (_singleSelect) { ClearSelection(resetAnchor: true, raiseSelectionChanged: false); @@ -615,7 +614,7 @@ namespace Avalonia.Controls { int anchorIndex = 0; var anchor = AnchorIndex; - + if (anchor != null) { anchorIndex = anchor.GetAt(0); @@ -634,7 +633,7 @@ namespace Avalonia.Controls var startGroupIndex = 0; var startItemIndex = 0; var anchorIndex = AnchorIndex; - + if (anchorIndex != null) { startGroupIndex = anchorIndex.GetAt(0); From 426c67088236386edeb98f5c28e532ebe7958500 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 5 Feb 2020 14:10:35 +0100 Subject: [PATCH 046/663] Added AutoSelect to ISelectionModel. --- src/Avalonia.Controls/ISelectionModel.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Avalonia.Controls/ISelectionModel.cs b/src/Avalonia.Controls/ISelectionModel.cs index aed21315bb..2f72095dda 100644 --- a/src/Avalonia.Controls/ISelectionModel.cs +++ b/src/Avalonia.Controls/ISelectionModel.cs @@ -16,6 +16,7 @@ namespace Avalonia.Controls object SelectedItem { get; } IReadOnlyList SelectedItems { get; } bool SingleSelect { get; set; } + bool AutoSelect { get; set; } object Source { get; set; } event EventHandler ChildrenRequested; From 6dadd96b7bfe16c7aeef340607984cff1ee664a1 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 6 Feb 2020 11:33:48 +0100 Subject: [PATCH 047/663] Added ISelectionModel.Update(). --- src/Avalonia.Controls/ISelectionModel.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Avalonia.Controls/ISelectionModel.cs b/src/Avalonia.Controls/ISelectionModel.cs index aed21315bb..7db05d849f 100644 --- a/src/Avalonia.Controls/ISelectionModel.cs +++ b/src/Avalonia.Controls/ISelectionModel.cs @@ -43,5 +43,6 @@ namespace Avalonia.Controls void SelectRangeFromAnchorTo(IndexPath index); void SetAnchorIndex(int index); void SetAnchorIndex(int groupIndex, int index); + IDisposable Update(); } } From 520dc16c2a879f4806e1ce8e7580914d011035ab Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 6 Feb 2020 16:12:13 +0100 Subject: [PATCH 048/663] Added failing test. Clearing a nested selection doesn't raise `SelectionChanged`. --- .../SelectionModelTests.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs index c76dc890a9..040448d6fd 100644 --- a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs @@ -1184,6 +1184,29 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(1, raised); } + [Fact] + public void Clearing_Nested_Selection_Raises_SelectionChanged() + { + var target = new SelectionModel(); + var raised = 0; + + target.Source = CreateNestedData(1, 2, 3); + target.Select(1, 1); + + target.SelectionChanged += (s, e) => + { + Assert.Equal(new[] { new IndexPath(1, 1) }, e.DeselectedIndices); + Assert.Equal(new object[] { 4 }, e.DeselectedItems); + Assert.Empty(e.SelectedIndices); + Assert.Empty(e.SelectedItems); + ++raised; + }; + + target.ClearSelection(); + + Assert.Equal(1, raised); + } + [Fact] public void Changing_Source_Raises_SelectionChanged() { From e3d11a82888ba8eef2f928a12e857c9683f0ec3e Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 6 Feb 2020 16:12:43 +0100 Subject: [PATCH 049/663] Fix clearing nested selection not raising SelectionChanged. --- src/Avalonia.Controls/SelectionModel.cs | 2 ++ src/Avalonia.Controls/SelectionNode.cs | 23 +++++++++++++---------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/Avalonia.Controls/SelectionModel.cs b/src/Avalonia.Controls/SelectionModel.cs index c8ebf59032..5d787fb1ca 100644 --- a/src/Avalonia.Controls/SelectionModel.cs +++ b/src/Avalonia.Controls/SelectionModel.cs @@ -324,6 +324,7 @@ namespace Avalonia.Controls public void Dispose() { ClearSelection(resetAnchor: false); + _rootNode.Cleanup(); _rootNode.Dispose(); _selectedIndicesCached = null; _selectedItemsCached = null; @@ -764,6 +765,7 @@ namespace Avalonia.Controls } OnSelectionChanged(e); + _rootNode.Cleanup(); } internal class SelectedItemInfo : ISelectedItemInfo diff --git a/src/Avalonia.Controls/SelectionNode.cs b/src/Avalonia.Controls/SelectionNode.cs index 04144e1ed0..81177f06ca 100644 --- a/src/Avalonia.Controls/SelectionNode.cs +++ b/src/Avalonia.Controls/SelectionNode.cs @@ -342,6 +342,19 @@ namespace Avalonia.Controls } } + public void Cleanup() + { + foreach (var child in _childrenNodes) + { + child?.Cleanup(); + + if (child?.SelectedCount == 0) + { + child.Dispose(); + } + } + } + public bool Select(int index, bool select) { return Select(index, select, raiseOnSelectionChanged: true); @@ -453,16 +466,6 @@ namespace Avalonia.Controls SelectedCount = 0; AnchorIndex = -1; - - // This will throw away all the children SelectionNodes - // causing them to be unhooked from their data source. This - // essentially cleans up the tree. - foreach (var child in _childrenNodes) - { - child?.Dispose(); - } - - _childrenNodes.Clear(); } private bool Select(int index, bool select, bool raiseOnSelectionChanged) From bd022cca397a9c5a83a4f5f3392117a95930e6ac Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 6 Feb 2020 23:57:37 +0100 Subject: [PATCH 050/663] Clean up when node removed. --- src/Avalonia.Controls/SelectionNode.cs | 1 + .../SelectionModelTests.cs | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/Avalonia.Controls/SelectionNode.cs b/src/Avalonia.Controls/SelectionNode.cs index 363eb35b94..5bfa76f83f 100644 --- a/src/Avalonia.Controls/SelectionNode.cs +++ b/src/Avalonia.Controls/SelectionNode.cs @@ -655,6 +655,7 @@ namespace Avalonia.Controls if (_childrenNodes[index] != null) { RealizedChildrenNodeCount--; + _childrenNodes[index]!.Dispose(); } _childrenNodes.RemoveAt(index); } diff --git a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs index 208d85d8fd..e1a22dd790 100644 --- a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs @@ -964,6 +964,20 @@ namespace Avalonia.Controls.UnitTests } } + [Fact] + public void Removing_Item_Unhooks_CollectionChanged_Handlers() + { + var data = CreateNestedData(2, 2, 2); + var target = new SelectionModel { Source = data }; + + target.SelectAll(); + + var toRemove = (AvaloniaList)data[1]; + data.Remove(toRemove); + + Assert.Equal(0, GetSubscriberCount(toRemove)); + } + [Fact] public void Should_Not_Treat_Strings_As_Nested_Selections() { From efab1c8266d5c21633fa23d1fa29c681024e7122 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 7 Feb 2020 09:12:15 +0100 Subject: [PATCH 051/663] Fix SelectionNode.Cleanup. - Removed disposed child nodes - Don't dispose child node with descendent selection --- src/Avalonia.Controls/SelectionNode.cs | 22 ++++++++++++++----- .../SelectionModelTests.cs | 15 +++++++++++++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Controls/SelectionNode.cs b/src/Avalonia.Controls/SelectionNode.cs index 024a7aa8e7..d93a54d458 100644 --- a/src/Avalonia.Controls/SelectionNode.cs +++ b/src/Avalonia.Controls/SelectionNode.cs @@ -342,17 +342,29 @@ namespace Avalonia.Controls } } - public void Cleanup() + public bool Cleanup() { - foreach (var child in _childrenNodes) + var result = SelectedCount == 0; + + for (var i = 0; i < _childrenNodes.Count; ++i) { - child?.Cleanup(); + var child = _childrenNodes[i]; - if (child?.SelectedCount == 0) + if (child != null) { - child.Dispose(); + if (child.Cleanup()) + { + child.Dispose(); + _childrenNodes[i] = null; + } + else + { + result = false; + } } } + + return result; } public bool Select(int index, bool select) diff --git a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs index a024105a55..9f29dce783 100644 --- a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs @@ -930,6 +930,21 @@ namespace Avalonia.Controls.UnitTests }); } + [Fact] + public void Should_Listen_For_Changes_After_Deselect() + { + var target = new SelectionModel(); + var data = CreateNestedData(1, 2, 3); + + target.Source = data; + target.Select(1, 0); + target.Deselect(1, 0); + target.Select(1, 0); + ((AvaloniaList)data[1]).Insert(0, "foo"); + + Assert.Equal(new IndexPath(1, 1), target.SelectedIndex); + } + [Fact] public void Selecting_Item_Raises_SelectionChanged() { From e4d45fc46d10a558d7193fe48e0a8362e6ad6d17 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 12 Feb 2020 08:33:00 +0100 Subject: [PATCH 052/663] ISelectionModel implements INotifyPropertyChanged. This will be needed for monitoring the `AnchorIndex` in order to auto-scroll. --- src/Avalonia.Controls/ISelectionModel.cs | 3 ++- src/Avalonia.Controls/SelectionModel.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/ISelectionModel.cs b/src/Avalonia.Controls/ISelectionModel.cs index aed21315bb..a939bfdc8c 100644 --- a/src/Avalonia.Controls/ISelectionModel.cs +++ b/src/Avalonia.Controls/ISelectionModel.cs @@ -5,10 +5,11 @@ using System; using System.Collections.Generic; +using System.ComponentModel; namespace Avalonia.Controls { - public interface ISelectionModel + public interface ISelectionModel : INotifyPropertyChanged { IndexPath AnchorIndex { get; set; } IndexPath SelectedIndex { get; set; } diff --git a/src/Avalonia.Controls/SelectionModel.cs b/src/Avalonia.Controls/SelectionModel.cs index ce8e53c994..325e2e8848 100644 --- a/src/Avalonia.Controls/SelectionModel.cs +++ b/src/Avalonia.Controls/SelectionModel.cs @@ -12,7 +12,7 @@ using Avalonia.Controls.Utils; namespace Avalonia.Controls { - public class SelectionModel : ISelectionModel, INotifyPropertyChanged, IDisposable + public class SelectionModel : ISelectionModel, IDisposable { private readonly SelectionNode _rootNode; private bool _singleSelect; From 12abeab47e99efe667a94548d748eca1858c7d4b Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 15 Feb 2020 09:47:54 +0100 Subject: [PATCH 053/663] Remove UWP stubs in unit tests. --- .../SelectionModelTests.cs | 1525 ++++++++--------- 1 file changed, 725 insertions(+), 800 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs index e1a22dd790..31e07d834a 100644 --- a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs @@ -17,248 +17,224 @@ namespace Avalonia.Controls.UnitTests { public class SelectionModelTests { - private LogWrapper Log { get; } + private readonly ITestOutputHelper _output; public SelectionModelTests(ITestOutputHelper output) { - Log = new LogWrapper(output); + _output = output; } [Fact] public void ValidateOneLevelSingleSelectionNoSource() { - RunOnUIThread.Execute(() => - { - SelectionModel selectionModel = new SelectionModel() { SingleSelect = true }; - Log.Comment("No source set."); - Select(selectionModel, 4, true); - ValidateSelection(selectionModel, new List() { Path(4) }); - Select(selectionModel, 4, false); - ValidateSelection(selectionModel, new List() { }); - }); + SelectionModel selectionModel = new SelectionModel() { SingleSelect = true }; + _output.WriteLine("No source set."); + Select(selectionModel, 4, true); + ValidateSelection(selectionModel, new List() { Path(4) }); + Select(selectionModel, 4, false); + ValidateSelection(selectionModel, new List() { }); } [Fact] public void ValidateOneLevelSingleSelection() { - RunOnUIThread.Execute(() => - { - SelectionModel selectionModel = new SelectionModel() { SingleSelect = true }; - Log.Comment("Set the source to 10 items"); - selectionModel.Source = Enumerable.Range(0, 10).ToList(); - Select(selectionModel, 3, true); - ValidateSelection(selectionModel, new List() { Path(3) }, new List() { Path() }); - Select(selectionModel, 3, false); - ValidateSelection(selectionModel, new List() { }); - }); + SelectionModel selectionModel = new SelectionModel() { SingleSelect = true }; + _output.WriteLine("Set the source to 10 items"); + selectionModel.Source = Enumerable.Range(0, 10).ToList(); + Select(selectionModel, 3, true); + ValidateSelection(selectionModel, new List() { Path(3) }, new List() { Path() }); + Select(selectionModel, 3, false); + ValidateSelection(selectionModel, new List() { }); } [Fact] public void ValidateSelectionChangedEvent() { - RunOnUIThread.Execute(() => - { - SelectionModel selectionModel = new SelectionModel(); - selectionModel.Source = Enumerable.Range(0, 10).ToList(); - - int selectionChangedFiredCount = 0; - selectionModel.SelectionChanged += delegate (object sender, SelectionModelSelectionChangedEventArgs args) - { - selectionChangedFiredCount++; - ValidateSelection(selectionModel, new List() { Path(4) }, new List() { Path() }); - }; + SelectionModel selectionModel = new SelectionModel(); + selectionModel.Source = Enumerable.Range(0, 10).ToList(); - Select(selectionModel, 4, true); + int selectionChangedFiredCount = 0; + selectionModel.SelectionChanged += delegate (object sender, SelectionModelSelectionChangedEventArgs args) + { + selectionChangedFiredCount++; ValidateSelection(selectionModel, new List() { Path(4) }, new List() { Path() }); - Assert.Equal(1, selectionChangedFiredCount); - }); + }; + + Select(selectionModel, 4, true); + ValidateSelection(selectionModel, new List() { Path(4) }, new List() { Path() }); + Assert.Equal(1, selectionChangedFiredCount); } [Fact] public void ValidateCanSetSelectedIndex() { - RunOnUIThread.Execute(() => - { - var model = new SelectionModel(); - var ip = IndexPath.CreateFrom(34); - model.SelectedIndex = ip; - Assert.Equal(0, ip.CompareTo(model.SelectedIndex)); - }); + var model = new SelectionModel(); + var ip = IndexPath.CreateFrom(34); + model.SelectedIndex = ip; + Assert.Equal(0, ip.CompareTo(model.SelectedIndex)); } [Fact] public void ValidateOneLevelMultipleSelection() { - RunOnUIThread.Execute(() => - { - SelectionModel selectionModel = new SelectionModel(); - selectionModel.Source = Enumerable.Range(0, 10).ToList(); - - Select(selectionModel, 4, true); - ValidateSelection(selectionModel, new List() { Path(4) }, new List() { Path() }); - SelectRangeFromAnchor(selectionModel, 8, true /* select */); - ValidateSelection(selectionModel, - new List() - { - Path(4), - Path(5), - Path(6), - Path(7), - Path(8) - }, - new List() { Path() }); - - ClearSelection(selectionModel); - SetAnchorIndex(selectionModel, 6); - SelectRangeFromAnchor(selectionModel, 3, true /* select */); - ValidateSelection(selectionModel, - new List() - { - Path(3), - Path(4), - Path(5), - Path(6) - }, - new List() { Path() }); - - SetAnchorIndex(selectionModel, 4); - SelectRangeFromAnchor(selectionModel, 5, false /* select */); - ValidateSelection(selectionModel, - new List() - { - Path(3), - Path(6) - }, - new List() { Path() }); - }); + SelectionModel selectionModel = new SelectionModel(); + selectionModel.Source = Enumerable.Range(0, 10).ToList(); + + Select(selectionModel, 4, true); + ValidateSelection(selectionModel, new List() { Path(4) }, new List() { Path() }); + SelectRangeFromAnchor(selectionModel, 8, true /* select */); + ValidateSelection(selectionModel, + new List() + { + Path(4), + Path(5), + Path(6), + Path(7), + Path(8) + }, + new List() { Path() }); + + ClearSelection(selectionModel); + SetAnchorIndex(selectionModel, 6); + SelectRangeFromAnchor(selectionModel, 3, true /* select */); + ValidateSelection(selectionModel, + new List() + { + Path(3), + Path(4), + Path(5), + Path(6) + }, + new List() { Path() }); + + SetAnchorIndex(selectionModel, 4); + SelectRangeFromAnchor(selectionModel, 5, false /* select */); + ValidateSelection(selectionModel, + new List() + { + Path(3), + Path(6) + }, + new List() { Path() }); } [Fact] public void ValidateTwoLevelSingleSelection() { - RunOnUIThread.Execute(() => - { - SelectionModel selectionModel = new SelectionModel(); - Log.Comment("Setting the source"); - selectionModel.Source = CreateNestedData(1 /* levels */ , 2 /* groupsAtLevel */, 2 /* countAtLeaf */); - Select(selectionModel, 1, 1, true); - ValidateSelection(selectionModel, - new List() { Path(1, 1) }, new List() { Path(), Path(1) }); - Select(selectionModel, 1, 1, false); - ValidateSelection(selectionModel, new List() { }); - }); + SelectionModel selectionModel = new SelectionModel(); + _output.WriteLine("Setting the source"); + selectionModel.Source = CreateNestedData(1 /* levels */ , 2 /* groupsAtLevel */, 2 /* countAtLeaf */); + Select(selectionModel, 1, 1, true); + ValidateSelection(selectionModel, + new List() { Path(1, 1) }, new List() { Path(), Path(1) }); + Select(selectionModel, 1, 1, false); + ValidateSelection(selectionModel, new List() { }); } [Fact] public void ValidateTwoLevelMultipleSelection() { - RunOnUIThread.Execute(() => - { - SelectionModel selectionModel = new SelectionModel(); - Log.Comment("Setting the source"); - selectionModel.Source = CreateNestedData(1 /* levels */ , 3 /* groupsAtLevel */, 3 /* countAtLeaf */); - - Select(selectionModel, 1, 2, true); - ValidateSelection(selectionModel, new List() { Path(1, 2) }, new List() { Path(), Path(1) }); - SelectRangeFromAnchor(selectionModel, 2, 2, true /* select */); - ValidateSelection(selectionModel, - new List() - { - Path(1, 2), - Path(2), // Inner node should be selected since everything 2.* is selected - Path(2, 0), - Path(2, 1), - Path(2, 2) - }, - new List() - { - Path(), - Path(1) - }, - 1 /* selectedInnerNodes */); - - ClearSelection(selectionModel); - SetAnchorIndex(selectionModel, 2, 1); - SelectRangeFromAnchor(selectionModel, 0, 1, true /* select */); - ValidateSelection(selectionModel, - new List() - { - Path(0, 1), - Path(0, 2), - Path(1, 0), - Path(1, 1), - Path(1, 2), - Path(1), - Path(2, 0), - Path(2, 1) - }, - new List() - { - Path(), - Path(0), - Path(2), - }, - 1 /* selectedInnerNodes */); - - SetAnchorIndex(selectionModel, 1, 1); - SelectRangeFromAnchor(selectionModel, 2, 0, false /* select */); - ValidateSelection(selectionModel, - new List() - { - Path(0, 1), - Path(0, 2), - Path(1, 0), - Path(2, 1) - }, - new List() - { - Path(), - Path(1), - Path(0), - Path(2), - }, - 0 /* selectedInnerNodes */); - - ClearSelection(selectionModel); - ValidateSelection(selectionModel, new List() { }); - }); + SelectionModel selectionModel = new SelectionModel(); + _output.WriteLine("Setting the source"); + selectionModel.Source = CreateNestedData(1 /* levels */ , 3 /* groupsAtLevel */, 3 /* countAtLeaf */); + + Select(selectionModel, 1, 2, true); + ValidateSelection(selectionModel, new List() { Path(1, 2) }, new List() { Path(), Path(1) }); + SelectRangeFromAnchor(selectionModel, 2, 2, true /* select */); + ValidateSelection(selectionModel, + new List() + { + Path(1, 2), + Path(2), // Inner node should be selected since everything 2.* is selected + Path(2, 0), + Path(2, 1), + Path(2, 2) + }, + new List() + { + Path(), + Path(1) + }, + 1 /* selectedInnerNodes */); + + ClearSelection(selectionModel); + SetAnchorIndex(selectionModel, 2, 1); + SelectRangeFromAnchor(selectionModel, 0, 1, true /* select */); + ValidateSelection(selectionModel, + new List() + { + Path(0, 1), + Path(0, 2), + Path(1, 0), + Path(1, 1), + Path(1, 2), + Path(1), + Path(2, 0), + Path(2, 1) + }, + new List() + { + Path(), + Path(0), + Path(2), + }, + 1 /* selectedInnerNodes */); + + SetAnchorIndex(selectionModel, 1, 1); + SelectRangeFromAnchor(selectionModel, 2, 0, false /* select */); + ValidateSelection(selectionModel, + new List() + { + Path(0, 1), + Path(0, 2), + Path(1, 0), + Path(2, 1) + }, + new List() + { + Path(), + Path(1), + Path(0), + Path(2), + }, + 0 /* selectedInnerNodes */); + + ClearSelection(selectionModel); + ValidateSelection(selectionModel, new List() { }); } [Fact] public void ValidateNestedSingleSelection() { - RunOnUIThread.Execute(() => - { - SelectionModel selectionModel = new SelectionModel() { SingleSelect = true }; - Log.Comment("Setting the source"); - selectionModel.Source = CreateNestedData(3 /* levels */ , 2 /* groupsAtLevel */, 2 /* countAtLeaf */); - var path = Path(1, 0, 1, 1); - Select(selectionModel, path, true); - ValidateSelection(selectionModel, - new List() { path }, - new List() - { - Path(), - Path(1), - Path(1, 0), - Path(1, 0, 1), - }); - Select(selectionModel, Path(0, 0, 1, 0), true); - ValidateSelection(selectionModel, - new List() - { - Path(0, 0, 1, 0) - }, - new List() - { - Path(), - Path(0), - Path(0, 0), - Path(0, 0, 1) - }); - Select(selectionModel, Path(0, 0, 1, 0), false); - ValidateSelection(selectionModel, new List() { }); - }); + SelectionModel selectionModel = new SelectionModel() { SingleSelect = true }; + _output.WriteLine("Setting the source"); + selectionModel.Source = CreateNestedData(3 /* levels */ , 2 /* groupsAtLevel */, 2 /* countAtLeaf */); + var path = Path(1, 0, 1, 1); + Select(selectionModel, path, true); + ValidateSelection(selectionModel, + new List() { path }, + new List() + { + Path(), + Path(1), + Path(1, 0), + Path(1, 0, 1), + }); + Select(selectionModel, Path(0, 0, 1, 0), true); + ValidateSelection(selectionModel, + new List() + { + Path(0, 0, 1, 0) + }, + new List() + { + Path(), + Path(0), + Path(0, 0), + Path(0, 0, 1) + }); + Select(selectionModel, Path(0, 0, 1, 0), false); + ValidateSelection(selectionModel, new List() { }); } [Theory] @@ -266,497 +242,470 @@ namespace Avalonia.Controls.UnitTests [InlineData(false)] public void ValidateNestedMultipleSelection(bool handleChildrenRequested) { - RunOnUIThread.Execute(() => + SelectionModel selectionModel = new SelectionModel(); + List sourcePaths = new List(); + + _output.WriteLine("Setting the source"); + selectionModel.Source = CreateNestedData(3 /* levels */ , 2 /* groupsAtLevel */, 4 /* countAtLeaf */); + if (handleChildrenRequested) { - SelectionModel selectionModel = new SelectionModel(); - List sourcePaths = new List(); + selectionModel.ChildrenRequested += (object sender, SelectionModelChildrenRequestedEventArgs args) => + { + _output.WriteLine("ChildrenRequestedIndexPath:" + args.SourceIndex); + sourcePaths.Add(args.SourceIndex); + args.Children = args.Source is IEnumerable ? args.Source : null; + }; + } - Log.Comment("Setting the source"); - selectionModel.Source = CreateNestedData(3 /* levels */ , 2 /* groupsAtLevel */, 4 /* countAtLeaf */); - if (handleChildrenRequested) + var startPath = Path(1, 0, 1, 0); + Select(selectionModel, startPath, true); + ValidateSelection(selectionModel, + new List() { startPath }, + new List() { - selectionModel.ChildrenRequested += (object sender, SelectionModelChildrenRequestedEventArgs args) => - { - Log.Comment("ChildrenRequestedIndexPath:" + args.SourceIndex); - sourcePaths.Add(args.SourceIndex); - args.Children = args.Source is IEnumerable ? args.Source : null; - }; - } + Path(), + Path(1), + Path(1, 0), + Path(1, 0, 1) + }); - var startPath = Path(1, 0, 1, 0); - Select(selectionModel, startPath, true); - ValidateSelection(selectionModel, - new List() { startPath }, - new List() - { - Path(), - Path(1), - Path(1, 0), - Path(1, 0, 1) - }); + var endPath = Path(1, 1, 1, 0); + SelectRangeFromAnchor(selectionModel, endPath, true /* select */); - var endPath = Path(1, 1, 1, 0); - SelectRangeFromAnchor(selectionModel, endPath, true /* select */); + if (handleChildrenRequested) + { + // Validate SourceIndices. + var expectedSourceIndices = new List() + { + Path(), + Path(1), + Path(1, 0), + Path(1), + Path(1, 0, 1), + Path(1, 0, 1), + Path(1, 0, 1), + Path(1, 0, 1), + Path(1, 1), + Path(1, 1), + Path(1, 1, 0), + Path(1, 1, 0), + Path(1, 1, 0), + Path(1, 1, 0), + Path(1, 1, 1) + }; - if (handleChildrenRequested) + Assert.Equal(expectedSourceIndices.Count, sourcePaths.Count); + for (int i = 0; i < expectedSourceIndices.Count; i++) { - // Validate SourceIndices. - var expectedSourceIndices = new List() - { - Path(), - Path(1), - Path(1, 0), - Path(1), - Path(1, 0, 1), - Path(1, 0, 1), - Path(1, 0, 1), - Path(1, 0, 1), - Path(1, 1), - Path(1, 1), - Path(1, 1, 0), - Path(1, 1, 0), - Path(1, 1, 0), - Path(1, 1, 0), - Path(1, 1, 1) - }; - - Assert.Equal(expectedSourceIndices.Count, sourcePaths.Count); - for (int i = 0; i < expectedSourceIndices.Count; i++) - { - Assert.True(AreEqual(expectedSourceIndices[i], sourcePaths[i])); - } + Assert.True(AreEqual(expectedSourceIndices[i], sourcePaths[i])); } + } - ValidateSelection(selectionModel, - new List() - { - Path(1, 0, 1, 0), - Path(1, 0, 1, 1), - Path(1, 0, 1, 2), - Path(1, 0, 1, 3), - Path(1, 0, 1), - Path(1, 1, 0, 0), - Path(1, 1, 0, 1), - Path(1, 1, 0, 2), - Path(1, 1, 0, 3), - Path(1, 1, 0), - Path(1, 1, 1, 0), - }, - new List() - { - Path(), - Path(1), - Path(1, 0), - Path(1, 1), - Path(1, 1, 1), - }, - 2 /* selectedInnerNodes */); - - ClearSelection(selectionModel); - ValidateSelection(selectionModel, new List() { }); - - startPath = Path(0, 1, 0, 2); - SetAnchorIndex(selectionModel, startPath); - endPath = Path(0, 0, 0, 2); - SelectRangeFromAnchor(selectionModel, endPath, true /* select */); - ValidateSelection(selectionModel, - new List() - { - Path(0, 0, 0, 2), - Path(0, 0, 0, 3), - Path(0, 0, 1, 0), - Path(0, 0, 1, 1), - Path(0, 0, 1, 2), - Path(0, 0, 1, 3), - Path(0, 0, 1), - Path(0, 1, 0, 0), - Path(0, 1, 0, 1), - Path(0, 1, 0, 2), - }, - new List() - { - Path(), - Path(0), - Path(0, 0), - Path(0, 0, 0), - Path(0, 1), - Path(0, 1, 0), - }, - 1 /* selectedInnerNodes */); - - startPath = Path(0, 1, 0, 2); - SetAnchorIndex(selectionModel, startPath); - endPath = Path(0, 0, 0, 2); - SelectRangeFromAnchor(selectionModel, endPath, false /* select */); - ValidateSelection(selectionModel, new List() { }); - }); + ValidateSelection(selectionModel, + new List() + { + Path(1, 0, 1, 0), + Path(1, 0, 1, 1), + Path(1, 0, 1, 2), + Path(1, 0, 1, 3), + Path(1, 0, 1), + Path(1, 1, 0, 0), + Path(1, 1, 0, 1), + Path(1, 1, 0, 2), + Path(1, 1, 0, 3), + Path(1, 1, 0), + Path(1, 1, 1, 0), + }, + new List() + { + Path(), + Path(1), + Path(1, 0), + Path(1, 1), + Path(1, 1, 1), + }, + 2 /* selectedInnerNodes */); + + ClearSelection(selectionModel); + ValidateSelection(selectionModel, new List() { }); + + startPath = Path(0, 1, 0, 2); + SetAnchorIndex(selectionModel, startPath); + endPath = Path(0, 0, 0, 2); + SelectRangeFromAnchor(selectionModel, endPath, true /* select */); + ValidateSelection(selectionModel, + new List() + { + Path(0, 0, 0, 2), + Path(0, 0, 0, 3), + Path(0, 0, 1, 0), + Path(0, 0, 1, 1), + Path(0, 0, 1, 2), + Path(0, 0, 1, 3), + Path(0, 0, 1), + Path(0, 1, 0, 0), + Path(0, 1, 0, 1), + Path(0, 1, 0, 2), + }, + new List() + { + Path(), + Path(0), + Path(0, 0), + Path(0, 0, 0), + Path(0, 1), + Path(0, 1, 0), + }, + 1 /* selectedInnerNodes */); + + startPath = Path(0, 1, 0, 2); + SetAnchorIndex(selectionModel, startPath); + endPath = Path(0, 0, 0, 2); + SelectRangeFromAnchor(selectionModel, endPath, false /* select */); + ValidateSelection(selectionModel, new List() { }); } [Fact] public void ValidateInserts() { - RunOnUIThread.Execute(() => - { - var data = new ObservableCollection(Enumerable.Range(0, 10)); - var selectionModel = new SelectionModel(); - selectionModel.Source = data; - - selectionModel.Select(3); - selectionModel.Select(4); - selectionModel.Select(5); - ValidateSelection(selectionModel, - new List() - { - Path(3), - Path(4), - Path(5), - }, - new List() - { - Path() - }); - - Log.Comment("Insert in selected range: Inserting 3 items at index 4"); - data.Insert(4, 41); - data.Insert(4, 42); - data.Insert(4, 43); - ValidateSelection(selectionModel, - new List() - { - Path(3), - Path(7), - Path(8), - }, - new List() - { - Path() - }); - - Log.Comment("Insert before selected range: Inserting 3 items at index 0"); - data.Insert(0, 100); - data.Insert(0, 101); - data.Insert(0, 102); - ValidateSelection(selectionModel, - new List() - { - Path(6), - Path(10), - Path(11), - }, - new List() - { - Path() - }); - - Log.Comment("Insert after selected range: Inserting 3 items at index 12"); - data.Insert(12, 1000); - data.Insert(12, 1001); - data.Insert(12, 1002); - ValidateSelection(selectionModel, - new List() - { - Path(6), - Path(10), - Path(11), - }, - new List() - { - Path() - }); - }); + var data = new ObservableCollection(Enumerable.Range(0, 10)); + var selectionModel = new SelectionModel(); + selectionModel.Source = data; + + selectionModel.Select(3); + selectionModel.Select(4); + selectionModel.Select(5); + ValidateSelection(selectionModel, + new List() + { + Path(3), + Path(4), + Path(5), + }, + new List() + { + Path() + }); + + _output.WriteLine("Insert in selected range: Inserting 3 items at index 4"); + data.Insert(4, 41); + data.Insert(4, 42); + data.Insert(4, 43); + ValidateSelection(selectionModel, + new List() + { + Path(3), + Path(7), + Path(8), + }, + new List() + { + Path() + }); + + _output.WriteLine("Insert before selected range: Inserting 3 items at index 0"); + data.Insert(0, 100); + data.Insert(0, 101); + data.Insert(0, 102); + ValidateSelection(selectionModel, + new List() + { + Path(6), + Path(10), + Path(11), + }, + new List() + { + Path() + }); + + _output.WriteLine("Insert after selected range: Inserting 3 items at index 12"); + data.Insert(12, 1000); + data.Insert(12, 1001); + data.Insert(12, 1002); + ValidateSelection(selectionModel, + new List() + { + Path(6), + Path(10), + Path(11), + }, + new List() + { + Path() + }); } [Fact] public void ValidateGroupInserts() { - RunOnUIThread.Execute(() => - { - var data = CreateNestedData(1 /* levels */ , 3 /* groupsAtLevel */, 3 /* countAtLeaf */); - var selectionModel = new SelectionModel(); - selectionModel.Source = data; + var data = CreateNestedData(1 /* levels */ , 3 /* groupsAtLevel */, 3 /* countAtLeaf */); + var selectionModel = new SelectionModel(); + selectionModel.Source = data; - selectionModel.Select(1, 1); - ValidateSelection(selectionModel, - new List() - { - Path(1, 1), - }, - new List() - { - Path(), - Path(1), - }); - - Log.Comment("Insert before selected range: Inserting item at group index 0"); - data.Insert(0, 100); - ValidateSelection(selectionModel, - new List() - { - Path(2, 1) - }, - new List() - { - Path(), - Path(2), - }); - - Log.Comment("Insert after selected range: Inserting item at group index 3"); - data.Insert(3, 1000); - ValidateSelection(selectionModel, - new List() - { - Path(2, 1) - }, - new List() - { - Path(), - Path(2), - }); - }); + selectionModel.Select(1, 1); + ValidateSelection(selectionModel, + new List() + { + Path(1, 1), + }, + new List() + { + Path(), + Path(1), + }); + + _output.WriteLine("Insert before selected range: Inserting item at group index 0"); + data.Insert(0, 100); + ValidateSelection(selectionModel, + new List() + { + Path(2, 1) + }, + new List() + { + Path(), + Path(2), + }); + + _output.WriteLine("Insert after selected range: Inserting item at group index 3"); + data.Insert(3, 1000); + ValidateSelection(selectionModel, + new List() + { + Path(2, 1) + }, + new List() + { + Path(), + Path(2), + }); } [Fact] public void ValidateRemoves() { - RunOnUIThread.Execute(() => - { - var data = new ObservableCollection(Enumerable.Range(0, 10)); - var selectionModel = new SelectionModel(); - selectionModel.Source = data; - - selectionModel.Select(6); - selectionModel.Select(7); - selectionModel.Select(8); - ValidateSelection(selectionModel, - new List() - { - Path(6), - Path(7), - Path(8) - }, - new List() - { - Path() - }); + var data = new ObservableCollection(Enumerable.Range(0, 10)); + var selectionModel = new SelectionModel(); + selectionModel.Source = data; + + selectionModel.Select(6); + selectionModel.Select(7); + selectionModel.Select(8); + ValidateSelection(selectionModel, + new List() + { + Path(6), + Path(7), + Path(8) + }, + new List() + { + Path() + }); - Log.Comment("Remove before selected range: Removing item at index 0"); - data.RemoveAt(0); - ValidateSelection(selectionModel, - new List() - { - Path(5), - Path(6), - Path(7) - }, - new List() - { - Path() - }); - - Log.Comment("Remove from before to middle of selected range: Removing items at index 3, 4, 5"); - data.RemoveAt(3); - data.RemoveAt(3); - data.RemoveAt(3); - ValidateSelection(selectionModel, - new List() - { - Path(3), - Path(4) - }, - new List() - { - Path() - }); + _output.WriteLine("Remove before selected range: Removing item at index 0"); + data.RemoveAt(0); + ValidateSelection(selectionModel, + new List() + { + Path(5), + Path(6), + Path(7) + }, + new List() + { + Path() + }); + + _output.WriteLine("Remove from before to middle of selected range: Removing items at index 3, 4, 5"); + data.RemoveAt(3); + data.RemoveAt(3); + data.RemoveAt(3); + ValidateSelection(selectionModel, + new List() + { + Path(3), + Path(4) + }, + new List() + { + Path() + }); - Log.Comment("Remove after selected range: Removing item at index 5"); - data.RemoveAt(5); - ValidateSelection(selectionModel, - new List() - { - Path(3), - Path(4) - }, - new List() - { - Path() - }); - }); + _output.WriteLine("Remove after selected range: Removing item at index 5"); + data.RemoveAt(5); + ValidateSelection(selectionModel, + new List() + { + Path(3), + Path(4) + }, + new List() + { + Path() + }); } [Fact] public void ValidateGroupRemoves() { - RunOnUIThread.Execute(() => - { - var data = CreateNestedData(1 /* levels */ , 3 /* groupsAtLevel */, 3 /* countAtLeaf */); - var selectionModel = new SelectionModel(); - selectionModel.Source = data; - - selectionModel.Select(1, 1); - selectionModel.Select(1, 2); - ValidateSelection(selectionModel, - new List() - { - Path(1, 1), - Path(1, 2) - }, - new List() - { - Path(), - Path(1), - }); - - Log.Comment("Remove before selected range: Removing item at group index 0"); - data.RemoveAt(0); - ValidateSelection(selectionModel, - new List() - { - Path(0, 1), - Path(0, 2) - }, - new List() - { - Path(), - Path(0), - }); - - Log.Comment("Remove after selected range: Removing item at group index 1"); - data.RemoveAt(1); - ValidateSelection(selectionModel, - new List() - { - Path(0, 1), - Path(0, 2) - }, - new List() - { - Path(), - Path(0), - }); + var data = CreateNestedData(1 /* levels */ , 3 /* groupsAtLevel */, 3 /* countAtLeaf */); + var selectionModel = new SelectionModel(); + selectionModel.Source = data; + + selectionModel.Select(1, 1); + selectionModel.Select(1, 2); + ValidateSelection(selectionModel, + new List() + { + Path(1, 1), + Path(1, 2) + }, + new List() + { + Path(), + Path(1), + }); + + _output.WriteLine("Remove before selected range: Removing item at group index 0"); + data.RemoveAt(0); + ValidateSelection(selectionModel, + new List() + { + Path(0, 1), + Path(0, 2) + }, + new List() + { + Path(), + Path(0), + }); + + _output.WriteLine("Remove after selected range: Removing item at group index 1"); + data.RemoveAt(1); + ValidateSelection(selectionModel, + new List() + { + Path(0, 1), + Path(0, 2) + }, + new List() + { + Path(), + Path(0), + }); - Log.Comment("Remove group containing selected items"); - data.RemoveAt(0); - ValidateSelection(selectionModel, new List()); - }); + _output.WriteLine("Remove group containing selected items"); + data.RemoveAt(0); + ValidateSelection(selectionModel, new List()); } [Fact] public void CanReplaceItem() { - RunOnUIThread.Execute(() => - { - var data = new ObservableCollection(Enumerable.Range(0, 10)); - var selectionModel = new SelectionModel(); - selectionModel.Source = data; - - selectionModel.Select(3); - selectionModel.Select(4); - selectionModel.Select(5); - ValidateSelection(selectionModel, - new List() - { - Path(3), - Path(4), - Path(5), - }, - new List() - { - Path() - }); + var data = new ObservableCollection(Enumerable.Range(0, 10)); + var selectionModel = new SelectionModel(); + selectionModel.Source = data; + + selectionModel.Select(3); + selectionModel.Select(4); + selectionModel.Select(5); + ValidateSelection(selectionModel, + new List() + { + Path(3), + Path(4), + Path(5), + }, + new List() + { + Path() + }); - data[3] = 300; - data[4] = 400; - ValidateSelection(selectionModel, - new List() - { - Path(5), - }, - new List() - { - Path() - }); - }); + data[3] = 300; + data[4] = 400; + ValidateSelection(selectionModel, + new List() + { + Path(5), + }, + new List() + { + Path() + }); } [Fact] public void ValidateGroupReplaceLosesSelection() { - RunOnUIThread.Execute(() => - { - var data = CreateNestedData(1 /* levels */ , 3 /* groupsAtLevel */, 3 /* countAtLeaf */); - var selectionModel = new SelectionModel(); - selectionModel.Source = data; + var data = CreateNestedData(1 /* levels */ , 3 /* groupsAtLevel */, 3 /* countAtLeaf */); + var selectionModel = new SelectionModel(); + selectionModel.Source = data; - selectionModel.Select(1, 1); - ValidateSelection(selectionModel, - new List() - { - Path(1, 1) - }, - new List() - { - Path(), - Path(1) - }); + selectionModel.Select(1, 1); + ValidateSelection(selectionModel, + new List() + { + Path(1, 1) + }, + new List() + { + Path(), + Path(1) + }); - data[1] = new ObservableCollection(Enumerable.Range(0, 5)); - ValidateSelection(selectionModel, new List()); - }); + data[1] = new ObservableCollection(Enumerable.Range(0, 5)); + ValidateSelection(selectionModel, new List()); } [Fact] public void ValidateClear() { - RunOnUIThread.Execute(() => - { - var data = new ObservableCollection(Enumerable.Range(0, 10)); - var selectionModel = new SelectionModel(); - selectionModel.Source = data; - - selectionModel.Select(3); - selectionModel.Select(4); - selectionModel.Select(5); - ValidateSelection(selectionModel, - new List() - { - Path(3), - Path(4), - Path(5), - }, - new List() - { - Path() - }); + var data = new ObservableCollection(Enumerable.Range(0, 10)); + var selectionModel = new SelectionModel(); + selectionModel.Source = data; + + selectionModel.Select(3); + selectionModel.Select(4); + selectionModel.Select(5); + ValidateSelection(selectionModel, + new List() + { + Path(3), + Path(4), + Path(5), + }, + new List() + { + Path() + }); - data.Clear(); - ValidateSelection(selectionModel, new List()); - }); + data.Clear(); + ValidateSelection(selectionModel, new List()); } [Fact] public void ValidateGroupClear() { - RunOnUIThread.Execute(() => - { - var data = CreateNestedData(1 /* levels */ , 3 /* groupsAtLevel */, 3 /* countAtLeaf */); - var selectionModel = new SelectionModel(); - selectionModel.Source = data; + var data = CreateNestedData(1 /* levels */ , 3 /* groupsAtLevel */, 3 /* countAtLeaf */); + var selectionModel = new SelectionModel(); + selectionModel.Source = data; - selectionModel.Select(1, 1); - ValidateSelection(selectionModel, - new List() - { - Path(1, 1) - }, - new List() - { - Path(), - Path(1) - }); + selectionModel.Select(1, 1); + ValidateSelection(selectionModel, + new List() + { + Path(1, 1) + }, + new List() + { + Path(), + Path(1) + }); - (data[1] as IList).Clear(); - ValidateSelection(selectionModel, new List()); - }); + (data[1] as IList).Clear(); + ValidateSelection(selectionModel, new List()); } // In some cases the leaf node might get a collection change that affects an ancestors selection @@ -767,167 +716,155 @@ namespace Avalonia.Controls.UnitTests [Fact] public void ValidateEventWhenInnerNodeChangesSelectionState() { - RunOnUIThread.Execute(() => - { - bool selectionChangedRaised = false; - var data = CreateNestedData(1 /* levels */ , 3 /* groupsAtLevel */, 3 /* countAtLeaf */); - var selectionModel = new SelectionModel(); - selectionModel.Source = data; - selectionModel.SelectionChanged += (sender, args) => { selectionChangedRaised = true; }; - - selectionModel.Select(1, 0); - selectionModel.Select(1, 1); - selectionModel.Select(1, 2); - ValidateSelection(selectionModel, - new List() - { - Path(1, 0), - Path(1, 1), - Path(1, 2), - Path(1) - }, - new List() - { - Path(), - }, - 1 /* selectedInnerNodes */); - - Log.Comment("Inserting 1.0"); - selectionChangedRaised = false; - (data[1] as AvaloniaList).Insert(0, 100); - Assert.True(selectionChangedRaised, "SelectionChanged event was not raised"); - ValidateSelection(selectionModel, - new List() - { - Path(1, 1), - Path(1, 2), - Path(1, 3), - }, - new List() - { - Path(), - Path(1), - }); - - Log.Comment("Removing 1.0"); - selectionChangedRaised = false; - (data[1] as AvaloniaList).RemoveAt(0); - Assert.True(selectionChangedRaised, "SelectionChanged event was not raised"); - ValidateSelection(selectionModel, - new List() - { - Path(1, 0), - Path(1, 1), - Path(1, 2), - Path(1) - }, - new List() - { - Path(), - }, - 1 /* selectedInnerNodes */); - }); + bool selectionChangedRaised = false; + var data = CreateNestedData(1 /* levels */ , 3 /* groupsAtLevel */, 3 /* countAtLeaf */); + var selectionModel = new SelectionModel(); + selectionModel.Source = data; + selectionModel.SelectionChanged += (sender, args) => { selectionChangedRaised = true; }; + + selectionModel.Select(1, 0); + selectionModel.Select(1, 1); + selectionModel.Select(1, 2); + ValidateSelection(selectionModel, + new List() + { + Path(1, 0), + Path(1, 1), + Path(1, 2), + Path(1) + }, + new List() + { + Path(), + }, + 1 /* selectedInnerNodes */); + + _output.WriteLine("Inserting 1.0"); + selectionChangedRaised = false; + (data[1] as AvaloniaList).Insert(0, 100); + Assert.True(selectionChangedRaised, "SelectionChanged event was not raised"); + ValidateSelection(selectionModel, + new List() + { + Path(1, 1), + Path(1, 2), + Path(1, 3), + }, + new List() + { + Path(), + Path(1), + }); + + _output.WriteLine("Removing 1.0"); + selectionChangedRaised = false; + (data[1] as AvaloniaList).RemoveAt(0); + Assert.True(selectionChangedRaised, "SelectionChanged event was not raised"); + ValidateSelection(selectionModel, + new List() + { + Path(1, 0), + Path(1, 1), + Path(1, 2), + Path(1) + }, + new List() + { + Path(), + }, + 1 /* selectedInnerNodes */); } [Fact] public void ValidatePropertyChangedEventIsRaised() { - RunOnUIThread.Execute(() => + var selectionModel = new SelectionModel(); + _output.WriteLine("Set the source to 10 items"); + selectionModel.Source = Enumerable.Range(0, 10).ToList(); + + bool selectedIndexChanged = false; + bool selectedIndicesChanged = false; + bool SelectedItemChanged = false; + bool SelectedItemsChanged = false; + bool AnchorIndexChanged = false; + selectionModel.PropertyChanged += (sender, args) => { - var selectionModel = new SelectionModel(); - Log.Comment("Set the source to 10 items"); - selectionModel.Source = Enumerable.Range(0, 10).ToList(); - - bool selectedIndexChanged = false; - bool selectedIndicesChanged = false; - bool SelectedItemChanged = false; - bool SelectedItemsChanged = false; - bool AnchorIndexChanged = false; - selectionModel.PropertyChanged += (sender, args) => - { - switch (args.PropertyName) - { - case "SelectedIndex": - selectedIndexChanged = true; - break; - case "SelectedIndices": - selectedIndicesChanged = true; - break; - case "SelectedItem": - SelectedItemChanged = true; - break; - case "SelectedItems": - SelectedItemsChanged = true; - break; - case "AnchorIndex": - AnchorIndexChanged = true; - break; - - default: - throw new InvalidOperationException(); - } - }; + switch (args.PropertyName) + { + case "SelectedIndex": + selectedIndexChanged = true; + break; + case "SelectedIndices": + selectedIndicesChanged = true; + break; + case "SelectedItem": + SelectedItemChanged = true; + break; + case "SelectedItems": + SelectedItemsChanged = true; + break; + case "AnchorIndex": + AnchorIndexChanged = true; + break; + + default: + throw new InvalidOperationException(); + } + }; - Select(selectionModel, 3, true); + Select(selectionModel, 3, true); - Assert.True(selectedIndexChanged); - Assert.True(selectedIndicesChanged); - Assert.True(SelectedItemChanged); - Assert.True(SelectedItemsChanged); - Assert.True(AnchorIndexChanged); - }); + Assert.True(selectedIndexChanged); + Assert.True(selectedIndicesChanged); + Assert.True(SelectedItemChanged); + Assert.True(SelectedItemsChanged); + Assert.True(AnchorIndexChanged); } [Fact] public void CanExtendSelectionModelINPC() { - RunOnUIThread.Execute(() => + var selectionModel = new CustomSelectionModel(); + bool intPropertyChanged = false; + selectionModel.PropertyChanged += (sender, args) => { - var selectionModel = new CustomSelectionModel(); - bool intPropertyChanged = false; - selectionModel.PropertyChanged += (sender, args) => + if (args.PropertyName == "IntProperty") { - if (args.PropertyName == "IntProperty") - { - intPropertyChanged = true; - } - }; + intPropertyChanged = true; + } + }; - selectionModel.IntProperty = 5; - Assert.True(intPropertyChanged); - }); + selectionModel.IntProperty = 5; + Assert.True(intPropertyChanged); } [Fact] public void SelectRangeRegressionTest() { - RunOnUIThread.Execute(() => + var selectionModel = new SelectionModel() { - var selectionModel = new SelectionModel() - { - Source = CreateNestedData(1, 2, 3) - }; + Source = CreateNestedData(1, 2, 3) + }; - // length of start smaller than end used to cause an out of range error. - selectionModel.SelectRange(IndexPath.CreateFrom(0), IndexPath.CreateFrom(1, 1)); - - ValidateSelection(selectionModel, - new List() - { - Path(0, 0), - Path(0, 1), - Path(0, 2), - Path(0), - Path(1, 0), - Path(1, 1) - }, - new List() - { - Path(), - Path(1) - }, - 1 /* selectedInnerNodes */); - }); + // length of start smaller than end used to cause an out of range error. + selectionModel.SelectRange(IndexPath.CreateFrom(0), IndexPath.CreateFrom(1, 1)); + + ValidateSelection(selectionModel, + new List() + { + Path(0, 0), + Path(0, 1), + Path(0, 2), + Path(0), + Path(1, 0), + Path(1, 1) + }, + new List() + { + Path(), + Path(1) + }, + 1 /* selectedInnerNodes */); } [Fact] @@ -1011,7 +948,7 @@ namespace Avalonia.Controls.UnitTests private void Select(SelectionModel manager, int index, bool select) { - Log.Comment((select ? "Selecting " : "DeSelecting ") + index); + _output.WriteLine((select ? "Selecting " : "DeSelecting ") + index); if (select) { manager.Select(index); @@ -1024,7 +961,7 @@ namespace Avalonia.Controls.UnitTests private void Select(SelectionModel manager, int groupIndex, int itemIndex, bool select) { - Log.Comment((select ? "Selecting " : "DeSelecting ") + groupIndex + "." + itemIndex); + _output.WriteLine((select ? "Selecting " : "DeSelecting ") + groupIndex + "." + itemIndex); if (select) { manager.Select(groupIndex, itemIndex); @@ -1037,7 +974,7 @@ namespace Avalonia.Controls.UnitTests private void Select(SelectionModel manager, IndexPath index, bool select) { - Log.Comment((select ? "Selecting " : "DeSelecting ") + index); + _output.WriteLine((select ? "Selecting " : "DeSelecting ") + index); if (select) { manager.SelectAt(index); @@ -1050,7 +987,7 @@ namespace Avalonia.Controls.UnitTests private void SelectRangeFromAnchor(SelectionModel manager, int index, bool select) { - Log.Comment("SelectRangeFromAnchor " + index + " select: " + select.ToString()); + _output.WriteLine("SelectRangeFromAnchor " + index + " select: " + select.ToString()); if (select) { manager.SelectRangeFromAnchor(index); @@ -1063,7 +1000,7 @@ namespace Avalonia.Controls.UnitTests private void SelectRangeFromAnchor(SelectionModel manager, int groupIndex, int itemIndex, bool select) { - Log.Comment("SelectRangeFromAnchor " + groupIndex + "." + itemIndex + " select:" + select.ToString()); + _output.WriteLine("SelectRangeFromAnchor " + groupIndex + "." + itemIndex + " select:" + select.ToString()); if (select) { manager.SelectRangeFromAnchor(groupIndex, itemIndex); @@ -1076,7 +1013,7 @@ namespace Avalonia.Controls.UnitTests private void SelectRangeFromAnchor(SelectionModel manager, IndexPath index, bool select) { - Log.Comment("SelectRangeFromAnchor " + index + " select: " + select.ToString()); + _output.WriteLine("SelectRangeFromAnchor " + index + " select: " + select.ToString()); if (select) { manager.SelectRangeFromAnchorTo(index); @@ -1089,25 +1026,25 @@ namespace Avalonia.Controls.UnitTests private void ClearSelection(SelectionModel manager) { - Log.Comment("ClearSelection"); + _output.WriteLine("ClearSelection"); manager.ClearSelection(); } private void SetAnchorIndex(SelectionModel manager, int index) { - Log.Comment("SetAnchorIndex " + index); + _output.WriteLine("SetAnchorIndex " + index); manager.SetAnchorIndex(index); } private void SetAnchorIndex(SelectionModel manager, int groupIndex, int itemIndex) { - Log.Comment("SetAnchor " + groupIndex + "." + itemIndex); + _output.WriteLine("SetAnchor " + groupIndex + "." + itemIndex); manager.SetAnchorIndex(groupIndex, itemIndex); } private void SetAnchorIndex(SelectionModel manager, IndexPath index) { - Log.Comment("SetAnchor " + index); + _output.WriteLine("SetAnchor " + index); manager.AnchorIndex = index; } @@ -1117,18 +1054,18 @@ namespace Avalonia.Controls.UnitTests List expectedPartialSelected = null, int selectedInnerNodes = 0) { - Log.Comment("Validating Selection..."); + _output.WriteLine("Validating Selection..."); - Log.Comment("Selection contains indices:"); + _output.WriteLine("Selection contains indices:"); foreach (var index in selectionModel.SelectedIndices) { - Log.Comment(" " + index.ToString()); + _output.WriteLine(" " + index.ToString()); } - Log.Comment("Selection contains items:"); + _output.WriteLine("Selection contains items:"); foreach (var item in selectionModel.SelectedItems) { - Log.Comment(" " + item.ToString()); + _output.WriteLine(" " + item.ToString()); } if (selectionModel.Source != null) @@ -1149,7 +1086,7 @@ namespace Avalonia.Controls.UnitTests { if (isSelected == null) { - Log.Comment("*************" + index + " is null"); + _output.WriteLine("*************" + index + " is null"); Assert.True(false, "Expected false but got null");; } else @@ -1168,7 +1105,7 @@ namespace Avalonia.Controls.UnitTests } if (expectedSelected.Count > 0) { - Log.Comment("SelectedIndex is " + selectionModel.SelectedIndex); + _output.WriteLine("SelectedIndex is " + selectionModel.SelectedIndex); Assert.Equal(0, selectionModel.SelectedIndex.CompareTo(expectedSelected[0])); if (selectionModel.Source != null) { @@ -1181,7 +1118,7 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(expectedSelected.Count - selectedInnerNodes, indicesCount); } - Log.Comment("Validating Selection... done"); + _output.WriteLine("Validating Selection... done"); } private object GetData(SelectionModel selectionModel, IndexPath indexPath) @@ -1225,12 +1162,12 @@ namespace Avalonia.Controls.UnitTests } }); - Log.Comment("All Paths in source.."); + _output.WriteLine("All Paths in source.."); foreach (var path in paths) { - Log.Comment(path.ToString()); + _output.WriteLine(path.ToString()); } - Log.Comment("done."); + _output.WriteLine("done."); return paths; } @@ -1328,18 +1265,6 @@ namespace Avalonia.Controls.UnitTests public IndexPath Path { get; set; } } - - private static class RunOnUIThread - { - public static void Execute(Action a) => a(); - } - - private class LogWrapper - { - private readonly ITestOutputHelper _output; - public LogWrapper(ITestOutputHelper output) => _output = output; - public void Comment(string s) => _output.WriteLine(s); - } } class CustomSelectionModel : SelectionModel From 95190e168b49f2eaf2ad5471e1931f8c0b45420f Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 15 Feb 2020 10:02:15 +0100 Subject: [PATCH 054/663] Fixed SelectionModelChildrenRequestedEventArgs returning incorrect so... MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …urce index fixed sourceindex childrenrequested args Port of https://github.com/microsoft/microsoft-ui-xaml/commit/ee03bace144e3a37650df0536955f5f658dd2aad --- src/Avalonia.Controls/SelectionModel.cs | 8 ++--- ...electionModelChildrenRequestedEventArgs.cs | 35 +++++++++++++------ src/Avalonia.Controls/SelectionNode.cs | 3 +- .../SelectionModelTests.cs | 20 +++++------ 4 files changed, 40 insertions(+), 26 deletions(-) diff --git a/src/Avalonia.Controls/SelectionModel.cs b/src/Avalonia.Controls/SelectionModel.cs index 325e2e8848..d55faf53f3 100644 --- a/src/Avalonia.Controls/SelectionModel.cs +++ b/src/Avalonia.Controls/SelectionModel.cs @@ -459,7 +459,7 @@ namespace Avalonia.Controls OnSelectionChanged(); } - internal object? ResolvePath(object data, SelectionNode sourceNode) + internal object? ResolvePath(object data, IndexPath dataIndexPath) { object? resolved = null; @@ -468,18 +468,18 @@ namespace Avalonia.Controls { if (_childrenRequestedEventArgs == null) { - _childrenRequestedEventArgs = new SelectionModelChildrenRequestedEventArgs(data, sourceNode); + _childrenRequestedEventArgs = new SelectionModelChildrenRequestedEventArgs(data, dataIndexPath, false); } else { - _childrenRequestedEventArgs.Initialize(data, sourceNode); + _childrenRequestedEventArgs.Initialize(data, dataIndexPath, 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, null); + _childrenRequestedEventArgs.Initialize(null, default, true); } else { diff --git a/src/Avalonia.Controls/SelectionModelChildrenRequestedEventArgs.cs b/src/Avalonia.Controls/SelectionModelChildrenRequestedEventArgs.cs index aa5a9b5cad..823e7b9447 100644 --- a/src/Avalonia.Controls/SelectionModelChildrenRequestedEventArgs.cs +++ b/src/Avalonia.Controls/SelectionModelChildrenRequestedEventArgs.cs @@ -12,12 +12,16 @@ namespace Avalonia.Controls public class SelectionModelChildrenRequestedEventArgs : EventArgs { private object? _source; - private SelectionNode? _sourceNode; - - internal SelectionModelChildrenRequestedEventArgs(object source, SelectionNode sourceNode) + private IndexPath _sourceIndexPath; + private bool _throwOnAccess; + + internal SelectionModelChildrenRequestedEventArgs( + object source, + IndexPath sourceIndexPath, + bool throwOnAccess) { - _source = source; - _sourceNode = sourceNode; + source = source ?? throw new ArgumentNullException(nameof(source)); + Initialize(source, sourceIndexPath, throwOnAccess); } public object? Children { get; set; } @@ -26,12 +30,12 @@ namespace Avalonia.Controls { get { - if (_source == null) + if (_throwOnAccess) { throw new ObjectDisposedException(nameof(SelectionModelChildrenRequestedEventArgs)); } - return _source; + return _source!; } } @@ -39,19 +43,28 @@ namespace Avalonia.Controls { get { - if (_sourceNode == null) + if (_throwOnAccess) { throw new ObjectDisposedException(nameof(SelectionModelChildrenRequestedEventArgs)); } - return _sourceNode.IndexPath; + return _sourceIndexPath; } } - internal void Initialize(object? source, SelectionNode? sourceNode) + internal void Initialize( + object? source, + IndexPath sourceIndexPath, + bool throwOnAccess) { + if (!throwOnAccess && source == null) + { + throw new ArgumentNullException(nameof(source)); + } + _source = source; - _sourceNode = sourceNode; + _sourceIndexPath = sourceIndexPath; + _throwOnAccess = throwOnAccess; } } } diff --git a/src/Avalonia.Controls/SelectionNode.cs b/src/Avalonia.Controls/SelectionNode.cs index 5bfa76f83f..bf21a5f2d1 100644 --- a/src/Avalonia.Controls/SelectionNode.cs +++ b/src/Avalonia.Controls/SelectionNode.cs @@ -128,7 +128,8 @@ namespace Avalonia.Controls if (childData != null) { - var resolvedChild = _manager.ResolvePath(childData, this); + var childDataIndexPath = IndexPath.CloneWithChildIndex(index); + var resolvedChild = _manager.ResolvePath(childData, childDataIndexPath); if (resolvedChild != null) { diff --git a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs index 31e07d834a..3233059718 100644 --- a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs @@ -277,21 +277,21 @@ namespace Avalonia.Controls.UnitTests // Validate SourceIndices. var expectedSourceIndices = new List() { - Path(), Path(1), Path(1, 0), - Path(1), - Path(1, 0, 1), - Path(1, 0, 1), - Path(1, 0, 1), Path(1, 0, 1), Path(1, 1), - Path(1, 1), - Path(1, 1, 0), - Path(1, 1, 0), - Path(1, 1, 0), + Path(1, 0, 1, 3), + Path(1, 0, 1, 2), + Path(1, 0, 1, 1), + Path(1, 0, 1, 0), + Path(1, 1, 1), Path(1, 1, 0), - Path(1, 1, 1) + Path(1, 1, 0, 3), + Path(1, 1, 0, 2), + Path(1, 1, 0, 1), + Path(1, 1, 0, 0), + Path(1, 1, 1, 0) }; Assert.Equal(expectedSourceIndices.Count, sourcePaths.Count); From 96fbd6c531313dc0ab9fc3eeb7265a693b58e75f Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 15 Feb 2020 23:55:21 +0100 Subject: [PATCH 055/663] Don't only select leaf nodes from SelectRange. Related issues: https://github.com/microsoft/microsoft-ui-xaml/issues/1969 https://github.com/microsoft/microsoft-ui-xaml/issues/1984 --- src/Avalonia.Controls/SelectionModel.cs | 6 +- .../SelectionModelTests.cs | 81 ++++++++++++++----- 2 files changed, 60 insertions(+), 27 deletions(-) diff --git a/src/Avalonia.Controls/SelectionModel.cs b/src/Avalonia.Controls/SelectionModel.cs index a2b080efc4..d41593b4eb 100644 --- a/src/Avalonia.Controls/SelectionModel.cs +++ b/src/Avalonia.Controls/SelectionModel.cs @@ -759,11 +759,7 @@ namespace Avalonia.Controls winrtEnd, info => { - if (info.Node.DataCount == 0) - { - // Select only leaf nodes - info.ParentNode!.Select(info.Path.GetAt(info.Path.GetSize() - 1), select); - } + info.ParentNode!.Select(info.Path.GetAt(info.Path.GetSize() - 1), select); }); } diff --git a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs index bbba360a44..0769df5b48 100644 --- a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs @@ -305,16 +305,19 @@ namespace Avalonia.Controls.UnitTests ValidateSelection(selectionModel, new List() { + Path(1, 0), + Path(1, 1), + Path(1, 0, 1), Path(1, 0, 1, 0), Path(1, 0, 1, 1), Path(1, 0, 1, 2), Path(1, 0, 1, 3), - Path(1, 0, 1), + Path(1, 1, 0), + Path(1, 1, 1), Path(1, 1, 0, 0), Path(1, 1, 0, 1), Path(1, 1, 0, 2), Path(1, 1, 0, 3), - Path(1, 1, 0), Path(1, 1, 1, 0), }, new List() @@ -324,8 +327,7 @@ namespace Avalonia.Controls.UnitTests Path(1, 0), Path(1, 1), Path(1, 1, 1), - }, - 2 /* selectedInnerNodes */); + }); ClearSelection(selectionModel); ValidateSelection(selectionModel, new List() { }); @@ -337,16 +339,20 @@ namespace Avalonia.Controls.UnitTests ValidateSelection(selectionModel, new List() { - Path(0, 0, 0, 2), - Path(0, 0, 0, 3), - Path(0, 0, 1, 0), - Path(0, 0, 1, 1), - Path(0, 0, 1, 2), - Path(0, 0, 1, 3), - Path(0, 0, 1), - Path(0, 1, 0, 0), - Path(0, 1, 0, 1), - Path(0, 1, 0, 2), + Path(0, 0), + Path(0, 1), + Path(0, 0, 0), + Path(0, 0, 1), + Path(0, 0, 0, 2), + Path(0, 0, 0, 3), + Path(0, 0, 1, 0), + Path(0, 0, 1, 1), + Path(0, 0, 1, 2), + Path(0, 0, 1, 3), + Path(0, 1, 0), + Path(0, 1, 0, 0), + Path(0, 1, 0, 1), + Path(0, 1, 0, 2), }, new List() { @@ -356,8 +362,7 @@ namespace Avalonia.Controls.UnitTests Path(0, 0, 0), Path(0, 1), Path(0, 1, 0), - }, - 1 /* selectedInnerNodes */); + }); startPath = Path(0, 1, 0, 2); SetAnchorIndex(selectionModel, startPath); @@ -853,10 +858,11 @@ namespace Avalonia.Controls.UnitTests ValidateSelection(selectionModel, new List() { + Path(0), + Path(1), Path(0, 0), Path(0, 1), Path(0, 2), - Path(0), Path(1, 0), Path(1, 1) }, @@ -864,8 +870,7 @@ namespace Avalonia.Controls.UnitTests { Path(), Path(1) - }, - 1 /* selectedInnerNodes */); + }); } [Fact] @@ -1271,7 +1276,7 @@ namespace Avalonia.Controls.UnitTests target.SelectionChanged += (s, e) => { Assert.Empty(e.DeselectedIndices); - Assert.Equal(new object[] { 0, 1, 2 }, e.DeselectedItems); + Assert.Equal(new object[] { new AvaloniaList { 0, 1, 2 }, 0, 1, 2 }, e.DeselectedItems); Assert.Empty(e.SelectedIndices); Assert.Empty(e.SelectedItems); ++raised; @@ -1371,6 +1376,38 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(0, GetSubscriberCount(toRemove)); } + [Fact] + public void SelectRange_Behaves_The_Same_As_Multiple_Selects() + { + var data = new[] { 1, 2, 3 }; + var target = new SelectionModel { Source = data }; + + target.Select(1); + + Assert.Equal(new[] { IndexPath.CreateFrom(1) }, target.SelectedIndices); + + target.ClearSelection(); + target.SelectRange(new IndexPath(1), new IndexPath(1)); + + Assert.Equal(new[] { IndexPath.CreateFrom(1) }, target.SelectedIndices); + } + + [Fact] + public void SelectRange_Behaves_The_Same_As_Multiple_Selects_Nested() + { + var data = CreateNestedData(3, 2, 2); + var target = new SelectionModel { Source = data }; + + target.Select(1); + + Assert.Equal(new[] { IndexPath.CreateFrom(1) }, target.SelectedIndices); + + target.ClearSelection(); + target.SelectRange(new IndexPath(1), new IndexPath(1)); + + Assert.Equal(new[] { IndexPath.CreateFrom(1) }, target.SelectedIndices); + } + [Fact] public void Should_Not_Treat_Strings_As_Nested_Selections() { @@ -2005,7 +2042,7 @@ namespace Avalonia.Controls.UnitTests foreach (var index in allIndices) { bool? isSelected = selectionModel.IsSelectedAt(index); - if (Contains(expectedSelected, index)) + if (Contains(expectedSelected, index) && !Contains(expectedPartialSelected, index)) { Assert.True(isSelected.Value, index + " is Selected"); } @@ -2037,7 +2074,7 @@ namespace Avalonia.Controls.UnitTests if (expectedSelected.Count > 0) { _output.WriteLine("SelectedIndex is " + selectionModel.SelectedIndex); - Assert.Equal(0, selectionModel.SelectedIndex.CompareTo(expectedSelected[0])); + Assert.Equal(expectedSelected[0], selectionModel.SelectedIndex); if (selectionModel.Source != null) { Assert.Equal(selectionModel.SelectedItem, GetData(selectionModel, expectedSelected[0])); From f103df6a9a8dba4ae5880b1abee937a76b30b37c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 16 Feb 2020 00:46:18 +0100 Subject: [PATCH 056/663] Changed IsSelected semantics. Move old `IsSelected` semantics to `IsSelectedWithPartial` and add a new `IsSelected` method which checks for direct selection. --- src/Avalonia.Controls/ISelectionModel.cs | 9 +++-- src/Avalonia.Controls/SelectionModel.cs | 34 ++++++++++++++++--- .../SelectionModelTests.cs | 4 +-- 3 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/Avalonia.Controls/ISelectionModel.cs b/src/Avalonia.Controls/ISelectionModel.cs index f7283d9d79..34fe626696 100644 --- a/src/Avalonia.Controls/ISelectionModel.cs +++ b/src/Avalonia.Controls/ISelectionModel.cs @@ -32,9 +32,12 @@ namespace Avalonia.Controls void DeselectRangeFromAnchor(int endGroupIndex, int endItemIndex); void DeselectRangeFromAnchorTo(IndexPath index); void Dispose(); - bool? IsSelected(int index); - bool? IsSelected(int groupIndex, int itemIndex); - bool? IsSelectedAt(IndexPath index); + bool IsSelected(int index); + bool IsSelected(int grouIndex, int itemIndex); + public bool IsSelectedAt(IndexPath index); + bool? IsSelectedWithPartial(int index); + bool? IsSelectedWithPartial(int groupIndex, int itemIndex); + bool? IsSelectedWithPartialAt(IndexPath index); void Select(int index); void Select(int groupIndex, int itemIndex); void SelectAll(); diff --git a/src/Avalonia.Controls/SelectionModel.cs b/src/Avalonia.Controls/SelectionModel.cs index d41593b4eb..0531174454 100644 --- a/src/Avalonia.Controls/SelectionModel.cs +++ b/src/Avalonia.Controls/SelectionModel.cs @@ -174,7 +174,7 @@ namespace Avalonia.Controls } set { - var isSelected = IsSelectedAt(value); + var isSelected = IsSelectedWithPartialAt(value); if (!isSelected.HasValue || !isSelected.Value) { @@ -393,7 +393,33 @@ namespace Avalonia.Controls ApplyAutoSelect(); } - public bool? IsSelected(int index) + 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, realizeChild: false); + + if (node == null) + { + return false; + } + } + + return node.IsSelected(index.GetAt(index.GetSize() - 1)); + } + + public bool? IsSelectedWithPartial(int index) { if (index < 0) { @@ -404,7 +430,7 @@ namespace Avalonia.Controls return isSelected; } - public bool? IsSelected(int groupIndex, int itemIndex) + public bool? IsSelectedWithPartial(int groupIndex, int itemIndex) { if (groupIndex < 0) { @@ -427,7 +453,7 @@ namespace Avalonia.Controls return isSelected; } - public bool? IsSelectedAt(IndexPath index) + public bool? IsSelectedWithPartialAt(IndexPath index) { var path = index; var isRealized = true; diff --git a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs index 0769df5b48..9c3b0c17b6 100644 --- a/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs @@ -2041,7 +2041,7 @@ namespace Avalonia.Controls.UnitTests List allIndices = GetIndexPathsInSource(selectionModel.Source); foreach (var index in allIndices) { - bool? isSelected = selectionModel.IsSelectedAt(index); + bool? isSelected = selectionModel.IsSelectedWithPartialAt(index); if (Contains(expectedSelected, index) && !Contains(expectedPartialSelected, index)) { Assert.True(isSelected.Value, index + " is Selected"); @@ -2068,7 +2068,7 @@ namespace Avalonia.Controls.UnitTests { foreach (var index in expectedSelected) { - Assert.True(selectionModel.IsSelectedAt(index).Value, index + " is Selected"); + Assert.True(selectionModel.IsSelectedWithPartialAt(index), index + " is Selected"); } } if (expectedSelected.Count > 0) From 0c894e20d3ccba4dac511195a51b36e00612cd69 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 17 Feb 2020 11:05:02 +0100 Subject: [PATCH 057/663] Added SelectedItemsSync. To sync between an `ISelectionModel` and a `SelectedItems` collection. --- .../Utils/SelectedItemsSync.cs | 226 ++++++++++++++++++ .../Utils/SelectedItemsSyncTests.cs | 210 ++++++++++++++++ 2 files changed, 436 insertions(+) create mode 100644 src/Avalonia.Controls/Utils/SelectedItemsSync.cs create mode 100644 tests/Avalonia.Controls.UnitTests/Utils/SelectedItemsSyncTests.cs diff --git a/src/Avalonia.Controls/Utils/SelectedItemsSync.cs b/src/Avalonia.Controls/Utils/SelectedItemsSync.cs new file mode 100644 index 0000000000..3d6c88cd99 --- /dev/null +++ b/src/Avalonia.Controls/Utils/SelectedItemsSync.cs @@ -0,0 +1,226 @@ +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Linq; +using Avalonia.Collections; + +#nullable enable + +namespace Avalonia.Controls.Utils +{ + /// + /// Synchronizes an with a list of SelectedItems. + /// + internal class SelectedItemsSync + { + private IList? _items; + private bool _updatingItems; + private bool _updatingModel; + + public SelectedItemsSync(ISelectionModel model) + { + Model = model; + } + + public ISelectionModel Model { get; private set; } + + public IList GetOrCreateItems() + { + if (_items == null) + { + var items = new AvaloniaList(Model.SelectedItems); + items.CollectionChanged += ItemsCollectionChanged; + Model.SelectionChanged += SelectionModelSelectionChanged; + _items = items; + } + + return _items; + } + + public void SetItems(IList items) + { + items = items ?? throw new ArgumentNullException(nameof(items)); + + if (items.IsFixedSize) + { + throw new NotSupportedException( + "Cannot assign fixed size selection to SelectedItems."); + } + + if (_items is INotifyCollectionChanged incc) + { + incc.CollectionChanged -= ItemsCollectionChanged; + } + + if (_items == null) + { + Model.SelectionChanged += SelectionModelSelectionChanged; + } + + try + { + _updatingModel = true; + _items = items; + + using (Model.Update()) + { + Model.ClearSelection(); + Add(items); + } + + if (_items is INotifyCollectionChanged incc2) + { + incc2.CollectionChanged += ItemsCollectionChanged; + } + } + finally + { + _updatingModel = false; + } + } + + public void SetModel(ISelectionModel model) + { + model = model ?? throw new ArgumentNullException(nameof(model)); + + if (_items != null) + { + Model.SelectionChanged -= SelectionModelSelectionChanged; + Model = model; + Model.SelectionChanged += SelectionModelSelectionChanged; + + try + { + _updatingItems = true; + _items.Clear(); + + foreach (var i in model.SelectedItems) + { + _items.Add(i); + } + } + finally + { + _updatingItems = false; + } + } + } + + private void ItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + if (_updatingItems) + { + return; + } + + if (_items == null) + { + throw new AvaloniaInternalException("CollectionChanged raised but we don't have items."); + } + + void Remove() + { + foreach (var i in e.OldItems) + { + var index = IndexOf(Model.Source, i); + + if (index != -1) + { + Model.Deselect(index); + } + } + } + + try + { + using var operation = Model.Update(); + + _updatingModel = true; + + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + Add(e.NewItems); + break; + case NotifyCollectionChangedAction.Remove: + Remove(); + break; + case NotifyCollectionChangedAction.Replace: + Remove(); + Add(e.NewItems); + break; + case NotifyCollectionChangedAction.Reset: + Model.ClearSelection(); + Add(_items); + break; + } + } + finally + { + _updatingModel = false; + } + } + + private void Add(IList newItems) + { + foreach (var i in newItems) + { + var index = IndexOf(Model.Source, i); + + if (index != -1) + { + Model.Select(index); + } + } + } + + private void SelectionModelSelectionChanged(object sender, SelectionModelSelectionChangedEventArgs e) + { + if (_updatingModel) + { + return; + } + + if (_items == null) + { + throw new AvaloniaInternalException("SelectionModelChanged raised but we don't have items."); + } + + try + { + var deselected = e.DeselectedItems.ToList(); + var selected = e.SelectedItems.ToList(); + + _updatingItems = true; + + foreach (var i in deselected) + { + _items.Remove(i); + } + + foreach (var i in selected) + { + _items.Add(i); + } + } + finally + { + _updatingItems = false; + } + } + + private static int IndexOf(object source, object item) + { + if (source is IList l) + { + return l.IndexOf(item); + } + else if (source is ItemsSourceView v) + { + return v.IndexOf(item); + } + + return -1; + } + } +} diff --git a/tests/Avalonia.Controls.UnitTests/Utils/SelectedItemsSyncTests.cs b/tests/Avalonia.Controls.UnitTests/Utils/SelectedItemsSyncTests.cs new file mode 100644 index 0000000000..917f422557 --- /dev/null +++ b/tests/Avalonia.Controls.UnitTests/Utils/SelectedItemsSyncTests.cs @@ -0,0 +1,210 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Avalonia.Collections; +using Avalonia.Controls.Utils; +using Xunit; + +namespace Avalonia.Controls.UnitTests.Utils +{ + public class SelectedItemsSyncTests + { + [Fact] + public void Initial_Items_Are_From_Model() + { + var target = CreateTarget(); + var items = target.GetOrCreateItems(); + + Assert.Equal(new[] { "bar", "baz" }, items); + } + + [Fact] + public void Selecting_On_Model_Adds_Item() + { + var target = CreateTarget(); + var items = target.GetOrCreateItems(); + + target.Model.Select(0); + + Assert.Equal(new[] { "bar", "baz", "foo" }, items); + } + + [Fact] + public void Selecting_Duplicate_On_Model_Adds_Item() + { + var target = CreateTarget(new[] { "foo", "bar", "baz", "foo", "bar", "baz" }); + var items = target.GetOrCreateItems(); + + target.Model.Select(4); + + Assert.Equal(new[] { "bar", "baz", "bar" }, items); + } + + [Fact] + public void Deselecting_On_Model_Removes_Item() + { + var target = CreateTarget(); + var items = target.GetOrCreateItems(); + + target.Model.Deselect(1); + + Assert.Equal(new[] { "baz" }, items); + } + + [Fact] + public void Deselecting_Duplicate_On_Model_Removes_Item() + { + var target = CreateTarget(new[] { "foo", "bar", "baz", "foo", "bar", "baz" }); + var items = target.GetOrCreateItems(); + + target.Model.Select(4); + target.Model.Deselect(4); + + Assert.Equal(new[] { "baz", "bar" }, items); + } + + [Fact] + public void Reassigning_Model_Resets_Items() + { + var target = CreateTarget(); + var items = target.GetOrCreateItems(); + + var newModel = new SelectionModel { Source = target.Model.Source }; + newModel.Select(0); + newModel.Select(1); + + target.SetModel(newModel); + + Assert.Equal(new[] { "foo", "bar" }, items); + } + + [Fact] + public void Reassigning_Model_Tracks_New_Model() + { + var target = CreateTarget(); + var items = target.GetOrCreateItems(); + + var newModel = new SelectionModel { Source = target.Model.Source }; + target.SetModel(newModel); + + newModel.Select(0); + newModel.Select(1); + + Assert.Equal(new[] { "foo", "bar" }, items); + } + + [Fact] + public void Adding_To_Items_Selects_On_Model() + { + var target = CreateTarget(); + var items = target.GetOrCreateItems(); + + items.Add("foo"); + + Assert.Equal( + new[] { new IndexPath(0), new IndexPath(1), new IndexPath(2) }, + target.Model.SelectedIndices); + Assert.Equal(new[] { "bar", "baz", "foo" }, items); + } + + [Fact] + public void Removing_From_Items_Deselects_On_Model() + { + var target = CreateTarget(); + var items = target.GetOrCreateItems(); + + items.Remove("baz"); + + Assert.Equal(new[] { new IndexPath(1) }, target.Model.SelectedIndices); + Assert.Equal(new[] { "bar" }, items); + } + + [Fact] + public void Replacing_Item_Updates_Model() + { + var target = CreateTarget(); + var items = target.GetOrCreateItems(); + + items[0] = "foo"; + + Assert.Equal(new[] { new IndexPath(0), new IndexPath(2) }, target.Model.SelectedIndices); + Assert.Equal(new[] { "foo", "baz" }, items); + } + + [Fact] + public void Clearing_Items_Updates_Model() + { + var target = CreateTarget(); + var items = target.GetOrCreateItems(); + + items.Clear(); + + Assert.Empty(target.Model.SelectedIndices); + } + + [Fact] + public void Setting_Items_Updates_Model() + { + var target = CreateTarget(); + var oldItems = target.GetOrCreateItems(); + + var newItems = new AvaloniaList { "foo", "baz" }; + target.SetItems(newItems); + + Assert.Equal(new[] { new IndexPath(0), new IndexPath(2) }, target.Model.SelectedIndices); + Assert.Same(newItems, target.GetOrCreateItems()); + Assert.NotSame(oldItems, target.GetOrCreateItems()); + Assert.Equal(new[] { "foo", "baz" }, newItems); + } + + [Fact] + public void Setting_Items_Subscribes_To_Model() + { + var target = CreateTarget(); + var items = new AvaloniaList { "foo", "baz" }; + + target.SetItems(items); + target.Model.Select(1); + + Assert.Equal(new[] { "foo", "baz", "bar" }, items); + } + + [Fact] + public void Handles_Null_Model_Source() + { + var model = new SelectionModel(); + model.Select(1); + + var target = new SelectedItemsSync(model); + var items = target.GetOrCreateItems(); + + Assert.Empty(items); + + model.Select(2); + model.Source = new[] { "foo", "bar", "baz" }; + + Assert.Equal(new[] { "bar", "baz" }, items); + } + + [Fact] + public void Does_Not_Accept_Fixed_Size_Items() + { + var target = CreateTarget(); + + Assert.Throws(() => + target.SetItems(new[] { "foo", "bar", "baz" })); + } + + private static SelectedItemsSync CreateTarget( + IEnumerable items = null) + { + items ??= new[] { "foo", "bar", "baz" }; + + var model = new SelectionModel { Source = items }; + model.SelectRange(new IndexPath(1), new IndexPath(2)); + + var target = new SelectedItemsSync(model); + return target; + } + } +} From 60011155734cabd13d9aaf2736b39f805ea4f262 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 17 Feb 2020 12:01:37 +0100 Subject: [PATCH 058/663] Use SelectionModel in SelectingItemsControl. --- samples/BindingDemo/MainWindow.xaml | 4 +- .../ViewModels/MainWindowViewModel.cs | 5 +- samples/ControlCatalog/Pages/ListBoxPage.xaml | 2 +- samples/VirtualizationDemo/MainWindow.xaml | 2 +- .../ViewModels/MainWindowViewModel.cs | 14 +- src/Avalonia.Controls/ComboBox.cs | 4 +- src/Avalonia.Controls/ListBox.cs | 19 +- .../Primitives/SelectingItemsControl.cs | 834 +++++------------- src/Avalonia.Controls/SelectionModel.cs | 3 +- .../Utils/SelectedItemsSync.cs | 5 +- .../CarouselTests.cs | 6 +- .../Primitives/SelectingItemsControlTests.cs | 31 +- .../SelectingItemsControlTests_AutoSelect.cs | 4 +- .../SelectingItemsControlTests_Multiple.cs | 211 ++++- .../Primitives/TabStripTests.cs | 9 +- .../TabControlTests.cs | 6 +- .../Utils/SelectedItemsSyncTests.cs | 13 + 17 files changed, 509 insertions(+), 663 deletions(-) diff --git a/samples/BindingDemo/MainWindow.xaml b/samples/BindingDemo/MainWindow.xaml index b57a9a0a9e..26a62ebca6 100644 --- a/samples/BindingDemo/MainWindow.xaml +++ b/samples/BindingDemo/MainWindow.xaml @@ -74,11 +74,11 @@ - + - + diff --git a/samples/BindingDemo/ViewModels/MainWindowViewModel.cs b/samples/BindingDemo/ViewModels/MainWindowViewModel.cs index 22d01e0765..a66038ff3e 100644 --- a/samples/BindingDemo/ViewModels/MainWindowViewModel.cs +++ b/samples/BindingDemo/ViewModels/MainWindowViewModel.cs @@ -6,6 +6,7 @@ using System.Reactive.Linq; using System.Threading.Tasks; using System.Threading; using ReactiveUI; +using Avalonia.Controls; namespace BindingDemo.ViewModels { @@ -27,7 +28,7 @@ namespace BindingDemo.ViewModels Detail = "Item " + x + " details", })); - SelectedItems = new ObservableCollection(); + Selection = new SelectionModel(); ShuffleItems = ReactiveCommand.Create(() => { @@ -56,7 +57,7 @@ namespace BindingDemo.ViewModels } public ObservableCollection Items { get; } - public ObservableCollection SelectedItems { get; } + public SelectionModel Selection { get; } public ReactiveCommand ShuffleItems { get; } public string BooleanString diff --git a/samples/ControlCatalog/Pages/ListBoxPage.xaml b/samples/ControlCatalog/Pages/ListBoxPage.xaml index b1b3112e60..47b4ce7151 100644 --- a/samples/ControlCatalog/Pages/ListBoxPage.xaml +++ b/samples/ControlCatalog/Pages/ListBoxPage.xaml @@ -10,7 +10,7 @@ HorizontalAlignment="Center" Spacing="16"> - + diff --git a/samples/VirtualizationDemo/MainWindow.xaml b/samples/VirtualizationDemo/MainWindow.xaml index 12137cd03d..4bd657bf93 100644 --- a/samples/VirtualizationDemo/MainWindow.xaml +++ b/samples/VirtualizationDemo/MainWindow.xaml @@ -45,7 +45,7 @@ SelectedItems { get; } - = new AvaloniaList(); + public SelectionModel Selection { get; } = new SelectionModel(); public AvaloniaList Items { @@ -141,9 +140,9 @@ namespace VirtualizationDemo.ViewModels { var index = Items.Count; - if (SelectedItems.Count > 0) + if (Selection.SelectedIndices.Count > 0) { - index = Items.IndexOf(SelectedItems[0]); + index = Selection.SelectedIndex.GetAt(0); } Items.Insert(index, new ItemViewModel(_newItemIndex++, NewItemString)); @@ -151,9 +150,9 @@ namespace VirtualizationDemo.ViewModels private void Remove() { - if (SelectedItems.Count > 0) + if (Selection.SelectedItems.Count > 0) { - Items.RemoveAll(SelectedItems); + Items.RemoveAll(Selection.SelectedItems.Cast().ToList()); } } @@ -167,8 +166,7 @@ namespace VirtualizationDemo.ViewModels private void SelectItem(int index) { - SelectedItems.Clear(); - SelectedItems.Add(Items[index]); + Selection.SelectedIndex = new IndexPath(index); } } } diff --git a/src/Avalonia.Controls/ComboBox.cs b/src/Avalonia.Controls/ComboBox.cs index c2cf20b32d..0722802962 100644 --- a/src/Avalonia.Controls/ComboBox.cs +++ b/src/Avalonia.Controls/ComboBox.cs @@ -289,9 +289,9 @@ namespace Avalonia.Controls { var container = ItemContainerGenerator.ContainerFromIndex(selectedIndex); - if (container == null && SelectedItems.Count > 0) + if (container == null && SelectedIndex != -1) { - ScrollIntoView(SelectedItems[0]); + ScrollIntoView(Selection.SelectedItem); container = ItemContainerGenerator.ContainerFromIndex(selectedIndex); } diff --git a/src/Avalonia.Controls/ListBox.cs b/src/Avalonia.Controls/ListBox.cs index 4966e669ed..a15aedd621 100644 --- a/src/Avalonia.Controls/ListBox.cs +++ b/src/Avalonia.Controls/ListBox.cs @@ -34,6 +34,12 @@ namespace Avalonia.Controls public static readonly new DirectProperty SelectedItemsProperty = SelectingItemsControl.SelectedItemsProperty; + /// + /// Defines the property. + /// + public static readonly new DirectProperty SelectionProperty = + SelectingItemsControl.SelectionProperty; + /// /// Defines the property. /// @@ -73,6 +79,15 @@ namespace Avalonia.Controls set => base.SelectedItems = value; } + /// + /// Gets or sets a model holding the current selection. + /// + public new ISelectionModel Selection + { + get => base.Selection; + set => base.Selection = value; + } + /// /// Gets or sets the selection mode. /// @@ -98,12 +113,12 @@ namespace Avalonia.Controls /// /// Selects all items in the . /// - public new void SelectAll() => base.SelectAll(); + public void SelectAll() => Selection.SelectAll(); /// /// Deselects all items in the . /// - public new void UnselectAll() => base.UnselectAll(); + public void UnselectAll() => Selection.ClearSelection(); /// protected override IItemContainerGenerator CreateItemContainerGenerator() diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index 6bc4e71508..b1a4379cae 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -5,15 +5,15 @@ using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; +using System.ComponentModel; using System.Diagnostics; using System.Linq; -using Avalonia.Collections; using Avalonia.Controls.Generators; +using Avalonia.Controls.Utils; using Avalonia.Data; using Avalonia.Input; using Avalonia.Input.Platform; using Avalonia.Interactivity; -using Avalonia.Logging; using Avalonia.VisualTree; namespace Avalonia.Controls.Primitives @@ -26,9 +26,9 @@ namespace Avalonia.Controls.Primitives /// provides a base class for s /// that maintain a selection (single or multiple). By default only its /// and properties are visible; the - /// current multiple selection together with the - /// properties are protected, however a derived class can expose - /// these if it wishes to support multiple selection. + /// current multiple and together with the + /// and properties are protected, however a derived class can + /// expose these if it wishes to support multiple selection. /// /// /// maintains a selection respecting the current @@ -77,6 +77,15 @@ namespace Avalonia.Controls.Primitives o => o.SelectedItems, (o, v) => o.SelectedItems = v); + /// + /// Defines the property. + /// + public static readonly DirectProperty SelectionProperty = + AvaloniaProperty.RegisterDirect( + nameof(Selection), + o => o.Selection, + (o, v) => o.Selection = v); + /// /// Defines the property. /// @@ -103,17 +112,22 @@ namespace Avalonia.Controls.Primitives RoutingStrategies.Bubble); private static readonly IList Empty = Array.Empty(); - - private readonly Selection _selection = new Selection(); + private readonly SelectedItemsSync _selectedItems; + private ISelectionModel _selection; private int _selectedIndex = -1; private object _selectedItem; - private IList _selectedItems; private bool _ignoreContainerSelectionChanged; - private bool _syncingSelectedItems; private int _updateCount; private int _updateSelectedIndex; private object _updateSelectedItem; + public SelectingItemsControl() + { + // Setting Selection to null causes a default SelectionModel to be created. + Selection = null; + _selectedItems = new SelectedItemsSync(Selection); + } + /// /// Initializes static members of the class. /// @@ -145,17 +159,15 @@ namespace Avalonia.Controls.Primitives /// public int SelectedIndex { - get - { - return _selectedIndex; - } - + get => Selection.SelectedIndex != default ? Selection.SelectedIndex.GetAt(0) : -1; set { if (_updateCount == 0) { - var effective = (value >= 0 && value < ItemCount) ? value : -1; - UpdateSelectedItem(effective); + if (value != SelectedIndex) + { + Selection.SelectedIndex = new IndexPath(value); + } } else { @@ -170,16 +182,12 @@ namespace Avalonia.Controls.Primitives /// public object SelectedItem { - get - { - return _selectedItem; - } - + get => Selection.SelectedItem; set { if (_updateCount == 0) { - UpdateSelectedItem(IndexOf(Items, value)); + SelectedIndex = IndexOf(Items, value); } else { @@ -190,32 +198,110 @@ namespace Avalonia.Controls.Primitives } /// - /// Gets the selected items. + /// Gets or sets the selected items. /// protected IList SelectedItems { - get - { - if (_selectedItems == null) - { - _selectedItems = new AvaloniaList(); - SubscribeToSelectedItems(); - } - - return _selectedItems; - } + get => _selectedItems.GetOrCreateItems(); + set => _selectedItems.SetItems(value); + } + /// + /// Gets or sets a model holding the current selection. + /// + protected ISelectionModel Selection + { + get => _selection; set { - if (value?.IsFixedSize == true || value?.IsReadOnly == true) + value ??= new SelectionModel { - throw new NotSupportedException( - "Cannot use a fixed size or read-only collection as SelectedItems."); - } + SingleSelect = !SelectionMode.HasFlagCustom(SelectionMode.Multiple), + AutoSelect = SelectionMode.HasFlagCustom(SelectionMode.AlwaysSelected), + RetainSelectionOnReset = true, + }; + + if (_selection != value) + { + if (value == null) + { + throw new ArgumentNullException(nameof(value), "Cannot set Selection to null."); + } + else if (value.Source != null && value.Source != Items) + { + throw new ArgumentException("Selection has invalid Source."); + } + + List oldSelection = null; + + if (_selection != null) + { + oldSelection = Selection.SelectedItems.ToList(); + _selection.PropertyChanged -= OnSelectionModelPropertyChanged; + _selection.SelectionChanged -= OnSelectionModelSelectionChanged; + MarkContainersUnselected(); + } + + _selection = value; - UnsubscribeFromSelectedItems(); - _selectedItems = value ?? new AvaloniaList(); - SubscribeToSelectedItems(); + if (oldSelection?.Count > 0) + { + RaiseEvent(new SelectionChangedEventArgs( + SelectionChangedEvent, + oldSelection, + Array.Empty())); + } + + if (_selection != null) + { + _selection.Source = Items; + _selection.PropertyChanged += OnSelectionModelPropertyChanged; + _selection.SelectionChanged += OnSelectionModelSelectionChanged; + + if (_selection.SingleSelect) + { + SelectionMode &= ~SelectionMode.Multiple; + } + else + { + SelectionMode |= SelectionMode.Multiple; + } + + if (_selection.AutoSelect) + { + SelectionMode |= SelectionMode.AlwaysSelected; + } + else + { + SelectionMode &= ~SelectionMode.AlwaysSelected; + } + + UpdateContainerSelection(); + + var selectedIndex = SelectedIndex; + var selectedItem = SelectedItem; + + if (_selectedIndex != selectedIndex) + { + RaisePropertyChanged(SelectedIndexProperty, _selectedIndex, selectedIndex); + _selectedIndex = selectedIndex; + } + + if (_selectedItem != selectedItem) + { + RaisePropertyChanged(SelectedItemProperty, _selectedItem, selectedItem); + _selectedItem = selectedItem; + } + + if (selectedIndex != -1) + { + RaiseEvent(new SelectionChangedEventArgs( + SelectionChangedEvent, + Array.Empty(), + Selection.SelectedItems.ToList())); + } + } + } } } @@ -285,81 +371,18 @@ namespace Avalonia.Controls.Primitives /// protected override void ItemsChanged(AvaloniaPropertyChangedEventArgs e) { - base.ItemsChanged(e); - if (_updateCount == 0) { - var newIndex = -1; - - if (SelectedIndex != -1) - { - newIndex = IndexOf((IEnumerable)e.NewValue, SelectedItem); - } - - if (AlwaysSelected && Items != null && Items.Cast().Any()) - { - newIndex = 0; - } - - SelectedIndex = newIndex; + Selection.Source = e.NewValue; } + + base.ItemsChanged(e); } /// protected override void ItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { - if (_updateCount > 0) - { - base.ItemsCollectionChanged(sender, e); - return; - } - - switch (e.Action) - { - case NotifyCollectionChangedAction.Add: - _selection.ItemsInserted(e.NewStartingIndex, e.NewItems.Count); - break; - case NotifyCollectionChangedAction.Remove: - _selection.ItemsRemoved(e.OldStartingIndex, e.OldItems.Count); - break; - } - base.ItemsCollectionChanged(sender, e); - - switch (e.Action) - { - case NotifyCollectionChangedAction.Add: - if (AlwaysSelected && SelectedIndex == -1) - { - SelectedIndex = 0; - } - else - { - UpdateSelectedItem(_selection.First(), false); - } - - break; - - case NotifyCollectionChangedAction.Remove: - UpdateSelectedItem(_selection.First(), false); - ResetSelectedItems(); - break; - - case NotifyCollectionChangedAction.Replace: - UpdateSelectedItem(SelectedIndex, false); - ResetSelectedItems(); - break; - - case NotifyCollectionChangedAction.Move: - case NotifyCollectionChangedAction.Reset: - SelectedIndex = IndexOf(Items, SelectedItem); - - if (AlwaysSelected && SelectedIndex == -1 && ItemCount > 0) - { - SelectedIndex = 0; - } - break; - } } /// @@ -367,36 +390,18 @@ namespace Avalonia.Controls.Primitives { base.OnContainersMaterialized(e); - var resetSelectedItems = false; - foreach (var container in e.Containers) { if ((container.ContainerControl as ISelectable)?.IsSelected == true) { - if (SelectionMode.HasFlag(SelectionMode.Multiple)) - { - if (_selection.Add(container.Index)) - { - resetSelectedItems = true; - } - } - else - { - SelectedIndex = container.Index; - } - + Selection.Select(container.Index); MarkContainerSelected(container.ContainerControl, true); } - else if (_selection.Contains(container.Index)) + else if (Selection.IsSelected(container.Index) == true) { MarkContainerSelected(container.ContainerControl, true); } } - - if (resetSelectedItems) - { - ResetSelectedItems(); - } } /// @@ -425,7 +430,7 @@ namespace Avalonia.Controls.Primitives { if (i.ContainerControl != null && i.Item != null) { - bool selected = _selection.Contains(i.Index); + bool selected = Selection.IsSelected(i.Index) == true; MarkContainerSelected(i.ContainerControl, selected); } } @@ -447,6 +452,18 @@ namespace Avalonia.Controls.Primitives InternalEndInit(); } + protected override void OnPropertyChanged(AvaloniaProperty property, Optional oldValue, BindingValue newValue, BindingPriority priority) + { + base.OnPropertyChanged(property, oldValue, newValue, priority); + + if (property == SelectionModeProperty) + { + var mode = newValue.GetValueOrDefault(); + Selection.SingleSelect = !mode.HasFlagCustom(SelectionMode.Multiple); + Selection.AutoSelect = mode.HasFlagCustom(SelectionMode.AlwaysSelected); + } + } + protected override void OnKeyDown(KeyEventArgs e) { base.OnKeyDown(e); @@ -461,7 +478,7 @@ namespace Avalonia.Controls.Primitives (((SelectionMode & SelectionMode.Multiple) != 0) || (SelectionMode & SelectionMode.Toggle) != 0)) { - SelectAll(); + Selection.SelectAll(); e.Handled = true; } } @@ -503,36 +520,6 @@ namespace Avalonia.Controls.Primitives return false; } - /// - /// Selects all items in the control. - /// - protected void SelectAll() - { - UpdateSelectedItems(() => - { - _selection.Clear(); - - for (var i = 0; i < ItemCount; ++i) - { - _selection.Add(i); - } - - UpdateSelectedItem(0, false); - - foreach (var container in ItemContainerGenerator.Containers) - { - MarkItemSelected(container.Index, true); - } - - ResetSelectedItems(); - }); - } - - /// - /// Deselects all items in the control. - /// - protected void UnselectAll() => UpdateSelectedItem(-1); - /// /// Updates the selection for an item based on user interaction. /// @@ -559,63 +546,35 @@ namespace Avalonia.Controls.Primitives if (rightButton) { - if (!_selection.Contains(index)) + if (Selection.IsSelected(index) == false) { - UpdateSelectedItem(index); + SelectedIndex = index; } } else if (range) { - UpdateSelectedItems(() => - { - var start = SelectedIndex != -1 ? SelectedIndex : 0; - var step = start < index ? 1 : -1; - - _selection.Clear(); + using var operation = Selection.Update(); + var anchor = Selection.AnchorIndex; - for (var i = start; i != index; i += step) - { - _selection.Add(i); - } - - _selection.Add(index); - - var first = Math.Min(start, index); - var last = Math.Max(start, index); - - foreach (var container in ItemContainerGenerator.Containers) - { - MarkItemSelected( - container.Index, - container.Index >= first && container.Index <= last); - } + if (anchor.GetSize() == 0) + { + anchor = new IndexPath(0); + } - ResetSelectedItems(); - }); + Selection.ClearSelection(); + Selection.AnchorIndex = anchor; + Selection.SelectRangeFromAnchor(index); } else if (multi && toggle) { - UpdateSelectedItems(() => + if (Selection.IsSelected(index) == true) { - if (!_selection.Contains(index)) - { - _selection.Add(index); - MarkItemSelected(index, true); - SelectedItems.Add(ElementAt(Items, index)); - } - else - { - _selection.Remove(index); - MarkItemSelected(index, false); - - if (index == _selectedIndex) - { - UpdateSelectedItem(_selection.First(), false); - } - - SelectedItems.Remove(ElementAt(Items, index)); - } - }); + Selection.Deselect(index); + } + else + { + Selection.Select(index); + } } else if (toggle) { @@ -623,7 +582,9 @@ namespace Avalonia.Controls.Primitives } else { - UpdateSelectedItem(index); + using var operation = Selection.Update(); + Selection.ClearSelection(); + Selection.Select(index); } if (Presenter?.Panel != null) @@ -696,25 +657,71 @@ namespace Avalonia.Controls.Primitives } /// - /// Gets a range of items from an IEnumerable. + /// Called when is raised. /// - /// The items. - /// The index of the first item. - /// The index of the last item. - /// The items. - private static List GetRange(IEnumerable items, int first, int last) + /// The sender. + /// The event args. + private void OnSelectionModelPropertyChanged(object sender, PropertyChangedEventArgs e) { - var list = (items as IList) ?? items.Cast().ToList(); - var step = first > last ? -1 : 1; - var result = new List(); + if (e.PropertyName == nameof(SelectionModel.AnchorIndex) && AutoScrollToSelectedItem) + { + var index = Selection.AnchorIndex.GetSize() > 0 ? Selection.AnchorIndex.GetAt(0) : -1; + var item = index != -1 ? ElementAt(Items, index) : null; + + if (item != null) + { + ScrollIntoView(item); + } + } + } + + /// + /// Called when is raised. + /// + /// The sender. + /// The event args. + private void OnSelectionModelSelectionChanged(object sender, SelectionModelSelectionChangedEventArgs e) + { + void Mark(int index, bool selected) + { + var container = ItemContainerGenerator.ContainerFromIndex(index); + + if (container != null) + { + MarkContainerSelected(container, selected); + } + } + + foreach (var i in e.SelectedIndices) + { + Mark(i.GetAt(0), true); + } - for (int i = first; i != last; i += step) + foreach (var i in e.DeselectedIndices) { - result.Add(list[i]); + Mark(i.GetAt(0), false); } - result.Add(list[last]); - return result; + var newSelectedIndex = SelectedIndex; + var newSelectedItem = SelectedItem; + + if (newSelectedIndex != _selectedIndex) + { + RaisePropertyChanged(SelectedIndexProperty, _selectedIndex, newSelectedIndex); + _selectedIndex = newSelectedIndex; + } + + if (newSelectedItem != _selectedItem) + { + RaisePropertyChanged(SelectedItemProperty, _selectedItem, newSelectedItem); + _selectedItem = newSelectedItem; + } + + var ev = new SelectionChangedEventArgs( + SelectionChangedEvent, + e.DeselectedItems.ToList(), + e.SelectedItems.ToList()); + RaiseEvent(ev); } /// @@ -794,301 +801,43 @@ namespace Avalonia.Controls.Primitives } } - /// - /// Sets an item container's 'selected' class or . - /// - /// The index of the item. - /// Whether the item should be selected or deselected. - private void MarkItemSelected(int index, bool selected) + private void MarkContainersUnselected() { - var container = ItemContainerGenerator?.ContainerFromIndex(index); - - if (container != null) + foreach (var container in ItemContainerGenerator.Containers) { - MarkContainerSelected(container, selected); + MarkContainerSelected(container.ContainerControl, false); } } - /// - /// Sets an item container's 'selected' class or . - /// - /// The item. - /// Whether the item should be selected or deselected. - private int MarkItemSelected(object item, bool selected) + private void UpdateContainerSelection() { - var index = IndexOf(Items, item); - - if (index != -1) - { - MarkItemSelected(index, selected); - } - - return index; - } - - private void ResetSelectedItems() - { - UpdateSelectedItems(() => - { - SelectedItems.Clear(); - - foreach (var i in _selection) - { - SelectedItems.Add(ElementAt(Items, i)); - } - }); - } - - /// - /// Called when the CollectionChanged event is raised. - /// - /// The event sender. - /// The event args. - private void SelectedItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - if (_syncingSelectedItems) - { - return; - } - - void Add(IList newItems, IList addedItems = null) - { - foreach (var item in newItems) - { - var index = MarkItemSelected(item, true); - - if (index != -1 && _selection.Add(index) && addedItems != null) - { - addedItems.Add(item); - } - } - } - - void UpdateSelection() - { - if ((SelectedIndex != -1 && !_selection.Contains(SelectedIndex)) || - (SelectedIndex == -1 && _selection.HasItems)) - { - _selectedIndex = _selection.First(); - _selectedItem = ElementAt(Items, _selectedIndex); - RaisePropertyChanged(SelectedIndexProperty, -1, _selectedIndex, BindingPriority.LocalValue); - RaisePropertyChanged(SelectedItemProperty, null, _selectedItem, BindingPriority.LocalValue); - } - } - - IList added = null; - IList removed = null; - - switch (e.Action) - { - case NotifyCollectionChangedAction.Add: - { - Add(e.NewItems); - UpdateSelection(); - added = e.NewItems; - } - - break; - - case NotifyCollectionChangedAction.Remove: - if (SelectedItems.Count == 0) - { - SelectedIndex = -1; - } - - foreach (var item in e.OldItems) - { - var index = MarkItemSelected(item, false); - _selection.Remove(index); - } - - removed = e.OldItems; - break; - - case NotifyCollectionChangedAction.Replace: - throw new NotSupportedException("Replacing items in a SelectedItems collection is not supported."); - - case NotifyCollectionChangedAction.Move: - throw new NotSupportedException("Moving items in a SelectedItems collection is not supported."); - - case NotifyCollectionChangedAction.Reset: - { - removed = new List(); - added = new List(); - - foreach (var index in _selection.ToList()) - { - var item = ElementAt(Items, index); - - if (!SelectedItems.Contains(item)) - { - MarkItemSelected(index, false); - removed.Add(item); - _selection.Remove(index); - } - } - - Add(SelectedItems, added); - UpdateSelection(); - } - - break; - } - - if (added?.Count > 0 || removed?.Count > 0) - { - var changed = new SelectionChangedEventArgs( - SelectionChangedEvent, - removed ?? Empty, - added ?? Empty); - RaiseEvent(changed); - } - } - - /// - /// Subscribes to the CollectionChanged event, if any. - /// - private void SubscribeToSelectedItems() - { - var incc = _selectedItems as INotifyCollectionChanged; - - if (incc != null) - { - incc.CollectionChanged += SelectedItemsCollectionChanged; - } - - SelectedItemsCollectionChanged( - _selectedItems, - new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); - } - - /// - /// Unsubscribes from the CollectionChanged event, if any. - /// - private void UnsubscribeFromSelectedItems() - { - var incc = _selectedItems as INotifyCollectionChanged; - - if (incc != null) + foreach (var container in ItemContainerGenerator.Containers) { - incc.CollectionChanged -= SelectedItemsCollectionChanged; + MarkContainerSelected( + container.ContainerControl, + Selection.IsSelected(container.Index) != false); } } /// - /// Updates the selection due to a change to or - /// . + /// Sets an item container's 'selected' class or . /// - /// The new selected index. - /// Whether to clear existing selection. - private void UpdateSelectedItem(int index, bool clear = true) + /// The index of the item. + /// Whether the item should be selected or deselected. + private void MarkItemSelected(int index, bool selected) { - var oldIndex = _selectedIndex; - var oldItem = _selectedItem; - - if (index == -1 && AlwaysSelected) - { - index = Math.Min(SelectedIndex, ItemCount - 1); - } - - var item = ElementAt(Items, index); - var itemChanged = !Equals(item, oldItem); - var added = -1; - HashSet removed = null; - - _selectedIndex = index; - _selectedItem = item; - - if (oldIndex != index || itemChanged || _selection.HasMultiple) - { - if (clear) - { - removed = _selection.Clear(); - } - - if (index != -1) - { - if (_selection.Add(index)) - { - added = index; - } - - if (removed?.Contains(index) == true) - { - removed.Remove(index); - added = -1; - } - } - - if (removed != null) - { - foreach (var i in removed) - { - MarkItemSelected(i, false); - } - } - - MarkItemSelected(index, true); - - RaisePropertyChanged( - SelectedIndexProperty, - oldIndex, - index); - } - - if (itemChanged) - { - RaisePropertyChanged( - SelectedItemProperty, - oldItem, - item); - } - - if (removed != null && index != -1) - { - removed.Remove(index); - } - - if (added != -1 || removed?.Count > 0) - { - ResetSelectedItems(); - - var e = new SelectionChangedEventArgs( - SelectionChangedEvent, - removed?.Select(x => ElementAt(Items, x)).ToArray() ?? Array.Empty(), - added != -1 ? new[] { ElementAt(Items, added) } : Array.Empty()); - RaiseEvent(e); - } - - if (AutoScrollToSelectedItem && _selectedIndex != -1) - { - ScrollIntoView(_selectedItem); - } - } + var container = ItemContainerGenerator?.ContainerFromIndex(index); - private void UpdateSelectedItems(Action action) - { - try - { - _syncingSelectedItems = true; - action(); - } - catch (Exception ex) - { - Logger.TryGet(LogEventLevel.Error)?.Log( - LogArea.Property, - this, - "Error thrown updating SelectedItems: {Error}", - ex); - } - finally + if (container != null) { - _syncingSelectedItems = false; + MarkContainerSelected(container, selected); } } private void UpdateFinished() { + Selection.Source = Items; + if (_updateSelectedItem != null) { SelectedItem = _updateSelectedItem; @@ -1133,104 +882,5 @@ namespace Avalonia.Controls.Primitives UpdateFinished(); } } - - private class Selection : IEnumerable - { - private readonly List _list = new List(); - private HashSet _set = new HashSet(); - - public bool HasItems => _set.Count > 0; - public bool HasMultiple => _set.Count > 1; - - public bool Add(int index) - { - if (index == -1) - { - throw new ArgumentException("Invalid index", "index"); - } - - if (_set.Add(index)) - { - _list.Add(index); - return true; - } - - return false; - } - - public bool Remove(int index) - { - if (_set.Remove(index)) - { - _list.RemoveAll(x => x == index); - return true; - } - - return false; - } - - public HashSet Clear() - { - var result = _set; - _list.Clear(); - _set = new HashSet(); - return result; - } - - public void ItemsInserted(int index, int count) - { - _set = new HashSet(); - - for (var i = 0; i < _list.Count; ++i) - { - var ix = _list[i]; - - if (ix >= index) - { - var newIndex = ix + count; - _list[i] = newIndex; - _set.Add(newIndex); - } - else - { - _set.Add(ix); - } - } - } - - public void ItemsRemoved(int index, int count) - { - var last = (index + count) - 1; - - _set = new HashSet(); - - for (var i = 0; i < _list.Count; ++i) - { - var ix = _list[i]; - - if (ix >= index && ix <= last) - { - _list.RemoveAt(i--); - } - else if (ix > last) - { - var newIndex = ix - count; - _list[i] = newIndex; - _set.Add(newIndex); - } - else - { - _set.Add(ix); - } - } - } - - public bool Contains(int index) => _set.Contains(index); - - public int First() => HasItems ? _list[0] : -1; - - public IEnumerator GetEnumerator() => _set.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } } } diff --git a/src/Avalonia.Controls/SelectionModel.cs b/src/Avalonia.Controls/SelectionModel.cs index 0531174454..5eb2a2da0a 100644 --- a/src/Avalonia.Controls/SelectionModel.cs +++ b/src/Avalonia.Controls/SelectionModel.cs @@ -176,11 +176,12 @@ namespace Avalonia.Controls { var isSelected = IsSelectedWithPartialAt(value); - if (!isSelected.HasValue || !isSelected.Value) + if (!IsSelectedAt(value) || SelectedItems.Count > 1) { using var operation = new Operation(this); ClearSelection(resetAnchor: true); SelectWithPathImpl(value, select: true); + ApplyAutoSelect(); } } } diff --git a/src/Avalonia.Controls/Utils/SelectedItemsSync.cs b/src/Avalonia.Controls/Utils/SelectedItemsSync.cs index 3d6c88cd99..c127771990 100644 --- a/src/Avalonia.Controls/Utils/SelectedItemsSync.cs +++ b/src/Avalonia.Controls/Utils/SelectedItemsSync.cs @@ -19,6 +19,7 @@ namespace Avalonia.Controls.Utils public SelectedItemsSync(ISelectionModel model) { + model = model ?? throw new ArgumentNullException(nameof(model)); Model = model; } @@ -37,9 +38,9 @@ namespace Avalonia.Controls.Utils return _items; } - public void SetItems(IList items) + public void SetItems(IList? items) { - items = items ?? throw new ArgumentNullException(nameof(items)); + items ??= new AvaloniaList(); if (items.IsFixedSize) { diff --git a/tests/Avalonia.Controls.UnitTests/CarouselTests.cs b/tests/Avalonia.Controls.UnitTests/CarouselTests.cs index b16ac6bb8e..8de762a99b 100644 --- a/tests/Avalonia.Controls.UnitTests/CarouselTests.cs +++ b/tests/Avalonia.Controls.UnitTests/CarouselTests.cs @@ -275,7 +275,7 @@ namespace Avalonia.Controls.UnitTests } [Fact] - public void Selected_Item_Changes_To_NextAvailable_Item_If_SelectedItem_Is_Removed_From_Middle() + public void Selected_Item_Changes_To_First_Item_If_SelectedItem_Is_Removed_From_Middle() { var items = new ObservableCollection { @@ -298,8 +298,8 @@ namespace Avalonia.Controls.UnitTests items.RemoveAt(1); - Assert.Equal(1, target.SelectedIndex); - Assert.Equal("FooBar", target.SelectedItem); + Assert.Equal(0, target.SelectedIndex); + Assert.Equal("Foo", target.SelectedItem); } private Control CreateTemplate(Carousel control, INameScope scope) diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs index 8c16dd0f70..f384fcc128 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs @@ -554,33 +554,6 @@ namespace Avalonia.Controls.UnitTests.Primitives Assert.Equal(new[] { removed }, receivedArgs.RemovedItems); } - [Fact] - public void Moving_Selected_Item_Should_Update_Selection() - { - var items = new AvaloniaList - { - new Item(), - new Item(), - }; - - var target = new SelectingItemsControl - { - Items = items, - Template = Template(), - }; - - target.ApplyTemplate(); - target.SelectedIndex = 0; - - Assert.Equal(items[0], target.SelectedItem); - Assert.Equal(0, target.SelectedIndex); - - items.Move(0, 1); - - Assert.Equal(items[1], target.SelectedItem); - Assert.Equal(1, target.SelectedIndex); - } - [Fact] public void Resetting_Items_Collection_Should_Clear_Selection() { @@ -1101,8 +1074,8 @@ namespace Avalonia.Controls.UnitTests.Primitives items[1] = "Qux"; - Assert.Equal(1, target.SelectedIndex); - Assert.Equal("Qux", target.SelectedItem); + Assert.Equal(-1, target.SelectedIndex); + Assert.Null(target.SelectedItem); } [Fact] diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_AutoSelect.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_AutoSelect.cs index a7010c521b..eb6b10fb44 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_AutoSelect.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_AutoSelect.cs @@ -78,8 +78,8 @@ namespace Avalonia.Controls.UnitTests.Primitives target.SelectedIndex = 2; items.RemoveAt(2); - Assert.Equal(2, target.SelectedIndex); - Assert.Equal("qux", target.SelectedItem); + Assert.Equal(0, target.SelectedIndex); + Assert.Equal("foo", target.SelectedItem); } [Fact] diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs index 3a8c98983f..ed5c94517a 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs @@ -73,8 +73,6 @@ namespace Avalonia.Controls.UnitTests.Primitives [Fact] public void Assigning_Multiple_SelectedItems_Should_Set_SelectedIndex() { - // Note that we don't need SelectionMode = Multiple here. Multiple selections can always - // be made in code. var target = new TestSelector { Items = new[] { "foo", "bar", "baz" }, @@ -340,7 +338,6 @@ namespace Avalonia.Controls.UnitTests.Primitives "qiz", "lol", }, - SelectionMode = SelectionMode.Multiple, Template = Template(), }; @@ -373,7 +370,7 @@ namespace Avalonia.Controls.UnitTests.Primitives target.SelectedIndex = 3; target.SelectRange(1); - Assert.Equal(new[] { "qux", "baz", "bar" }, target.SelectedItems.Cast().ToList()); + Assert.Equal(new[] { "bar", "baz", "qux" }, target.SelectedItems.Cast().ToList()); } [Fact] @@ -1117,7 +1114,7 @@ namespace Avalonia.Controls.UnitTests.Primitives target.SelectAll(); items[1] = "Qux"; - Assert.Equal(new[] { "Foo", "Qux", "Baz" }, target.SelectedItems); + Assert.Equal(new[] { "Foo", "Baz" }, target.SelectedItems); } [Fact] @@ -1255,6 +1252,195 @@ namespace Avalonia.Controls.UnitTests.Primitives Assert.Equal(1, target.SelectedItems.Count); } + [Fact] + public void Adding_To_Selection_Should_Set_SelectedIndex() + { + var target = new TestSelector + { + Items = new[] { "foo", "bar" }, + Template = Template(), + }; + + target.ApplyTemplate(); + target.Selection.Select(1); + + Assert.Equal(1, target.SelectedIndex); + } + + [Fact] + public void Assigning_Null_To_Selection_Should_Create_New_SelectionModel() + { + var target = new TestSelector + { + Items = new[] { "foo", "bar" }, + Template = Template(), + }; + + var oldSelection = target.Selection; + + target.Selection = null; + + Assert.NotNull(target.Selection); + Assert.NotSame(oldSelection, target.Selection); + } + + [Fact] + public void Assigning_SelectionModel_With_Different_Source_To_Selection_Should_Fail() + { + var target = new TestSelector + { + Items = new[] { "foo", "bar" }, + Template = Template(), + }; + + var selection = new SelectionModel { Source = new[] { "baz" } }; + Assert.Throws(() => target.Selection = selection); + } + + [Fact] + public void Assigning_SelectionModel_With_Null_Source_To_Selection_Should_Set_Source() + { + var target = new TestSelector + { + Items = new[] { "foo", "bar" }, + Template = Template(), + }; + + var selection = new SelectionModel(); + target.Selection = selection; + + Assert.Same(target.Items, selection.Source); + } + + [Fact] + public void Assigning_Single_Selected_Item_To_Selection_Should_Set_SelectedIndex() + { + var target = new TestSelector + { + Items = new[] { "foo", "bar" }, + Template = Template(), + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + + var selection = new SelectionModel { Source = target.Items }; + selection.Select(1); + target.Selection = selection; + + Assert.Equal(1, target.SelectedIndex); + Assert.Equal(new[] { "bar" }, target.Selection.SelectedItems); + Assert.Equal(new[] { 1 }, SelectedContainers(target)); + } + + [Fact] + public void Assigning_Multiple_Selected_Items_To_Selection_Should_Set_SelectedIndex() + { + var target = new TestSelector + { + Items = new[] { "foo", "bar", "baz" }, + Template = Template(), + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + + var selection = new SelectionModel { Source = target.Items }; + selection.SelectRange(new IndexPath(0), new IndexPath(2)); + target.Selection = selection; + + Assert.Equal(0, target.SelectedIndex); + Assert.Equal(new[] { "foo", "bar", "baz" }, target.Selection.SelectedItems); + Assert.Equal(new[] { 0, 1, 2 }, SelectedContainers(target)); + } + + [Fact] + public void Reassigning_Selection_Should_Clear_Selection() + { + var target = new TestSelector + { + Items = new[] { "foo", "bar" }, + Template = Template(), + }; + + target.ApplyTemplate(); + target.Selection.Select(1); + target.Selection = new SelectionModel(); + + Assert.Equal(-1, target.SelectedIndex); + Assert.Null(target.SelectedItem); + } + + [Fact] + public void Assigning_Selection_Should_Set_Item_IsSelected() + { + var items = new[] + { + new ListBoxItem(), + new ListBoxItem(), + new ListBoxItem(), + }; + + var target = new TestSelector + { + Items = items, + Template = Template(), + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + + var selection = new SelectionModel { Source = items }; + selection.SelectRange(new IndexPath(0), new IndexPath(1)); + target.Selection = selection; + + Assert.True(items[0].IsSelected); + Assert.True(items[1].IsSelected); + Assert.False(items[2].IsSelected); + } + + [Fact] + public void Assigning_Selection_Should_Raise_SelectionChanged() + { + var items = new[] { "foo", "bar", "baz" }; + + var target = new TestSelector + { + Items = items, + Template = Template(), + SelectedItem = "bar", + }; + + var raised = 0; + + target.SelectionChanged += (s, e) => + { + if (raised == 0) + { + Assert.Empty(e.AddedItems.Cast()); + Assert.Equal(new[] { "bar" }, e.RemovedItems.Cast()); + } + else + { + Assert.Equal(new[] { "foo", "baz" }, e.AddedItems.Cast()); + Assert.Empty(e.RemovedItems.Cast()); + } + + ++raised; + }; + + target.ApplyTemplate(); + target.Presenter.ApplyTemplate(); + + + var selection = new SelectionModel { Source = items }; + selection.Select(0); + selection.Select(2); + target.Selection = selection; + + Assert.Equal(2, raised); + } + private IEnumerable SelectedContainers(SelectingItemsControl target) { return target.Presenter.Panel.Children @@ -1278,20 +1464,31 @@ namespace Avalonia.Controls.UnitTests.Primitives public static readonly new AvaloniaProperty SelectedItemsProperty = SelectingItemsControl.SelectedItemsProperty; + public TestSelector() + { + SelectionMode = SelectionMode.Multiple; + } + public new IList SelectedItems { get { return base.SelectedItems; } set { base.SelectedItems = value; } } + public new ISelectionModel Selection + { + get => base.Selection; + set => base.Selection = value; + } + public new SelectionMode SelectionMode { get { return base.SelectionMode; } set { base.SelectionMode = value; } } - public new void SelectAll() => base.SelectAll(); - public new void UnselectAll() => base.UnselectAll(); + public void SelectAll() => Selection.SelectAll(); + public void UnselectAll() => Selection.ClearSelection(); public void SelectRange(int index) => UpdateSelection(index, true, true); public void Toggle(int index) => UpdateSelection(index, true, false, true); } diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/TabStripTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/TabStripTests.cs index b4570ec229..707723f809 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/TabStripTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/TabStripTests.cs @@ -70,7 +70,7 @@ namespace Avalonia.Controls.UnitTests.Primitives } [Fact] - public void Removing_Selected_Should_Select_Next() + public void Removing_Selected_Should_Select_First() { var items = new ObservableCollection() { @@ -99,10 +99,9 @@ namespace Avalonia.Controls.UnitTests.Primitives Assert.Same(items[1], target.SelectedItem); items.RemoveAt(1); - // Assert for former element [2] now [1] == "3rd" - Assert.Equal(1, target.SelectedIndex); - Assert.Same(items[1], target.SelectedItem); - Assert.Same("3rd", ((TabItem)target.SelectedItem).Name); + Assert.Equal(0, target.SelectedIndex); + Assert.Same(items[0], target.SelectedItem); + Assert.Same("first", ((TabItem)target.SelectedItem).Name); } private Control CreateTabStripTemplate(TabStrip parent, INameScope scope) diff --git a/tests/Avalonia.Controls.UnitTests/TabControlTests.cs b/tests/Avalonia.Controls.UnitTests/TabControlTests.cs index a9e86d71ee..d6d5428434 100644 --- a/tests/Avalonia.Controls.UnitTests/TabControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TabControlTests.cs @@ -95,7 +95,7 @@ namespace Avalonia.Controls.UnitTests } [Fact] - public void Removal_Should_Set_Next_Tab() + public void Removal_Should_Set_First_Tab() { var collection = new ObservableCollection() { @@ -126,11 +126,9 @@ namespace Avalonia.Controls.UnitTests target.SelectedItem = collection[1]; collection.RemoveAt(1); - // compare with former [2] now [1] == "3rd" - Assert.Same(collection[1], target.SelectedItem); + Assert.Same(collection[0], target.SelectedItem); } - [Fact] public void TabItem_Templates_Should_Be_Set_Before_TabItem_ApplyTemplate() { diff --git a/tests/Avalonia.Controls.UnitTests/Utils/SelectedItemsSyncTests.cs b/tests/Avalonia.Controls.UnitTests/Utils/SelectedItemsSyncTests.cs index 917f422557..3ab5950974 100644 --- a/tests/Avalonia.Controls.UnitTests/Utils/SelectedItemsSyncTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Utils/SelectedItemsSyncTests.cs @@ -169,6 +169,19 @@ namespace Avalonia.Controls.UnitTests.Utils Assert.Equal(new[] { "foo", "baz", "bar" }, items); } + [Fact] + public void Setting_Items_To_Null_Creates_Empty_Items() + { + var target = CreateTarget(); + var oldItems = target.GetOrCreateItems(); + + target.SetItems(null); + + var newItems = Assert.IsType>(target.GetOrCreateItems()); + + Assert.NotSame(oldItems, newItems); + } + [Fact] public void Handles_Null_Model_Source() { From c4afd15d900f639994d479f20bebd18f8f83c0b5 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 17 Feb 2020 12:29:43 +0100 Subject: [PATCH 059/663] Use SelectionModel in TreeView. --- .../ControlCatalog/Pages/TreeViewPage.xaml | 2 +- .../ControlCatalog/Pages/TreeViewPage.xaml.cs | 17 +- src/Avalonia.Controls/TreeView.cs | 663 ++++++++---------- .../TreeViewTests.cs | 17 +- 4 files changed, 315 insertions(+), 384 deletions(-) diff --git a/samples/ControlCatalog/Pages/TreeViewPage.xaml b/samples/ControlCatalog/Pages/TreeViewPage.xaml index 3a81e2ed02..c9e3fafb6d 100644 --- a/samples/ControlCatalog/Pages/TreeViewPage.xaml +++ b/samples/ControlCatalog/Pages/TreeViewPage.xaml @@ -10,7 +10,7 @@ HorizontalAlignment="Center" Spacing="16"> - + diff --git a/samples/ControlCatalog/Pages/TreeViewPage.xaml.cs b/samples/ControlCatalog/Pages/TreeViewPage.xaml.cs index 1f35f05f1d..5893796b8b 100644 --- a/samples/ControlCatalog/Pages/TreeViewPage.xaml.cs +++ b/samples/ControlCatalog/Pages/TreeViewPage.xaml.cs @@ -28,21 +28,22 @@ namespace ControlCatalog.Pages { Node root = new Node(); Items = root.Children; - SelectedItems = new ObservableCollection(); + Selection = new SelectionModel(); AddItemCommand = ReactiveCommand.Create(() => { - Node parentItem = SelectedItems.Count > 0 ? SelectedItems[0] : root; + Node parentItem = Selection.SelectedItems.Count > 0 ? + (Node)Selection.SelectedItems[0] : root; parentItem.AddNewItem(); }); RemoveItemCommand = ReactiveCommand.Create(() => { - while (SelectedItems.Count > 0) + while (Selection.SelectedItems.Count > 0) { - Node lastItem = SelectedItems[0]; + Node lastItem = (Node)Selection.SelectedItems[0]; RecursiveRemove(Items, lastItem); - SelectedItems.Remove(lastItem); + Selection.DeselectAt(Selection.SelectedIndices[0]); } bool RecursiveRemove(ObservableCollection items, Node selectedItem) @@ -67,7 +68,7 @@ namespace ControlCatalog.Pages public ObservableCollection Items { get; } - public ObservableCollection SelectedItems { get; } + public SelectionModel Selection { get; } public ReactiveCommand AddItemCommand { get; } @@ -78,7 +79,7 @@ namespace ControlCatalog.Pages get => _selectionMode; set { - SelectedItems.Clear(); + Selection.ClearSelection(); this.RaiseAndSetIfChanged(ref _selectionMode, value); } } @@ -109,7 +110,7 @@ namespace ControlCatalog.Pages public override string ToString() => Header; - private Node CreateNewNode() => new Node {Header = $"Item {_counter++}"}; + private Node CreateNewNode() => new Node { Header = $"Item {_counter++}" }; } } } diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs index 3a7ad97763..19d378404e 100644 --- a/src/Avalonia.Controls/TreeView.cs +++ b/src/Avalonia.Controls/TreeView.cs @@ -5,11 +5,12 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Collections.Specialized; +using System.ComponentModel; using System.Linq; -using Avalonia.Collections; using Avalonia.Controls.Generators; using Avalonia.Controls.Primitives; +using Avalonia.Controls.Utils; +using Avalonia.Data; using Avalonia.Input; using Avalonia.Input.Platform; using Avalonia.Interactivity; @@ -45,15 +46,29 @@ namespace Avalonia.Controls o => o.SelectedItems, (o, v) => o.SelectedItems = v); + /// + /// Defines the property. + /// + public static readonly DirectProperty SelectionProperty = + SelectingItemsControl.SelectionProperty.AddOwner( + o => o.Selection, + (o, v) => o.Selection = v); + /// /// Defines the property. /// public static readonly StyledProperty SelectionModeProperty = ListBox.SelectionModeProperty.AddOwner(); - private static readonly IList Empty = Array.Empty(); + /// + /// Defines the property. + /// + public static RoutedEvent SelectionChangedEvent = + SelectingItemsControl.SelectionChangedEvent; + private object _selectedItem; - private IList _selectedItems; + private ISelectionModel _selection; + private readonly SelectedItemsSync _selectedItems; /// /// Initializes static members of the class. @@ -63,6 +78,13 @@ namespace Avalonia.Controls // HACK: Needed or SelectedItem property will not be found in Release build. } + public TreeView() + { + // Setting Selection to null causes a default SelectionModel to be created. + Selection = null; + _selectedItems = new SelectedItemsSync(Selection); + } + /// /// Occurs when the control's selection changes. /// @@ -87,8 +109,6 @@ namespace Avalonia.Controls set => SetValue(AutoScrollToSelectedItemProperty, value); } - private bool _syncingSelectedItems; - /// /// Gets or sets the selection mode. /// @@ -98,61 +118,102 @@ namespace Avalonia.Controls set => SetValue(SelectionModeProperty, value); } + /// + /// Gets or sets the selected item. + /// /// /// Gets or sets the selected item. /// public object SelectedItem { - get => _selectedItem; - set - { - var selectedItems = SelectedItems; - - SetAndRaise(SelectedItemProperty, ref _selectedItem, value); + get => Selection.SelectedItem; + set => Selection.SelectedIndex = IndexFromItem(value); + } - if (value != null) - { - if (selectedItems.Count != 1 || selectedItems[0] != value) - { - _syncingSelectedItems = true; - SelectSingleItem(value); - _syncingSelectedItems = false; - } - } - else if (SelectedItems.Count > 0) - { - SelectedItems.Clear(); - } - } + /// + /// Gets or sets the selected items. + /// + protected IList SelectedItems + { + get => _selectedItems.GetOrCreateItems(); + set => _selectedItems.SetItems(value); } /// - /// Gets the selected items. + /// Gets or sets a model holding the current selection. /// - public IList SelectedItems + public ISelectionModel Selection { - get + get => _selection; + set { - if (_selectedItems == null) + value ??= new SelectionModel { - _selectedItems = new AvaloniaList(); - SubscribeToSelectedItems(); - } - - return _selectedItems; - } + SingleSelect = !SelectionMode.HasFlagCustom(SelectionMode.Multiple), + AutoSelect = SelectionMode.HasFlagCustom(SelectionMode.AlwaysSelected), + RetainSelectionOnReset = true, + }; - set - { - if (value?.IsFixedSize == true || value?.IsReadOnly == true) + if (_selection != value) { - throw new NotSupportedException( - "Cannot use a fixed size or read-only collection as SelectedItems."); - } + if (value == null) + { + throw new ArgumentNullException(nameof(value), "Cannot set Selection to null."); + } + else if (value.Source != null && value.Source != Items) + { + throw new ArgumentException("Selection has invalid Source."); + } + + List oldSelection = null; + + if (_selection != null) + { + oldSelection = Selection.SelectedItems.ToList(); + _selection.PropertyChanged -= OnSelectionModelPropertyChanged; + _selection.SelectionChanged -= OnSelectionModelSelectionChanged; + _selection.ChildrenRequested -= OnSelectionModelChildrenRequested; + MarkContainersUnselected(); + } + + _selection = value; + + if (_selection != null) + { + _selection.Source = Items; + _selection.PropertyChanged += OnSelectionModelPropertyChanged; + _selection.SelectionChanged += OnSelectionModelSelectionChanged; + _selection.ChildrenRequested += OnSelectionModelChildrenRequested; - UnsubscribeFromSelectedItems(); - _selectedItems = value ?? new AvaloniaList(); - SubscribeToSelectedItems(); + if (_selection.SingleSelect) + { + SelectionMode &= ~SelectionMode.Multiple; + } + else + { + SelectionMode |= SelectionMode.Multiple; + } + + if (_selection.AutoSelect) + { + SelectionMode |= SelectionMode.AlwaysSelected; + } + else + { + SelectionMode &= ~SelectionMode.AlwaysSelected; + } + + UpdateContainerSelection(); + + var selectedItem = SelectedItem; + + if (_selectedItem != selectedItem) + { + RaisePropertyChanged(SelectedItemProperty, _selectedItem, selectedItem); + _selectedItem = selectedItem; + } + } + } } } @@ -185,186 +246,12 @@ namespace Avalonia.Controls /// Note that this method only selects nodes currently visible due to their parent nodes /// being expanded: it does not expand nodes. /// - public void SelectAll() - { - SynchronizeItems(SelectedItems, ItemContainerGenerator.Index.Items); - } + public void SelectAll() => Selection.SelectAll(); /// /// Deselects all items in the . /// - public void UnselectAll() - { - SelectedItems.Clear(); - } - - /// - /// Subscribes to the CollectionChanged event, if any. - /// - private void SubscribeToSelectedItems() - { - if (_selectedItems is INotifyCollectionChanged incc) - { - incc.CollectionChanged += SelectedItemsCollectionChanged; - } - - SelectedItemsCollectionChanged( - _selectedItems, - new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); - } - - private void SelectSingleItem(object item) - { - SelectedItems.Clear(); - SelectedItems.Add(item); - } - - /// - /// Called when the CollectionChanged event is raised. - /// - /// The event sender. - /// The event args. - private void SelectedItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - IList added = null; - IList removed = null; - - switch (e.Action) - { - case NotifyCollectionChangedAction.Add: - - SelectedItemsAdded(e.NewItems.Cast().ToArray()); - - if (AutoScrollToSelectedItem) - { - var container = (TreeViewItem)ItemContainerGenerator.Index.ContainerFromItem(e.NewItems[0]); - - container?.BringIntoView(); - } - - added = e.NewItems; - - break; - case NotifyCollectionChangedAction.Remove: - - if (!_syncingSelectedItems) - { - if (SelectedItems.Count == 0) - { - SelectedItem = null; - } - else - { - var selectedIndex = SelectedItems.IndexOf(_selectedItem); - - if (selectedIndex == -1) - { - var old = _selectedItem; - _selectedItem = SelectedItems[0]; - - RaisePropertyChanged(SelectedItemProperty, old, _selectedItem); - } - } - } - - foreach (var item in e.OldItems) - { - MarkItemSelected(item, false); - } - - removed = e.OldItems; - - break; - case NotifyCollectionChangedAction.Reset: - - foreach (IControl container in ItemContainerGenerator.Index.Containers) - { - MarkContainerSelected(container, false); - } - - if (SelectedItems.Count > 0) - { - SelectedItemsAdded(SelectedItems); - - added = SelectedItems; - } - else if (!_syncingSelectedItems) - { - SelectedItem = null; - } - - break; - case NotifyCollectionChangedAction.Replace: - - foreach (var item in e.OldItems) - { - MarkItemSelected(item, false); - } - - foreach (var item in e.NewItems) - { - MarkItemSelected(item, true); - } - - if (SelectedItem != SelectedItems[0] && !_syncingSelectedItems) - { - var oldItem = SelectedItem; - var item = SelectedItems[0]; - _selectedItem = item; - RaisePropertyChanged(SelectedItemProperty, oldItem, item); - } - - added = e.NewItems; - removed = e.OldItems; - - break; - } - - if (added?.Count > 0 || removed?.Count > 0) - { - var changed = new SelectionChangedEventArgs( - SelectingItemsControl.SelectionChangedEvent, - removed ?? Empty, - added ?? Empty); - RaiseEvent(changed); - } - } - - private void MarkItemSelected(object item, bool selected) - { - var container = ItemContainerGenerator.Index.ContainerFromItem(item); - - MarkContainerSelected(container, selected); - } - - private void SelectedItemsAdded(IList items) - { - if (items.Count == 0) - { - return; - } - - foreach (object item in items) - { - MarkItemSelected(item, true); - } - - if (SelectedItem == null && !_syncingSelectedItems) - { - SetAndRaise(SelectedItemProperty, ref _selectedItem, items[0]); - } - } - - /// - /// Unsubscribes from the CollectionChanged event, if any. - /// - private void UnsubscribeFromSelectedItems() - { - if (_selectedItems is INotifyCollectionChanged incc) - { - incc.CollectionChanged -= SelectedItemsCollectionChanged; - } - } + public void UnselectAll() => Selection.ClearSelection(); (bool handled, IInputElement next) ICustomKeyboardNavigation.GetNext(IInputElement element, NavigationDirection direction) @@ -448,6 +335,72 @@ namespace Avalonia.Controls } } + /// + /// Called when is raised. + /// + /// The sender. + /// The event args. + private void OnSelectionModelPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(SelectionModel.AnchorIndex) && AutoScrollToSelectedItem) + { + var container = ContainerFromIndex(Selection.AnchorIndex); + + if (container != null) + { + container.BringIntoView(); + } + } + } + + /// + /// Called when is raised. + /// + /// The sender. + /// The event args. + private void OnSelectionModelSelectionChanged(object sender, SelectionModelSelectionChangedEventArgs e) + { + void Mark(IndexPath index, bool selected) + { + var container = ContainerFromIndex(index); + + if (container != null) + { + MarkContainerSelected(container, selected); + } + } + + foreach (var i in e.SelectedIndices) + { + Mark(i, true); + } + + foreach (var i in e.DeselectedIndices) + { + Mark(i, false); + } + + var newSelectedItem = SelectedItem; + + if (newSelectedItem != _selectedItem) + { + RaisePropertyChanged(SelectedItemProperty, _selectedItem, newSelectedItem); + _selectedItem = newSelectedItem; + } + + var ev = new SelectionChangedEventArgs( + SelectionChangedEvent, + e.DeselectedItems.ToList(), + e.SelectedItems.ToList()); + RaiseEvent(ev); + } + + private void OnSelectionModelChildrenRequested(object sender, SelectionModelChildrenRequestedEventArgs e) + { + var container = ItemContainerGenerator.Index.ContainerFromItem(e.Source) as ItemsControl; + e.Children = container?.Items as IEnumerable; + } + private TreeViewItem GetContainerInDirection( TreeViewItem from, NavigationDirection direction, @@ -501,6 +454,12 @@ namespace Avalonia.Controls return result; } + protected override void ItemsChanged(AvaloniaPropertyChangedEventArgs e) + { + Selection.Source = Items; + base.ItemsChanged(e); + } + /// protected override void OnPointerPressed(PointerPressedEventArgs e) { @@ -522,6 +481,18 @@ namespace Avalonia.Controls } } + protected override void OnPropertyChanged(AvaloniaProperty property, Optional oldValue, BindingValue newValue, BindingPriority priority) + { + base.OnPropertyChanged(property, oldValue, newValue, priority); + + if (property == SelectionModeProperty) + { + var mode = newValue.GetValueOrDefault(); + Selection.SingleSelect = !mode.HasFlagCustom(SelectionMode.Multiple); + Selection.AutoSelect = mode.HasFlagCustom(SelectionMode.AlwaysSelected); + } + } + /// /// Updates the selection for an item based on user interaction. /// @@ -537,9 +508,9 @@ namespace Avalonia.Controls bool toggleModifier = false, bool rightButton = false) { - var item = ItemContainerGenerator.Index.ItemFromContainer(container); + var index = IndexFromContainer((TreeViewItem)container); - if (item == null) + if (index.GetSize() == 0) { return; } @@ -556,41 +527,48 @@ namespace Avalonia.Controls var multi = (mode & SelectionMode.Multiple) != 0; var range = multi && selectedContainer != null && rangeModifier; - if (rightButton) + if (!select) + { + Selection.DeselectAt(index); + } + else if (rightButton) { - if (!SelectedItems.Contains(item)) + if (!Selection.IsSelectedAt(index)) { - SelectSingleItem(item); + Selection.SelectedIndex = index; } } else if (!toggle && !range) { - SelectSingleItem(item); + Selection.SelectedIndex = index; } else if (multi && range) { - SynchronizeItems( - SelectedItems, - GetItemsInRange(selectedContainer as TreeViewItem, container as TreeViewItem)); + using var operation = Selection.Update(); + var anchor = Selection.AnchorIndex; + + if (anchor.GetSize() == 0) + { + anchor = new IndexPath(0); + } + + Selection.ClearSelection(); + Selection.AnchorIndex = anchor; + Selection.SelectRangeFromAnchorTo(index); } else { - var i = SelectedItems.IndexOf(item); - - if (i != -1) + if (Selection.IsSelectedAt(index)) { - SelectedItems.Remove(item); + Selection.DeselectAt(index); + } + else if (multi) + { + Selection.SelectAt(index); } else { - if (multi) - { - SelectedItems.Add(item); - } - else - { - SelectedItem = item; - } + Selection.SelectedIndex = index; } } } @@ -613,117 +591,6 @@ namespace Avalonia.Controls } } - /// - /// Find which node is first in hierarchy. - /// - /// Search root. - /// Nodes to find. - /// Node to find. - /// Found first node. - private static TreeViewItem FindFirstNode(TreeView treeView, TreeViewItem nodeA, TreeViewItem nodeB) - { - return FindInContainers(treeView.ItemContainerGenerator, nodeA, nodeB); - } - - private static TreeViewItem FindInContainers(ITreeItemContainerGenerator containerGenerator, - TreeViewItem nodeA, - TreeViewItem nodeB) - { - IEnumerable containers = containerGenerator.Containers; - - foreach (ItemContainerInfo container in containers) - { - TreeViewItem node = FindFirstNode(container.ContainerControl as TreeViewItem, nodeA, nodeB); - - if (node != null) - { - return node; - } - } - - return null; - } - - private static TreeViewItem FindFirstNode(TreeViewItem node, TreeViewItem nodeA, TreeViewItem nodeB) - { - if (node == null) - { - return null; - } - - TreeViewItem match = node == nodeA ? nodeA : node == nodeB ? nodeB : null; - - if (match != null) - { - return match; - } - - return FindInContainers(node.ItemContainerGenerator, nodeA, nodeB); - } - - /// - /// Returns all items that belong to containers between and . - /// The range is inclusive. - /// - /// From container. - /// To container. - private List GetItemsInRange(TreeViewItem from, TreeViewItem to) - { - var items = new List(); - - if (from == null || to == null) - { - return items; - } - - TreeViewItem firstItem = FindFirstNode(this, from, to); - - if (firstItem == null) - { - return items; - } - - bool wasReversed = false; - - if (firstItem == to) - { - var temp = from; - - from = to; - to = temp; - - wasReversed = true; - } - - TreeViewItem node = from; - - while (node != to) - { - var item = ItemContainerGenerator.Index.ItemFromContainer(node); - - if (item != null) - { - items.Add(item); - } - - node = GetContainerInDirection(node, NavigationDirection.Down, true); - } - - var toItem = ItemContainerGenerator.Index.ItemFromContainer(to); - - if (toItem != null) - { - items.Add(toItem); - } - - if (wasReversed) - { - items.Reverse(); - } - - return items; - } - /// /// Updates the selection based on an event that may have originated in a container that /// belongs to the control. @@ -829,26 +696,90 @@ namespace Avalonia.Controls } } - /// - /// Makes a list of objects equal another (though doesn't preserve order). - /// - /// The items collection. - /// The desired items. - private static void SynchronizeItems(IList items, IEnumerable desired) + private void MarkContainersUnselected() { - var list = items.Cast().ToList(); - var toRemove = list.Except(desired).ToList(); - var toAdd = desired.Except(list).ToList(); + foreach (var container in ItemContainerGenerator.Index.Containers) + { + MarkContainerSelected(container, false); + } + } + + private void UpdateContainerSelection() + { + var index = ItemContainerGenerator.Index; + + foreach (var container in index.Containers) + { + var i = IndexFromContainer((TreeViewItem)container); + + MarkContainerSelected( + container, + Selection.IsSelectedAt(i) != false); + } + } + + private static IndexPath IndexFromContainer(TreeViewItem container) + { + var result = new List(); + + while (true) + { + if (container.Level == 0) + { + var treeView = container.FindAncestorOfType(); + + if (treeView == null) + { + return default; + } + + result.Add(treeView.ItemContainerGenerator.IndexFromContainer(container)); + result.Reverse(); + return new IndexPath(result); + } + else + { + var parent = container.FindAncestorOfType(); + + if (parent == null) + { + return default; + } + + result.Add(parent.ItemContainerGenerator.IndexFromContainer(container)); + container = parent; + } + } + } + + private IndexPath IndexFromItem(object item) + { + var container = ItemContainerGenerator.Index.ContainerFromItem(item) as TreeViewItem; - foreach (var i in toRemove) + if (container != null) { - items.Remove(i); + return IndexFromContainer(container); } - foreach (var i in toAdd) + return default; + } + + private TreeViewItem ContainerFromIndex(IndexPath index) + { + TreeViewItem treeViewItem = null; + + for (var i = 0; i < index.GetSize(); ++i) { - items.Add(i); + var generator = treeViewItem?.ItemContainerGenerator ?? ItemContainerGenerator; + treeViewItem = generator.ContainerFromIndex(index.GetAt(i)) as TreeViewItem; + + if (treeViewItem == null) + { + return null; + } } + + return treeViewItem; } } } diff --git a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs index a2892be5c2..1ce1f8cc4c 100644 --- a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs @@ -10,7 +10,6 @@ using Avalonia.Controls.Presenters; using Avalonia.Controls.Templates; using Avalonia.Data; using Avalonia.Data.Core; -using Avalonia.Diagnostics; using Avalonia.Input; using Avalonia.Input.Platform; using Avalonia.Interactivity; @@ -244,12 +243,12 @@ namespace Avalonia.Controls.UnitTests ClickContainer(item2Container, InputModifiers.Control); Assert.True(item2Container.IsSelected); - Assert.Equal(new[] {item1, item2}, target.SelectedItems.OfType()); + Assert.Equal(new[] {item1, item2}, target.Selection.SelectedItems.OfType()); ClickContainer(item1Container, InputModifiers.Control); Assert.False(item1Container.IsSelected); - Assert.DoesNotContain(item1, target.SelectedItems.OfType()); + Assert.DoesNotContain(item1, target.Selection.SelectedItems.OfType()); } [Fact] @@ -749,11 +748,11 @@ namespace Avalonia.Controls.UnitTests target.SelectAll(); AssertChildrenSelected(target, tree[0]); - Assert.Equal(5, target.SelectedItems.Count); + Assert.Equal(5, target.Selection.SelectedItems.Count); _mouse.Click((Interactive)target.Presenter.Panel.Children[0], MouseButton.Right); - Assert.Equal(5, target.SelectedItems.Count); + Assert.Equal(5, target.Selection.SelectedItems.Count); } [Fact] @@ -785,11 +784,11 @@ namespace Avalonia.Controls.UnitTests ClickContainer(fromContainer, InputModifiers.None); ClickContainer(toContainer, InputModifiers.Shift); - Assert.Equal(2, target.SelectedItems.Count); + Assert.Equal(2, target.Selection.SelectedItems.Count); _mouse.Click(thenContainer, MouseButton.Right); - Assert.Equal(1, target.SelectedItems.Count); + Assert.Equal(1, target.Selection.SelectedItems.Count); } [Fact] @@ -819,7 +818,7 @@ namespace Avalonia.Controls.UnitTests _mouse.Click(fromContainer); _mouse.Click(toContainer, MouseButton.Right, modifiers: InputModifiers.Shift); - Assert.Equal(1, target.SelectedItems.Count); + Assert.Equal(1, target.Selection.SelectedItems.Count); } [Fact] @@ -849,7 +848,7 @@ namespace Avalonia.Controls.UnitTests _mouse.Click(fromContainer); _mouse.Click(toContainer, MouseButton.Right, modifiers: InputModifiers.Control); - Assert.Equal(1, target.SelectedItems.Count); + Assert.Equal(1, target.Selection.SelectedItems.Count); } [Fact] From 0f9ceb25b740b05a84d7e9660973607aedf90373 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 17 Feb 2020 12:52:08 +0100 Subject: [PATCH 060/663] Make ScrollIntoView accept an index, not an item. Fixes #2569. --- src/Avalonia.Controls/ComboBox.cs | 2 +- .../Presenters/IItemsPresenter.cs | 2 +- .../Presenters/ItemVirtualizer.cs | 4 ++-- .../Presenters/ItemVirtualizerNone.cs | 15 +++++---------- .../Presenters/ItemVirtualizerSimple.cs | 15 +++++---------- .../Presenters/ItemsPresenter.cs | 4 ++-- .../Presenters/ItemsPresenterBase.cs | 2 +- .../Primitives/SelectingItemsControl.cs | 15 +++++++++------ src/Avalonia.Dialogs/ManagedFileChooser.xaml.cs | 2 +- .../ItemsPresenterTests_Virtualization_Simple.cs | 8 ++++---- 10 files changed, 31 insertions(+), 38 deletions(-) diff --git a/src/Avalonia.Controls/ComboBox.cs b/src/Avalonia.Controls/ComboBox.cs index 0722802962..724c155efd 100644 --- a/src/Avalonia.Controls/ComboBox.cs +++ b/src/Avalonia.Controls/ComboBox.cs @@ -291,7 +291,7 @@ namespace Avalonia.Controls if (container == null && SelectedIndex != -1) { - ScrollIntoView(Selection.SelectedItem); + ScrollIntoView(Selection.SelectedIndex); container = ItemContainerGenerator.ContainerFromIndex(selectedIndex); } diff --git a/src/Avalonia.Controls/Presenters/IItemsPresenter.cs b/src/Avalonia.Controls/Presenters/IItemsPresenter.cs index c4acf1ebef..a487ee390b 100644 --- a/src/Avalonia.Controls/Presenters/IItemsPresenter.cs +++ b/src/Avalonia.Controls/Presenters/IItemsPresenter.cs @@ -14,6 +14,6 @@ namespace Avalonia.Controls.Presenters void ItemsChanged(NotifyCollectionChangedEventArgs e); - void ScrollIntoView(object item); + void ScrollIntoView(int index); } } diff --git a/src/Avalonia.Controls/Presenters/ItemVirtualizer.cs b/src/Avalonia.Controls/Presenters/ItemVirtualizer.cs index ae52e733b7..a25855ae49 100644 --- a/src/Avalonia.Controls/Presenters/ItemVirtualizer.cs +++ b/src/Avalonia.Controls/Presenters/ItemVirtualizer.cs @@ -257,8 +257,8 @@ namespace Avalonia.Controls.Presenters /// /// Scrolls the specified item into view. /// - /// The item. - public virtual void ScrollIntoView(object item) + /// The index of the item. + public virtual void ScrollIntoView(int index) { } diff --git a/src/Avalonia.Controls/Presenters/ItemVirtualizerNone.cs b/src/Avalonia.Controls/Presenters/ItemVirtualizerNone.cs index 56f64779f6..f0b7fb41ff 100644 --- a/src/Avalonia.Controls/Presenters/ItemVirtualizerNone.cs +++ b/src/Avalonia.Controls/Presenters/ItemVirtualizerNone.cs @@ -67,18 +67,13 @@ namespace Avalonia.Controls.Presenters /// /// Scrolls the specified item into view. /// - /// The item. - public override void ScrollIntoView(object item) + /// The index of the item. + public override void ScrollIntoView(int index) { - if (Items != null) + if (index != -1) { - var index = Items.IndexOf(item); - - if (index != -1) - { - var container = Owner.ItemContainerGenerator.ContainerFromIndex(index); - container?.BringIntoView(); - } + var container = Owner.ItemContainerGenerator.ContainerFromIndex(index); + container?.BringIntoView(); } } diff --git a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs index d27de7a80d..2139c85f31 100644 --- a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs +++ b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs @@ -289,20 +289,15 @@ namespace Avalonia.Controls.Presenters break; } - return ScrollIntoView(newItemIndex); + return ScrollIntoViewCore(newItemIndex); } /// - public override void ScrollIntoView(object item) + public override void ScrollIntoView(int index) { - if (Items != null) + if (index != -1) { - var index = Items.IndexOf(item); - - if (index != -1) - { - ScrollIntoView(index); - } + ScrollIntoViewCore(index); } } @@ -514,7 +509,7 @@ namespace Avalonia.Controls.Presenters /// /// The item index. /// The container that was brought into view. - private IControl ScrollIntoView(int index) + private IControl ScrollIntoViewCore(int index) { var panel = VirtualizingPanel; var generator = Owner.ItemContainerGenerator; diff --git a/src/Avalonia.Controls/Presenters/ItemsPresenter.cs b/src/Avalonia.Controls/Presenters/ItemsPresenter.cs index ab40fbd53b..51e6b80d60 100644 --- a/src/Avalonia.Controls/Presenters/ItemsPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ItemsPresenter.cs @@ -120,9 +120,9 @@ namespace Avalonia.Controls.Presenters return Virtualizer?.GetControlInDirection(direction, from); } - public override void ScrollIntoView(object item) + public override void ScrollIntoView(int index) { - Virtualizer?.ScrollIntoView(item); + Virtualizer?.ScrollIntoView(index); } /// diff --git a/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs b/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs index ef1f277162..f120d74b9a 100644 --- a/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs +++ b/src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs @@ -142,7 +142,7 @@ namespace Avalonia.Controls.Presenters } /// - public virtual void ScrollIntoView(object item) + public virtual void ScrollIntoView(int index) { } diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index b1a4379cae..dc08448cd1 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -339,11 +339,17 @@ namespace Avalonia.Controls.Primitives base.EndInit(); } + /// + /// Scrolls the specified item into view. + /// + /// The index of the item. + public void ScrollIntoView(int index) => Presenter?.ScrollIntoView(index); + /// /// Scrolls the specified item into view. /// /// The item. - public void ScrollIntoView(object item) => Presenter?.ScrollIntoView(item); + public void ScrollIntoView(object item) => ScrollIntoView(IndexOf(Items, item)); /// /// Tries to get the container that was the source of an event. @@ -665,12 +671,9 @@ namespace Avalonia.Controls.Primitives { if (e.PropertyName == nameof(SelectionModel.AnchorIndex) && AutoScrollToSelectedItem) { - var index = Selection.AnchorIndex.GetSize() > 0 ? Selection.AnchorIndex.GetAt(0) : -1; - var item = index != -1 ? ElementAt(Items, index) : null; - - if (item != null) + if (Selection.AnchorIndex.GetSize() > 0) { - ScrollIntoView(item); + ScrollIntoView(Selection.AnchorIndex.GetAt(0)); } } } diff --git a/src/Avalonia.Dialogs/ManagedFileChooser.xaml.cs b/src/Avalonia.Dialogs/ManagedFileChooser.xaml.cs index b967b40c0d..bf29381eab 100644 --- a/src/Avalonia.Dialogs/ManagedFileChooser.xaml.cs +++ b/src/Avalonia.Dialogs/ManagedFileChooser.xaml.cs @@ -81,7 +81,7 @@ namespace Avalonia.Dialogs if (indexOfPreselected > 1) { - _filesView.ScrollIntoView(model.Items[indexOfPreselected - 1]); + _filesView.ScrollIntoView(indexOfPreselected - 1); } } } diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs index 7a6cf0fba7..05124a282c 100644 --- a/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs +++ b/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs @@ -575,7 +575,7 @@ namespace Avalonia.Controls.UnitTests.Presenters target.Arrange(Rect.Empty); // Check for issue #591: this should not throw. - target.ScrollIntoView(items[0]); + target.ScrollIntoView(0); } } @@ -729,7 +729,7 @@ namespace Avalonia.Controls.UnitTests.Presenters var last = (target.Items as IList)[10]; - target.ScrollIntoView(last); + target.ScrollIntoView(10); Assert.Equal(new Vector(0, 1), ((ILogicalScrollable)target).Offset); Assert.Same(target.Panel.Children[9].DataContext, last); @@ -746,12 +746,12 @@ namespace Avalonia.Controls.UnitTests.Presenters var last = (target.Items as IList)[10]; - target.ScrollIntoView(last); + target.ScrollIntoView(10); Assert.Equal(new Vector(0, 1), ((ILogicalScrollable)target).Offset); Assert.Same(target.Panel.Children[9].DataContext, last); - target.ScrollIntoView(last); + target.ScrollIntoView(10); Assert.Equal(new Vector(0, 1), ((ILogicalScrollable)target).Offset); Assert.Same(target.Panel.Children[9].DataContext, last); From e80e52c3036fc76b3e2cd8d5be37096e2198d951 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 17 Feb 2020 15:27:35 +0100 Subject: [PATCH 061/663] TryGetValue doesn't allow null as the value. --- .../Generators/TreeContainerIndex.cs | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Controls/Generators/TreeContainerIndex.cs b/src/Avalonia.Controls/Generators/TreeContainerIndex.cs index 0da84008f6..ca417e0d45 100644 --- a/src/Avalonia.Controls/Generators/TreeContainerIndex.cs +++ b/src/Avalonia.Controls/Generators/TreeContainerIndex.cs @@ -97,9 +97,13 @@ namespace Avalonia.Controls.Generators /// The container, or null of not found. public IControl ContainerFromItem(object item) { - IControl result; - _itemToContainer.TryGetValue(item, out result); - return result; + if (item != null) + { + _itemToContainer.TryGetValue(item, out var result); + return result; + } + + return null; } /// @@ -109,9 +113,13 @@ namespace Avalonia.Controls.Generators /// The item, or null of not found. public object ItemFromContainer(IControl container) { - object result; - _containerToItem.TryGetValue(container, out result); - return result; + if (container != null) + { + _containerToItem.TryGetValue(container, out var result); + return result; + } + + return null; } } } From 6c0265b6e87924b89780d9f5c994e951b991a179 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 22 Feb 2020 10:53:20 +0100 Subject: [PATCH 062/663] Fix comment. Fix spelling, and `VectorView` is `IReadOnlyList` in C#. --- src/Avalonia.Controls/SelectionModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/SelectionModel.cs b/src/Avalonia.Controls/SelectionModel.cs index 5eb2a2da0a..023c4ee65e 100644 --- a/src/Avalonia.Controls/SelectionModel.cs +++ b/src/Avalonia.Controls/SelectionModel.cs @@ -227,7 +227,7 @@ namespace Avalonia.Controls } // Instead of creating a dumb vector that takes up the space for all the selected items, - // we create a custom VectorView implimentation that calls back using a delegate to find + // 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. From 1cbed83405318aeab89b99d833154a8dcf25eaa1 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 22 Feb 2020 10:54:01 +0100 Subject: [PATCH 063/663] Tweak SelectionModelChangeSet. - Make field readonly - Use lambda syntax so C# caches delegates --- src/Avalonia.Controls/SelectionModelChangeSet.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Controls/SelectionModelChangeSet.cs b/src/Avalonia.Controls/SelectionModelChangeSet.cs index bff84eca92..6e77dc5755 100644 --- a/src/Avalonia.Controls/SelectionModelChangeSet.cs +++ b/src/Avalonia.Controls/SelectionModelChangeSet.cs @@ -7,7 +7,7 @@ namespace Avalonia.Controls { internal class SelectionModelChangeSet { - private List _changes; + private readonly List _changes; public SelectionModelChangeSet(List changes) { @@ -63,7 +63,7 @@ namespace Avalonia.Controls { static int GetCount(SelectionNodeOperation info) => info.DeselectedCount; static List? GetRanges(SelectionNodeOperation info) => info.DeselectedRanges; - return GetIndexAt(infos, index, GetCount, GetRanges); + return GetIndexAt(infos, index, x => GetCount(x), x => GetRanges(x)); } private IndexPath GetSelectedIndexAt( @@ -72,7 +72,7 @@ namespace Avalonia.Controls { static int GetCount(SelectionNodeOperation info) => info.SelectedCount; static List? GetRanges(SelectionNodeOperation info) => info.SelectedRanges; - return GetIndexAt(infos, index, GetCount, GetRanges); + return GetIndexAt(infos, index, x => GetCount(x), x => GetRanges(x)); } private object? GetDeselectedItemAt( @@ -81,7 +81,7 @@ namespace Avalonia.Controls { static int GetCount(SelectionNodeOperation info) => info.Items != null ? info.DeselectedCount : 0; static List? GetRanges(SelectionNodeOperation info) => info.DeselectedRanges; - return GetItemAt(infos, index, GetCount, GetRanges); + return GetItemAt(infos, index, x => GetCount(x), x => GetRanges(x)); } private object? GetSelectedItemAt( @@ -90,7 +90,7 @@ namespace Avalonia.Controls { static int GetCount(SelectionNodeOperation info) => info.Items != null ? info.SelectedCount : 0; static List? GetRanges(SelectionNodeOperation info) => info.SelectedRanges; - return GetItemAt(infos, index, GetCount, GetRanges); + return GetItemAt(infos, index, x => GetCount(x), x => GetRanges(x)); } private IndexPath GetIndexAt( From 170eb56e043448c2fbd061287908c08bc46d5e5c Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 4 Mar 2020 18:46:20 +0300 Subject: [PATCH 064/663] Better error and cleanup handling --- src/Avalonia.OpenGL/GlInterface.cs | 4 + src/Avalonia.OpenGL/OpenGlControlBase.cs | 129 ++++++++++++++++------- 2 files changed, 97 insertions(+), 36 deletions(-) diff --git a/src/Avalonia.OpenGL/GlInterface.cs b/src/Avalonia.OpenGL/GlInterface.cs index e6107d2349..b3e8f09e20 100644 --- a/src/Avalonia.OpenGL/GlInterface.cs +++ b/src/Avalonia.OpenGL/GlInterface.cs @@ -92,6 +92,10 @@ namespace Avalonia.OpenGL [GlEntryPoint("glGenRenderbuffers")] public GlGenRenderbuffers GenRenderbuffers { get; } + public delegate void GlDeleteRenderbuffers(int count, int[] renderbuffers); + [GlEntryPoint("glDeleteRenderbuffers")] + public GlDeleteTextures DeleteRenderbuffers { get; } + public delegate void GlBindRenderbuffer(int target, int fb); [GlEntryPoint("glBindRenderbuffer")] public GlBindRenderbuffer BindRenderbuffer { get; } diff --git a/src/Avalonia.OpenGL/OpenGlControlBase.cs b/src/Avalonia.OpenGL/OpenGlControlBase.cs index b0b83f36fc..f532944709 100644 --- a/src/Avalonia.OpenGL/OpenGlControlBase.cs +++ b/src/Avalonia.OpenGL/OpenGlControlBase.cs @@ -1,7 +1,10 @@ using System; using Avalonia.Controls; +using Avalonia.Logging; using Avalonia.Media; using Avalonia.OpenGL.Imaging; +using Avalonia.Rendering; +using Avalonia.VisualTree; using static Avalonia.OpenGL.GlConsts; namespace Avalonia.OpenGL @@ -11,19 +14,21 @@ namespace Avalonia.OpenGL private IGlContext _context; private int _fb, _texture, _renderBuffer; private OpenGlTextureBitmap _bitmap; - private Size _oldSize; + private PixelSize _oldSize; + private bool _glFailed; protected GlDisplayType DisplayType { get; private set; } public sealed override void Render(DrawingContext context) { if(!EnsureInitialized()) return; + using (_context.MakeCurrent()) { using (_bitmap.Lock()) { var gl = _context.Display.GlInterface; gl.BindFramebuffer(GL_FRAMEBUFFER, _fb); - if (_oldSize != Bounds.Size) + if (_oldSize != GetPixelSize()) ResizeTexture(gl); OnOpenGlRender(gl, _fb); @@ -35,28 +40,39 @@ namespace Avalonia.OpenGL base.Render(context); } - protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + void DoCleanup(bool callUserDeinit) { if (_context != null) { using (_context.MakeCurrent()) { - OnOpenGlDeinit(_context.Display.GlInterface, _fb); var gl = _context.Display.GlInterface; gl.BindTexture(GL_TEXTURE_2D, 0); gl.BindFramebuffer(GL_FRAMEBUFFER, 0); gl.DeleteFramebuffers(1, new[] { _fb }); - using (_bitmap.Lock()) - { + using (_bitmap.Lock()) _bitmap.SetTexture(0, 0, new PixelSize(1, 1), 1); - gl.DeleteTextures(1, new[] { _texture }); - } + gl.DeleteTextures(1, new[] { _texture }); + gl.DeleteRenderbuffers(1, new[] { _renderBuffer }); _bitmap.Dispose(); - _context.Dispose(); - _context = null; + try + { + if (callUserDeinit) + OnOpenGlDeinit(_context.Display.GlInterface, _fb); + } + finally + { + _context.Dispose(); + _context = null; + } } } + } + + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + DoCleanup(true); base.OnDetachedFromVisualTree(e); } @@ -65,45 +81,84 @@ namespace Avalonia.OpenGL if (_context != null) return true; + if (_glFailed) + return false; + var feature = AvaloniaLocator.Current.GetService(); if (feature == null) return false; - _context = feature.CreateContext(); + try + { + _context = feature.CreateContext(); + + } + catch (Exception e) + { + Logger.TryGet(LogEventLevel.Error)?.Log("OpenGL", "OpenGlControlBase", + "Unable to initialize OpenGL: unable to create additional OpenGL context: {exception}", e); + _glFailed = true; + return false; + } + DisplayType = feature.Display.Type; try { _bitmap = new OpenGlTextureBitmap(); } - catch (PlatformNotSupportedException) + catch (Exception e) { _context.Dispose(); _context = null; + Logger.TryGet(LogEventLevel.Error)?.Log("OpenGL", "OpenGlControlBase", + "Unable to initialize OpenGL: unable to create OpenGlTextureBitmap: {exception}", e); + _glFailed = true; return false; } using (_context.MakeCurrent()) { - _oldSize = Bounds.Size; - var gl = _context.Display.GlInterface; - var oneArr = new int[1]; - gl.GenFramebuffers(1, oneArr); - _fb = oneArr[0]; - gl.BindFramebuffer(GL_FRAMEBUFFER, _fb); - - gl.GenTextures(1, oneArr); - _texture = oneArr[0]; - gl.BindTexture(GL_TEXTURE_2D, _texture); - ResizeTexture(gl); - - gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0); - - var status = gl.CheckFramebufferStatus(GL_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE) + try { - //TODO: Cleanup - return false; + _oldSize = GetPixelSize(); + var gl = _context.Display.GlInterface; + var oneArr = new int[1]; + gl.GenFramebuffers(1, oneArr); + _fb = oneArr[0]; + gl.BindFramebuffer(GL_FRAMEBUFFER, _fb); + + gl.GenTextures(1, oneArr); + _texture = oneArr[0]; + gl.BindTexture(GL_TEXTURE_2D, _texture); + ResizeTexture(gl); + + gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0); + + var status = gl.CheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) + { + int code; + while ((code = gl.GetError()) != 0) + Logger.TryGet(LogEventLevel.Error)?.Log("OpenGL", "OpenGlControlBase", + "Unable to initialize OpenGL FBO: {code}", code); + + _glFailed = true; + return false; + } + } + catch(Exception e) + { + Logger.TryGet(LogEventLevel.Error)?.Log("OpenGL", "OpenGlControlBase", + "Unable to initialize OpenGL FBO: {exception}", e); + _glFailed = true; } - OnOpenGlInit(_context.Display.GlInterface, _fb); + + if (!_glFailed) + OnOpenGlInit(_context.Display.GlInterface, _fb); + } + + if (_glFailed) + { + DoCleanup(false); } return true; @@ -118,7 +173,7 @@ namespace Avalonia.OpenGL gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - //TODO: destroy the previous one + gl.DeleteRenderbuffers(1, new[] { _renderBuffer }); var oneArr = new int[1]; gl.GenRenderbuffers(1, oneArr); _renderBuffer = oneArr[0]; @@ -131,10 +186,12 @@ namespace Avalonia.OpenGL _bitmap.SetTexture(_texture, GL_RGBA8, size, 1); } - //TODO: dpi - PixelSize GetPixelSize() => - new PixelSize(Math.Max(1, (int)Bounds.Width), - Math.Max(1, (int)Bounds.Height)); + PixelSize GetPixelSize() + { + var scaling = VisualRoot.RenderScaling; + return new PixelSize(Math.Max(1, (int)(Bounds.Width * scaling)), + Math.Max(1, (int)(Bounds.Height * scaling))); + } protected virtual void OnOpenGlInit(GlInterface gl, int fb) From d9387b43179b6e4408b1034d4b2e05817d5f1d72 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 4 Mar 2020 17:56:01 +0200 Subject: [PATCH 065/663] Shader GLES compatibility fixes --- .../ControlCatalog/Pages/OpenGlPage.xaml.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs b/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs index 6931e07dfd..358efea4d0 100644 --- a/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs +++ b/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs @@ -99,10 +99,10 @@ namespace ControlCatalog.Pages uniform float uDisco; void main() { - float discoScale = sin(uTime*10)/10; - float distortionX = 1 + uDisco * cos(uTime*20)/10; + float discoScale = sin(uTime * 10.0) / 10.0; + float distortionX = 1.0 + uDisco * cos(uTime * 20.0) / 10.0; - float scale = 1 + uDisco * discoScale; + float scale = 1.0 + uDisco * discoScale; vec3 scaledPos = aPos; scaledPos.x = scaledPos.x * distortionX; @@ -127,15 +127,15 @@ namespace ControlCatalog.Pages void main() { float y = (VecPos.y - uMinY) / (uMaxY - uMinY); - float c = cos(atan(VecPos.x, VecPos.z)*20.0+uTime*40.0+y*50); - float s = sin(-atan(VecPos.z, VecPos.x)*20.0-uTime*20.0-y*30); + float c = cos(atan(VecPos.x, VecPos.z) * 20.0 + uTime * 40.0 + y * 50.0); + float s = sin(-atan(VecPos.z, VecPos.x) * 20.0 - uTime * 20.0 - y * 30.0); vec3 discoColor = vec3( - 0.5 + abs(0.5-y)*cos(uTime*10), - 0.25 + (smoothstep(0.3, 0.8, y)*(0.5-c/4)), - 0.25 + abs((smoothstep(0.1, 0.4, y)*(0.5-s/4)))); + 0.5 + abs(0.5 - y) * cos(uTime * 10), + 0.25 + (smoothstep(0.3, 0.8, y) * (0.5 - c/4)), + 0.25 + abs((smoothstep(0.1, 0.4, y) * (0.5 - s/4)))); - vec3 objectColor = vec3((1.0 - y), 0.40 + y / 4.0, y * 0.75+0.25); + vec3 objectColor = vec3((1.0 - y), 0.40 + y / 4.0, y * 0.75 + 0.25); objectColor = objectColor * (1-uDisco) + discoColor * uDisco; float ambientStrength = 0.3; From 1dea1a32bb43a2cc8eaf3d07400ddb82dd6bbb22 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 4 Mar 2020 19:15:13 +0300 Subject: [PATCH 066/663] GLES compat --- samples/ControlCatalog/Pages/OpenGlPage.xaml.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs b/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs index 358efea4d0..eb75d905a6 100644 --- a/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs +++ b/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs @@ -85,7 +85,7 @@ namespace ControlCatalog.Pages private string WithVersionAndPrecision(string shader, string precision) => WithVersion(DisplayType == GlDisplayType.OpenGLES ? $"precision {precision};\n{shader}" : shader); - private string VertexShaderSource => WithVersion(@" + private string VertexShaderSource => WithVersionAndPrecision(@" attribute vec3 aPos; attribute vec3 aNormal; uniform mat4 uModel; @@ -113,7 +113,7 @@ namespace ControlCatalog.Pages VecPos = aPos; Normal = normalize(vec3(uModel * vec4(aNormal, 1.0))); } -"); +", "mediump float"); private string FragmentShaderSource => WithVersionAndPrecision(@" varying vec3 FragPos; @@ -131,12 +131,12 @@ namespace ControlCatalog.Pages float s = sin(-atan(VecPos.z, VecPos.x) * 20.0 - uTime * 20.0 - y * 30.0); vec3 discoColor = vec3( - 0.5 + abs(0.5 - y) * cos(uTime * 10), - 0.25 + (smoothstep(0.3, 0.8, y) * (0.5 - c/4)), - 0.25 + abs((smoothstep(0.1, 0.4, y) * (0.5 - s/4)))); + 0.5 + abs(0.5 - y) * cos(uTime * 10.0), + 0.25 + (smoothstep(0.3, 0.8, y) * (0.5 - c / 4.0)), + 0.25 + abs((smoothstep(0.1, 0.4, y) * (0.5 - s / 4.0)))); vec3 objectColor = vec3((1.0 - y), 0.40 + y / 4.0, y * 0.75 + 0.25); - objectColor = objectColor * (1-uDisco) + discoColor * uDisco; + objectColor = objectColor * (1.0 - uDisco) + discoColor * uDisco; float ambientStrength = 0.3; vec3 lightColor = vec3(1.0, 1.0, 1.0); From db3e2780f1d4d29d96dff389d03a611142acaed7 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 4 Mar 2020 21:07:43 +0300 Subject: [PATCH 067/663] OSX support --- samples/ControlCatalog/Pages/OpenGlPage.xaml | 3 + .../ControlCatalog/Pages/OpenGlPage.xaml.cs | 58 +++++++++++++++---- src/Avalonia.Native/GlPlatformFeature.cs | 7 ++- src/Avalonia.OpenGL/GlInterfaceBase.cs | 3 +- 4 files changed, 59 insertions(+), 12 deletions(-) diff --git a/samples/ControlCatalog/Pages/OpenGlPage.xaml b/samples/ControlCatalog/Pages/OpenGlPage.xaml index afd8001263..0eb09004ba 100644 --- a/samples/ControlCatalog/Pages/OpenGlPage.xaml +++ b/samples/ControlCatalog/Pages/OpenGlPage.xaml @@ -4,6 +4,9 @@ xmlns:pages="clr-namespace:ControlCatalog.Pages"> + + + Yaw diff --git a/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs b/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs index eb75d905a6..12a3c17fe3 100644 --- a/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs +++ b/samples/ControlCatalog/Pages/OpenGlPage.xaml.cs @@ -66,6 +66,16 @@ namespace ControlCatalog.Pages set => SetAndRaise(DiscoProperty, ref _disco, value); } + private string _info; + + public static readonly DirectProperty InfoProperty = + AvaloniaProperty.RegisterDirect("Info", o => o.Info, (o, v) => o.Info = v); + + public string Info + { + get => _info; + private set => SetAndRaise(InfoProperty, ref _info, value); + } static OpenGlPageControl() { @@ -79,13 +89,33 @@ namespace ControlCatalog.Pages private int _indexBufferObject; private int _vertexArrayObject; - private string WithVersion(string shader) => - $"#version {(DisplayType == GlDisplayType.OpenGl ? 120 : 100)}\n" + shader; + private string GetShader(bool fragment, string shader) + { + var version = (DisplayType == GlDisplayType.OpenGl ? + RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 150 : 120 : + 100); + var data = "#version " + version + "\n"; + if (DisplayType == GlDisplayType.OpenGLES) + data += "precision mediump float\n"; + if (version >= 150) + { + shader = shader.Replace("attribute", "in"); + if (fragment) + shader = shader + .Replace("varying", "in") + .Replace("//DECLAREGLFRAG", "out vec4 outFragColor;") + .Replace("gl_FragColor", "outFragColor"); + else + shader = shader.Replace("varying", "out"); + } + + data += shader; + + return data; + } - private string WithVersionAndPrecision(string shader, string precision) => - WithVersion(DisplayType == GlDisplayType.OpenGLES ? $"precision {precision};\n{shader}" : shader); - private string VertexShaderSource => WithVersionAndPrecision(@" + private string VertexShaderSource => GetShader(false, @" attribute vec3 aPos; attribute vec3 aNormal; uniform mat4 uModel; @@ -113,9 +143,9 @@ namespace ControlCatalog.Pages VecPos = aPos; Normal = normalize(vec3(uModel * vec4(aNormal, 1.0))); } -", "mediump float"); +"); - private string FragmentShaderSource => WithVersionAndPrecision(@" + private string FragmentShaderSource => GetShader(true, @" varying vec3 FragPos; varying vec3 VecPos; varying vec3 Normal; @@ -123,6 +153,7 @@ namespace ControlCatalog.Pages uniform float uMinY; uniform float uTime; uniform float uDisco; + //DECLAREGLFRAG void main() { @@ -154,7 +185,7 @@ namespace ControlCatalog.Pages gl_FragColor = vec4(result, 1.0); } -", "mediump float"); +"); [StructLayout(LayoutKind.Sequential, Pack = 4)] private struct Vertex @@ -224,6 +255,9 @@ namespace ControlCatalog.Pages protected unsafe override void OnOpenGlInit(GlInterface GL, int fb) { CheckError(GL); + + Info = $"Renderer: {GL.GetString(GL_RENDERER)} Version: {GL.GetString(GL_VERSION)}"; + // Load the source of the vertex shader and compile it. _vertexShader = GL.CreateShader(GL_VERTEX_SHADER); Console.WriteLine(GL.CompileShaderAndGetError(_vertexShader, VertexShaderSource)); @@ -247,6 +281,7 @@ namespace ControlCatalog.Pages _vertexBufferObject = GL.GenBuffer(); // Bind the VBO and copy the vertex data into it. GL.BindBuffer(GL_ARRAY_BUFFER, _vertexBufferObject); + CheckError(GL); var vertexSize = Marshal.SizeOf(); fixed (void* pdata = _points) GL.BufferData(GL_ARRAY_BUFFER, new IntPtr(_points.Length * vertexSize), @@ -254,13 +289,14 @@ namespace ControlCatalog.Pages _indexBufferObject = GL.GenBuffer(); GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferObject); + CheckError(GL); fixed (void* pdata = _indices) GL.BufferData(GL_ELEMENT_ARRAY_BUFFER, new IntPtr(_indices.Length * sizeof(ushort)), new IntPtr(pdata), GL_STATIC_DRAW); - + CheckError(GL); _vertexArrayObject = GL.GenVertexArray(); GL.BindVertexArray(_vertexArrayObject); - + CheckError(GL); GL.VertexAttribPointer(positionLocation, 3, GL_FLOAT, 0, vertexSize, IntPtr.Zero); GL.VertexAttribPointer(normalLocation, 3, GL_FLOAT, @@ -300,6 +336,7 @@ namespace ControlCatalog.Pages GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferObject); GL.BindVertexArray(_vertexArrayObject); GL.UseProgram(_shaderProgram); + CheckError(GL); var projection = Matrix4x4.CreatePerspectiveFieldOfView((float)(Math.PI / 4), (float)(Bounds.Width / Bounds.Height), 0.01f, 1000); @@ -321,6 +358,7 @@ namespace ControlCatalog.Pages GL.Uniform1f(minYLoc, _minY); GL.Uniform1f(timeLoc, (float)St.Elapsed.TotalSeconds); GL.Uniform1f(discoLoc, _disco); + CheckError(GL); GL.DrawElements(GL_TRIANGLES, _indices.Length, GL_UNSIGNED_SHORT, IntPtr.Zero); CheckError(GL); diff --git a/src/Avalonia.Native/GlPlatformFeature.cs b/src/Avalonia.Native/GlPlatformFeature.cs index b86404d15b..83c741658a 100644 --- a/src/Avalonia.Native/GlPlatformFeature.cs +++ b/src/Avalonia.Native/GlPlatformFeature.cs @@ -8,8 +8,11 @@ namespace Avalonia.Native { class GlPlatformFeature : IWindowingPlatformGlFeature { + private readonly IAvnGlDisplay _display; + public GlPlatformFeature(IAvnGlDisplay display) { + _display = display; var immediate = display.CreateContext(null); var deferred = display.CreateContext(immediate); GlDisplay = new GlDisplay(display, immediate.SampleCount, immediate.StencilSize); @@ -22,7 +25,9 @@ namespace Avalonia.Native internal GlContext DeferredContext { get; } internal GlDisplay GlDisplay; public IGlDisplay Display => GlDisplay; - public IGlContext CreateContext() => new GlContext(GlDisplay, ((GlContext)ImmediateContext).Context); + + public IGlContext CreateContext() => new GlContext(GlDisplay, + _display.CreateContext(((GlContext)ImmediateContext).Context)); } class GlDisplay : IGlDisplay diff --git a/src/Avalonia.OpenGL/GlInterfaceBase.cs b/src/Avalonia.OpenGL/GlInterfaceBase.cs index 445777b065..7715438389 100644 --- a/src/Avalonia.OpenGL/GlInterfaceBase.cs +++ b/src/Avalonia.OpenGL/GlInterfaceBase.cs @@ -30,7 +30,8 @@ namespace Avalonia.OpenGL foreach (var ep in a.EntryPoints) { proc = getProcAddress(ep, true); - + if(proc != IntPtr.Zero) + break; } if (proc == IntPtr.Zero && !a.Optional) From 3267c8fb27fa03113026525e8f409fac36d2160b Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 4 Mar 2020 21:08:44 +0300 Subject: [PATCH 068/663] Proper page position --- samples/ControlCatalog/MainView.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index a4fc1bfd82..bbfa3be4ab 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -14,7 +14,6 @@ - @@ -48,6 +47,7 @@ + From dd481fd35e50a4b7db26234a2e12b3d3b2a5c66d Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 8 Mar 2020 12:31:52 +0300 Subject: [PATCH 069/663] Bind texture before resizing. Also reset texture and renderbuffer to previous values --- src/Avalonia.OpenGL/OpenGlControlBase.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.OpenGL/OpenGlControlBase.cs b/src/Avalonia.OpenGL/OpenGlControlBase.cs index f532944709..e133938701 100644 --- a/src/Avalonia.OpenGL/OpenGlControlBase.cs +++ b/src/Avalonia.OpenGL/OpenGlControlBase.cs @@ -128,7 +128,7 @@ namespace Avalonia.OpenGL gl.GenTextures(1, oneArr); _texture = oneArr[0]; - gl.BindTexture(GL_TEXTURE_2D, _texture); + ResizeTexture(gl); gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0); @@ -167,12 +167,17 @@ namespace Avalonia.OpenGL void ResizeTexture(GlInterface gl) { var size = GetPixelSize(); + + gl.GetIntegerv( GL_TEXTURE_BINDING_2D, out var oldTexture); + gl.BindTexture(GL_TEXTURE_2D, _texture); gl.TexImage2D(GL_TEXTURE_2D, 0, DisplayType == GlDisplayType.OpenGLES ? GL_RGBA : GL_RGBA8, size.Width, size.Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, IntPtr.Zero); gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl.BindTexture(GL_TEXTURE_2D, oldTexture); + gl.GetIntegerv(GL_RENDERBUFFER_BINDING, out var oldRenderBuffer); gl.DeleteRenderbuffers(1, new[] { _renderBuffer }); var oneArr = new int[1]; gl.GenRenderbuffers(1, oneArr); @@ -182,6 +187,7 @@ namespace Avalonia.OpenGL DisplayType == GlDisplayType.OpenGLES ? GL_DEPTH_COMPONENT16 : GL_DEPTH_COMPONENT, size.Width, size.Height); gl.FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _renderBuffer); + gl.BindRenderbuffer(GL_RENDERBUFFER, oldRenderBuffer); using (_bitmap.Lock()) _bitmap.SetTexture(_texture, GL_RGBA8, size, 1); } From 9fa1ab92827d43895d3cb0241457dcaef5db7af7 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 6 Apr 2020 22:48:07 +0200 Subject: [PATCH 070/663] Add failing tests for shared context menus. Tests for #497. --- .../ContextMenuTests.cs | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs b/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs index 5a47a86e51..a54b93cfff 100644 --- a/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs @@ -1,7 +1,10 @@ using System; using Avalonia.Input; +using Avalonia.Markup.Xaml; +using Avalonia.Markup.Xaml.MarkupExtensions; using Avalonia.Platform; using Avalonia.UnitTests; +using Castle.DynamicProxy.Generators; using Moq; using Xunit; @@ -168,6 +171,90 @@ namespace Avalonia.Controls.UnitTests } } + [Fact] + public void Context_Menu_In_Resources_Can_Be_Shared() + { + using (Application()) + { + var xaml = @" + + + + Foo + + + + + + + +"; + + var loader = new AvaloniaXamlLoader(); + var window = (Window)loader.Load(xaml); + var target1 = window.Find("target1"); + var target2 = window.Find("target2"); + var mouse = new MouseTestHelper(); + + Assert.NotNull(target1.ContextMenu); + Assert.NotNull(target2.ContextMenu); + Assert.Same(target1.ContextMenu, target2.ContextMenu); + + window.Show(); + + var menu = target1.ContextMenu; + mouse.Click(target1, MouseButton.Right); + Assert.True(menu.IsOpen); + mouse.Click(target2, MouseButton.Right); + Assert.True(menu.IsOpen); + } + } + + [Fact] + public void Context_Menu_Can_Be_Set_In_Style() + { + using (Application()) + { + var xaml = @" + + + + + + + + + +"; + + var loader = new AvaloniaXamlLoader(); + var window = (Window)loader.Load(xaml); + var target1 = window.Find("target1"); + var target2 = window.Find("target2"); + var mouse = new MouseTestHelper(); + + Assert.NotNull(target1.ContextMenu); + Assert.NotNull(target2.ContextMenu); + Assert.Same(target1.ContextMenu, target2.ContextMenu); + + window.Show(); + + var menu = target1.ContextMenu; + mouse.Click(target1, MouseButton.Right); + Assert.True(menu.IsOpen); + mouse.Click(target2, MouseButton.Right); + Assert.True(menu.IsOpen); + } + } + [Fact(Skip = "The only reason this test was 'passing' before was that the author forgot to call Window.ApplyTemplate()")] public void Cancelling_Closing_Leaves_ContextMenuOpen() { From 204894854120d9b3fa898906ffd74202ccc7a05a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 6 Apr 2020 22:48:25 +0200 Subject: [PATCH 071/663] Allow context menus to be shared. Either via resources or styles. --- src/Avalonia.Controls/ContextMenu.cs | 39 ++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs index 1735599988..86499530da 100644 --- a/src/Avalonia.Controls/ContextMenu.cs +++ b/src/Avalonia.Controls/ContextMenu.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.ComponentModel; using System.Linq; using Avalonia.Controls.Generators; @@ -9,18 +10,19 @@ using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Layout; using Avalonia.LogicalTree; +using Avalonia.Styling; namespace Avalonia.Controls { /// /// A control context menu. /// - public class ContextMenu : MenuBase + public class ContextMenu : MenuBase, ISetterValue { private static readonly ITemplate DefaultPanel = new FuncTemplate(() => new StackPanel { Orientation = Orientation.Vertical }); private Popup _popup; - private Control _attachedControl; + private List _attachedControls; private IInputElement _previousFocus; /// @@ -74,13 +76,14 @@ namespace Avalonia.Controls if (e.OldValue is ContextMenu oldMenu) { control.PointerReleased -= ControlPointerReleased; - oldMenu._attachedControl = null; + oldMenu._attachedControls?.Remove(control); ((ISetLogicalParent)oldMenu._popup)?.SetParent(null); } if (e.NewValue is ContextMenu newMenu) { - newMenu._attachedControl = control; + newMenu._attachedControls ??= new List(); + newMenu._attachedControls.Add(control); control.PointerReleased += ControlPointerReleased; } } @@ -96,18 +99,22 @@ namespace Avalonia.Controls /// The control. public void Open(Control control) { - if (control is null && _attachedControl is null) + if (control is null && (_attachedControls is null || _attachedControls.Count == 0)) { throw new ArgumentNullException(nameof(control)); } - if (control is object && _attachedControl is object && control != _attachedControl) + if (control is object && + _attachedControls is object && + !_attachedControls.Contains(control)) { throw new ArgumentException( "Cannot show ContentMenu on a different control to the one it is attached to.", nameof(control)); } + control ??= _attachedControls[0]; + if (IsOpen) { return; @@ -126,7 +133,12 @@ namespace Avalonia.Controls _popup.Closed += PopupClosed; } - ((ISetLogicalParent)_popup).SetParent(control); + if (_popup.Parent != control) + { + ((ISetLogicalParent)_popup).SetParent(null); + ((ISetLogicalParent)_popup).SetParent(control); + } + _popup.Child = this; _popup.IsOpen = true; @@ -155,6 +167,17 @@ namespace Avalonia.Controls } } + void ISetterValue.Initialize(ISetter setter) + { + // ContextMenu can be assigned to the ContextMenu property in a setter. This overrides + // the behavior defined in Control which requires controls to be wrapped in a