A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

108 lines
3.1 KiB

using System;
using System.Collections;
using System.Collections.Generic;
using Avalonia.Logging;
namespace Avalonia.Layout
{
internal class LayoutQueue<T> : IReadOnlyCollection<T>, IDisposable
where T : notnull
{
private struct Info
{
public bool Active;
public int Count;
}
public LayoutQueue(Func<T, bool> shouldEnqueue)
{
_shouldEnqueue = shouldEnqueue;
}
private readonly Func<T, bool> _shouldEnqueue;
private readonly Queue<T> _inner = new Queue<T>();
private readonly Dictionary<T, Info> _loopQueueInfo = new Dictionary<T, Info>();
private readonly List<KeyValuePair<T, Info>> _notFinalizedBuffer = new List<KeyValuePair<T, Info>>();
private int _maxEnqueueCountPerLoop = 1;
public int Count => _inner.Count;
public IEnumerator<T> GetEnumerator() => (_inner as IEnumerable<T>).GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _inner.GetEnumerator();
public T Dequeue()
{
var result = _inner.Dequeue();
if (_loopQueueInfo.TryGetValue(result, out var info))
{
info.Active = false;
_loopQueueInfo[result] = info;
}
return result;
}
public void Enqueue(T item)
{
_loopQueueInfo.TryGetValue(item, out var info);
if (!info.Active)
{
if (info.Count < _maxEnqueueCountPerLoop)
{
_inner.Enqueue(item);
_loopQueueInfo[item] = new Info() { Active = true, Count = info.Count + 1 };
}
else
{
Logger.TryGet(LogEventLevel.Warning, LogArea.Layout)?.Log(
this,
"Layout cycle detected. Item {Item} was enqueued {Count} times.",
item,
info.Count);
}
}
}
public void BeginLoop(int maxEnqueueCountPerLoop)
{
_maxEnqueueCountPerLoop = maxEnqueueCountPerLoop;
}
public void EndLoop()
{
foreach (KeyValuePair<T, Info> info in _loopQueueInfo)
{
if (info.Value.Count >= _maxEnqueueCountPerLoop)
{
_notFinalizedBuffer.Add(info);
}
}
_loopQueueInfo.Clear();
// Prevent layout cycle but add to next layout the non arranged/measured items that might have caused cycle
// one more time as a final attempt.
foreach (var item in _notFinalizedBuffer)
{
if (_shouldEnqueue(item.Key))
{
_loopQueueInfo[item.Key] = new Info() { Active = true, Count = 0 };
_inner.Enqueue(item.Key);
}
}
_notFinalizedBuffer.Clear();
}
public void Dispose()
{
_inner.Clear();
_loopQueueInfo.Clear();
_notFinalizedBuffer.Clear();
}
}
}