зеркало из https://github.com/DeGsoft/maui-linux.git
Implement FormsStack and FormsQueue internally for VS 2017 Compatibility (#8403)
* FormsStack and FormsQueue for VS 2017 Compat * - sync stable vs mac versions * - mono version * - gtk * - fix mono provision * Update Xamarin.Forms.Platform.Android/Xamarin.Forms.Platform.Android.csproj * - remove uwp changes * - add 2019 back in * Update azure-pipelines.yml
This commit is contained in:
Родитель
7865f64776
Коммит
34c346c521
|
@ -209,8 +209,8 @@ namespace Xamarin.Forms.Controls.Issues
|
|||
int _totalWidth = 0;
|
||||
int _totalHeight = 0;
|
||||
|
||||
Queue<string> _buttons = new Queue<string>();
|
||||
Queue<string> _result = new Queue<string>();
|
||||
FormsQueue<string> _buttons = new FormsQueue<string>();
|
||||
FormsQueue<string> _result = new FormsQueue<string>();
|
||||
|
||||
public void Tap(string button)
|
||||
{
|
||||
|
|
|
@ -367,9 +367,9 @@ namespace Xamarin.Forms
|
|||
|
||||
if ((context.Attributes & BindableContextAttributes.IsBeingSet) != 0)
|
||||
{
|
||||
Queue<SetValueArgs> delayQueue = context.DelayedSetters;
|
||||
FormsQueue<SetValueArgs> delayQueue = context.DelayedSetters;
|
||||
if (delayQueue == null)
|
||||
context.DelayedSetters = delayQueue = new Queue<SetValueArgs>();
|
||||
context.DelayedSetters = delayQueue = new FormsQueue<SetValueArgs>();
|
||||
|
||||
delayQueue.Enqueue(new SetValueArgs(property, context, value, currentlyApplying, attributes));
|
||||
}
|
||||
|
@ -378,7 +378,7 @@ namespace Xamarin.Forms
|
|||
context.Attributes |= BindableContextAttributes.IsBeingSet;
|
||||
SetValueActual(property, context, value, currentlyApplying, attributes, silent);
|
||||
|
||||
Queue<SetValueArgs> delayQueue = context.DelayedSetters;
|
||||
FormsQueue<SetValueArgs> delayQueue = context.DelayedSetters;
|
||||
if (delayQueue != null)
|
||||
{
|
||||
while (delayQueue.Count > 0)
|
||||
|
@ -527,7 +527,7 @@ namespace Xamarin.Forms
|
|||
{
|
||||
public BindableContextAttributes Attributes;
|
||||
public BindingBase Binding;
|
||||
public Queue<SetValueArgs> DelayedSetters;
|
||||
public FormsQueue<SetValueArgs> DelayedSetters;
|
||||
public BindableProperty Property;
|
||||
public object Value;
|
||||
}
|
||||
|
|
|
@ -376,7 +376,7 @@ namespace Xamarin.Forms
|
|||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public IEnumerable<Element> Descendants()
|
||||
{
|
||||
var queue = new Queue<Element>(16);
|
||||
var queue = new FormsQueue<Element>(16);
|
||||
queue.Enqueue(this);
|
||||
|
||||
while (queue.Count > 0)
|
||||
|
@ -513,7 +513,7 @@ namespace Xamarin.Forms
|
|||
|
||||
internal IEnumerable<Element> VisibleDescendants()
|
||||
{
|
||||
var queue = new Queue<Element>(16);
|
||||
var queue = new FormsQueue<Element>(16);
|
||||
queue.Enqueue(this);
|
||||
|
||||
while (queue.Count > 0)
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace Xamarin.Forms
|
|||
internal class LockingSemaphore
|
||||
{
|
||||
static readonly Task Completed = Task.FromResult(true);
|
||||
readonly Queue<TaskCompletionSource<bool>> _waiters = new Queue<TaskCompletionSource<bool>>();
|
||||
readonly FormsQueue<TaskCompletionSource<bool>> _waiters = new FormsQueue<TaskCompletionSource<bool>>();
|
||||
int _currentCount;
|
||||
|
||||
public LockingSemaphore(int initialCount)
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace Xamarin.Forms.Internals
|
|||
}
|
||||
public static List<Datum> Data = new List<Datum>(Capacity);
|
||||
|
||||
static Stack<Profile> Stack = new Stack<Profile>(Capacity);
|
||||
static FormsStack<Profile> Stack = new FormsStack<Profile>(Capacity);
|
||||
static int Depth = 0;
|
||||
static bool Running = false;
|
||||
static Stopwatch Stopwatch = new Stopwatch();
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace Xamarin.Forms.StyleSheets
|
|||
_reader = reader;
|
||||
}
|
||||
|
||||
readonly Queue<char> _cache = new Queue<char>();
|
||||
readonly FormsQueue<char> _cache = new FormsQueue<char>();
|
||||
|
||||
//skip comments
|
||||
//TODO unescape escaped sequences
|
||||
|
|
|
@ -83,7 +83,7 @@ namespace Xamarin.Forms
|
|||
// done before we attempt to make the new template.
|
||||
if (oldValue != null)
|
||||
{
|
||||
var queue = new Queue<Element>(16);
|
||||
var queue = new FormsQueue<Element>(16);
|
||||
queue.Enqueue((Element)self);
|
||||
|
||||
while (queue.Count > 0)
|
||||
|
|
|
@ -0,0 +1,514 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
/*=============================================================================
|
||||
**
|
||||
**
|
||||
** Purpose: A circular-array implementation of a generic queue.
|
||||
**
|
||||
**
|
||||
=============================================================================*/
|
||||
|
||||
/*
|
||||
* Copied from https://raw.githubusercontent.com/dotnet/corefx/9cf92cbef7cf5fcf46a1b556f9c6250e67d421ab/src/System.Collections/src/System/Collections/Generic/Queue.cs
|
||||
* Pre C# 8.0 features
|
||||
* */
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Xamarin.Forms
|
||||
{
|
||||
// A simple Queue of generic objects. Internally it is implemented as a
|
||||
// circular buffer, so Enqueue can be O(n). Dequeue is O(1).
|
||||
[DebuggerDisplay("Count = {Count}")]
|
||||
internal sealed class FormsQueue<T> : IEnumerable<T>,
|
||||
System.Collections.ICollection,
|
||||
IReadOnlyCollection<T>
|
||||
{
|
||||
private T[] _array;
|
||||
private int _head; // The index from which to dequeue if the queue isn't empty.
|
||||
private int _tail; // The index at which to enqueue if the queue isn't full.
|
||||
private int _size; // Number of elements.
|
||||
private int _version;
|
||||
bool _isReference;
|
||||
|
||||
private const int MinimumGrow = 4;
|
||||
private const int GrowFactor = 200; // double each time
|
||||
|
||||
// Creates a queue with room for capacity objects. The default initial
|
||||
// capacity and grow factor are used.
|
||||
public FormsQueue()
|
||||
{
|
||||
Init();
|
||||
_array = new T[0];
|
||||
}
|
||||
|
||||
// Creates a queue with room for capacity objects. The default grow factor
|
||||
// is used.
|
||||
public FormsQueue(int capacity)
|
||||
{
|
||||
Init();
|
||||
if (capacity < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(capacity), capacity, "ArgumentOutOfRange_NeedNonNegNum");
|
||||
_array = new T[capacity];
|
||||
}
|
||||
|
||||
// Fills a Queue with the elements of an ICollection. Uses the enumerator
|
||||
// to get each of the elements.
|
||||
public FormsQueue(IEnumerable<T> collection)
|
||||
{
|
||||
Init();
|
||||
if (collection == null)
|
||||
throw new ArgumentNullException(nameof(collection));
|
||||
|
||||
_array = FormsEnumerableHelpers.ToArray(collection, out _size);
|
||||
if (_size != _array.Length)
|
||||
_tail = _size;
|
||||
}
|
||||
void Init()
|
||||
{
|
||||
|
||||
#if NETSTANDARD1_0
|
||||
_isReference = !typeof(T).GetTypeInfo().IsValueType;
|
||||
#else
|
||||
_isReference = !typeof(T).IsValueType;
|
||||
#endif
|
||||
}
|
||||
// The original version of this is IsReferenceOrContainsReferences
|
||||
// But I don't think we have to satisfy the Contains reference portion of the check
|
||||
public bool IsReference()
|
||||
{
|
||||
return _isReference;
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return _size; }
|
||||
}
|
||||
|
||||
bool ICollection.IsSynchronized
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
object ICollection.SyncRoot => this;
|
||||
|
||||
// Removes all Objects from the queue.
|
||||
public void Clear()
|
||||
{
|
||||
if (_size != 0)
|
||||
{
|
||||
if (IsReference())
|
||||
{
|
||||
if (_head < _tail)
|
||||
{
|
||||
Array.Clear(_array, _head, _size);
|
||||
}
|
||||
else
|
||||
{
|
||||
Array.Clear(_array, _head, _array.Length - _head);
|
||||
Array.Clear(_array, 0, _tail);
|
||||
}
|
||||
}
|
||||
|
||||
_size = 0;
|
||||
}
|
||||
|
||||
_head = 0;
|
||||
_tail = 0;
|
||||
_version++;
|
||||
}
|
||||
|
||||
// CopyTo copies a collection into an Array, starting at a particular
|
||||
// index into the array.
|
||||
public void CopyTo(T[] array, int arrayIndex)
|
||||
{
|
||||
if (array == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(array));
|
||||
}
|
||||
|
||||
if (arrayIndex < 0 || arrayIndex > array.Length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(arrayIndex), arrayIndex, "ArgumentOutOfRange_Index");
|
||||
}
|
||||
|
||||
int arrayLen = array.Length;
|
||||
if (arrayLen - arrayIndex < _size)
|
||||
{
|
||||
throw new ArgumentException("Argument_InvalidOffLen");
|
||||
}
|
||||
|
||||
int numToCopy = _size;
|
||||
if (numToCopy == 0)
|
||||
return;
|
||||
|
||||
int firstPart = Math.Min(_array.Length - _head, numToCopy);
|
||||
Array.Copy(_array, _head, array, arrayIndex, firstPart);
|
||||
numToCopy -= firstPart;
|
||||
if (numToCopy > 0)
|
||||
{
|
||||
Array.Copy(_array, 0, array, arrayIndex + _array.Length - _head, numToCopy);
|
||||
}
|
||||
}
|
||||
|
||||
void ICollection.CopyTo(Array array, int index)
|
||||
{
|
||||
if (array == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(array));
|
||||
}
|
||||
|
||||
if (array.Rank != 1)
|
||||
{
|
||||
throw new ArgumentException("Arg_RankMultiDimNotSupported", nameof(array));
|
||||
}
|
||||
|
||||
if (array.GetLowerBound(0) != 0)
|
||||
{
|
||||
throw new ArgumentException("Arg_NonZeroLowerBound", nameof(array));
|
||||
}
|
||||
|
||||
int arrayLen = array.Length;
|
||||
if (index < 0 || index > arrayLen)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(index), index, "ArgumentOutOfRange_Index");
|
||||
}
|
||||
|
||||
if (arrayLen - index < _size)
|
||||
{
|
||||
throw new ArgumentException("Argument_InvalidOffLen");
|
||||
}
|
||||
|
||||
int numToCopy = _size;
|
||||
if (numToCopy == 0)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
int firstPart = (_array.Length - _head < numToCopy) ? _array.Length - _head : numToCopy;
|
||||
Array.Copy(_array, _head, array, index, firstPart);
|
||||
numToCopy -= firstPart;
|
||||
|
||||
if (numToCopy > 0)
|
||||
{
|
||||
Array.Copy(_array, 0, array, index + _array.Length - _head, numToCopy);
|
||||
}
|
||||
}
|
||||
catch (ArrayTypeMismatchException)
|
||||
{
|
||||
throw new ArgumentException("Argument_InvalidArrayType", nameof(array));
|
||||
}
|
||||
}
|
||||
|
||||
// Adds item to the tail of the queue.
|
||||
public void Enqueue(T item)
|
||||
{
|
||||
if (_size == _array.Length)
|
||||
{
|
||||
int newcapacity = (int)((long)_array.Length * (long)GrowFactor / 100);
|
||||
if (newcapacity < _array.Length + MinimumGrow)
|
||||
{
|
||||
newcapacity = _array.Length + MinimumGrow;
|
||||
}
|
||||
SetCapacity(newcapacity);
|
||||
}
|
||||
|
||||
_array[_tail] = item;
|
||||
MoveNext(ref _tail);
|
||||
_size++;
|
||||
_version++;
|
||||
}
|
||||
|
||||
// GetEnumerator returns an IEnumerator over this Queue. This
|
||||
// Enumerator will support removing.
|
||||
public Enumerator GetEnumerator()
|
||||
{
|
||||
return new Enumerator(this);
|
||||
}
|
||||
|
||||
/// <internalonly/>
|
||||
IEnumerator<T> IEnumerable<T>.GetEnumerator()
|
||||
{
|
||||
return new Enumerator(this);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return new Enumerator(this);
|
||||
}
|
||||
|
||||
// Removes the object at the head of the queue and returns it. If the queue
|
||||
// is empty, this method throws an
|
||||
// InvalidOperationException.
|
||||
public T Dequeue()
|
||||
{
|
||||
int head = _head;
|
||||
T[] array = _array;
|
||||
|
||||
if (_size == 0)
|
||||
{
|
||||
ThrowForEmptyQueue();
|
||||
}
|
||||
|
||||
T removed = array[head];
|
||||
if (IsReference())
|
||||
{
|
||||
array[head] = default;
|
||||
}
|
||||
MoveNext(ref _head);
|
||||
_size--;
|
||||
_version++;
|
||||
return removed;
|
||||
}
|
||||
|
||||
public bool TryDequeue(out T result)
|
||||
{
|
||||
int head = _head;
|
||||
T[] array = _array;
|
||||
|
||||
if (_size == 0)
|
||||
{
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
result = array[head];
|
||||
if (IsReference())
|
||||
{
|
||||
array[head] = default;
|
||||
}
|
||||
MoveNext(ref _head);
|
||||
_size--;
|
||||
_version++;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns the object at the head of the queue. The object remains in the
|
||||
// queue. If the queue is empty, this method throws an
|
||||
// InvalidOperationException.
|
||||
public T Peek()
|
||||
{
|
||||
if (_size == 0)
|
||||
{
|
||||
ThrowForEmptyQueue();
|
||||
}
|
||||
|
||||
return _array[_head];
|
||||
}
|
||||
|
||||
public bool TryPeek(out T result)
|
||||
{
|
||||
if (_size == 0)
|
||||
{
|
||||
result = default(T);
|
||||
return false;
|
||||
}
|
||||
|
||||
result = _array[_head];
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns true if the queue contains at least one object equal to item.
|
||||
// Equality is determined using EqualityComparer<T>.Default.Equals().
|
||||
public bool Contains(T item)
|
||||
{
|
||||
if (_size == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_head < _tail)
|
||||
{
|
||||
return Array.IndexOf(_array, item, _head, _size) >= 0;
|
||||
}
|
||||
|
||||
// We've wrapped around. Check both partitions, the least recently enqueued first.
|
||||
return
|
||||
Array.IndexOf(_array, item, _head, _array.Length - _head) >= 0 ||
|
||||
Array.IndexOf(_array, item, 0, _tail) >= 0;
|
||||
}
|
||||
|
||||
// Iterates over the objects in the queue, returning an array of the
|
||||
// objects in the Queue, or an empty array if the queue is empty.
|
||||
// The order of elements in the array is first in to last in, the same
|
||||
// order produced by successive calls to Dequeue.
|
||||
public T[] ToArray()
|
||||
{
|
||||
if (_size == 0)
|
||||
{
|
||||
return new T[0];
|
||||
}
|
||||
|
||||
T[] arr = new T[_size];
|
||||
|
||||
if (_head < _tail)
|
||||
{
|
||||
Array.Copy(_array, _head, arr, 0, _size);
|
||||
}
|
||||
else
|
||||
{
|
||||
Array.Copy(_array, _head, arr, 0, _array.Length - _head);
|
||||
Array.Copy(_array, 0, arr, _array.Length - _head, _tail);
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
// PRIVATE Grows or shrinks the buffer to hold capacity objects. Capacity
|
||||
// must be >= _size.
|
||||
private void SetCapacity(int capacity)
|
||||
{
|
||||
T[] newarray = new T[capacity];
|
||||
if (_size > 0)
|
||||
{
|
||||
if (_head < _tail)
|
||||
{
|
||||
Array.Copy(_array, _head, newarray, 0, _size);
|
||||
}
|
||||
else
|
||||
{
|
||||
Array.Copy(_array, _head, newarray, 0, _array.Length - _head);
|
||||
Array.Copy(_array, 0, newarray, _array.Length - _head, _tail);
|
||||
}
|
||||
}
|
||||
|
||||
_array = newarray;
|
||||
_head = 0;
|
||||
_tail = (_size == capacity) ? 0 : _size;
|
||||
_version++;
|
||||
}
|
||||
|
||||
// Increments the index wrapping it if necessary.
|
||||
private void MoveNext(ref int index)
|
||||
{
|
||||
// It is tempting to use the remainder operator here but it is actually much slower
|
||||
// than a simple comparison and a rarely taken branch.
|
||||
// JIT produces better code than with ternary operator ?:
|
||||
int tmp = index + 1;
|
||||
if (tmp == _array.Length)
|
||||
{
|
||||
tmp = 0;
|
||||
}
|
||||
index = tmp;
|
||||
}
|
||||
|
||||
private void ThrowForEmptyQueue()
|
||||
{
|
||||
Debug.Assert(_size == 0);
|
||||
throw new InvalidOperationException("InvalidOperation_EmptyQueue");
|
||||
}
|
||||
|
||||
public void TrimExcess()
|
||||
{
|
||||
int threshold = (int)(((double)_array.Length) * 0.9);
|
||||
if (_size < threshold)
|
||||
{
|
||||
SetCapacity(_size);
|
||||
}
|
||||
}
|
||||
|
||||
// Implements an enumerator for a Queue. The enumerator uses the
|
||||
// internal version number of the list to ensure that no modifications are
|
||||
// made to the list while an enumeration is in progress.
|
||||
[SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes", Justification = "not an expected scenario")]
|
||||
public struct Enumerator : IEnumerator<T>,
|
||||
System.Collections.IEnumerator
|
||||
{
|
||||
private readonly FormsQueue<T> _q;
|
||||
private readonly int _version;
|
||||
private int _index; // -1 = not started, -2 = ended/disposed
|
||||
private T _currentElement;
|
||||
|
||||
internal Enumerator(FormsQueue<T> q)
|
||||
{
|
||||
_q = q;
|
||||
_version = q._version;
|
||||
_index = -1;
|
||||
_currentElement = default(T);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_index = -2;
|
||||
_currentElement = default(T);
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (_version != _q._version)
|
||||
throw new InvalidOperationException("InvalidOperation_EnumFailedVersion");
|
||||
|
||||
if (_index == -2)
|
||||
return false;
|
||||
|
||||
_index++;
|
||||
|
||||
if (_index == _q._size)
|
||||
{
|
||||
// We've run past the last element
|
||||
_index = -2;
|
||||
_currentElement = default(T);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cache some fields in locals to decrease code size
|
||||
T[] array = _q._array;
|
||||
int capacity = array.Length;
|
||||
|
||||
// _index represents the 0-based index into the queue, however the queue
|
||||
// doesn't have to start from 0 and it may not even be stored contiguously in memory.
|
||||
|
||||
int arrayIndex = _q._head + _index; // this is the actual index into the queue's backing array
|
||||
if (arrayIndex >= capacity)
|
||||
{
|
||||
// NOTE: Originally we were using the modulo operator here, however
|
||||
// on Intel processors it has a very high instruction latency which
|
||||
// was slowing down the loop quite a bit.
|
||||
// Replacing it with simple comparison/subtraction operations sped up
|
||||
// the average foreach loop by 2x.
|
||||
|
||||
arrayIndex -= capacity; // wrap around if needed
|
||||
}
|
||||
|
||||
_currentElement = array[arrayIndex];
|
||||
return true;
|
||||
}
|
||||
|
||||
public T Current
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_index < 0)
|
||||
ThrowEnumerationNotStartedOrEnded();
|
||||
return _currentElement;
|
||||
}
|
||||
}
|
||||
|
||||
private void ThrowEnumerationNotStartedOrEnded()
|
||||
{
|
||||
Debug.Assert(_index == -1 || _index == -2);
|
||||
throw new InvalidOperationException(_index == -1 ? "InvalidOperation_EnumNotStarted" : "InvalidOperation_EnumEnded");
|
||||
}
|
||||
|
||||
object IEnumerator.Current
|
||||
{
|
||||
get { return Current; }
|
||||
}
|
||||
|
||||
void IEnumerator.Reset()
|
||||
{
|
||||
if (_version != _q._version)
|
||||
throw new InvalidOperationException("InvalidOperation_EnumFailedVersion");
|
||||
_index = -1;
|
||||
_currentElement = default(T);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,497 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
/*=============================================================================
|
||||
**
|
||||
**
|
||||
** Purpose: An array implementation of a generic stack.
|
||||
**
|
||||
**
|
||||
=============================================================================*/
|
||||
|
||||
/*
|
||||
* Copied from https://raw.githubusercontent.com/dotnet/corefx/9cf92cbef7cf5fcf46a1b556f9c6250e67d421ab/src/System.Collections/src/System/Collections/Generic/Stack.cs
|
||||
* Pre C# 8.0 features
|
||||
* */
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Xamarin.Forms
|
||||
{
|
||||
[DebuggerDisplay("Count = {Count}")]
|
||||
internal sealed class FormsStack<T> : IEnumerable<T>,
|
||||
System.Collections.ICollection,
|
||||
IReadOnlyCollection<T>
|
||||
{
|
||||
T[] _array; // Storage for stack elements. Do not rename (binary serialization)
|
||||
int _size; // Number of items in the stack. Do not rename (binary serialization)
|
||||
int _version; // Used to keep enumerator in sync w/ collection. Do not rename (binary serialization)
|
||||
|
||||
private const int DefaultCapacity = 4;
|
||||
bool _isReference;
|
||||
|
||||
|
||||
public FormsStack()
|
||||
{
|
||||
_array = new T[0];
|
||||
#if NETSTANDARD1_0
|
||||
_isReference = !typeof(T).GetTypeInfo().IsValueType;
|
||||
#else
|
||||
_isReference = !typeof(T).IsValueType;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Create a stack with a specific initial capacity. The initial capacity
|
||||
// must be a non-negative number.
|
||||
public FormsStack(int capacity)
|
||||
{
|
||||
if (capacity < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(capacity), capacity, "ArgumentOutOfRange_NeedNonNegNum");
|
||||
_array = new T[capacity];
|
||||
}
|
||||
|
||||
// Fills a Stack with the contents of a particular collection. The items are
|
||||
// pushed onto the stack in the same order they are read by the enumerator.
|
||||
public FormsStack(IEnumerable<T> collection)
|
||||
{
|
||||
if (collection == null)
|
||||
throw new ArgumentNullException(nameof(collection));
|
||||
_array = FormsEnumerableHelpers.ToArray(collection, out _size);
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return _size; }
|
||||
}
|
||||
|
||||
bool ICollection.IsSynchronized
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
object ICollection.SyncRoot => this;
|
||||
|
||||
// Removes all Objects from the Stack.
|
||||
public void Clear()
|
||||
{
|
||||
if (IsReference())
|
||||
{
|
||||
Array.Clear(_array, 0, _size); // Don't need to doc this but we clear the elements so that the gc can reclaim the references.
|
||||
}
|
||||
_size = 0;
|
||||
_version++;
|
||||
}
|
||||
|
||||
public bool Contains(T item)
|
||||
{
|
||||
// Compare items using the default equality comparer
|
||||
|
||||
// PERF: Internally Array.LastIndexOf calls
|
||||
// EqualityComparer<T>.Default.LastIndexOf, which
|
||||
// is specialized for different types. This
|
||||
// boosts performance since instead of making a
|
||||
// virtual method call each iteration of the loop,
|
||||
// via EqualityComparer<T>.Default.Equals, we
|
||||
// only make one virtual call to EqualityComparer.LastIndexOf.
|
||||
|
||||
return _size != 0 && Array.LastIndexOf(_array, item, _size - 1) != -1;
|
||||
}
|
||||
|
||||
// Copies the stack into an array.
|
||||
public void CopyTo(T[] array, int arrayIndex)
|
||||
{
|
||||
if (array == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(array));
|
||||
}
|
||||
|
||||
if (arrayIndex < 0 || arrayIndex > array.Length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(arrayIndex), arrayIndex, "ArgumentOutOfRange_Index");
|
||||
}
|
||||
|
||||
if (array.Length - arrayIndex < _size)
|
||||
{
|
||||
throw new ArgumentException("Argument_InvalidOffLen");
|
||||
}
|
||||
|
||||
Debug.Assert(array != _array);
|
||||
int srcIndex = 0;
|
||||
int dstIndex = arrayIndex + _size;
|
||||
while (srcIndex < _size)
|
||||
{
|
||||
array[--dstIndex] = _array[srcIndex++];
|
||||
}
|
||||
}
|
||||
|
||||
void ICollection.CopyTo(Array array, int arrayIndex)
|
||||
{
|
||||
if (array == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(array));
|
||||
}
|
||||
|
||||
if (array.Rank != 1)
|
||||
{
|
||||
throw new ArgumentException("Arg_RankMultiDimNotSupported", nameof(array));
|
||||
}
|
||||
|
||||
if (array.GetLowerBound(0) != 0)
|
||||
{
|
||||
throw new ArgumentException("Arg_NonZeroLowerBound", nameof(array));
|
||||
}
|
||||
|
||||
if (arrayIndex < 0 || arrayIndex > array.Length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(arrayIndex), arrayIndex, "ArgumentOutOfRange_Index");
|
||||
}
|
||||
|
||||
if (array.Length - arrayIndex < _size)
|
||||
{
|
||||
throw new ArgumentException("Argument_InvalidOffLen");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Array.Copy(_array, 0, array, arrayIndex, _size);
|
||||
Array.Reverse(array, arrayIndex, _size);
|
||||
}
|
||||
catch (ArrayTypeMismatchException)
|
||||
{
|
||||
throw new ArgumentException("Argument_InvalidArrayType", nameof(array));
|
||||
}
|
||||
}
|
||||
|
||||
// Returns an IEnumerator for this Stack.
|
||||
public Enumerator GetEnumerator()
|
||||
{
|
||||
return new Enumerator(this);
|
||||
}
|
||||
|
||||
/// <internalonly/>
|
||||
IEnumerator<T> IEnumerable<T>.GetEnumerator()
|
||||
{
|
||||
return new Enumerator(this);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return new Enumerator(this);
|
||||
}
|
||||
|
||||
public void TrimExcess()
|
||||
{
|
||||
int threshold = (int)(((double)_array.Length) * 0.9);
|
||||
if (_size < threshold)
|
||||
{
|
||||
Array.Resize(ref _array, _size);
|
||||
_version++;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the top object on the stack without removing it. If the stack
|
||||
// is empty, Peek throws an InvalidOperationException.
|
||||
public T Peek()
|
||||
{
|
||||
int size = _size - 1;
|
||||
T[] array = _array;
|
||||
|
||||
if ((uint)size >= (uint)array.Length)
|
||||
{
|
||||
ThrowForEmptyStack();
|
||||
}
|
||||
|
||||
return array[size];
|
||||
}
|
||||
|
||||
public bool TryPeek(out T result)
|
||||
{
|
||||
int size = _size - 1;
|
||||
T[] array = _array;
|
||||
|
||||
if ((uint)size >= (uint)array.Length)
|
||||
{
|
||||
result = default(T);
|
||||
return false;
|
||||
}
|
||||
result = array[size];
|
||||
return true;
|
||||
}
|
||||
|
||||
// Pops an item from the top of the stack. If the stack is empty, Pop
|
||||
// throws an InvalidOperationException.
|
||||
public T Pop()
|
||||
{
|
||||
int size = _size - 1;
|
||||
T[] array = _array;
|
||||
|
||||
// if (_size == 0) is equivalent to if (size == -1), and this case
|
||||
// is covered with (uint)size, thus allowing bounds check elimination
|
||||
// https://github.com/dotnet/coreclr/pull/9773
|
||||
if ((uint)size >= (uint)array.Length)
|
||||
{
|
||||
ThrowForEmptyStack();
|
||||
}
|
||||
|
||||
_version++;
|
||||
_size = size;
|
||||
T item = array[size];
|
||||
if (IsReference())
|
||||
{
|
||||
array[size] = default(T); // Free memory quicker.
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
public bool TryPop(out T result)
|
||||
{
|
||||
int size = _size - 1;
|
||||
T[] array = _array;
|
||||
|
||||
if ((uint)size >= (uint)array.Length)
|
||||
{
|
||||
result = default(T);
|
||||
return false;
|
||||
}
|
||||
|
||||
_version++;
|
||||
_size = size;
|
||||
result = array[size];
|
||||
if (IsReference())
|
||||
{
|
||||
array[size] = default(T);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Pushes an item to the top of the stack.
|
||||
public void Push(T item)
|
||||
{
|
||||
int size = _size;
|
||||
T[] array = _array;
|
||||
|
||||
if ((uint)size < (uint)array.Length)
|
||||
{
|
||||
array[size] = item;
|
||||
_version++;
|
||||
_size = size + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
PushWithResize(item);
|
||||
}
|
||||
}
|
||||
|
||||
// Non-inline from Stack.Push to improve its code quality as uncommon path
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private void PushWithResize(T item)
|
||||
{
|
||||
Array.Resize(ref _array, (_array.Length == 0) ? DefaultCapacity : 2 * _array.Length);
|
||||
_array[_size] = item;
|
||||
_version++;
|
||||
_size++;
|
||||
}
|
||||
|
||||
// Copies the Stack to an array, in the same order Pop would return the items.
|
||||
public T[] ToArray()
|
||||
{
|
||||
if (_size == 0)
|
||||
return new T[0];
|
||||
|
||||
T[] objArray = new T[_size];
|
||||
int i = 0;
|
||||
while (i < _size)
|
||||
{
|
||||
objArray[i] = _array[_size - i - 1];
|
||||
i++;
|
||||
}
|
||||
return objArray;
|
||||
}
|
||||
|
||||
private void ThrowForEmptyStack()
|
||||
{
|
||||
Debug.Assert(_size == 0);
|
||||
throw new InvalidOperationException("InvalidOperation_EmptyStack");
|
||||
}
|
||||
|
||||
// The original version of this is IsReferenceOrContainsReferences
|
||||
// But I don't think we have to satisfy the Contains reference portion of the check
|
||||
public bool IsReference()
|
||||
{
|
||||
return _isReference;
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes", Justification = "not an expected scenario")]
|
||||
public struct Enumerator : IEnumerator<T>, System.Collections.IEnumerator
|
||||
{
|
||||
private readonly FormsStack<T> _stack;
|
||||
private readonly int _version;
|
||||
private int _index;
|
||||
private T _currentElement;
|
||||
|
||||
internal Enumerator(FormsStack<T> stack)
|
||||
{
|
||||
_stack = stack;
|
||||
_version = stack._version;
|
||||
_index = -2;
|
||||
_currentElement = default;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_index = -1;
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
bool retval;
|
||||
if (_version != _stack._version)
|
||||
throw new InvalidOperationException("InvalidOperation_EnumFailedVersion");
|
||||
if (_index == -2)
|
||||
{ // First call to enumerator.
|
||||
_index = _stack._size - 1;
|
||||
retval = (_index >= 0);
|
||||
if (retval)
|
||||
_currentElement = _stack._array[_index];
|
||||
return retval;
|
||||
}
|
||||
if (_index == -1)
|
||||
{ // End of enumeration.
|
||||
return false;
|
||||
}
|
||||
|
||||
retval = (--_index >= 0);
|
||||
if (retval)
|
||||
_currentElement = _stack._array[_index];
|
||||
else
|
||||
_currentElement = default;
|
||||
return retval;
|
||||
}
|
||||
|
||||
public T Current
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_index < 0)
|
||||
ThrowEnumerationNotStartedOrEnded();
|
||||
return _currentElement;
|
||||
}
|
||||
}
|
||||
|
||||
private void ThrowEnumerationNotStartedOrEnded()
|
||||
{
|
||||
Debug.Assert(_index == -1 || _index == -2);
|
||||
throw new InvalidOperationException(_index == -2 ? "InvalidOperation_EnumNotStarted" : "InvalidOperation_EnumEnded");
|
||||
}
|
||||
|
||||
object System.Collections.IEnumerator.Current
|
||||
{
|
||||
get { return Current; }
|
||||
}
|
||||
|
||||
void IEnumerator.Reset()
|
||||
{
|
||||
if (_version != _stack._version)
|
||||
throw new InvalidOperationException("InvalidOperation_EnumFailedVersion");
|
||||
_index = -2;
|
||||
_currentElement = default;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal helper functions for working with enumerables.
|
||||
/// </summary>
|
||||
internal static partial class FormsEnumerableHelpers
|
||||
{
|
||||
/// <summary>Converts an enumerable to an array using the same logic as List{T}.</summary>
|
||||
/// <param name="source">The enumerable to convert.</param>
|
||||
/// <param name="length">The number of items stored in the resulting array, 0-indexed.</param>
|
||||
/// <returns>
|
||||
/// The resulting array. The length of the array may be greater than <paramref name="length"/>,
|
||||
/// which is the actual number of elements in the array.
|
||||
/// </returns>
|
||||
internal static T[] ToArray<T>(IEnumerable<T> source, out int length)
|
||||
{
|
||||
if (source is ICollection<T> ic)
|
||||
{
|
||||
int count = ic.Count;
|
||||
if (count != 0)
|
||||
{
|
||||
// Allocate an array of the desired size, then copy the elements into it. Note that this has the same
|
||||
// issue regarding concurrency as other existing collections like List<T>. If the collection size
|
||||
// concurrently changes between the array allocation and the CopyTo, we could end up either getting an
|
||||
// exception from overrunning the array (if the size went up) or we could end up not filling as many
|
||||
// items as 'count' suggests (if the size went down). This is only an issue for concurrent collections
|
||||
// that implement ICollection<T>, which as of .NET 4.6 is just ConcurrentDictionary<TKey, TValue>.
|
||||
T[] arr = new T[count];
|
||||
ic.CopyTo(arr, 0);
|
||||
length = count;
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var en = source.GetEnumerator())
|
||||
{
|
||||
if (en.MoveNext())
|
||||
{
|
||||
const int DefaultCapacity = 4;
|
||||
T[] arr = new T[DefaultCapacity];
|
||||
arr[0] = en.Current;
|
||||
int count = 1;
|
||||
|
||||
while (en.MoveNext())
|
||||
{
|
||||
if (count == arr.Length)
|
||||
{
|
||||
// MaxArrayLength is defined in Array.MaxArrayLength and in gchelpers in CoreCLR.
|
||||
// It represents the maximum number of elements that can be in an array where
|
||||
// the size of the element is greater than one byte; a separate, slightly larger constant,
|
||||
// is used when the size of the element is one.
|
||||
const int MaxArrayLength = 0x7FEFFFFF;
|
||||
|
||||
// This is the same growth logic as in List<T>:
|
||||
// If the array is currently empty, we make it a default size. Otherwise, we attempt to
|
||||
// double the size of the array. Doubling will overflow once the size of the array reaches
|
||||
// 2^30, since doubling to 2^31 is 1 larger than Int32.MaxValue. In that case, we instead
|
||||
// constrain the length to be MaxArrayLength (this overflow check works because of the
|
||||
// cast to uint). Because a slightly larger constant is used when T is one byte in size, we
|
||||
// could then end up in a situation where arr.Length is MaxArrayLength or slightly larger, such
|
||||
// that we constrain newLength to be MaxArrayLength but the needed number of elements is actually
|
||||
// larger than that. For that case, we then ensure that the newLength is large enough to hold
|
||||
// the desired capacity. This does mean that in the very rare case where we've grown to such a
|
||||
// large size, each new element added after MaxArrayLength will end up doing a resize.
|
||||
int newLength = count << 1;
|
||||
if ((uint)newLength > MaxArrayLength)
|
||||
{
|
||||
newLength = MaxArrayLength <= count ? count + 1 : MaxArrayLength;
|
||||
}
|
||||
|
||||
Array.Resize(ref arr, newLength);
|
||||
}
|
||||
|
||||
arr[count++] = en.Current;
|
||||
}
|
||||
|
||||
length = count;
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
length = 0;
|
||||
return new T[0];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
{
|
||||
public sealed class RendererPool
|
||||
{
|
||||
readonly Dictionary<Type, Stack<IVisualElementRenderer>> _freeRenderers = new Dictionary<Type, Stack<IVisualElementRenderer>>();
|
||||
readonly Dictionary<Type, FormsStack<IVisualElementRenderer>> _freeRenderers = new Dictionary<Type, FormsStack<IVisualElementRenderer>>();
|
||||
|
||||
readonly VisualElement _oldElement;
|
||||
|
||||
|
@ -38,7 +38,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
|
||||
Type rendererType = Internals.Registrar.Registered.GetHandlerTypeForObject(view) ?? typeof(ViewRenderer);
|
||||
|
||||
Stack<IVisualElementRenderer> renderers;
|
||||
FormsStack<IVisualElementRenderer> renderers;
|
||||
if (!_freeRenderers.TryGetValue(rendererType, out renderers) || renderers.Count == 0)
|
||||
return null;
|
||||
|
||||
|
@ -82,9 +82,9 @@ namespace Xamarin.Forms.Platform.Android
|
|||
var reflectableType = renderer as System.Reflection.IReflectableType;
|
||||
var rendererType = reflectableType != null ? reflectableType.GetTypeInfo().AsType() : renderer.GetType();
|
||||
|
||||
Stack<IVisualElementRenderer> renderers;
|
||||
FormsStack<IVisualElementRenderer> renderers;
|
||||
if (!_freeRenderers.TryGetValue(rendererType, out renderers))
|
||||
_freeRenderers[rendererType] = renderers = new Stack<IVisualElementRenderer>();
|
||||
_freeRenderers[rendererType] = renderers = new FormsStack<IVisualElementRenderer>();
|
||||
|
||||
renderers.Push(renderer);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
{
|
||||
public class ViewPool : IDisposable
|
||||
{
|
||||
readonly Dictionary<Type, Stack<AView>> _freeViews = new Dictionary<Type, Stack<AView>>();
|
||||
readonly Dictionary<Type, FormsStack<AView>> _freeViews = new Dictionary<Type, FormsStack<AView>>();
|
||||
readonly ViewGroup _viewGroup;
|
||||
|
||||
bool _disposed;
|
||||
|
@ -22,7 +22,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
if (_disposed)
|
||||
return;
|
||||
|
||||
foreach (Stack<AView> views in _freeViews.Values)
|
||||
foreach (FormsStack<AView> views in _freeViews.Values)
|
||||
{
|
||||
foreach (AView view in views)
|
||||
view.Dispose();
|
||||
|
@ -44,7 +44,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
if (_disposed)
|
||||
throw new ObjectDisposedException(null);
|
||||
|
||||
Stack<AView> views;
|
||||
FormsStack<AView> views;
|
||||
if (_freeViews.TryGetValue(typeof(TView), out views) && views.Count > 0)
|
||||
return (TView)views.Pop();
|
||||
|
||||
|
@ -66,9 +66,9 @@ namespace Xamarin.Forms.Platform.Android
|
|||
ClearChildren(g);
|
||||
|
||||
Type childType = child.GetType();
|
||||
Stack<AView> stack;
|
||||
FormsStack<AView> stack;
|
||||
if (!_freeViews.TryGetValue(childType, out stack))
|
||||
_freeViews[childType] = stack = new Stack<AView>();
|
||||
_freeViews[childType] = stack = new FormsStack<AView>();
|
||||
|
||||
stack.Push(child);
|
||||
}
|
||||
|
|
|
@ -58,6 +58,12 @@
|
|||
<Reference Include="System.Net.Http" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Xamarin.Forms.Core\corefx\FormsQueue.cs">
|
||||
<Link>corefx\FormsQueue.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Xamarin.Forms.Core\corefx\FormsStack.cs">
|
||||
<Link>corefx\FormsStack.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="ActivityResultCallbackRegistry.cs" />
|
||||
<Compile Include="AndroidApplicationLifecycleState.cs" />
|
||||
<Compile Include="AndroidTitleBarVisibility.cs" />
|
||||
|
@ -395,4 +401,4 @@
|
|||
<MSBuild Targets="Restore" Projects="Xamarin.Forms.Platform.Android.csproj" Properties="CreateAllAndroidTargets=false;Configuration=$(Configuration);Platform=$(Platform);">
|
||||
</MSBuild>
|
||||
</Target>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace Xamarin.Forms.Platform.GTK.Renderers
|
|||
{
|
||||
private const int NavigationAnimationDuration = 250; // Ms
|
||||
|
||||
private Stack<NavigationChildPage> _currentStack;
|
||||
private FormsStack<NavigationChildPage> _currentStack;
|
||||
|
||||
INavigationPageController NavigationController => Element as INavigationPageController;
|
||||
|
||||
|
@ -28,7 +28,7 @@ namespace Xamarin.Forms.Platform.GTK.Renderers
|
|||
|
||||
public NavigationPageRenderer()
|
||||
{
|
||||
_currentStack = new Stack<NavigationChildPage>();
|
||||
_currentStack = new FormsStack<NavigationChildPage>();
|
||||
_toolbarTracker = new GtkToolbarTracker();
|
||||
}
|
||||
|
||||
|
@ -486,7 +486,7 @@ namespace Xamarin.Forms.Platform.GTK.Renderers
|
|||
{
|
||||
if (removeFromStack)
|
||||
{
|
||||
var newStack = new Stack<NavigationChildPage>();
|
||||
var newStack = new FormsStack<NavigationChildPage>();
|
||||
foreach (var stack in _currentStack)
|
||||
{
|
||||
if (stack.Page != page)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<Description>GTK# Backend for Xamarin.Forms</Description>
|
||||
<AssemblyName>Xamarin.Forms.Platform.GTK</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
@ -95,6 +95,12 @@
|
|||
<PackageReference Include="OpenTK" Version="3.0.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Xamarin.Forms.Core\corefx\FormsQueue.cs">
|
||||
<Link>corefx\FormsQueue.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Xamarin.Forms.Core\corefx\FormsStack.cs">
|
||||
<Link>corefx\FormsStack.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Animations\BaseAnimation.cs" />
|
||||
<Compile Include="Animations\FloatAnimation.cs" />
|
||||
<Compile Include="Cells\CellBase.cs" />
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace Xamarin.Forms.Platform.MacOS
|
|||
bool _appeared;
|
||||
EventTracker _events;
|
||||
VisualElementTracker _tracker;
|
||||
Stack<NavigationChildPageWrapper> _currentStack = new Stack<NavigationChildPageWrapper>();
|
||||
FormsStack<NavigationChildPageWrapper> _currentStack = new FormsStack<NavigationChildPageWrapper>();
|
||||
|
||||
NavigationPage NavigationPage => Element as NavigationPage;
|
||||
|
||||
|
@ -255,7 +255,7 @@ namespace Xamarin.Forms.Platform.MacOS
|
|||
var beforePageIndex = currentList.IndexOf(p => p.Page == before);
|
||||
var pageWrapper = new NavigationChildPageWrapper(page);
|
||||
currentList.Insert(beforePageIndex, pageWrapper);
|
||||
_currentStack = new Stack<NavigationChildPageWrapper>(currentList);
|
||||
_currentStack = new FormsStack<NavigationChildPageWrapper>(currentList);
|
||||
|
||||
var vc = CreateViewControllerForPage(page);
|
||||
vc.SetElementSize(new Size(View.Bounds.Width, View.Bounds.Height));
|
||||
|
@ -306,7 +306,7 @@ namespace Xamarin.Forms.Platform.MacOS
|
|||
if (removeFromStack)
|
||||
{
|
||||
var newSource = _currentStack.Reverse().Where(w => w.Page != page);
|
||||
_currentStack = new Stack<NavigationChildPageWrapper>(newSource);
|
||||
_currentStack = new FormsStack<NavigationChildPageWrapper>(newSource);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace Xamarin.Forms.Platform.MacOS
|
|||
|
||||
if (disposing)
|
||||
{
|
||||
var viewsToLookAt = new Stack<NSView>(Subviews);
|
||||
var viewsToLookAt = new FormsStack<NSView>(Subviews);
|
||||
while (viewsToLookAt.Count > 0)
|
||||
{
|
||||
var view = viewsToLookAt.Pop();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<Description>macOS Backend for Xamarin.Forms</Description>
|
||||
<AssemblyName>Xamarin.Forms.Platform.macOS</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
@ -71,6 +71,12 @@
|
|||
<Reference Include="System.Net.Http" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Xamarin.Forms.Core\corefx\FormsQueue.cs">
|
||||
<Link>corefx\FormsQueue.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Xamarin.Forms.Core\corefx\FormsStack.cs">
|
||||
<Link>corefx\FormsStack.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Xamarin.Forms.Platform.iOS\Extensions\LabelExtensions.cs">
|
||||
<Link>Extensions\LabelExtensions.cs</Link>
|
||||
</Compile>
|
||||
|
@ -264,4 +270,4 @@
|
|||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Mac\Xamarin.Mac.CSharp.targets" />
|
||||
<Target Name="_VerifyBuildSignature" />
|
||||
</Project>
|
||||
</Project>
|
|
@ -115,7 +115,7 @@ namespace Xamarin.Forms.Platform.MacOS
|
|||
|
||||
internal static T FindDescendantView<T>(this UIView view) where T : UIView
|
||||
{
|
||||
var queue = new Queue<UIView>();
|
||||
var queue = new FormsQueue<UIView>();
|
||||
queue.Enqueue(view);
|
||||
|
||||
while (queue.Count > 0)
|
||||
|
|
|
@ -10,8 +10,8 @@ namespace Xamarin.Forms.Platform.MacOS
|
|||
{
|
||||
public sealed class RendererPool
|
||||
{
|
||||
readonly Dictionary<Type, Stack<IVisualElementRenderer>> _freeRenderers =
|
||||
new Dictionary<Type, Stack<IVisualElementRenderer>>();
|
||||
readonly Dictionary<Type, FormsStack<IVisualElementRenderer>> _freeRenderers =
|
||||
new Dictionary<Type, FormsStack<IVisualElementRenderer>>();
|
||||
|
||||
readonly VisualElement _oldElement;
|
||||
|
||||
|
@ -36,7 +36,7 @@ namespace Xamarin.Forms.Platform.MacOS
|
|||
|
||||
var rendererType = Internals.Registrar.Registered.GetHandlerTypeForObject(view) ?? typeof(ViewRenderer);
|
||||
|
||||
Stack<IVisualElementRenderer> renderers;
|
||||
FormsStack<IVisualElementRenderer> renderers;
|
||||
if (!_freeRenderers.TryGetValue(rendererType, out renderers) || renderers.Count == 0)
|
||||
return null;
|
||||
|
||||
|
@ -127,9 +127,9 @@ namespace Xamarin.Forms.Platform.MacOS
|
|||
var reflectableType = renderer as System.Reflection.IReflectableType;
|
||||
var rendererType = reflectableType != null ? reflectableType.GetTypeInfo().AsType() : renderer.GetType();
|
||||
|
||||
Stack<IVisualElementRenderer> renderers;
|
||||
FormsStack<IVisualElementRenderer> renderers;
|
||||
if (!_freeRenderers.TryGetValue(rendererType, out renderers))
|
||||
_freeRenderers[rendererType] = renderers = new Stack<IVisualElementRenderer>();
|
||||
_freeRenderers[rendererType] = renderers = new FormsStack<IVisualElementRenderer>();
|
||||
|
||||
renderers.Push(renderer);
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
_insetTracker.Dispose();
|
||||
_insetTracker = null;
|
||||
|
||||
var viewsToLookAt = new Stack<UIView>(Subviews);
|
||||
var viewsToLookAt = new FormsStack<UIView>(Subviews);
|
||||
while (viewsToLookAt.Count > 0)
|
||||
{
|
||||
var view = viewsToLookAt.Pop();
|
||||
|
|
|
@ -107,6 +107,12 @@
|
|||
<Compile Include="$(MSBuildThisFileDirectory)Renderers\WebViewRenderer.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Extensions\CellExtensions.cs" />
|
||||
<Compile Include="..\Xamarin.Forms.Core\corefx\FormsQueue.cs">
|
||||
<Link>corefx\FormsQueue.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Xamarin.Forms.Core\corefx\FormsStack.cs">
|
||||
<Link>corefx\FormsStack.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="CADisplayLinkTicker.cs" />
|
||||
<Compile Include="CollectionView\CarouselViewRenderer.cs" />
|
||||
<Compile Include="CollectionView\CollectionViewRenderer.cs" />
|
||||
|
|
|
@ -76,7 +76,7 @@ namespace Xamarin.Forms.XamlcUnitTests
|
|||
resolver.AddAssembly(Uri.UnescapeDataString((new UriBuilder(typeof(BindableObject).Assembly.CodeBase)).Path));
|
||||
resolver.AddAssembly(Uri.UnescapeDataString((new UriBuilder(typeof(object).Assembly.CodeBase)).Path));
|
||||
resolver.AddAssembly(Uri.UnescapeDataString((new UriBuilder(typeof(IList<>).Assembly.CodeBase)).Path));
|
||||
resolver.AddAssembly(Uri.UnescapeDataString((new UriBuilder(typeof(Queue<>).Assembly.CodeBase)).Path));
|
||||
resolver.AddAssembly(Uri.UnescapeDataString((new UriBuilder(typeof(FormsQueue<>).Assembly.CodeBase)).Path));
|
||||
|
||||
module = ModuleDefinition.CreateModule("foo", new ModuleParameters {
|
||||
AssemblyResolver = resolver,
|
||||
|
@ -89,7 +89,7 @@ namespace Xamarin.Forms.XamlcUnitTests
|
|||
[TestCase(typeof(List<string>), typeof(BindableObject), ExpectedResult = false)]
|
||||
[TestCase(typeof(List<string>), typeof(IEnumerable<string>), ExpectedResult = true)]
|
||||
[TestCase(typeof(List<Button>), typeof(BindableObject), ExpectedResult = false)]
|
||||
[TestCase(typeof(Queue<KeyValuePair<string, string>>), typeof(BindableObject), ExpectedResult = false)]
|
||||
[TestCase(typeof(FormsQueue<KeyValuePair<string, string>>), typeof(BindableObject), ExpectedResult = false)]
|
||||
[TestCase(typeof(double), typeof(double), ExpectedResult = true)]
|
||||
[TestCase(typeof(object), typeof(IList<TriggerBase>), ExpectedResult = false)]
|
||||
[TestCase(typeof(object), typeof(double), ExpectedResult = false)]
|
||||
|
|
|
@ -21,6 +21,10 @@ variables:
|
|||
value: 5.0.2
|
||||
- name: DOTNET_SKIP_FIRST_TIME_EXPERIENCE
|
||||
value: true
|
||||
- name: DOTNET_VERSION
|
||||
value: 3.0.100
|
||||
- name: winVmImage
|
||||
value: XamarinForms
|
||||
|
||||
resources:
|
||||
repositories:
|
||||
|
|
111
build.cake
111
build.cake
|
@ -24,6 +24,7 @@ PowerShell:
|
|||
#addin "nuget:?package=Cake.Android.SdkManager&version=3.0.2"
|
||||
#addin "nuget:?package=Cake.Boots&version=1.0.0.291"
|
||||
|
||||
#addin "nuget:?package=Cake.FileHelpers&version=3.2.0"
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// TOOLS
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
@ -43,35 +44,40 @@ var informationalVersion = gitVersion.InformationalVersion;
|
|||
var buildVersion = gitVersion.FullBuildMetaData;
|
||||
var nugetversion = Argument<string>("packageVersion", gitVersion.NuGetVersion);
|
||||
|
||||
var ANDROID_HOME = EnvironmentVariable ("ANDROID_HOME") ??
|
||||
var ANDROID_HOME = EnvironmentVariable ("ANDROID_HOME") ??
|
||||
(IsRunningOnWindows () ? "C:\\Program Files (x86)\\Android\\android-sdk\\" : "");
|
||||
|
||||
string monoMajorVersion = "5.14.0";
|
||||
string monoPatchVersion = "177";
|
||||
string monoMajorVersion = "5.18.1";
|
||||
string monoPatchVersion = "28";
|
||||
string monoVersion = $"{monoMajorVersion}.{monoPatchVersion}";
|
||||
|
||||
string monoSDK_windows = $"https://download.mono-project.com/archive/{monoMajorVersion}/windows-installer/mono-{monoVersion}-x64-0.msi";
|
||||
string monoSDK_windows = "";//$"https://download.mono-project.com/archive/{monoMajorVersion}/windows-installer/mono-{monoVersion}-x64-0.msi";
|
||||
string androidSDK_windows = "";//"https://aka.ms/xamarin-android-commercial-d15-9-windows";
|
||||
string iOSSDK_windows = "https://download.visualstudio.microsoft.com/download/pr/71f33151-5db4-49cc-ac70-ba835a9f81e2/d256c6c50cd80ec0207783c5c7a4bc2f/xamarin.visualstudio.apple.sdk.4.12.3.83.vsix";
|
||||
string iOSSDK_windows = "";//"https://download.visualstudio.microsoft.com/download/pr/71f33151-5db4-49cc-ac70-ba835a9f81e2/d256c6c50cd80ec0207783c5c7a4bc2f/xamarin.visualstudio.apple.sdk.4.12.3.83.vsix";
|
||||
string macSDK_windows = "";
|
||||
|
||||
string androidSDK_macos = "https://aka.ms/xamarin-android-commercial-d15-9-macos";
|
||||
monoMajorVersion = "6.4.0";
|
||||
monoPatchVersion = "198";
|
||||
monoVersion = $"{monoMajorVersion}.{monoPatchVersion}";
|
||||
|
||||
string androidSDK_macos = "https://aka.ms/xamarin-android-commercial-d16-3-macos";
|
||||
string monoSDK_macos = $"https://download.mono-project.com/archive/{monoMajorVersion}/macos-10-universal/MonoFramework-MDK-{monoVersion}.macos10.xamarin.universal.pkg";
|
||||
string iOSSDK_macos = $"https://bosstoragemirror.blob.core.windows.net/wrench/jenkins/xcode10.2/9c8d8e0a50e68d9abc8cd48fcd47a669e981fcc9/53/package/xamarin.ios-12.4.0.64.pkg";
|
||||
string macSDK_macos = $"https://bosstoragemirror.blob.core.windows.net/wrench/jenkins/xcode10.2/9c8d8e0a50e68d9abc8cd48fcd47a669e981fcc9/53/package/xamarin.mac-5.4.0.64.pkg";
|
||||
string iOSSDK_macos = $"https://bosstoragemirror.blob.core.windows.net/wrench/jenkins/d16-3/5e8a208b5f44c4885060d95e3c3ad68d6a5e95e8/40/package/xamarin.ios-13.2.0.42.pkg";
|
||||
string macSDK_macos = $"https://bosstoragemirror.blob.core.windows.net/wrench/jenkins/d16-3/5e8a208b5f44c4885060d95e3c3ad68d6a5e95e8/40/package/xamarin.mac-6.2.0.42.pkg";
|
||||
|
||||
string androidSDK = IsRunningOnWindows() ? androidSDK_windows : androidSDK_macos;
|
||||
string monoSDK = IsRunningOnWindows() ? monoSDK_windows : monoSDK_macos;
|
||||
string iosSDK = IsRunningOnWindows() ? "" : iOSSDK_macos;
|
||||
string iosSDK = IsRunningOnWindows() ? iOSSDK_windows : iOSSDK_macos;
|
||||
string macSDK = IsRunningOnWindows() ? "" : macSDK_macos;
|
||||
|
||||
string[] androidSdkManagerInstalls = new string[0];//new [] { "platforms;android-24", "platforms;android-28"};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// TASKS
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
Task("Clean")
|
||||
.Description("Deletes all the obj/bin directories")
|
||||
.Does(() =>
|
||||
{
|
||||
CleanDirectories("./**/obj", (fsi)=> !fsi.Path.FullPath.Contains("XFCorePostProcessor") && !fsi.Path.FullPath.StartsWith("tools"));
|
||||
|
@ -79,6 +85,7 @@ Task("Clean")
|
|||
});
|
||||
|
||||
Task("provision-macsdk")
|
||||
.Description("Install Xamarin.Mac SDK")
|
||||
.Does(async () =>
|
||||
{
|
||||
if(!IsRunningOnWindows() && !String.IsNullOrWhiteSpace(macSDK))
|
||||
|
@ -88,13 +95,11 @@ Task("provision-macsdk")
|
|||
});
|
||||
|
||||
Task("provision-iossdk")
|
||||
.Description("Install Xamarin.iOS SDK")
|
||||
.Does(async () =>
|
||||
{
|
||||
if(!IsRunningOnWindows())
|
||||
{
|
||||
if(!String.IsNullOrWhiteSpace(iosSDK))
|
||||
await Boots(iosSDK);
|
||||
}
|
||||
if(!String.IsNullOrWhiteSpace(iosSDK))
|
||||
await Boots(iosSDK);
|
||||
});
|
||||
|
||||
Task("provision-androidsdk")
|
||||
|
@ -105,12 +110,12 @@ Task("provision-androidsdk")
|
|||
|
||||
if(androidSdkManagerInstalls.Length > 0)
|
||||
{
|
||||
var androidSdkSettings = new AndroidSdkManagerToolSettings {
|
||||
var androidSdkSettings = new AndroidSdkManagerToolSettings {
|
||||
SdkRoot = ANDROID_HOME,
|
||||
SkipVersionCheck = true
|
||||
};
|
||||
|
||||
|
||||
|
||||
AcceptLicenses (androidSdkSettings);
|
||||
|
||||
AndroidSdkManagerUpdateAll (androidSdkSettings);
|
||||
|
@ -124,13 +129,20 @@ Task("provision-androidsdk")
|
|||
});
|
||||
|
||||
Task("provision-monosdk")
|
||||
.Description("Install Mono SDK")
|
||||
.Does(async () =>
|
||||
{
|
||||
if(IsRunningOnWindows())
|
||||
{
|
||||
if(!String.IsNullOrWhiteSpace(monoSDK))
|
||||
{
|
||||
string monoPath = $"{System.IO.Path.GetTempPath()}/mono.msi";
|
||||
string monoPath = $"{System.IO.Path.GetTempPath()}mono.msi";
|
||||
|
||||
if(!String.IsNullOrWhiteSpace(EnvironmentVariable("Build.Repository.LocalPath")))
|
||||
monoPath = EnvironmentVariable("Build.Repository.LocalPath") + "\\" + "mono.msi";
|
||||
|
||||
Information("Mono Path: {0}", monoPath);
|
||||
Information("Mono Version: {0}", monoSDK);
|
||||
DownloadFile(monoSDK, monoPath);
|
||||
|
||||
StartProcess("msiexec", new ProcessSettings {
|
||||
|
@ -145,34 +157,41 @@ Task("provision-monosdk")
|
|||
else
|
||||
{
|
||||
if(!String.IsNullOrWhiteSpace(monoSDK))
|
||||
await Boots(monoSDK);
|
||||
await Boots(monoSDK);
|
||||
}
|
||||
});
|
||||
|
||||
Task("provision")
|
||||
.Description("Install SDKs required to build project")
|
||||
.IsDependentOn("provision-macsdk")
|
||||
.IsDependentOn("provision-iossdk")
|
||||
.IsDependentOn("provision-monosdk")
|
||||
.IsDependentOn("provision-androidsdk");
|
||||
|
||||
Task("NuGetPack")
|
||||
.Description("Build and Create Nugets")
|
||||
.IsDependentOn("Build")
|
||||
.IsDependentOn("_NuGetPack");
|
||||
|
||||
|
||||
Task("_NuGetPack")
|
||||
.Description("Create Nugets without building anything")
|
||||
.Does(() =>
|
||||
{
|
||||
var nugetVersionFile =
|
||||
GetFiles(".XamarinFormsVersionFile.txt");
|
||||
var nugetversion = FileReadText(nugetVersionFile.First());
|
||||
|
||||
Information("Nuget Version: {0}", nugetversion);
|
||||
|
||||
|
||||
var nugetPackageDir = Directory("./artifacts");
|
||||
var nuGetPackSettings = new NuGetPackSettings
|
||||
{
|
||||
{
|
||||
OutputDirectory = nugetPackageDir,
|
||||
Version = nugetversion
|
||||
};
|
||||
|
||||
var nugetFilePaths =
|
||||
var nugetFilePaths =
|
||||
GetFiles("./.nuspec/*.nuspec");
|
||||
|
||||
nuGetPackSettings.Properties.Add("configuration", configuration);
|
||||
|
@ -183,6 +202,7 @@ Task("_NuGetPack")
|
|||
|
||||
|
||||
Task("Restore")
|
||||
.Description("Restore target on Xamarin.Forms.sln")
|
||||
.Does(() =>
|
||||
{
|
||||
try{
|
||||
|
@ -195,23 +215,12 @@ Task("Restore")
|
|||
}
|
||||
});
|
||||
|
||||
|
||||
Task("BuildHack")
|
||||
.IsDependentOn("Restore")
|
||||
.Does(() =>
|
||||
{
|
||||
if(!IsRunningOnWindows())
|
||||
{
|
||||
MSBuild("./Xamarin.Forms.Build.Tasks/Xamarin.Forms.Build.Tasks.csproj", GetMSBuildSettings().WithRestore());
|
||||
}
|
||||
});
|
||||
|
||||
Task("Build")
|
||||
.Description("Builds all necessary projects to create Nuget Packages")
|
||||
.IsDependentOn("Restore")
|
||||
.IsDependentOn("BuildHack")
|
||||
.IsDependentOn("Android81")
|
||||
.Does(() =>
|
||||
{
|
||||
{
|
||||
try{
|
||||
MSBuild("./Xamarin.Forms.sln", GetMSBuildSettings().WithRestore());
|
||||
}
|
||||
|
@ -223,10 +232,10 @@ Task("Build")
|
|||
});
|
||||
|
||||
Task("Android81")
|
||||
.IsDependentOn("BuildHack")
|
||||
.Description("Builds Monodroid81 targets")
|
||||
.Does(() =>
|
||||
{
|
||||
string[] androidProjects =
|
||||
string[] androidProjects =
|
||||
new []
|
||||
{
|
||||
"./Xamarin.Forms.Platform.Android/Xamarin.Forms.Platform.Android.csproj",
|
||||
|
@ -236,20 +245,20 @@ Task("Android81")
|
|||
};
|
||||
|
||||
foreach(var project in androidProjects)
|
||||
MSBuild(project,
|
||||
MSBuild(project,
|
||||
GetMSBuildSettings()
|
||||
.WithRestore()
|
||||
.WithProperty("AndroidTargetFrameworkVersion", "v8.1"));
|
||||
});
|
||||
|
||||
Task("VSMAC")
|
||||
.IsDependentOn("BuildHack")
|
||||
.Description("Builds projects necessary so solution compiles on VSMAC")
|
||||
.Does(() =>
|
||||
{
|
||||
StartProcess("open", new ProcessSettings{ Arguments = "Xamarin.Forms.sln" });
|
||||
StartProcess("open", new ProcessSettings{ Arguments = "Xamarin.Forms.sln" });
|
||||
});
|
||||
|
||||
/*
|
||||
/*
|
||||
Task("Deploy")
|
||||
.IsDependentOn("DeployiOS")
|
||||
.IsDependentOn("DeployAndroid");
|
||||
|
@ -258,16 +267,16 @@ Task("Deploy")
|
|||
// TODO? Not sure how to make this work
|
||||
Task("DeployiOS")
|
||||
.Does(() =>
|
||||
{
|
||||
{
|
||||
// not sure how to get this to deploy to iOS
|
||||
BuildiOSIpa("./Xamarin.Forms.sln", platform:"iPhoneSimulator", configuration:"Debug");
|
||||
|
||||
});
|
||||
*/
|
||||
Task("DeployAndroid")
|
||||
.IsDependentOn("BuildHack")
|
||||
.Description("Builds and deploy Android Control Gallery")
|
||||
.Does(() =>
|
||||
{
|
||||
{
|
||||
MSBuild("./Xamarin.Forms.Build.Tasks/Xamarin.Forms.Build.Tasks.csproj", GetMSBuildSettings().WithRestore());
|
||||
MSBuild("./Xamarin.Forms.ControlGallery.Android/Xamarin.Forms.ControlGallery.Android.csproj", GetMSBuildSettings().WithRestore());
|
||||
BuildAndroidApk("./Xamarin.Forms.ControlGallery.Android/Xamarin.Forms.ControlGallery.Android.csproj", sign:true, configuration:configuration);
|
||||
|
@ -293,11 +302,9 @@ RunTarget(target);
|
|||
|
||||
MSBuildSettings GetMSBuildSettings()
|
||||
{
|
||||
var msbuildSettings = new MSBuildSettings();
|
||||
|
||||
msbuildSettings.PlatformTarget = PlatformTarget.MSIL;
|
||||
msbuildSettings.MSBuildPlatform = (Cake.Common.Tools.MSBuild.MSBuildPlatform)1;
|
||||
msbuildSettings.Configuration = configuration;
|
||||
return msbuildSettings;
|
||||
|
||||
}
|
||||
return new MSBuildSettings {
|
||||
PlatformTarget = PlatformTarget.MSIL,
|
||||
MSBuildPlatform = Cake.Common.Tools.MSBuild.MSBuildPlatform.x86,
|
||||
Configuration = configuration,
|
||||
};
|
||||
}
|
Загрузка…
Ссылка в новой задаче