Slots implementation
This commit is contained in:
parent
a5fc6179fb
commit
cf69baab9f
@ -8,18 +8,13 @@ namespace NEG.UI.Area
|
|||||||
public interface IArea : IUiElement
|
public interface IArea : IUiElement
|
||||||
{
|
{
|
||||||
IEnumerable<IWindowSlot> AvailableSlots { get; }
|
IEnumerable<IWindowSlot> AvailableSlots { get; }
|
||||||
IEnumerable<IPopup> CurrentPopups { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Open window
|
/// Open window
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="window"></param>
|
/// <param name="window"></param>
|
||||||
void OpenWindow(IWindow window);
|
/// <param name="data"></param>
|
||||||
/// <summary>
|
void OpenWindow(IWindow window, object data = null);
|
||||||
/// Open popup
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="popup"></param>
|
|
||||||
void OpenPopup(IPopup popup);
|
|
||||||
|
|
||||||
void CloseAllWindows()
|
void CloseAllWindows()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: ffa2155176774ab691d499979707a1bb
|
guid: f7cf5ef3a347e1c4b98411f4d564b988
|
||||||
timeCreated: 1670690282
|
timeCreated: 1670690282
|
||||||
@ -1,58 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
using NEG.UI.Popup;
|
|
||||||
using NEG.UI.Window;
|
|
||||||
using NEG.UI.WindowSlot;
|
|
||||||
|
|
||||||
namespace NEG.UI.Area
|
|
||||||
{
|
|
||||||
public class MonoArea : MonoBehaviour, IArea
|
|
||||||
{
|
|
||||||
public IEnumerable<IWindowSlot> AvailableSlots => windowSlots;
|
|
||||||
public IWindowSlot DefaultWindowSlot => windowSlots[0];
|
|
||||||
public IEnumerable<IWindow> CurrentWindows { get; }
|
|
||||||
public IEnumerable<IPopup> CurrentPopups => currentPopups;
|
|
||||||
|
|
||||||
[SerializeField] private List<MonoWindowSlot> windowSlots;
|
|
||||||
|
|
||||||
[SerializeField] private Queue<IPopup> currentPopups = new();
|
|
||||||
|
|
||||||
public void SetEnabled(bool setEnabled) => gameObject.SetActive(setEnabled);
|
|
||||||
|
|
||||||
public void OpenWindow(IWindow window) => DefaultWindowSlot.AttachWindow(window);
|
|
||||||
|
|
||||||
public void OpenPopup(IPopup popup)
|
|
||||||
{
|
|
||||||
currentPopups.Enqueue(popup);
|
|
||||||
popup.OnPopupClosed += OnPopupClosed;
|
|
||||||
popup.Show();
|
|
||||||
UpdatePopupStates();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdatePopupStates()
|
|
||||||
{
|
|
||||||
if(currentPopups.Count == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
while (currentPopups.TryPeek(out var popup))
|
|
||||||
{
|
|
||||||
if(popup.IsValid)
|
|
||||||
popup.SetEnabled(true);
|
|
||||||
|
|
||||||
currentPopups.Dequeue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnPopupClosed(IPopup popup)
|
|
||||||
{
|
|
||||||
if (!currentPopups.Contains(popup))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if(currentPopups.Peek() != popup)
|
|
||||||
return;
|
|
||||||
|
|
||||||
currentPopups.Dequeue();
|
|
||||||
UpdatePopupStates();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "NegUi",
|
"name": "NEG.UI",
|
||||||
"rootNamespace": "",
|
"rootNamespace": "",
|
||||||
"references": [],
|
"references": [],
|
||||||
"includePlatforms": [],
|
"includePlatforms": [],
|
||||||
|
|||||||
28
NEG/UI/Popup/DefaultPopupData.cs
Normal file
28
NEG/UI/Popup/DefaultPopupData.cs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace NEG.UI.Popup
|
||||||
|
{
|
||||||
|
public class DefaultPopupData : PopupData
|
||||||
|
{
|
||||||
|
private readonly IDefaultPopup defaultPopup;
|
||||||
|
|
||||||
|
private readonly string title;
|
||||||
|
private readonly string content;
|
||||||
|
private readonly List<(string, Action)> options;
|
||||||
|
|
||||||
|
public DefaultPopupData(IDefaultPopup popup, string title, string content, List<(string, Action)> options) : base(popup)
|
||||||
|
{
|
||||||
|
defaultPopup = popup;
|
||||||
|
this.title = title;
|
||||||
|
this.content = content;
|
||||||
|
this.options = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Show()
|
||||||
|
{
|
||||||
|
defaultPopup.SetContent(title, content, options);
|
||||||
|
base.Show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
NEG/UI/Popup/DefaultPopupData.cs.meta
Normal file
3
NEG/UI/Popup/DefaultPopupData.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 372c6df9ec044cb3adc86c7776b2ef61
|
||||||
|
timeCreated: 1672432934
|
||||||
10
NEG/UI/Popup/IDefaultPopup.cs
Normal file
10
NEG/UI/Popup/IDefaultPopup.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace NEG.UI.Popup
|
||||||
|
{
|
||||||
|
public interface IDefaultPopup : IPopup
|
||||||
|
{
|
||||||
|
public void SetContent(string title, string content, List<(string, Action)> options);
|
||||||
|
}
|
||||||
|
}
|
||||||
3
NEG/UI/Popup/IDefaultPopup.cs.meta
Normal file
3
NEG/UI/Popup/IDefaultPopup.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0a6d0871ada44d1d95bea6c8e8701769
|
||||||
|
timeCreated: 1672153906
|
||||||
@ -5,41 +5,18 @@ using System;
|
|||||||
namespace NEG.UI.Popup
|
namespace NEG.UI.Popup
|
||||||
{
|
{
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public interface IPopup : IUiElement
|
public interface IPopup
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Call when popup is not forcly closed
|
/// Show popup
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event Action<IPopup> OnPopupClosed;
|
/// <param name="data">data assigned to popup, used to give info that popup is closed</param>
|
||||||
|
void Show(PopupData data);
|
||||||
/// <summary>
|
|
||||||
/// Is popup still valid to show
|
|
||||||
/// </summary>
|
|
||||||
bool IsValid { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Mark popup as ready to show.
|
|
||||||
/// </summary>
|
|
||||||
internal void Show();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Close popup or mark as closed if not visible
|
/// Close popup or mark as closed if not visible
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="isForced">Is closing by forced by system, ex. close area</param>
|
/// <param name="silence">if true hide visually, without firing callbacks</param>
|
||||||
void Close(bool isForced = false);
|
void Close(bool silence = false);
|
||||||
}
|
|
||||||
|
|
||||||
public static class PopupExtensions
|
|
||||||
{
|
|
||||||
public static void Show(this IPopup popup, IArea area = null)
|
|
||||||
{
|
|
||||||
if (area != null)
|
|
||||||
{
|
|
||||||
area.OpenPopup(popup);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
UiManager.Instance.CurrentArea.OpenPopup(popup);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,24 +0,0 @@
|
|||||||
using NEG.UI.Area;
|
|
||||||
using System;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace NEG.UI.Popup
|
|
||||||
{
|
|
||||||
public class MonoPopup : MonoBehaviour, IPopup
|
|
||||||
{
|
|
||||||
public event Action<IPopup> OnPopupClosed;
|
|
||||||
public bool IsValid { get; private set; }
|
|
||||||
|
|
||||||
public void Close(bool isForced = false)
|
|
||||||
{
|
|
||||||
IsValid = false;
|
|
||||||
gameObject.SetActive(false);
|
|
||||||
if(!isForced)
|
|
||||||
OnPopupClosed?.Invoke(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetEnabled(bool setEnabled) => gameObject.SetActive(setEnabled);
|
|
||||||
|
|
||||||
void IPopup.Show() => IsValid = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
20
NEG/UI/Popup/PopupData.cs
Normal file
20
NEG/UI/Popup/PopupData.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
namespace NEG.UI.Popup
|
||||||
|
{
|
||||||
|
public class PopupData
|
||||||
|
{
|
||||||
|
public bool IsValid { get; protected set; }
|
||||||
|
|
||||||
|
private IPopup popup;
|
||||||
|
|
||||||
|
public PopupData(IPopup popup)
|
||||||
|
{
|
||||||
|
this.popup = popup;
|
||||||
|
IsValid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Show() => popup.Show(this);
|
||||||
|
public virtual void Hide() => popup.Close(true);
|
||||||
|
|
||||||
|
public virtual void Invalidate() => IsValid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
3
NEG/UI/Popup/PopupData.cs.meta
Normal file
3
NEG/UI/Popup/PopupData.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e6e5ebb367aa4dfba5e5f853c9b31a3d
|
||||||
|
timeCreated: 1672430446
|
||||||
999
NEG/UI/PriorityQueue.cs
Normal file
999
NEG/UI/PriorityQueue.cs
Normal file
@ -0,0 +1,999 @@
|
|||||||
|
#nullable enable
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace System.Collections.Generic
|
||||||
|
{
|
||||||
|
// Licensed to the .NET Foundation under one or more agreements.
|
||||||
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
|
|
||||||
|
// ported from:
|
||||||
|
// https://github.com/dotnet/runtime/blob/main/src/libraries/System.Collections/src/System/Collections/Generic/PriorityQueue.cs
|
||||||
|
internal sealed class PriorityQueueDebugView<TElement, TPriority>
|
||||||
|
{
|
||||||
|
private readonly PriorityQueue<TElement, TPriority> _queue;
|
||||||
|
private readonly bool _sort;
|
||||||
|
|
||||||
|
public PriorityQueueDebugView(PriorityQueue<TElement, TPriority> queue)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(queue);
|
||||||
|
|
||||||
|
_queue = queue;
|
||||||
|
_sort = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PriorityQueueDebugView(PriorityQueue<TElement, TPriority>.UnorderedItemsCollection collection)
|
||||||
|
{
|
||||||
|
_queue = collection?._queue ?? throw new System.ArgumentNullException(nameof(collection));
|
||||||
|
}
|
||||||
|
|
||||||
|
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
|
||||||
|
public (TElement Element, TPriority Priority)[] Items
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
List<(TElement Element, TPriority Priority)> list = new(_queue.UnorderedItems);
|
||||||
|
if (_sort) list.Sort((i1, i2) => _queue.Comparer.Compare(i1.Priority, i2.Priority));
|
||||||
|
|
||||||
|
return list.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||||
|
internal static class SR
|
||||||
|
{
|
||||||
|
internal const string ArgumentOutOfRange_NeedNonNegNum = "Non-negative number required.";
|
||||||
|
internal const string ArgumentOutOfRange_IndexMustBeLessOrEqual = "Index must be less or equal";
|
||||||
|
internal const string InvalidOperation_EmptyQueue = "The queue is empty.";
|
||||||
|
internal const string InvalidOperation_EnumFailedVersion = "Collection modified while iterating over it.";
|
||||||
|
internal const string Arg_NonZeroLowerBound = "Non-zero lower bound required.";
|
||||||
|
internal const string Arg_RankMultiDimNotSupported = "Multi-dimensional arrays not supported.";
|
||||||
|
internal const string Argument_InvalidArrayType = "Invalid array type.";
|
||||||
|
internal const string Argument_InvalidOffLen = "Invalid offset or length.";
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class ArgumentNullException
|
||||||
|
{
|
||||||
|
public static void ThrowIfNull(object o)
|
||||||
|
{
|
||||||
|
if (o == null)
|
||||||
|
throw new System.ArgumentNullException(); // hard to do it differently without C# 10's features
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class ArrayEx
|
||||||
|
{
|
||||||
|
internal const int MaxLength = int.MaxValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Internal helper functions for working with enumerables.
|
||||||
|
/// </summary>
|
||||||
|
internal static class EnumerableHelpers
|
||||||
|
{
|
||||||
|
/// <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>.
|
||||||
|
var 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;
|
||||||
|
var arr = new T[DefaultCapacity];
|
||||||
|
arr[0] = en.Current;
|
||||||
|
int count = 1;
|
||||||
|
|
||||||
|
while (en.MoveNext())
|
||||||
|
{
|
||||||
|
if (count == arr.Length)
|
||||||
|
{
|
||||||
|
// 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 Array.MaxLength (this overflow check works because of the
|
||||||
|
// cast to uint).
|
||||||
|
int newLength = count << 1;
|
||||||
|
if ((uint)newLength > ArrayEx.MaxLength)
|
||||||
|
newLength = ArrayEx.MaxLength <= count ? count + 1 : ArrayEx.MaxLength;
|
||||||
|
|
||||||
|
Array.Resize(ref arr, newLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
arr[count++] = en.Current;
|
||||||
|
}
|
||||||
|
|
||||||
|
length = count;
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
length = 0;
|
||||||
|
return Array.Empty<T>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a min priority queue.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TElement">Specifies the type of elements in the queue.</typeparam>
|
||||||
|
/// <typeparam name="TPriority">Specifies the type of priority associated with enqueued elements.</typeparam>
|
||||||
|
/// <remarks>
|
||||||
|
/// Implements an array-backed quaternary min-heap. Each element is enqueued with an associated priority
|
||||||
|
/// that determines the dequeue order: elements with the lowest priority get dequeued first.
|
||||||
|
/// </remarks>
|
||||||
|
[DebuggerDisplay("Count = {Count}")]
|
||||||
|
[DebuggerTypeProxy(typeof(PriorityQueueDebugView<,>))]
|
||||||
|
public class PriorityQueue<TElement, TPriority>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the arity of the d-ary heap, which here is quaternary.
|
||||||
|
/// It is assumed that this value is a power of 2.
|
||||||
|
/// </summary>
|
||||||
|
private const int Arity = 4;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The binary logarithm of <see cref="Arity" />.
|
||||||
|
/// </summary>
|
||||||
|
private const int Log2Arity = 2;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Custom comparer used to order the heap.
|
||||||
|
/// </summary>
|
||||||
|
private readonly IComparer<TPriority>? _comparer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an implicit heap-ordered complete d-ary tree, stored as an array.
|
||||||
|
/// </summary>
|
||||||
|
private (TElement Element, TPriority Priority)[] _nodes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The number of nodes in the heap.
|
||||||
|
/// </summary>
|
||||||
|
private int _size;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Lazily-initialized collection used to expose the contents of the queue.
|
||||||
|
/// </summary>
|
||||||
|
private UnorderedItemsCollection? _unorderedItems;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Version updated on mutation to help validate enumerators operate on a consistent state.
|
||||||
|
/// </summary>
|
||||||
|
private int _version;
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
static PriorityQueue()
|
||||||
|
{
|
||||||
|
Debug.Assert(Log2Arity > 0 && Math.Pow(2, Log2Arity) == Arity);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="PriorityQueue{TElement, TPriority}" /> class.
|
||||||
|
/// </summary>
|
||||||
|
public PriorityQueue()
|
||||||
|
{
|
||||||
|
_nodes = Array.Empty<(TElement, TPriority)>();
|
||||||
|
_comparer = InitializeComparer(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="PriorityQueue{TElement, TPriority}" /> class
|
||||||
|
/// with the specified initial capacity.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="initialCapacity">Initial capacity to allocate in the underlying heap array.</param>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException">
|
||||||
|
/// The specified <paramref name="initialCapacity" /> was negative.
|
||||||
|
/// </exception>
|
||||||
|
public PriorityQueue(int initialCapacity)
|
||||||
|
: this(initialCapacity, null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="PriorityQueue{TElement, TPriority}" /> class
|
||||||
|
/// with the specified custom priority comparer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="comparer">
|
||||||
|
/// Custom comparer dictating the ordering of elements.
|
||||||
|
/// Uses <see cref="Comparer{T}.Default" /> if the argument is <see langword="null" />.
|
||||||
|
/// </param>
|
||||||
|
public PriorityQueue(IComparer<TPriority>? comparer)
|
||||||
|
{
|
||||||
|
_nodes = Array.Empty<(TElement, TPriority)>();
|
||||||
|
_comparer = InitializeComparer(comparer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="PriorityQueue{TElement, TPriority}" /> class
|
||||||
|
/// with the specified initial capacity and custom priority comparer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="initialCapacity">Initial capacity to allocate in the underlying heap array.</param>
|
||||||
|
/// <param name="comparer">
|
||||||
|
/// Custom comparer dictating the ordering of elements.
|
||||||
|
/// Uses <see cref="Comparer{T}.Default" /> if the argument is <see langword="null" />.
|
||||||
|
/// </param>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException">
|
||||||
|
/// The specified <paramref name="initialCapacity" /> was negative.
|
||||||
|
/// </exception>
|
||||||
|
public PriorityQueue(int initialCapacity, IComparer<TPriority>? comparer)
|
||||||
|
{
|
||||||
|
if (initialCapacity < 0)
|
||||||
|
throw new ArgumentOutOfRangeException(
|
||||||
|
nameof(initialCapacity), initialCapacity, SR.ArgumentOutOfRange_NeedNonNegNum);
|
||||||
|
|
||||||
|
_nodes = new (TElement, TPriority)[initialCapacity];
|
||||||
|
_comparer = InitializeComparer(comparer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="PriorityQueue{TElement, TPriority}" /> class
|
||||||
|
/// that is populated with the specified elements and priorities.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="items">The pairs of elements and priorities with which to populate the queue.</param>
|
||||||
|
/// <exception cref="ArgumentNullException">
|
||||||
|
/// The specified <paramref name="items" /> argument was <see langword="null" />.
|
||||||
|
/// </exception>
|
||||||
|
/// <remarks>
|
||||||
|
/// Constructs the heap using a heapify operation,
|
||||||
|
/// which is generally faster than enqueuing individual elements sequentially.
|
||||||
|
/// </remarks>
|
||||||
|
public PriorityQueue(IEnumerable<(TElement Element, TPriority Priority)> items)
|
||||||
|
: this(items, null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="PriorityQueue{TElement, TPriority}" /> class
|
||||||
|
/// that is populated with the specified elements and priorities,
|
||||||
|
/// and with the specified custom priority comparer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="items">The pairs of elements and priorities with which to populate the queue.</param>
|
||||||
|
/// <param name="comparer">
|
||||||
|
/// Custom comparer dictating the ordering of elements.
|
||||||
|
/// Uses <see cref="Comparer{T}.Default" /> if the argument is <see langword="null" />.
|
||||||
|
/// </param>
|
||||||
|
/// <exception cref="ArgumentNullException">
|
||||||
|
/// The specified <paramref name="items" /> argument was <see langword="null" />.
|
||||||
|
/// </exception>
|
||||||
|
/// <remarks>
|
||||||
|
/// Constructs the heap using a heapify operation,
|
||||||
|
/// which is generally faster than enqueuing individual elements sequentially.
|
||||||
|
/// </remarks>
|
||||||
|
public PriorityQueue(IEnumerable<(TElement Element, TPriority Priority)> items, IComparer<TPriority>? comparer)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(items);
|
||||||
|
|
||||||
|
_nodes = EnumerableHelpers.ToArray(items, out _size);
|
||||||
|
_comparer = InitializeComparer(comparer);
|
||||||
|
|
||||||
|
if (_size > 1) Heapify();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the number of elements contained in the <see cref="PriorityQueue{TElement, TPriority}" />.
|
||||||
|
/// </summary>
|
||||||
|
public int Count => _size;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the priority comparer used by the <see cref="PriorityQueue{TElement, TPriority}" />.
|
||||||
|
/// </summary>
|
||||||
|
public IComparer<TPriority> Comparer => _comparer ?? Comparer<TPriority>.Default;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a collection that enumerates the elements of the queue in an unordered manner.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The enumeration does not order items by priority, since that would require N * log(N) time and N space.
|
||||||
|
/// Items are instead enumerated following the internal array heap layout.
|
||||||
|
/// </remarks>
|
||||||
|
public UnorderedItemsCollection UnorderedItems => _unorderedItems ??= new UnorderedItemsCollection(this);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the specified element with associated priority to the <see cref="PriorityQueue{TElement, TPriority}" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="element">The element to add to the <see cref="PriorityQueue{TElement, TPriority}" />.</param>
|
||||||
|
/// <param name="priority">The priority with which to associate the new element.</param>
|
||||||
|
public void Enqueue(TElement element, TPriority priority)
|
||||||
|
{
|
||||||
|
// Virtually add the node at the end of the underlying array.
|
||||||
|
// Note that the node being enqueued does not need to be physically placed
|
||||||
|
// there at this point, as such an assignment would be redundant.
|
||||||
|
|
||||||
|
int currentSize = _size++;
|
||||||
|
_version++;
|
||||||
|
|
||||||
|
if (_nodes.Length == currentSize) Grow(currentSize + 1);
|
||||||
|
|
||||||
|
if (_comparer == null)
|
||||||
|
MoveUpDefaultComparer((element, priority), currentSize);
|
||||||
|
else
|
||||||
|
MoveUpCustomComparer((element, priority), currentSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the minimal element from the <see cref="PriorityQueue{TElement, TPriority}" /> without removing it.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="InvalidOperationException">The <see cref="PriorityQueue{TElement, TPriority}" /> is empty.</exception>
|
||||||
|
/// <returns>The minimal element of the <see cref="PriorityQueue{TElement, TPriority}" />.</returns>
|
||||||
|
public TElement Peek()
|
||||||
|
{
|
||||||
|
if (_size == 0) throw new InvalidOperationException(SR.InvalidOperation_EmptyQueue);
|
||||||
|
|
||||||
|
return _nodes[0].Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes and returns the minimal element from the <see cref="PriorityQueue{TElement, TPriority}" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="InvalidOperationException">The queue is empty.</exception>
|
||||||
|
/// <returns>The minimal element of the <see cref="PriorityQueue{TElement, TPriority}" />.</returns>
|
||||||
|
public TElement Dequeue()
|
||||||
|
{
|
||||||
|
if (_size == 0) throw new InvalidOperationException(SR.InvalidOperation_EmptyQueue);
|
||||||
|
|
||||||
|
var element = _nodes[0].Element;
|
||||||
|
RemoveRootNode();
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the minimal element from the <see cref="PriorityQueue{TElement, TPriority}" />,
|
||||||
|
/// and copies it to the <paramref name="element" /> parameter,
|
||||||
|
/// and its associated priority to the <paramref name="priority" /> parameter.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="element">The removed element.</param>
|
||||||
|
/// <param name="priority">The priority associated with the removed element.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// <see langword="true" /> if the element is successfully removed;
|
||||||
|
/// <see langword="false" /> if the <see cref="PriorityQueue{TElement, TPriority}" /> is empty.
|
||||||
|
/// </returns>
|
||||||
|
public bool TryDequeue([MaybeNullWhen(false)] out TElement element,
|
||||||
|
[MaybeNullWhen(false)] out TPriority priority)
|
||||||
|
{
|
||||||
|
if (_size != 0)
|
||||||
|
{
|
||||||
|
(element, priority) = _nodes[0];
|
||||||
|
RemoveRootNode();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
element = default;
|
||||||
|
priority = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a value that indicates whether there is a minimal element in the
|
||||||
|
/// <see cref="PriorityQueue{TElement, TPriority}" />,
|
||||||
|
/// and if one is present, copies it to the <paramref name="element" /> parameter,
|
||||||
|
/// and its associated priority to the <paramref name="priority" /> parameter.
|
||||||
|
/// The element is not removed from the <see cref="PriorityQueue{TElement, TPriority}" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="element">The minimal element in the queue.</param>
|
||||||
|
/// <param name="priority">The priority associated with the minimal element.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// <see langword="true" /> if there is a minimal element;
|
||||||
|
/// <see langword="false" /> if the <see cref="PriorityQueue{TElement, TPriority}" /> is empty.
|
||||||
|
/// </returns>
|
||||||
|
public bool TryPeek([MaybeNullWhen(false)] out TElement element,
|
||||||
|
[MaybeNullWhen(false)] out TPriority priority)
|
||||||
|
{
|
||||||
|
if (_size != 0)
|
||||||
|
{
|
||||||
|
(element, priority) = _nodes[0];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
element = default;
|
||||||
|
priority = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the specified element with associated priority to the <see cref="PriorityQueue{TElement, TPriority}" />,
|
||||||
|
/// and immediately removes the minimal element, returning the result.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="element">The element to add to the <see cref="PriorityQueue{TElement, TPriority}" />.</param>
|
||||||
|
/// <param name="priority">The priority with which to associate the new element.</param>
|
||||||
|
/// <returns>The minimal element removed after the enqueue operation.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// Implements an insert-then-extract heap operation that is generally more efficient
|
||||||
|
/// than sequencing Enqueue and Dequeue operations: in the worst case scenario only one
|
||||||
|
/// shift-down operation is required.
|
||||||
|
/// </remarks>
|
||||||
|
public TElement EnqueueDequeue(TElement element, TPriority priority)
|
||||||
|
{
|
||||||
|
if (_size != 0)
|
||||||
|
{
|
||||||
|
var root = _nodes[0];
|
||||||
|
|
||||||
|
if (_comparer == null)
|
||||||
|
{
|
||||||
|
if (Comparer<TPriority>.Default.Compare(priority, root.Priority) > 0)
|
||||||
|
{
|
||||||
|
MoveDownDefaultComparer((element, priority), 0);
|
||||||
|
_version++;
|
||||||
|
return root.Element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_comparer.Compare(priority, root.Priority) > 0)
|
||||||
|
{
|
||||||
|
MoveDownCustomComparer((element, priority), 0);
|
||||||
|
_version++;
|
||||||
|
return root.Element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enqueues a sequence of element/priority pairs to the <see cref="PriorityQueue{TElement, TPriority}" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="items">The pairs of elements and priorities to add to the queue.</param>
|
||||||
|
/// <exception cref="ArgumentNullException">
|
||||||
|
/// The specified <paramref name="items" /> argument was <see langword="null" />.
|
||||||
|
/// </exception>
|
||||||
|
public void EnqueueRange(IEnumerable<(TElement Element, TPriority Priority)> items)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(items);
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
var collection =
|
||||||
|
items as ICollection<(TElement Element, TPriority Priority)>;
|
||||||
|
if (collection is not null && (count = collection.Count) > _nodes.Length - _size) Grow(_size + count);
|
||||||
|
|
||||||
|
if (_size == 0)
|
||||||
|
{
|
||||||
|
// build using Heapify() if the queue is empty.
|
||||||
|
|
||||||
|
if (collection is not null)
|
||||||
|
{
|
||||||
|
collection.CopyTo(_nodes, 0);
|
||||||
|
_size = count;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
(TElement, TPriority)[] nodes = _nodes;
|
||||||
|
foreach ((var element, var priority) in items)
|
||||||
|
{
|
||||||
|
if (nodes.Length == i)
|
||||||
|
{
|
||||||
|
Grow(i + 1);
|
||||||
|
nodes = _nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes[i++] = (element, priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
_size = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
_version++;
|
||||||
|
|
||||||
|
if (_size > 1) Heapify();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach ((var element, var priority) in items) Enqueue(element, priority);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enqueues a sequence of elements pairs to the <see cref="PriorityQueue{TElement, TPriority}" />,
|
||||||
|
/// all associated with the specified priority.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="elements">The elements to add to the queue.</param>
|
||||||
|
/// <param name="priority">The priority to associate with the new elements.</param>
|
||||||
|
/// <exception cref="ArgumentNullException">
|
||||||
|
/// The specified <paramref name="elements" /> argument was <see langword="null" />.
|
||||||
|
/// </exception>
|
||||||
|
public void EnqueueRange(IEnumerable<TElement> elements, TPriority priority)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(elements);
|
||||||
|
|
||||||
|
int count;
|
||||||
|
if (elements is ICollection<(TElement Element, TPriority Priority)> collection &&
|
||||||
|
(count = collection.Count) > _nodes.Length - _size)
|
||||||
|
Grow(_size + count);
|
||||||
|
|
||||||
|
if (_size == 0)
|
||||||
|
{
|
||||||
|
// build using Heapify() if the queue is empty.
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
(TElement, TPriority)[] nodes = _nodes;
|
||||||
|
foreach (var element in elements)
|
||||||
|
{
|
||||||
|
if (nodes.Length == i)
|
||||||
|
{
|
||||||
|
Grow(i + 1);
|
||||||
|
nodes = _nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes[i++] = (element, priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
_size = i;
|
||||||
|
_version++;
|
||||||
|
|
||||||
|
if (i > 1) Heapify();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var element in elements) Enqueue(element, priority);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes all items from the <see cref="PriorityQueue{TElement, TPriority}" />.
|
||||||
|
/// </summary>
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
if (RuntimeHelpers.IsReferenceOrContainsReferences<(TElement, TPriority)>())
|
||||||
|
// Clear the elements so that the gc can reclaim the references
|
||||||
|
Array.Clear(_nodes, 0, _size);
|
||||||
|
|
||||||
|
_size = 0;
|
||||||
|
_version++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ensures that the <see cref="PriorityQueue{TElement, TPriority}" /> can hold up to
|
||||||
|
/// <paramref name="capacity" /> items without further expansion of its backing storage.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="capacity">The minimum capacity to be used.</param>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException">
|
||||||
|
/// The specified <paramref name="capacity" /> is negative.
|
||||||
|
/// </exception>
|
||||||
|
/// <returns>The current capacity of the <see cref="PriorityQueue{TElement, TPriority}" />.</returns>
|
||||||
|
public int EnsureCapacity(int capacity)
|
||||||
|
{
|
||||||
|
if (capacity < 0)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(capacity), capacity, SR.ArgumentOutOfRange_NeedNonNegNum);
|
||||||
|
|
||||||
|
if (_nodes.Length < capacity)
|
||||||
|
{
|
||||||
|
Grow(capacity);
|
||||||
|
_version++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _nodes.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the capacity to the actual number of items in the <see cref="PriorityQueue{TElement, TPriority}" />,
|
||||||
|
/// if that is less than 90 percent of current capacity.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This method can be used to minimize a collection's memory overhead
|
||||||
|
/// if no new elements will be added to the collection.
|
||||||
|
/// </remarks>
|
||||||
|
public void TrimExcess()
|
||||||
|
{
|
||||||
|
int threshold = (int)(_nodes.Length * 0.9);
|
||||||
|
if (_size < threshold)
|
||||||
|
{
|
||||||
|
Array.Resize(ref _nodes, _size);
|
||||||
|
_version++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Grows the priority queue to match the specified min capacity.
|
||||||
|
/// </summary>
|
||||||
|
private void Grow(int minCapacity)
|
||||||
|
{
|
||||||
|
Debug.Assert(_nodes.Length < minCapacity);
|
||||||
|
|
||||||
|
const int GrowFactor = 2;
|
||||||
|
const int MinimumGrow = 4;
|
||||||
|
|
||||||
|
int newcapacity = GrowFactor * _nodes.Length;
|
||||||
|
|
||||||
|
// Allow the queue to grow to maximum possible capacity (~2G elements) before encountering overflow.
|
||||||
|
// Note that this check works even when _nodes.Length overflowed thanks to the (uint) cast
|
||||||
|
if ((uint)newcapacity > ArrayEx.MaxLength) newcapacity = ArrayEx.MaxLength;
|
||||||
|
|
||||||
|
// Ensure minimum growth is respected.
|
||||||
|
newcapacity = Math.Max(newcapacity, _nodes.Length + MinimumGrow);
|
||||||
|
|
||||||
|
// If the computed capacity is still less than specified, set to the original argument.
|
||||||
|
// Capacities exceeding Array.MaxLength will be surfaced as OutOfMemoryException by Array.Resize.
|
||||||
|
if (newcapacity < minCapacity) newcapacity = minCapacity;
|
||||||
|
|
||||||
|
Array.Resize(ref _nodes, newcapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the node from the root of the heap
|
||||||
|
/// </summary>
|
||||||
|
private void RemoveRootNode()
|
||||||
|
{
|
||||||
|
int lastNodeIndex = --_size;
|
||||||
|
_version++;
|
||||||
|
|
||||||
|
if (lastNodeIndex > 0)
|
||||||
|
{
|
||||||
|
var lastNode = _nodes[lastNodeIndex];
|
||||||
|
if (_comparer == null)
|
||||||
|
MoveDownDefaultComparer(lastNode, 0);
|
||||||
|
else
|
||||||
|
MoveDownCustomComparer(lastNode, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RuntimeHelpers.IsReferenceOrContainsReferences<(TElement, TPriority)>())
|
||||||
|
_nodes[lastNodeIndex] = default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the index of an element's parent.
|
||||||
|
/// </summary>
|
||||||
|
private static int GetParentIndex(int index) => (index - 1) >> Log2Arity;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the index of the first child of an element.
|
||||||
|
/// </summary>
|
||||||
|
private static int GetFirstChildIndex(int index) => (index << Log2Arity) + 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts an unordered list into a heap.
|
||||||
|
/// </summary>
|
||||||
|
private void Heapify()
|
||||||
|
{
|
||||||
|
// Leaves of the tree are in fact 1-element heaps, for which there
|
||||||
|
// is no need to correct them. The heap property needs to be restored
|
||||||
|
// only for higher nodes, starting from the first node that has children.
|
||||||
|
// It is the parent of the very last element in the array.
|
||||||
|
|
||||||
|
var nodes = _nodes;
|
||||||
|
int lastParentWithChildren = GetParentIndex(_size - 1);
|
||||||
|
|
||||||
|
if (_comparer == null)
|
||||||
|
for (int index = lastParentWithChildren; index >= 0; --index)
|
||||||
|
MoveDownDefaultComparer(nodes[index], index);
|
||||||
|
else
|
||||||
|
for (int index = lastParentWithChildren; index >= 0; --index)
|
||||||
|
MoveDownCustomComparer(nodes[index], index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Moves a node up in the tree to restore heap order.
|
||||||
|
/// </summary>
|
||||||
|
private void MoveUpDefaultComparer((TElement Element, TPriority Priority) node, int nodeIndex)
|
||||||
|
{
|
||||||
|
// Instead of swapping items all the way to the root, we will perform
|
||||||
|
// a similar optimization as in the insertion sort.
|
||||||
|
|
||||||
|
Debug.Assert(_comparer is null);
|
||||||
|
Debug.Assert(0 <= nodeIndex && nodeIndex < _size);
|
||||||
|
|
||||||
|
var nodes = _nodes;
|
||||||
|
|
||||||
|
while (nodeIndex > 0)
|
||||||
|
{
|
||||||
|
int parentIndex = GetParentIndex(nodeIndex);
|
||||||
|
var parent = nodes[parentIndex];
|
||||||
|
|
||||||
|
if (Comparer<TPriority>.Default.Compare(node.Priority, parent.Priority) < 0)
|
||||||
|
{
|
||||||
|
nodes[nodeIndex] = parent;
|
||||||
|
nodeIndex = parentIndex;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes[nodeIndex] = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Moves a node up in the tree to restore heap order.
|
||||||
|
/// </summary>
|
||||||
|
private void MoveUpCustomComparer((TElement Element, TPriority Priority) node, int nodeIndex)
|
||||||
|
{
|
||||||
|
// Instead of swapping items all the way to the root, we will perform
|
||||||
|
// a similar optimization as in the insertion sort.
|
||||||
|
|
||||||
|
Debug.Assert(_comparer is not null);
|
||||||
|
Debug.Assert(0 <= nodeIndex && nodeIndex < _size);
|
||||||
|
|
||||||
|
var comparer = _comparer;
|
||||||
|
var nodes = _nodes;
|
||||||
|
|
||||||
|
while (nodeIndex > 0)
|
||||||
|
{
|
||||||
|
int parentIndex = GetParentIndex(nodeIndex);
|
||||||
|
var parent = nodes[parentIndex];
|
||||||
|
|
||||||
|
if (comparer.Compare(node.Priority, parent.Priority) < 0)
|
||||||
|
{
|
||||||
|
nodes[nodeIndex] = parent;
|
||||||
|
nodeIndex = parentIndex;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes[nodeIndex] = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Moves a node down in the tree to restore heap order.
|
||||||
|
/// </summary>
|
||||||
|
private void MoveDownDefaultComparer((TElement Element, TPriority Priority) node, int nodeIndex)
|
||||||
|
{
|
||||||
|
// The node to move down will not actually be swapped every time.
|
||||||
|
// Rather, values on the affected path will be moved up, thus leaving a free spot
|
||||||
|
// for this value to drop in. Similar optimization as in the insertion sort.
|
||||||
|
|
||||||
|
Debug.Assert(_comparer is null);
|
||||||
|
Debug.Assert(0 <= nodeIndex && nodeIndex < _size);
|
||||||
|
|
||||||
|
var nodes = _nodes;
|
||||||
|
int size = _size;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
while ((i = GetFirstChildIndex(nodeIndex)) < size)
|
||||||
|
{
|
||||||
|
// Find the child node with the minimal priority
|
||||||
|
var minChild = nodes[i];
|
||||||
|
int minChildIndex = i;
|
||||||
|
|
||||||
|
int childIndexUpperBound = Math.Min(i + Arity, size);
|
||||||
|
while (++i < childIndexUpperBound)
|
||||||
|
{
|
||||||
|
var nextChild = nodes[i];
|
||||||
|
if (Comparer<TPriority>.Default.Compare(nextChild.Priority, minChild.Priority) < 0)
|
||||||
|
{
|
||||||
|
minChild = nextChild;
|
||||||
|
minChildIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Heap property is satisfied; insert node in this location.
|
||||||
|
if (Comparer<TPriority>.Default.Compare(node.Priority, minChild.Priority) <= 0) break;
|
||||||
|
|
||||||
|
// Move the minimal child up by one node and
|
||||||
|
// continue recursively from its location.
|
||||||
|
nodes[nodeIndex] = minChild;
|
||||||
|
nodeIndex = minChildIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes[nodeIndex] = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Moves a node down in the tree to restore heap order.
|
||||||
|
/// </summary>
|
||||||
|
private void MoveDownCustomComparer((TElement Element, TPriority Priority) node, int nodeIndex)
|
||||||
|
{
|
||||||
|
// The node to move down will not actually be swapped every time.
|
||||||
|
// Rather, values on the affected path will be moved up, thus leaving a free spot
|
||||||
|
// for this value to drop in. Similar optimization as in the insertion sort.
|
||||||
|
|
||||||
|
Debug.Assert(_comparer is not null);
|
||||||
|
Debug.Assert(0 <= nodeIndex && nodeIndex < _size);
|
||||||
|
|
||||||
|
var comparer = _comparer;
|
||||||
|
var nodes = _nodes;
|
||||||
|
int size = _size;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
while ((i = GetFirstChildIndex(nodeIndex)) < size)
|
||||||
|
{
|
||||||
|
// Find the child node with the minimal priority
|
||||||
|
var minChild = nodes[i];
|
||||||
|
int minChildIndex = i;
|
||||||
|
|
||||||
|
int childIndexUpperBound = Math.Min(i + Arity, size);
|
||||||
|
while (++i < childIndexUpperBound)
|
||||||
|
{
|
||||||
|
var nextChild = nodes[i];
|
||||||
|
if (comparer.Compare(nextChild.Priority, minChild.Priority) < 0)
|
||||||
|
{
|
||||||
|
minChild = nextChild;
|
||||||
|
minChildIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Heap property is satisfied; insert node in this location.
|
||||||
|
if (comparer.Compare(node.Priority, minChild.Priority) <= 0) break;
|
||||||
|
|
||||||
|
// Move the minimal child up by one node and continue recursively from its location.
|
||||||
|
nodes[nodeIndex] = minChild;
|
||||||
|
nodeIndex = minChildIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes[nodeIndex] = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes the custom comparer to be used internally by the heap.
|
||||||
|
/// </summary>
|
||||||
|
private static IComparer<TPriority>? InitializeComparer(IComparer<TPriority>? comparer)
|
||||||
|
{
|
||||||
|
if (typeof(TPriority).IsValueType)
|
||||||
|
{
|
||||||
|
if (comparer == Comparer<TPriority>.Default)
|
||||||
|
// if the user manually specifies the default comparer,
|
||||||
|
// revert to using the optimized path.
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return comparer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Currently the JIT doesn't optimize direct Comparer<T>.Default.Compare
|
||||||
|
// calls for reference types, so we want to cache the comparer instance instead.
|
||||||
|
// TODO https://github.com/dotnet/runtime/issues/10050: Update if this changes in the future.
|
||||||
|
return comparer ?? Comparer<TPriority>.Default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enumerates the contents of a <see cref="PriorityQueue{TElement, TPriority}" />, without any ordering guarantees.
|
||||||
|
/// </summary>
|
||||||
|
[DebuggerDisplay("Count = {Count}")]
|
||||||
|
[DebuggerTypeProxy(typeof(PriorityQueueDebugView<,>))]
|
||||||
|
public sealed class UnorderedItemsCollection : IReadOnlyCollection<(TElement Element, TPriority Priority)>,
|
||||||
|
ICollection
|
||||||
|
{
|
||||||
|
internal readonly PriorityQueue<TElement, TPriority> _queue;
|
||||||
|
|
||||||
|
internal UnorderedItemsCollection(PriorityQueue<TElement, TPriority> queue)
|
||||||
|
{
|
||||||
|
_queue = queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
object ICollection.SyncRoot => this;
|
||||||
|
bool ICollection.IsSynchronized => false;
|
||||||
|
|
||||||
|
void ICollection.CopyTo(Array array, int index)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(array);
|
||||||
|
|
||||||
|
if (array.Rank != 1) throw new ArgumentException(SR.Arg_RankMultiDimNotSupported, nameof(array));
|
||||||
|
|
||||||
|
if (array.GetLowerBound(0) != 0) throw new ArgumentException(SR.Arg_NonZeroLowerBound, nameof(array));
|
||||||
|
|
||||||
|
if (index < 0 || index > array.Length)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(index), index,
|
||||||
|
SR.ArgumentOutOfRange_IndexMustBeLessOrEqual);
|
||||||
|
|
||||||
|
if (array.Length - index < _queue._size) throw new ArgumentException(SR.Argument_InvalidOffLen);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Array.Copy(_queue._nodes, 0, array, index, _queue._size);
|
||||||
|
}
|
||||||
|
catch (ArrayTypeMismatchException)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(SR.Argument_InvalidArrayType, nameof(array));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count => _queue._size;
|
||||||
|
|
||||||
|
IEnumerator<(TElement Element, TPriority Priority)> IEnumerable<(TElement Element, TPriority Priority)>.
|
||||||
|
GetEnumerator() => GetEnumerator();
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an enumerator that iterates through the <see cref="UnorderedItems" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An <see cref="Enumerator" /> for the <see cref="UnorderedItems" />.</returns>
|
||||||
|
public Enumerator GetEnumerator() => new(_queue);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enumerates the element and priority pairs of a <see cref="PriorityQueue{TElement, TPriority}" />,
|
||||||
|
/// without any ordering guarantees.
|
||||||
|
/// </summary>
|
||||||
|
public struct Enumerator : IEnumerator<(TElement Element, TPriority Priority)>
|
||||||
|
{
|
||||||
|
private readonly PriorityQueue<TElement, TPriority> _queue;
|
||||||
|
private readonly int _version;
|
||||||
|
private int _index;
|
||||||
|
|
||||||
|
internal Enumerator(PriorityQueue<TElement, TPriority> queue)
|
||||||
|
{
|
||||||
|
_queue = queue;
|
||||||
|
_index = 0;
|
||||||
|
_version = queue._version;
|
||||||
|
Current = default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Releases all resources used by the <see cref="Enumerator" />.
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Advances the enumerator to the next element of the <see cref="UnorderedItems" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// <see langword="true" /> if the enumerator was successfully advanced to the next element;
|
||||||
|
/// <see langword="false" /> if the enumerator has passed the end of the collection.
|
||||||
|
/// </returns>
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
var localQueue = _queue;
|
||||||
|
|
||||||
|
if (_version == localQueue._version && (uint)_index < (uint)localQueue._size)
|
||||||
|
{
|
||||||
|
Current = localQueue._nodes[_index];
|
||||||
|
_index++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MoveNextRare();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool MoveNextRare()
|
||||||
|
{
|
||||||
|
if (_version != _queue._version)
|
||||||
|
throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion);
|
||||||
|
|
||||||
|
_index = _queue._size + 1;
|
||||||
|
Current = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the element at the current position of the enumerator.
|
||||||
|
/// </summary>
|
||||||
|
public (TElement Element, TPriority Priority) Current { get; private set; }
|
||||||
|
|
||||||
|
object IEnumerator.Current => Current;
|
||||||
|
|
||||||
|
void IEnumerator.Reset()
|
||||||
|
{
|
||||||
|
if (_version != _queue._version)
|
||||||
|
throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion);
|
||||||
|
|
||||||
|
_index = 0;
|
||||||
|
Current = default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
NEG/UI/PriorityQueue.cs.meta
Normal file
11
NEG/UI/PriorityQueue.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e1b6a9a70d997fd468f30caa1e760078
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@ -1,13 +1,16 @@
|
|||||||
using NEG.UI.Area;
|
using NEG.UI.Area;
|
||||||
|
using NEG.UI.Popup;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace NEG.UI
|
namespace NEG.UI
|
||||||
{
|
{
|
||||||
public class UiManager
|
public abstract class UiManager
|
||||||
{
|
{
|
||||||
//TODO: use default unity selection
|
//TODO: use default unity selection
|
||||||
//TODO: window snaping to slots
|
//TODO: window snaping to slots
|
||||||
|
//TODO: Default prefabs?
|
||||||
|
|
||||||
public static UiManager Instance { get; private set; }
|
public static UiManager Instance { get; private set; }
|
||||||
|
|
||||||
@ -31,8 +34,15 @@ namespace NEG.UI
|
|||||||
}
|
}
|
||||||
|
|
||||||
private IArea currentArea;
|
private IArea currentArea;
|
||||||
|
protected IDefaultPopup currentDefaultPopup;
|
||||||
|
private (PopupData data, int priority) currentShownPopup;
|
||||||
|
|
||||||
public UiManager(IArea startArea)
|
private PriorityQueue<PopupData, int> popupsToShow = new();
|
||||||
|
|
||||||
|
//TODO: localize
|
||||||
|
private string localizedYes = "Yes", localizedNo = "No", localizedOk = "Ok";
|
||||||
|
|
||||||
|
protected UiManager(IArea startArea)
|
||||||
{
|
{
|
||||||
if (Instance != null)
|
if (Instance != null)
|
||||||
{
|
{
|
||||||
@ -44,6 +54,107 @@ namespace NEG.UI
|
|||||||
CurrentArea = startArea;
|
CurrentArea = startArea;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Show popup if there is non other currently shown. Otherwise add current popup to ordered queue and show it later. It will be closed after pressing ok button.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="title">popup title</param>
|
||||||
|
/// <param name="content">popup content</param>
|
||||||
|
/// <param name="okText">text to show on ok button, empty for localized "Ok"</param>
|
||||||
|
/// <param name="okPressed">additional action on ok pressed</param>
|
||||||
|
/// <param name="priority">priority of popup (lower number -> show first)</param>
|
||||||
|
/// <param name="forceShow">force show current popup only if currently shown has lower priority</param>
|
||||||
|
/// <returns>data for created popup, can be used to invalidate popup (will not show)</returns>
|
||||||
|
public PopupData ShowOkPopup(string title, string content, string okText = null, Action okPressed = null, int priority = 0, bool forceShow = false)
|
||||||
|
{
|
||||||
|
var data = new DefaultPopupData(currentDefaultPopup, title, content,
|
||||||
|
new List<(string, Action)>() { (okText ?? localizedOk, okPressed) });
|
||||||
|
ShowPopup(data, priority, forceShow);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Show popup if there is non other currently shown. Otherwise add current popup to ordered queue and show it later. It will be closed after pressing yes or no button.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="title">popup title</param>
|
||||||
|
/// <param name="content">popup content</param>
|
||||||
|
/// <param name="yesText">text to show on yes button, empty for localized "Yes"</param>
|
||||||
|
/// <param name="noText">text to show on no button, empty for localized "No"</param>
|
||||||
|
/// <param name="yesPressed">additional action on yes pressed</param>
|
||||||
|
/// <param name="noPressed">additional action on no pressed</param>
|
||||||
|
/// <param name="priority">priority of popup (lower number -> show first)</param>
|
||||||
|
/// <param name="forceShow">force show current popup only if currently shown has lower priority</param>
|
||||||
|
/// <returns>data for created popup, can be used to invalidate popup (will not show)</returns>
|
||||||
|
public PopupData ShowYesNoPopup(string title, string content, string yesText = null, string noText = null, Action yesPressed = null, Action noPressed = null, int priority = 0, bool forceShow = false)
|
||||||
|
{
|
||||||
|
var data = new DefaultPopupData(currentDefaultPopup, title, content,
|
||||||
|
new List<(string, Action)>() { (yesText ?? localizedYes, yesPressed), (noText ?? localizedNo, noPressed) });
|
||||||
|
ShowPopup(data, priority, forceShow);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Show popup if there is non other currently shown. Otherwise add current popup to ordered queue and show it later. It will be closed after pressing any button.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="title">popup title</param>
|
||||||
|
/// <param name="content">popup content</param>
|
||||||
|
/// <param name="actions">list of actions</param>
|
||||||
|
/// <param name="priority">priority of popup (lower number -> show first)</param>
|
||||||
|
/// <param name="forceShow">force show current popup only if currently shown has lower priority</param>
|
||||||
|
/// <returns>data for created popup, can be used to invalidate popup (will not show)</returns>
|
||||||
|
public PopupData ShowPopup(string title, string content, List<(string, Action)> actions, int priority = 0, bool forceShow = false)
|
||||||
|
{
|
||||||
|
var data = new DefaultPopupData(currentDefaultPopup, title, content, actions);
|
||||||
|
ShowPopup(data, priority, forceShow);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Show popup if there is non other currently shown. Otherwise add current popup to ordered queue and show it later.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">popup data object</param>
|
||||||
|
/// <param name="priority">priority of popup (lower number -> show first)</param>
|
||||||
|
/// <param name="forceShow">force show current popup only if currently shown has lower priority</param>
|
||||||
|
public void ShowPopup(PopupData data, int priority = 0, bool forceShow = false)
|
||||||
|
{
|
||||||
|
popupsToShow.Enqueue(data, priority);
|
||||||
|
UpdatePopupsState(forceShow, priority, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PopupClosed(PopupData data)
|
||||||
|
{
|
||||||
|
if (currentShownPopup.data != data)
|
||||||
|
{
|
||||||
|
Debug.LogError("Popup was not shown");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
UpdatePopupsState(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void SetDefaultPopup(IDefaultPopup popup) => currentDefaultPopup = popup;
|
||||||
|
|
||||||
|
private void UpdatePopupsState(bool forceShow, int priority = 0, PopupData data = null)
|
||||||
|
{
|
||||||
|
if (forceShow)
|
||||||
|
{
|
||||||
|
if(currentShownPopup.priority <= priority)
|
||||||
|
return;
|
||||||
|
|
||||||
|
popupsToShow.Enqueue(currentShownPopup.data, currentShownPopup.priority);
|
||||||
|
ShowPopup(data, priority);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!popupsToShow.TryDequeue(out var d, out int p))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ShowPopup(d, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowPopup(PopupData data, int priority)
|
||||||
|
{
|
||||||
|
currentShownPopup = (data, priority);
|
||||||
|
data.Show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
3
NEG/UI/UnityUi/Area.meta
Normal file
3
NEG/UI/UnityUi/Area.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a99aed857b7b9e7459811b14fefdb04f
|
||||||
|
timeCreated: 1670706939
|
||||||
30
NEG/UI/UnityUi/Area/MonoArea.cs
Normal file
30
NEG/UI/UnityUi/Area/MonoArea.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using NEG.UI.Popup;
|
||||||
|
using NEG.UI.UnityUi.WindowSlot;
|
||||||
|
using NEG.UI.Window;
|
||||||
|
using NEG.UI.WindowSlot;
|
||||||
|
|
||||||
|
namespace NEG.UI.Area
|
||||||
|
{
|
||||||
|
public class MonoArea : MonoBehaviour, IArea
|
||||||
|
{
|
||||||
|
public IEnumerable<IWindowSlot> AvailableSlots => windowSlots;
|
||||||
|
public IWindowSlot DefaultWindowSlot => windowSlots[0];
|
||||||
|
public IEnumerable<IWindow> CurrentWindows { get; }
|
||||||
|
|
||||||
|
[SerializeField] private List<MonoWindowSlot> windowSlots;
|
||||||
|
|
||||||
|
[SerializeField] private Queue<IPopup> currentPopups = new();
|
||||||
|
|
||||||
|
public void SetEnabled(bool setEnabled) => gameObject.SetActive(setEnabled);
|
||||||
|
|
||||||
|
public void OpenWindow(IWindow window, object data = null)
|
||||||
|
{
|
||||||
|
DefaultWindowSlot.AttachWindow(window);
|
||||||
|
window.SetData(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,3 +1,3 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: bbfe2293348b4c9096a3763479ec00c7
|
guid: 39eb59ca1ef60934abb3f0c64169be65
|
||||||
timeCreated: 1670707479
|
timeCreated: 1670707479
|
||||||
@ -12,6 +12,9 @@ namespace NEG.UI.UnityUi.Buttons
|
|||||||
{
|
{
|
||||||
public event Action OnButtonPressed;
|
public event Action OnButtonPressed;
|
||||||
|
|
||||||
|
public bool Interactable { get => serializeFields.Button.interactable; set => serializeFields.Button.interactable = value; }
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
protected ButtonSerializeFields serializeFields;
|
protected ButtonSerializeFields serializeFields;
|
||||||
|
|
||||||
private bool isHovered;
|
private bool isHovered;
|
||||||
@ -42,13 +45,29 @@ namespace NEG.UI.UnityUi.Buttons
|
|||||||
serializeFields.Text.color = serializeFields.DeselectedTextColor;
|
serializeFields.Text.color = serializeFields.DeselectedTextColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetText(string text)
|
||||||
|
{
|
||||||
|
if(serializeFields == null)
|
||||||
|
return;
|
||||||
|
if(serializeFields.Text == null)
|
||||||
|
return;
|
||||||
|
serializeFields.Text.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual void Awake()
|
protected virtual void Awake()
|
||||||
{
|
{
|
||||||
serializeFields = GetComponent<ButtonSerializeFields>();
|
if(serializeFields == null)
|
||||||
|
serializeFields = GetComponent<ButtonSerializeFields>();
|
||||||
serializeFields.Button.onClick.AddListener(OnClicked);
|
serializeFields.Button.onClick.AddListener(OnClicked);
|
||||||
OnDeselect(null);
|
OnDeselect(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnValidate()
|
||||||
|
{
|
||||||
|
if(serializeFields == null)
|
||||||
|
serializeFields = GetComponent<ButtonSerializeFields>();
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual void OnClicked()
|
protected virtual void OnClicked()
|
||||||
{
|
{
|
||||||
OnDeselect(null);
|
OnDeselect(null);
|
||||||
|
|||||||
11
NEG/UI/UnityUi/Buttons/CloseAllWindows.cs
Normal file
11
NEG/UI/UnityUi/Buttons/CloseAllWindows.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NEG.UI.UnityUi.Buttons
|
||||||
|
{
|
||||||
|
[RequireComponent(typeof(BaseButton))]
|
||||||
|
public class CloseAllWindows : MonoBehaviour
|
||||||
|
{
|
||||||
|
private void Awake() => GetComponent<BaseButton>().OnButtonPressed += OnClicked;
|
||||||
|
private void OnClicked() => UiManager.Instance.CurrentArea.CloseAllWindows();
|
||||||
|
}
|
||||||
|
}
|
||||||
3
NEG/UI/UnityUi/Buttons/CloseAllWindows.cs.meta
Normal file
3
NEG/UI/UnityUi/Buttons/CloseAllWindows.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2fa43803dc014b7694d265d48c1fc680
|
||||||
|
timeCreated: 1672767354
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using NEG.UI.Window;
|
using NEG.UI.UnityUi.Window;
|
||||||
|
using NEG.UI.Window;
|
||||||
using System;
|
using System;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
using System;
|
using NEG.UI.UnityUi.Window;
|
||||||
|
using NEG.UI.UnityUi.WindowSlot;
|
||||||
|
using System;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using NEG.UI.Window;
|
using NEG.UI.Window;
|
||||||
using NEG.UI.WindowSlot;
|
using NEG.UI.WindowSlot;
|
||||||
|
|||||||
8
NEG/UI/UnityUi/CarouselList.meta
Normal file
8
NEG/UI/UnityUi/CarouselList.meta
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d6bf45a37eca4dc4aa1926eb7dce5ab6
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
71
NEG/UI/UnityUi/CarouselList/CarouselList.cs
Normal file
71
NEG/UI/UnityUi/CarouselList/CarouselList.cs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
using NEG.UI.UnityUi.Buttons;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using TMPro;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
|
||||||
|
namespace NEG.UI.UnityUi
|
||||||
|
{
|
||||||
|
public class CarouselList : MonoBehaviour
|
||||||
|
{
|
||||||
|
public string CurrentOption { get; private set; }
|
||||||
|
public int CurrentOptionId { get; private set; }
|
||||||
|
|
||||||
|
[SerializeField] private BaseButton nextButton;
|
||||||
|
[SerializeField] private BaseButton prevButton;
|
||||||
|
[SerializeField] private TMP_Text currentOptionText;
|
||||||
|
|
||||||
|
private List<string> options;
|
||||||
|
|
||||||
|
public void SetOptions(List<string> options)
|
||||||
|
{
|
||||||
|
this.options = options;
|
||||||
|
SelectOption(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SelectNextOption() => ChangeOption(true);
|
||||||
|
public void SelectPrevOption() => ChangeOption(false);
|
||||||
|
|
||||||
|
public void SelectOption(int option)
|
||||||
|
{
|
||||||
|
if (option < 0 || option >= options.Count)
|
||||||
|
{
|
||||||
|
Debug.LogError("Invalid option number");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CurrentOptionId = option;
|
||||||
|
CurrentOption = options[option];
|
||||||
|
currentOptionText.text = CurrentOption;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Select option with provided value. Use with caution, better use <see cref="SelectOption(int)"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="option">option value to select</param>
|
||||||
|
public void SelectOption(string option)
|
||||||
|
{
|
||||||
|
if (options.Count == 0)
|
||||||
|
{
|
||||||
|
Debug.LogError("Carousel List cannot be empty when selecting option");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = options.IndexOf(option);
|
||||||
|
if (index == -1)
|
||||||
|
{
|
||||||
|
Debug.LogError($"Option {option} not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectOption(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
nextButton.OnButtonPressed += SelectNextOption;
|
||||||
|
prevButton.OnButtonPressed += SelectPrevOption;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ChangeOption(bool next) => SelectOption((CurrentOptionId + (next ? 1 : -1) + options.Count) % options.Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
3
NEG/UI/UnityUi/CarouselList/CarouselList.cs.meta
Normal file
3
NEG/UI/UnityUi/CarouselList/CarouselList.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9915ee4d467e47e485c4a48be295b5c2
|
||||||
|
timeCreated: 1672834830
|
||||||
49
NEG/UI/UnityUi/MonoUiManager.cs
Normal file
49
NEG/UI/UnityUi/MonoUiManager.cs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
using NEG.UI.Area;
|
||||||
|
using NEG.UI.Popup;
|
||||||
|
using NEG.UI.UnityUi.Popup;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.AddressableAssets;
|
||||||
|
using UnityEngine.Assertions;
|
||||||
|
using UnityEngine.SceneManagement;
|
||||||
|
|
||||||
|
namespace NEG.UI.UnityUi
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Implements ui using UnityUI and Unity Event System with New Input System.
|
||||||
|
/// <para>You have to provide prefabs with addresses:</para>
|
||||||
|
/// <para> - NEG/UI/PopupCanvas - prefab with canvas to create popups (will be created on every scene)</para>
|
||||||
|
/// <para> - NEG/UI/DefaultPopupPrefab - prefab of default popup with 2 options (has to have <see cref="MonoDefaultPopup"/> component)</para>
|
||||||
|
/// </summary>
|
||||||
|
public class MonoUiManager : UiManager
|
||||||
|
{
|
||||||
|
private readonly MonoDefaultPopup defaultPopupPrefab;
|
||||||
|
private readonly GameObject canvasPrefab;
|
||||||
|
|
||||||
|
public MonoUiManager(IArea startArea) : base(startArea)
|
||||||
|
{
|
||||||
|
var prefabs =
|
||||||
|
Addressables.LoadAssetsAsync<GameObject>(new List<string>() { "NEG/UI/PopupCanvas", "NEG/UI/DefaultPopupPrefab" }, (_) => { }, Addressables.MergeMode.Union).WaitForCompletion();
|
||||||
|
|
||||||
|
Assert.AreEqual(prefabs.Count, 2, "No prefabs was provided. Please check MonoUiManager class documentation");
|
||||||
|
Assert.IsNotNull(prefabs[0].GetComponent<Canvas>());
|
||||||
|
Assert.IsNotNull(prefabs[1].GetComponent<MonoDefaultPopup>());
|
||||||
|
|
||||||
|
canvasPrefab = prefabs[0];
|
||||||
|
defaultPopupPrefab = prefabs[1].GetComponent<MonoDefaultPopup>();
|
||||||
|
|
||||||
|
SpawnDefaultPopup();
|
||||||
|
|
||||||
|
SceneManager.activeSceneChanged += (_, _) => SpawnDefaultPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SpawnDefaultPopup()
|
||||||
|
{
|
||||||
|
var canvas = Object.Instantiate(canvasPrefab);
|
||||||
|
canvas.name = "DefaultPopupCanvas";
|
||||||
|
SetDefaultPopup(Object.Instantiate(defaultPopupPrefab, canvas.transform));
|
||||||
|
currentDefaultPopup.Close(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
3
NEG/UI/UnityUi/MonoUiManager.cs.meta
Normal file
3
NEG/UI/UnityUi/MonoUiManager.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: db04ec3144594df1bf4739e142d1b064
|
||||||
|
timeCreated: 1672155707
|
||||||
@ -5,7 +5,9 @@
|
|||||||
"GUID:343deaaf83e0cee4ca978e7df0b80d21",
|
"GUID:343deaaf83e0cee4ca978e7df0b80d21",
|
||||||
"GUID:7361f1d9c43da6649923760766194746",
|
"GUID:7361f1d9c43da6649923760766194746",
|
||||||
"GUID:6055be8ebefd69e48b49212b09b47b2f",
|
"GUID:6055be8ebefd69e48b49212b09b47b2f",
|
||||||
"GUID:0c752da273b17c547ae705acf0f2adf2"
|
"GUID:0c752da273b17c547ae705acf0f2adf2",
|
||||||
|
"GUID:9e24947de15b9834991c9d8411ea37cf",
|
||||||
|
"GUID:84651a3751eca9349aac36a66bba901b"
|
||||||
],
|
],
|
||||||
"includePlatforms": [],
|
"includePlatforms": [],
|
||||||
"excludePlatforms": [],
|
"excludePlatforms": [],
|
||||||
|
|||||||
3
NEG/UI/UnityUi/Popup.meta
Normal file
3
NEG/UI/UnityUi/Popup.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 31e9b0c7a4425a248841ec2f02b92157
|
||||||
|
timeCreated: 1670707809
|
||||||
36
NEG/UI/UnityUi/Popup/MonoDefaultPopup.cs
Normal file
36
NEG/UI/UnityUi/Popup/MonoDefaultPopup.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
using NEG.UI.Popup;
|
||||||
|
using NEG.UI.UnityUi.Buttons;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using TMPro;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NEG.UI.UnityUi.Popup
|
||||||
|
{
|
||||||
|
public class MonoDefaultPopup : MonoPopup, IDefaultPopup
|
||||||
|
{
|
||||||
|
[SerializeField] private TMP_Text titleText;
|
||||||
|
[SerializeField] private TMP_Text contentText;
|
||||||
|
[SerializeField] private Transform buttonsParent;
|
||||||
|
[SerializeField] private BaseButton buttonPrefab;
|
||||||
|
|
||||||
|
public void SetContent(string title, string content, List<(string, Action)> options)
|
||||||
|
{
|
||||||
|
foreach (Transform child in buttonsParent)
|
||||||
|
{
|
||||||
|
Destroy(child.gameObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
titleText.text = title;
|
||||||
|
contentText.text = content;
|
||||||
|
|
||||||
|
foreach ((string text, Action action) item in options)
|
||||||
|
{
|
||||||
|
var button = Instantiate(buttonPrefab, buttonsParent);
|
||||||
|
button.SetText(item.text);
|
||||||
|
button.OnButtonPressed += item.action;
|
||||||
|
button.OnButtonPressed += () => Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
NEG/UI/UnityUi/Popup/MonoDefaultPopup.cs.meta
Normal file
3
NEG/UI/UnityUi/Popup/MonoDefaultPopup.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 831fb29e6c6d4997a7d471c646eec077
|
||||||
|
timeCreated: 1672154589
|
||||||
28
NEG/UI/UnityUi/Popup/MonoPopup.cs
Normal file
28
NEG/UI/UnityUi/Popup/MonoPopup.cs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
using NEG.UI.Popup;
|
||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NEG.UI.UnityUi.Popup
|
||||||
|
{
|
||||||
|
public class MonoPopup : MonoBehaviour, IPopup
|
||||||
|
{
|
||||||
|
protected PopupData data;
|
||||||
|
|
||||||
|
public void Show(PopupData data)
|
||||||
|
{
|
||||||
|
this.data = data;
|
||||||
|
gameObject.SetActive(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Close(bool silence = false)
|
||||||
|
{
|
||||||
|
gameObject.SetActive(false);
|
||||||
|
|
||||||
|
if(silence)
|
||||||
|
return;
|
||||||
|
|
||||||
|
UiManager.Instance.PopupClosed(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,3 +1,3 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: d3057d16be014bd58847b118a24c21dd
|
guid: 5d3466b2fe771184eae1ca72c1006d0c
|
||||||
timeCreated: 1670707831
|
timeCreated: 1670707831
|
||||||
3
NEG/UI/UnityUi/Window.meta
Normal file
3
NEG/UI/UnityUi/Window.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 65adacad835c2cf4b9062296be6098d2
|
||||||
|
timeCreated: 1670707788
|
||||||
48
NEG/UI/UnityUi/Window/MonoWindow.cs
Normal file
48
NEG/UI/UnityUi/Window/MonoWindow.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
using NEG.UI.Area;
|
||||||
|
using NEG.UI.UnityUi.WindowSlot;
|
||||||
|
using NEG.UI.Window;
|
||||||
|
using NEG.UI.WindowSlot;
|
||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NEG.UI.UnityUi.Window
|
||||||
|
{
|
||||||
|
public class MonoWindow : MonoBehaviour, IWindow
|
||||||
|
{
|
||||||
|
public IWindowSlot Parent { get; private set; }
|
||||||
|
public IWindowSlot ChildWindowSlot => childWindowArea;
|
||||||
|
|
||||||
|
[SerializeField] private MonoWindowSlot childWindowArea;
|
||||||
|
[SerializeField] private WindowController controller;
|
||||||
|
|
||||||
|
void IWindow.SetOpenedState(IWindowSlot parentSlot)
|
||||||
|
{
|
||||||
|
gameObject.SetActive(true);
|
||||||
|
Parent = parentSlot;
|
||||||
|
if (controller != null)
|
||||||
|
controller.OnOpened();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetData(object data)
|
||||||
|
{
|
||||||
|
if (controller != null)
|
||||||
|
controller.SetData(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IWindow.SetClosedState()
|
||||||
|
{
|
||||||
|
gameObject.SetActive(false);
|
||||||
|
Parent = null;
|
||||||
|
if(childWindowArea != null)
|
||||||
|
ChildWindowSlot.CloseAllWindows();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Awake() => ((IWindow)this).SetClosedState();
|
||||||
|
|
||||||
|
private void OnValidate()
|
||||||
|
{
|
||||||
|
if (controller == null)
|
||||||
|
controller = GetComponent<WindowController>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,3 +1,3 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 3807b2039b1941d999642e241ce1f463
|
guid: 85d136d6850728d4b96c26fa286ffe3c
|
||||||
timeCreated: 1670709296
|
timeCreated: 1670709296
|
||||||
18
NEG/UI/UnityUi/Window/WindowController.cs
Normal file
18
NEG/UI/UnityUi/Window/WindowController.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NEG.UI.UnityUi.Window
|
||||||
|
{
|
||||||
|
[RequireComponent(typeof(MonoWindow))]
|
||||||
|
//Due to prefab variants we need this
|
||||||
|
public abstract class WindowController : MonoBehaviour
|
||||||
|
{
|
||||||
|
protected MonoWindow window;
|
||||||
|
|
||||||
|
public abstract void SetData(object data);
|
||||||
|
|
||||||
|
public abstract void OnOpened();
|
||||||
|
|
||||||
|
protected virtual void Awake() => window = GetComponent<MonoWindow>();
|
||||||
|
}
|
||||||
|
}
|
||||||
3
NEG/UI/UnityUi/Window/WindowController.cs.meta
Normal file
3
NEG/UI/UnityUi/Window/WindowController.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9e3614c1d2b94294937351b640139829
|
||||||
|
timeCreated: 1672766736
|
||||||
3
NEG/UI/UnityUi/WindowSlot.meta
Normal file
3
NEG/UI/UnityUi/WindowSlot.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 47e385f6fb552d74ab4cf25ef6c76595
|
||||||
|
timeCreated: 1670707801
|
||||||
@ -1,8 +1,9 @@
|
|||||||
using NEG.UI.Area;
|
using NEG.UI.Area;
|
||||||
using NEG.UI.Window;
|
using NEG.UI.Window;
|
||||||
|
using NEG.UI.WindowSlot;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace NEG.UI.WindowSlot
|
namespace NEG.UI.UnityUi.WindowSlot
|
||||||
{
|
{
|
||||||
public abstract class MonoWindowSlot : MonoBehaviour, IWindowSlot
|
public abstract class MonoWindowSlot : MonoBehaviour, IWindowSlot
|
||||||
{
|
{
|
||||||
@ -1,3 +1,3 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 7bc46ac7f2454eaa934561b7ef20ff88
|
guid: e5a19b184ce9338449ad6ba899a26cda
|
||||||
timeCreated: 1670709404
|
timeCreated: 1670709404
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using NEG.UI.Window;
|
using NEG.UI.UnityUi.WindowSlot;
|
||||||
|
using NEG.UI.Window;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace NEG.UI.WindowSlot
|
namespace NEG.UI.WindowSlot
|
||||||
@ -10,9 +11,9 @@ namespace NEG.UI.WindowSlot
|
|||||||
get => currentWindow;
|
get => currentWindow;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
currentWindow?.Close();
|
currentWindow?.SetClosedState();
|
||||||
currentWindow = value;
|
currentWindow = value;
|
||||||
currentWindow?.Open(this);
|
currentWindow?.SetOpenedState(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1,3 +1,3 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 9358679df24f458f8836506344eb169d
|
guid: 79c0738c63b2b5c40b96acd89441f78f
|
||||||
timeCreated: 1670716632
|
timeCreated: 1670716632
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using NEG.UI.Area;
|
using JetBrains.Annotations;
|
||||||
|
using NEG.UI.Area;
|
||||||
using NEG.UI.WindowSlot;
|
using NEG.UI.WindowSlot;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
@ -7,34 +8,39 @@ namespace NEG.UI.Window
|
|||||||
public interface IWindow
|
public interface IWindow
|
||||||
{
|
{
|
||||||
IWindowSlot Parent { get; }
|
IWindowSlot Parent { get; }
|
||||||
IArea ChildWindowArea { get; }
|
IWindowSlot ChildWindowSlot { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Call internally by slot to open window
|
/// Called internally by slot to open window.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="parentSlot">Slot that opens window</param>
|
/// <param name="parentSlot">slot that opens window</param>
|
||||||
internal void Open(IWindowSlot parentSlot);
|
void SetOpenedState(IWindowSlot parentSlot);
|
||||||
|
|
||||||
|
void SetData(object data);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Call internally to close window by slot
|
/// Called internally to close window by slot.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal void Close();
|
void SetClosedState();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class WindowInterfaceExtensions
|
public static class WindowInterfaceExtensions
|
||||||
{
|
{
|
||||||
//Open
|
//Open
|
||||||
public static void Open(this IWindow window, IWindowSlot slot = null)
|
public static void Open(this IWindow window, IWindowSlot slot = null, object data = null)
|
||||||
{
|
{
|
||||||
if(slot != null)
|
if (slot != null)
|
||||||
|
{
|
||||||
slot.AttachWindow(window);
|
slot.AttachWindow(window);
|
||||||
|
window.SetData(data);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
UiManager.Instance.CurrentArea.OpenWindow(window);
|
UiManager.Instance.CurrentArea.OpenWindow(window, data);
|
||||||
}
|
}
|
||||||
public static void Open(this IWindow window, IArea area) => area.OpenWindow(window);
|
public static void Open(this IWindow window, IArea area, object data = null) => area.OpenWindow(window, data);
|
||||||
public static void OpenChildWindow(this IWindow window, IWindow windowToOpen, IWindowSlot slot = null)
|
public static void OpenChildWindow(this IWindow window, IWindow windowToOpen, object data = null)
|
||||||
{
|
{
|
||||||
if (window.ChildWindowArea == null)
|
if (window.ChildWindowSlot == null)
|
||||||
{
|
{
|
||||||
Debug.LogError("This window doesn't contain area for child windows");
|
Debug.LogError("This window doesn't contain area for child windows");
|
||||||
return;
|
return;
|
||||||
@ -44,5 +50,6 @@ namespace NEG.UI.Window
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void Close(this IWindow window) => window.Parent.DetachWindow(window);
|
public static void Close(this IWindow window) => window.Parent.DetachWindow(window);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,30 +0,0 @@
|
|||||||
using NEG.UI.Area;
|
|
||||||
using NEG.UI.Popup;
|
|
||||||
using NEG.UI.WindowSlot;
|
|
||||||
using System;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace NEG.UI.Window
|
|
||||||
{
|
|
||||||
public class MonoWindow : MonoBehaviour, IWindow
|
|
||||||
{
|
|
||||||
public IWindowSlot Parent { get; private set; }
|
|
||||||
public IArea ChildWindowArea => childWindowArea;
|
|
||||||
|
|
||||||
[SerializeField] private MonoArea childWindowArea;
|
|
||||||
|
|
||||||
void IWindow.Open(IWindowSlot parentSlot)
|
|
||||||
{
|
|
||||||
gameObject.SetActive(true);
|
|
||||||
Parent = parentSlot;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IWindow.Close()
|
|
||||||
{
|
|
||||||
gameObject.SetActive(false);
|
|
||||||
Parent = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Awake() => ((IWindow)this).Close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user