Merge branch 'main' into MultiSelectChip
This commit is contained in:
commit
c0923cc884
30
Editor/ComponentsAdditionalItems/CanvasScalerDefault.cs
Normal file
30
Editor/ComponentsAdditionalItems/CanvasScalerDefault.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
|
||||||
|
namespace NEG.Utils.Editor.ComponentsAdditionalItems
|
||||||
|
{
|
||||||
|
public static class CanvasScalerDefault
|
||||||
|
{
|
||||||
|
[MenuItem("CONTEXT/CanvasScaler/Full HD horizontal", false, 2000)]
|
||||||
|
public static void SetFullHdHorizontal(MenuCommand command) => SetComponent(command, 1920, 1080);
|
||||||
|
[MenuItem("CONTEXT/CanvasScaler/Full HD vertical", false, 2000)]
|
||||||
|
public static void SetFullHdVertical(MenuCommand command) => SetComponent(command, 1080, 1920);
|
||||||
|
[MenuItem("CONTEXT/CanvasScaler/Full 2k horizontal", false, 2000)]
|
||||||
|
public static void Set2KHorizontal(MenuCommand command) => SetComponent(command, 2560, 1440 );
|
||||||
|
[MenuItem("CONTEXT/CanvasScaler/Full 2k vertical", false, 2000)]
|
||||||
|
public static void Set2KVertical(MenuCommand command) => SetComponent(command, 1440, 2560);
|
||||||
|
[MenuItem("CONTEXT/CanvasScaler/Full 4k horizontal", false, 2000)]
|
||||||
|
public static void Set4KHorizontal(MenuCommand command) => SetComponent(command, 3840, 2160);
|
||||||
|
[MenuItem("CONTEXT/CanvasScaler/Full 4k vertical", false, 2000)]
|
||||||
|
public static void Set4KVertical(MenuCommand command) => SetComponent(command, 2160, 3840);
|
||||||
|
|
||||||
|
private static void SetComponent(MenuCommand command, int width, int height)
|
||||||
|
{
|
||||||
|
var scaler = (CanvasScaler)command.context;
|
||||||
|
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
|
||||||
|
scaler.matchWidthOrHeight = width > height ? 1f : 0f;
|
||||||
|
scaler.referenceResolution = new Vector2(width, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Editor/ComponentsAdditionalItems/CanvasScalerDefault.cs.meta
Normal file
11
Editor/ComponentsAdditionalItems/CanvasScalerDefault.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c7ddebddb1a1c1947be05ac9e96aceb8
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
18
Editor/NEG.Utils.Editor.asmdef
Normal file
18
Editor/NEG.Utils.Editor.asmdef
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"name": "NEG.Utils.Editor",
|
||||||
|
"rootNamespace": "",
|
||||||
|
"references": [
|
||||||
|
"GUID:3c4294719a93e3c4e831a9ff0c261e8a"
|
||||||
|
],
|
||||||
|
"includePlatforms": [
|
||||||
|
"Editor"
|
||||||
|
],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false
|
||||||
|
}
|
||||||
7
Editor/NEG.Utils.Editor.asmdef.meta
Normal file
7
Editor/NEG.Utils.Editor.asmdef.meta
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5928dc8d9173fd348aa77d4593ca3fd8
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
3
NEG.meta
Normal file
3
NEG.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 61eeed9106e147d8a76d7f0140d3571d
|
||||||
|
timeCreated: 1670708950
|
||||||
3
NEG/UI.meta
Normal file
3
NEG/UI.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: bbdebf147b9a40b0bc94b6f710c7aa6b
|
||||||
|
timeCreated: 1670690396
|
||||||
3
NEG/UI/Area.meta
Normal file
3
NEG/UI/Area.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 853f6a7d0f224278afbb5457d0fb8bde
|
||||||
|
timeCreated: 1670706939
|
||||||
9
NEG/UI/Area/IArea.cs
Normal file
9
NEG/UI/Area/IArea.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
using NEG.UI.WindowSlot;
|
||||||
|
|
||||||
|
namespace NEG.UI.Area
|
||||||
|
{
|
||||||
|
public interface IArea : ISlotsHolder, IUiElement
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
3
NEG/UI/Area/IArea.cs.meta
Normal file
3
NEG/UI/Area/IArea.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f7cf5ef3a347e1c4b98411f4d564b988
|
||||||
|
timeCreated: 1670690282
|
||||||
11
NEG/UI/IUiElement.cs
Normal file
11
NEG/UI/IUiElement.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
namespace NEG.UI
|
||||||
|
{
|
||||||
|
public interface IUiElement
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Sets only visible state of element
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="setEnabled"></param>
|
||||||
|
void SetEnabled(bool setEnabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
3
NEG/UI/IUiElement.cs.meta
Normal file
3
NEG/UI/IUiElement.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 296bf6969a6347f8aea788a7bdd086af
|
||||||
|
timeCreated: 1670693177
|
||||||
14
NEG/UI/NEG.UI.asmdef
Normal file
14
NEG/UI/NEG.UI.asmdef
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name": "NEG.UI",
|
||||||
|
"rootNamespace": "",
|
||||||
|
"references": [],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false
|
||||||
|
}
|
||||||
7
NEG/UI/NEG.UI.asmdef.meta
Normal file
7
NEG/UI/NEG.UI.asmdef.meta
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7361f1d9c43da6649923760766194746
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
3
NEG/UI/Popup.meta
Normal file
3
NEG/UI/Popup.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 76fc21263637443ca8268859d4cb5378
|
||||||
|
timeCreated: 1670707809
|
||||||
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
|
||||||
16
NEG/UI/Popup/IDefaultPopup.cs
Normal file
16
NEG/UI/Popup/IDefaultPopup.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace NEG.UI.Popup
|
||||||
|
{
|
||||||
|
public interface IDefaultPopup : IPopup
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Sets content based on provided data.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="title">popup title</param>
|
||||||
|
/// <param name="content">popup content</param>
|
||||||
|
/// <param name="options">list of tuples (name, action on click), to set buttons. Do not pass here popup closing logic, implementing class should do it</param>
|
||||||
|
public void SetContent(string title, string content, List<(string name, Action 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
|
||||||
26
NEG/UI/Popup/IPopup.cs
Normal file
26
NEG/UI/Popup/IPopup.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
using JetBrains.Annotations;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace NEG.UI.Popup
|
||||||
|
{
|
||||||
|
[PublicAPI]
|
||||||
|
public interface IPopup
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Event to fire when popup is closed
|
||||||
|
/// </summary>
|
||||||
|
event Action<PopupData> OnPopupClosed;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Show popup
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">data assigned to popup, used to give info that popup is closed</param>
|
||||||
|
public void Show(PopupData data);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Close popup or mark as closed if not visible
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="silent">if true hide visually, without firing callbacks to properly close</param>
|
||||||
|
void Close(bool silent = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
3
NEG/UI/Popup/IPopup.cs.meta
Normal file
3
NEG/UI/Popup/IPopup.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 79767831e4324605974f3bb0bb5026fb
|
||||||
|
timeCreated: 1670692499
|
||||||
50
NEG/UI/Popup/PopupData.cs
Normal file
50
NEG/UI/Popup/PopupData.cs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
using JetBrains.Annotations;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace NEG.UI.Popup
|
||||||
|
{
|
||||||
|
[PublicAPI]
|
||||||
|
public class PopupData
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Event that is fired on closing popup.
|
||||||
|
/// </summary>
|
||||||
|
public event Action<PopupData> PopupClosedEvent
|
||||||
|
{
|
||||||
|
add => popup.OnPopupClosed += value;
|
||||||
|
remove => popup.OnPopupClosed -= value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is this data is still valid. If set to false, popup will not show.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsValid { get; protected set; }
|
||||||
|
|
||||||
|
private readonly IPopup popup;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// PopupData constructor.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="popup">attached to this data, can be used by different data instances</param>
|
||||||
|
public PopupData(IPopup popup)
|
||||||
|
{
|
||||||
|
this.popup = popup;
|
||||||
|
IsValid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Show popup and pass needed data.
|
||||||
|
/// </summary>
|
||||||
|
public virtual void Show() => popup.Show(this);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hide popup. Close visuals without firing events;
|
||||||
|
/// </summary>
|
||||||
|
public virtual void Hide() => popup.Close(true);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invalidate popup, <see cref="UiManager"/> will automatically skip this popup
|
||||||
|
/// </summary>
|
||||||
|
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 != null && 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 != null && comparer.Compare(nextChild.Priority, minChild.Priority) < 0)
|
||||||
|
{
|
||||||
|
minChild = nextChild;
|
||||||
|
minChildIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Heap property is satisfied; insert node in this location.
|
||||||
|
if (comparer != null && 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:
|
||||||
169
NEG/UI/UiManager.cs
Normal file
169
NEG/UI/UiManager.cs
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
using JetBrains.Annotations;
|
||||||
|
using NEG.UI.Area;
|
||||||
|
using NEG.UI.Popup;
|
||||||
|
using NEG.UI.Window;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NEG.UI
|
||||||
|
{
|
||||||
|
[PublicAPI]
|
||||||
|
public abstract class UiManager
|
||||||
|
{
|
||||||
|
public static UiManager Instance { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Current area shown on screen.
|
||||||
|
/// </summary>
|
||||||
|
public IArea CurrentArea
|
||||||
|
{
|
||||||
|
get => currentArea;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
currentArea?.SetEnabled(false);
|
||||||
|
|
||||||
|
currentArea = value;
|
||||||
|
|
||||||
|
currentArea?.SetEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Current window that is considered main (focused, lastly opened). Can be null.
|
||||||
|
/// </summary>
|
||||||
|
public IWindow CurrentMainWindow { get; protected set; }
|
||||||
|
|
||||||
|
private IArea currentArea;
|
||||||
|
private (PopupData data, int priority) currentShownPopup;
|
||||||
|
protected IDefaultPopup currentDefaultPopup;
|
||||||
|
|
||||||
|
private PriorityQueue<PopupData, int> popupsToShow = new();
|
||||||
|
|
||||||
|
//TODO: localize
|
||||||
|
private string localizedYes = "Yes", localizedNo = "No", localizedOk = "Ok";
|
||||||
|
|
||||||
|
protected UiManager(IArea startArea)
|
||||||
|
{
|
||||||
|
if (Instance != null)
|
||||||
|
{
|
||||||
|
Debug.LogError("Only one instance od UiManager is allowed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Instance = this;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected 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)
|
||||||
|
{
|
||||||
|
if (currentShownPopup.data != null)
|
||||||
|
{
|
||||||
|
currentShownPopup.data.PopupClosedEvent -= PopupClosed;
|
||||||
|
currentShownPopup.data.Hide();
|
||||||
|
}
|
||||||
|
currentShownPopup = (data, priority);
|
||||||
|
data.Show();
|
||||||
|
data.PopupClosedEvent += PopupClosed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
11
NEG/UI/UiManager.cs.meta
Normal file
11
NEG/UI/UiManager.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c322beedd2ec8f844903c18b1ef74b15
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
NEG/UI/UnityUi.meta
Normal file
8
NEG/UI/UnityUi.meta
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a0be272f427b40848b8aeff5b04770b4
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
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
|
||||||
15
NEG/UI/UnityUi/Area/AutoWindowOpen.cs
Normal file
15
NEG/UI/UnityUi/Area/AutoWindowOpen.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using NEG.UI.UnityUi.Window;
|
||||||
|
using NEG.UI.Window;
|
||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NEG.UI.Area
|
||||||
|
{
|
||||||
|
[Tooltip(tooltip: "Automatically open attached window on start")]
|
||||||
|
public class AutoWindowOpen : MonoBehaviour
|
||||||
|
{
|
||||||
|
[SerializeField] private MonoWindow window;
|
||||||
|
|
||||||
|
private void Start() => window.Open();
|
||||||
|
}
|
||||||
|
}
|
||||||
3
NEG/UI/UnityUi/Area/AutoWindowOpen.cs.meta
Normal file
3
NEG/UI/UnityUi/Area/AutoWindowOpen.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e439a77332dc4e3a881348c9506556c6
|
||||||
|
timeCreated: 1673791915
|
||||||
41
NEG/UI/UnityUi/Area/MonoArea.cs
Normal file
41
NEG/UI/UnityUi/Area/MonoArea.cs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using NEG.UI.Popup;
|
||||||
|
using NEG.UI.UnityUi.Window;
|
||||||
|
using NEG.UI.UnityUi.WindowSlot;
|
||||||
|
using NEG.UI.Window;
|
||||||
|
using NEG.UI.WindowSlot;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace NEG.UI.Area
|
||||||
|
{
|
||||||
|
public class MonoArea : MonoBehaviour, IArea
|
||||||
|
{
|
||||||
|
public IEnumerable<IWindowSlot> AvailableSlots => windowSlots;
|
||||||
|
public IWindowSlot DefaultWindowSlot => windowSlots[0];
|
||||||
|
|
||||||
|
[SerializeField] private bool setAsDefaultArea;
|
||||||
|
|
||||||
|
[SerializeField] private List<MonoWindowSlot> windowSlots;
|
||||||
|
|
||||||
|
public virtual void SetEnabled(bool setEnabled) => gameObject.SetActive(setEnabled);
|
||||||
|
|
||||||
|
public virtual void OpenWindow(IWindow window, object data = null)
|
||||||
|
{
|
||||||
|
DefaultWindowSlot.AttachWindow(window);
|
||||||
|
window.SetData(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Awake()
|
||||||
|
{
|
||||||
|
if (setAsDefaultArea)
|
||||||
|
UiManager.Instance.CurrentArea = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnDestroy()
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(UiManager.Instance.CurrentArea, this))
|
||||||
|
UiManager.Instance.CurrentArea = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
NEG/UI/UnityUi/Area/MonoArea.cs.meta
Normal file
3
NEG/UI/UnityUi/Area/MonoArea.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 39eb59ca1ef60934abb3f0c64169be65
|
||||||
|
timeCreated: 1670707479
|
||||||
3
NEG/UI/UnityUi/Buttons.meta
Normal file
3
NEG/UI/UnityUi/Buttons.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: dcaa262d2bcd4b19aa25512a19830555
|
||||||
|
timeCreated: 1670777190
|
||||||
78
NEG/UI/UnityUi/Buttons/BaseButton.cs
Normal file
78
NEG/UI/UnityUi/Buttons/BaseButton.cs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
using FMOD.Studio;
|
||||||
|
using FMODUnity;
|
||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.EventSystems;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
|
||||||
|
namespace NEG.UI.UnityUi.Buttons
|
||||||
|
{
|
||||||
|
[RequireComponent(typeof(ButtonSerializeFields))]
|
||||||
|
public class BaseButton : MonoBehaviour, ISelectHandler, IDeselectHandler, IPointerEnterHandler, IPointerExitHandler
|
||||||
|
{
|
||||||
|
public event Action OnButtonPressed;
|
||||||
|
|
||||||
|
public bool Interactable { get => serializeFields.Button.interactable; set => serializeFields.Button.interactable = value; }
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
protected ButtonSerializeFields serializeFields;
|
||||||
|
|
||||||
|
private bool isHovered;
|
||||||
|
|
||||||
|
public virtual void OnSelect(BaseEventData eventData)
|
||||||
|
{
|
||||||
|
if (serializeFields.Text)
|
||||||
|
serializeFields.Text.color = serializeFields.SelectedTextColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnDeselect(BaseEventData eventData)
|
||||||
|
{
|
||||||
|
if (serializeFields.Text)
|
||||||
|
serializeFields.Text.color = serializeFields.DeselectedTextColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnPointerEnter(PointerEventData eventData)
|
||||||
|
{
|
||||||
|
isHovered = true;
|
||||||
|
if (serializeFields.Text)
|
||||||
|
serializeFields.Text.color = serializeFields.SelectedTextColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnPointerExit(PointerEventData eventData)
|
||||||
|
{
|
||||||
|
isHovered = false;
|
||||||
|
if (serializeFields.Text)
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
if(serializeFields == null)
|
||||||
|
serializeFields = GetComponent<ButtonSerializeFields>();
|
||||||
|
serializeFields.Button.onClick.AddListener(OnClicked);
|
||||||
|
OnDeselect(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnValidate()
|
||||||
|
{
|
||||||
|
if(serializeFields == null)
|
||||||
|
serializeFields = GetComponent<ButtonSerializeFields>();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnClicked()
|
||||||
|
{
|
||||||
|
OnDeselect(null);
|
||||||
|
isHovered = false;
|
||||||
|
OnButtonPressed?.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
NEG/UI/UnityUi/Buttons/BaseButton.cs.meta
Normal file
3
NEG/UI/UnityUi/Buttons/BaseButton.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3a250ef2d0c34e7396a16fc5eddbdb01
|
||||||
|
timeCreated: 1670777213
|
||||||
15
NEG/UI/UnityUi/Buttons/ButtonReaction.cs
Normal file
15
NEG/UI/UnityUi/Buttons/ButtonReaction.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NEG.UI.UnityUi.Buttons
|
||||||
|
{
|
||||||
|
[RequireComponent(typeof(BaseButton))]
|
||||||
|
public abstract class ButtonReaction : MonoBehaviour
|
||||||
|
{
|
||||||
|
private void Awake() => GetComponent<BaseButton>().OnButtonPressed += OnClicked;
|
||||||
|
|
||||||
|
private void OnDestroy() => GetComponent<BaseButton>().OnButtonPressed -= OnClicked;
|
||||||
|
|
||||||
|
protected abstract void OnClicked();
|
||||||
|
}
|
||||||
|
}
|
||||||
3
NEG/UI/UnityUi/Buttons/ButtonReaction.cs.meta
Normal file
3
NEG/UI/UnityUi/Buttons/ButtonReaction.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a9761128a04b49c2a26eddfabe70331f
|
||||||
|
timeCreated: 1675707257
|
||||||
33
NEG/UI/UnityUi/Buttons/ButtonSerializeFields.cs
Normal file
33
NEG/UI/UnityUi/Buttons/ButtonSerializeFields.cs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
using FMODUnity;
|
||||||
|
using System;
|
||||||
|
using TMPro;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
|
||||||
|
namespace NEG.UI.UnityUi.Buttons
|
||||||
|
{
|
||||||
|
public class ButtonSerializeFields : MonoBehaviour
|
||||||
|
{
|
||||||
|
[field: SerializeField]
|
||||||
|
public Button Button { get; private set; }
|
||||||
|
[field: SerializeField]
|
||||||
|
public TMP_Text Text { get; private set; }
|
||||||
|
[field: SerializeField]
|
||||||
|
public Color SelectedTextColor { get; private set; }
|
||||||
|
[field: SerializeField]
|
||||||
|
public Color DeselectedTextColor { get; private set; }
|
||||||
|
[field: SerializeField]
|
||||||
|
public EventReference HoverEventRef { get; private set; }
|
||||||
|
[field: SerializeField]
|
||||||
|
public EventReference ClickEventRef { get; private set; }
|
||||||
|
|
||||||
|
private void OnValidate()
|
||||||
|
{
|
||||||
|
if(Button == null)
|
||||||
|
Button = GetComponent<Button>();
|
||||||
|
if (Text == null)
|
||||||
|
Text = GetComponentInChildren<TMP_Text>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
3
NEG/UI/UnityUi/Buttons/ButtonSerializeFields.cs.meta
Normal file
3
NEG/UI/UnityUi/Buttons/ButtonSerializeFields.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3f8c6cf4cf18463c86ec1165c61c79b2
|
||||||
|
timeCreated: 1670777232
|
||||||
25
NEG/UI/UnityUi/Buttons/ChangeSceneButton.cs
Normal file
25
NEG/UI/UnityUi/Buttons/ChangeSceneButton.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.SceneManagement;
|
||||||
|
|
||||||
|
namespace NEG.UI.UnityUi.Buttons
|
||||||
|
{
|
||||||
|
[RequireComponent(typeof(BaseButton))]
|
||||||
|
public class ChangeSceneButton : ButtonReaction
|
||||||
|
{
|
||||||
|
[Header("Leave empty to use int value")]
|
||||||
|
[SerializeField] private string sceneName;
|
||||||
|
|
||||||
|
[SerializeField] private int sceneIndex;
|
||||||
|
|
||||||
|
protected override void OnClicked()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(sceneName))
|
||||||
|
SceneManager.LoadScene(sceneIndex);
|
||||||
|
else
|
||||||
|
SceneManager.LoadScene(sceneName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
NEG/UI/UnityUi/Buttons/ChangeSceneButton.cs.meta
Normal file
11
NEG/UI/UnityUi/Buttons/ChangeSceneButton.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 15537f041c9c1204ebf05a5fb7ff6a3d
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
9
NEG/UI/UnityUi/Buttons/CloseAllWindows.cs
Normal file
9
NEG/UI/UnityUi/Buttons/CloseAllWindows.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NEG.UI.UnityUi.Buttons
|
||||||
|
{
|
||||||
|
public class CloseAllWindows : ButtonReaction
|
||||||
|
{
|
||||||
|
protected override 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
|
||||||
22
NEG/UI/UnityUi/Buttons/CloseWindow.cs
Normal file
22
NEG/UI/UnityUi/Buttons/CloseWindow.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
using NEG.UI.UnityUi.Window;
|
||||||
|
using NEG.UI.Window;
|
||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NEG.UI.UnityUi.Buttons
|
||||||
|
{
|
||||||
|
[RequireComponent(typeof(BaseButton))]
|
||||||
|
public class CloseWindow : ButtonReaction
|
||||||
|
{
|
||||||
|
[SerializeField] private MonoWindow windowToClose;
|
||||||
|
|
||||||
|
protected override void OnClicked() => windowToClose.Close();
|
||||||
|
|
||||||
|
private void OnValidate()
|
||||||
|
{
|
||||||
|
if(windowToClose != null)
|
||||||
|
return;
|
||||||
|
windowToClose = GetComponentInParent<MonoWindow>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
NEG/UI/UnityUi/Buttons/CloseWindow.cs.meta
Normal file
3
NEG/UI/UnityUi/Buttons/CloseWindow.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3001b971ff1e4915a29784efdf190bd6
|
||||||
|
timeCreated: 1670807872
|
||||||
14
NEG/UI/UnityUi/Buttons/CloseWindowOnImageTouch.cs
Normal file
14
NEG/UI/UnityUi/Buttons/CloseWindowOnImageTouch.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using NEG.UI.UnityUi.Window;
|
||||||
|
using NEG.UI.Window;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.EventSystems;
|
||||||
|
|
||||||
|
namespace NEG.UI.UnityUi.Buttons
|
||||||
|
{
|
||||||
|
public class CloseWindowOnImageTouch : MonoBehaviour, IPointerDownHandler
|
||||||
|
{
|
||||||
|
[SerializeField] private MonoWindow windowToClose;
|
||||||
|
|
||||||
|
public void OnPointerDown(PointerEventData eventData) => windowToClose.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
3
NEG/UI/UnityUi/Buttons/CloseWindowOnImageTouch.cs.meta
Normal file
3
NEG/UI/UnityUi/Buttons/CloseWindowOnImageTouch.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: dc1aab636014456da7f768a05d0f143e
|
||||||
|
timeCreated: 1673909580
|
||||||
19
NEG/UI/UnityUi/Buttons/OpenWindow.cs
Normal file
19
NEG/UI/UnityUi/Buttons/OpenWindow.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
using NEG.UI.UnityUi.Window;
|
||||||
|
using NEG.UI.UnityUi.WindowSlot;
|
||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
using NEG.UI.Window;
|
||||||
|
using NEG.UI.WindowSlot;
|
||||||
|
|
||||||
|
namespace NEG.UI.UnityUi.Buttons
|
||||||
|
{
|
||||||
|
[RequireComponent(typeof(BaseButton))]
|
||||||
|
public class OpenWindow : ButtonReaction
|
||||||
|
{
|
||||||
|
[SerializeField] private MonoWindow window;
|
||||||
|
[Header("Open on default area slot if empty")]
|
||||||
|
[SerializeField] private MonoWindowSlot slot;
|
||||||
|
|
||||||
|
protected override void OnClicked() => window.Open(slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
3
NEG/UI/UnityUi/Buttons/OpenWindow.cs.meta
Normal file
3
NEG/UI/UnityUi/Buttons/OpenWindow.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 66a5c05f0ba84b2a9b54f3e05a21d495
|
||||||
|
timeCreated: 1670806968
|
||||||
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:
|
||||||
95
NEG/UI/UnityUi/CarouselList/CarouselList.cs
Normal file
95
NEG/UI/UnityUi/CarouselList/CarouselList.cs
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
using JetBrains.Annotations;
|
||||||
|
using NEG.UI.UnityUi.Buttons;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using TMPro;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
|
||||||
|
namespace NEG.UI.UnityUi
|
||||||
|
{
|
||||||
|
[PublicAPI]
|
||||||
|
public class CarouselList : MonoBehaviour
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Current option
|
||||||
|
/// </summary>
|
||||||
|
public string CurrentOption { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Current selected option id
|
||||||
|
/// </summary>
|
||||||
|
public int CurrentOptionId { get; private set; }
|
||||||
|
|
||||||
|
[SerializeField] private BaseButton nextButton;
|
||||||
|
[SerializeField] private BaseButton prevButton;
|
||||||
|
[SerializeField] private TMP_Text currentOptionText;
|
||||||
|
|
||||||
|
private List<string> options;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets new options list, automatically first will be selected.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="options">list of options names</param>
|
||||||
|
public void SetOptions(List<string> options)
|
||||||
|
{
|
||||||
|
this.options = options;
|
||||||
|
SelectOption(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SelectNextOption() => ChangeOption(true);
|
||||||
|
public void SelectPrevOption() => ChangeOption(false);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Selects option with provided id.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="option">option number</param>
|
||||||
|
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 OnDestroy()
|
||||||
|
{
|
||||||
|
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
|
||||||
52
NEG/UI/UnityUi/MonoUiManager.cs
Normal file
52
NEG/UI/UnityUi/MonoUiManager.cs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
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
|
||||||
|
{
|
||||||
|
//TODO: use default unity selection
|
||||||
|
//TODO: window snaping to slots
|
||||||
|
|
||||||
|
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
|
||||||
24
NEG/UI/UnityUi/NEG.UI.UnityUi.asmdef
Normal file
24
NEG/UI/UnityUi/NEG.UI.UnityUi.asmdef
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"name": "NEG.UI.UnityUi",
|
||||||
|
"rootNamespace": "",
|
||||||
|
"references": [
|
||||||
|
"GUID:343deaaf83e0cee4ca978e7df0b80d21",
|
||||||
|
"GUID:7361f1d9c43da6649923760766194746",
|
||||||
|
"GUID:6055be8ebefd69e48b49212b09b47b2f",
|
||||||
|
"GUID:0c752da273b17c547ae705acf0f2adf2",
|
||||||
|
"GUID:9e24947de15b9834991c9d8411ea37cf",
|
||||||
|
"GUID:84651a3751eca9349aac36a66bba901b",
|
||||||
|
"GUID:23eed6c2401dca1419d1ebd180e58c5a"
|
||||||
|
],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [
|
||||||
|
""
|
||||||
|
],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false
|
||||||
|
}
|
||||||
7
NEG/UI/UnityUi/NEG.UI.UnityUi.asmdef.meta
Normal file
7
NEG/UI/UnityUi/NEG.UI.UnityUi.asmdef.meta
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e2aaf8effe1c9634d87b2edda6988a6a
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
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
|
||||||
30
NEG/UI/UnityUi/Popup/MonoPopup.cs
Normal file
30
NEG/UI/UnityUi/Popup/MonoPopup.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
using NEG.UI.Popup;
|
||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NEG.UI.UnityUi.Popup
|
||||||
|
{
|
||||||
|
public class MonoPopup : MonoBehaviour, IPopup
|
||||||
|
{
|
||||||
|
public event Action<PopupData> OnPopupClosed;
|
||||||
|
|
||||||
|
protected PopupData data;
|
||||||
|
|
||||||
|
public void Show(PopupData data)
|
||||||
|
{
|
||||||
|
this.data = data;
|
||||||
|
gameObject.SetActive(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Close(bool silent = false)
|
||||||
|
{
|
||||||
|
gameObject.SetActive(false);
|
||||||
|
|
||||||
|
if(silent)
|
||||||
|
return;
|
||||||
|
|
||||||
|
OnPopupClosed?.Invoke(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
3
NEG/UI/UnityUi/Popup/MonoPopup.cs.meta
Normal file
3
NEG/UI/UnityUi/Popup/MonoPopup.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5d3466b2fe771184eae1ca72c1006d0c
|
||||||
|
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
|
||||||
59
NEG/UI/UnityUi/Window/MonoWindow.cs
Normal file
59
NEG/UI/UnityUi/Window/MonoWindow.cs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
using NEG.UI.Area;
|
||||||
|
using NEG.UI.UnityUi.Buttons;
|
||||||
|
using NEG.UI.UnityUi.WindowSlot;
|
||||||
|
using NEG.UI.Window;
|
||||||
|
using NEG.UI.WindowSlot;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NEG.UI.UnityUi.Window
|
||||||
|
{
|
||||||
|
public class MonoWindow : MonoBehaviour, IWindow
|
||||||
|
{
|
||||||
|
public IEnumerable<IWindowSlot> AvailableSlots => windowSlots;
|
||||||
|
public IWindowSlot Parent { get; private set; }
|
||||||
|
|
||||||
|
public bool IsMainWindow { get; private set; }
|
||||||
|
|
||||||
|
private IWindowSlot DefaultWindowSlot => windowSlots[0];
|
||||||
|
|
||||||
|
[SerializeField] private List<MonoWindowSlot> windowSlots;
|
||||||
|
[SerializeField] private WindowController controller;
|
||||||
|
|
||||||
|
public void SetOpenedState(IWindowSlot parentSlot)
|
||||||
|
{
|
||||||
|
gameObject.SetActive(true);
|
||||||
|
Parent = parentSlot;
|
||||||
|
if (controller != null)
|
||||||
|
controller.OnOpened();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetData(object data)
|
||||||
|
{
|
||||||
|
if (controller != null)
|
||||||
|
controller.SetData(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetClosedState()
|
||||||
|
{
|
||||||
|
gameObject.SetActive(false);
|
||||||
|
Parent = null;
|
||||||
|
((ISlotsHolder)this).CloseAllWindows();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Awake() => ((IWindow)this).SetClosedState();
|
||||||
|
|
||||||
|
private void OnValidate()
|
||||||
|
{
|
||||||
|
if (controller == null)
|
||||||
|
controller = GetComponent<WindowController>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OpenWindow(IWindow window, object data = null)
|
||||||
|
{
|
||||||
|
DefaultWindowSlot.AttachWindow(window);
|
||||||
|
window.SetData(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
NEG/UI/UnityUi/Window/MonoWindow.cs.meta
Normal file
3
NEG/UI/UnityUi/Window/MonoWindow.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 85d136d6850728d4b96c26fa286ffe3c
|
||||||
|
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
|
||||||
20
NEG/UI/UnityUi/WindowSlot/MonoWindowSlot.cs
Normal file
20
NEG/UI/UnityUi/WindowSlot/MonoWindowSlot.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
using NEG.UI.Area;
|
||||||
|
using NEG.UI.Window;
|
||||||
|
using NEG.UI.WindowSlot;
|
||||||
|
using UnityEngine;
|
||||||
|
using TNRD;
|
||||||
|
|
||||||
|
namespace NEG.UI.UnityUi.WindowSlot
|
||||||
|
{
|
||||||
|
public abstract class MonoWindowSlot : MonoBehaviour, IWindowSlot
|
||||||
|
{
|
||||||
|
[field: SerializeField] public bool OpenWindowAsMain { get; private set; }
|
||||||
|
|
||||||
|
public ISlotsHolder ParentHolder => slotsHolder.Value;
|
||||||
|
public abstract void AttachWindow(IWindow window);
|
||||||
|
public abstract void DetachWindow(IWindow window);
|
||||||
|
public abstract void CloseAllWindows();
|
||||||
|
|
||||||
|
[SerializeField] private SerializableInterface<ISlotsHolder> slotsHolder;
|
||||||
|
}
|
||||||
|
}
|
||||||
3
NEG/UI/UnityUi/WindowSlot/MonoWindowSlot.cs.meta
Normal file
3
NEG/UI/UnityUi/WindowSlot/MonoWindowSlot.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e5a19b184ce9338449ad6ba899a26cda
|
||||||
|
timeCreated: 1670709404
|
||||||
26
NEG/UI/UnityUi/WindowSlot/SingleWindowSlot.cs
Normal file
26
NEG/UI/UnityUi/WindowSlot/SingleWindowSlot.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
using NEG.UI.UnityUi.WindowSlot;
|
||||||
|
using NEG.UI.Window;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NEG.UI.WindowSlot
|
||||||
|
{
|
||||||
|
public class SingleWindowSlot : MonoWindowSlot
|
||||||
|
{
|
||||||
|
public IWindow CurrentWindow
|
||||||
|
{
|
||||||
|
get => currentWindow;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
currentWindow?.SetClosedState();
|
||||||
|
currentWindow = value;
|
||||||
|
currentWindow?.SetOpenedState(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IWindow currentWindow;
|
||||||
|
|
||||||
|
public override void AttachWindow(IWindow window) => CurrentWindow = window;
|
||||||
|
public override void DetachWindow(IWindow window) => CurrentWindow = null;
|
||||||
|
public override void CloseAllWindows() => CurrentWindow = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
3
NEG/UI/UnityUi/WindowSlot/SingleWindowSlot.cs.meta
Normal file
3
NEG/UI/UnityUi/WindowSlot/SingleWindowSlot.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 79c0738c63b2b5c40b96acd89441f78f
|
||||||
|
timeCreated: 1670716632
|
||||||
3
NEG/UI/Window.meta
Normal file
3
NEG/UI/Window.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8e57424db9924004a7945bcbbea7bab9
|
||||||
|
timeCreated: 1670707788
|
||||||
106
NEG/UI/Window/IWindow.cs
Normal file
106
NEG/UI/Window/IWindow.cs
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
using JetBrains.Annotations;
|
||||||
|
using NEG.UI.Area;
|
||||||
|
using NEG.UI.WindowSlot;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace NEG.UI.Window
|
||||||
|
{
|
||||||
|
public interface IWindow : ISlotsHolder
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Parent slot of this window.
|
||||||
|
/// </summary>
|
||||||
|
IWindowSlot Parent { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called internally by slot to open window.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parentSlot">slot that opens window</param>
|
||||||
|
void SetOpenedState(IWindowSlot parentSlot);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets data for window, usually used for dynamic content set by external logic controller.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">can be any type, window should cast for expected type</param>
|
||||||
|
void SetData(object data);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called internally to close window by slot.
|
||||||
|
/// </summary>
|
||||||
|
void SetClosedState();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class WindowInterfaceExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Opens window as slot child. If slot is null or not provided, as child of current area.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="window">window to open</param>
|
||||||
|
/// <param name="slot">slot to attach window</param>
|
||||||
|
/// <param name="data">data to send to window</param>
|
||||||
|
public static void Open(this IWindow window, IWindowSlot slot = null, object data = null)
|
||||||
|
{
|
||||||
|
if (slot != null)
|
||||||
|
{
|
||||||
|
slot.AttachWindow(window);
|
||||||
|
window.SetData(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UiManager.Instance.CurrentArea.OpenWindow(window, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opens window as child of selected area.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="window">window to open</param>
|
||||||
|
/// <param name="area">area to attach window</param>
|
||||||
|
/// <param name="data">data to send to window</param>
|
||||||
|
public static void Open(this IWindow window, IArea area, object data = null) => area.OpenWindow(window, data);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Open passed window as child of this window.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="window">parent window</param>
|
||||||
|
/// <param name="windowToOpen">window to open</param>
|
||||||
|
/// <param name="data">data to send to window</param>
|
||||||
|
public static void OpenChild(this IWindow window, IWindow windowToOpen, object data = null)
|
||||||
|
{
|
||||||
|
if (windowToOpen == null)
|
||||||
|
{
|
||||||
|
Debug.LogError($"Window to open cannot be null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.OpenWindow(windowToOpen, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Open window as child of provided window. If <typeparamref name="parentWindow"/> is null, as child of current main window in <see cref="UiManager"/>. If there is no main window, open on current area.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="window">window to open</param>
|
||||||
|
/// <param name="parentWindow">parent window</param>
|
||||||
|
/// <param name="data">data to send to window</param>
|
||||||
|
public static void OpenAsChild(this IWindow window, IWindow parentWindow = null, object data = null)
|
||||||
|
{
|
||||||
|
if (parentWindow != null)
|
||||||
|
{
|
||||||
|
parentWindow.OpenWindow(window, data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UiManager.Instance.CurrentMainWindow != null)
|
||||||
|
{
|
||||||
|
UiManager.Instance.CurrentMainWindow.OpenWindow(window, data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UiManager.Instance.CurrentArea.OpenWindow(window, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Close window.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="window">window to close</param>
|
||||||
|
public static void Close(this IWindow window) => window.Parent.DetachWindow(window);
|
||||||
|
}
|
||||||
|
}
|
||||||
3
NEG/UI/Window/IWindow.cs.meta
Normal file
3
NEG/UI/Window/IWindow.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c68ab5f7e5d9440e9fbd26307211e8f1
|
||||||
|
timeCreated: 1670690267
|
||||||
3
NEG/UI/WindowSlot.meta
Normal file
3
NEG/UI/WindowSlot.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1f54322bf2ce451dbfccb7c41e89dd62
|
||||||
|
timeCreated: 1670707801
|
||||||
25
NEG/UI/WindowSlot/ISlotsHolder.cs
Normal file
25
NEG/UI/WindowSlot/ISlotsHolder.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using NEG.UI.Window;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace NEG.UI.WindowSlot
|
||||||
|
{
|
||||||
|
public interface ISlotsHolder
|
||||||
|
{
|
||||||
|
IEnumerable<IWindowSlot> AvailableSlots { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Open window
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="window"></param>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
void OpenWindow(IWindow window, object data = null);
|
||||||
|
|
||||||
|
void CloseAllWindows()
|
||||||
|
{
|
||||||
|
foreach (var slot in AvailableSlots)
|
||||||
|
{
|
||||||
|
slot.CloseAllWindows();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
NEG/UI/WindowSlot/ISlotsHolder.cs.meta
Normal file
3
NEG/UI/WindowSlot/ISlotsHolder.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ace3899aabb0491d80f8b17bd827854b
|
||||||
|
timeCreated: 1674408955
|
||||||
14
NEG/UI/WindowSlot/IWindowSlot.cs
Normal file
14
NEG/UI/WindowSlot/IWindowSlot.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using NEG.UI.Area;
|
||||||
|
using NEG.UI.Window;
|
||||||
|
|
||||||
|
namespace NEG.UI.WindowSlot
|
||||||
|
{
|
||||||
|
public interface IWindowSlot
|
||||||
|
{
|
||||||
|
bool OpenWindowAsMain { get; }
|
||||||
|
ISlotsHolder ParentHolder { get; }
|
||||||
|
void AttachWindow(IWindow window);
|
||||||
|
void DetachWindow(IWindow window);
|
||||||
|
void CloseAllWindows();
|
||||||
|
}
|
||||||
|
}
|
||||||
3
NEG/UI/WindowSlot/IWindowSlot.cs.meta
Normal file
3
NEG/UI/WindowSlot/IWindowSlot.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f4f7edf281324a2ba86f4099eff57552
|
||||||
|
timeCreated: 1670694831
|
||||||
Loading…
x
Reference in New Issue
Block a user