Update Cryville.Common.

This commit is contained in:
2022-10-14 23:32:43 +08:00
parent d3cac8a28d
commit c8eee7ab3f
18 changed files with 497 additions and 47 deletions

View File

@@ -7,32 +7,52 @@ using UnityEngine.Rendering;
#endif
namespace Cryville.Common.Unity {
/// <summary>
/// A worker that performs network tasks in the background.
/// </summary>
/// <remarks>
/// It is required to call <see cref="TickBackgroundTasks" /> every tick to keep the worker working.
/// </remarks>
public class NetworkTaskWorker {
bool suspended;
NetworkTask currentNetworkTask;
readonly Queue<NetworkTask> networkTasks = new Queue<NetworkTask>();
/// <summary>
/// Current queued task count.
/// </summary>
public int TaskCount { get { return networkTasks.Count; } }
/// <summary>
/// Submits a new network task.
/// </summary>
/// <param name="task">The task.</param>
public void SubmitNetworkTask(NetworkTask task) {
networkTasks.Enqueue(task);
}
/// <summary>
/// Ticks the worker.
/// </summary>
/// <returns>The status of the worker.</returns>
public WorkerStatus TickBackgroundTasks() {
if (suspended) return WorkerStatus.Suspended;
if (currentNetworkTask != null) {
if (currentNetworkTask.Canceled) currentNetworkTask = null;
if (currentNetworkTask.Cancelled) currentNetworkTask = null;
else if (currentNetworkTask.Done()) currentNetworkTask = null;
}
while (networkTasks.Count > 0 && currentNetworkTask == null) {
var task = networkTasks.Dequeue();
if (task.Canceled) continue;
if (task.Cancelled) continue;
currentNetworkTask = task;
currentNetworkTask.Start();
}
return currentNetworkTask == null ? WorkerStatus.Idle : WorkerStatus.Working;
}
/// <summary>
/// Cancels the current working task (if present) and suspends all background tasks.
/// </summary>
public void SuspendBackgroundTasks() {
suspended = true;
if (currentNetworkTask != null) {
@@ -41,63 +61,123 @@ namespace Cryville.Common.Unity {
}
}
/// <summary>
/// Resumes background tasks.
/// </summary>
public void ResumeBackgroundTasks() {
suspended = false;
}
}
/// <summary>
/// Status of a <see cref="NetworkTaskWorker" />.
/// </summary>
public enum WorkerStatus {
Idle, Working, Suspended,
/// <summary>
/// The worker is not working nor suspended.
/// </summary>
Idle,
/// <summary>
/// The worker is working on a task.
/// </summary>
Working,
/// <summary>
/// The worker is suspended.
/// </summary>
Suspended,
}
/// <summary>
/// A network task.
/// </summary>
public abstract class NetworkTask {
protected NetworkTask(string uri) {
Uri = uri;
}
/// <summary>
/// The URI of the resource.
/// </summary>
public string Uri { get; private set; }
public bool Canceled { get; private set; }
/// <summary>
/// Whether the task is cancelled.
/// </summary>
public bool Cancelled { get; private set; }
/// <summary>
/// Cancels the task.
/// </summary>
public virtual void Cancel() {
Canceled = true;
Cancelled = true;
}
#if UNITY_5_4_OR_NEWER
protected UnityWebRequest www;
/// <summary>
/// Starts the task.
/// </summary>
public virtual void Start() {
www = new UnityWebRequest(Uri);
www.SendWebRequest();
}
/// <summary>
/// Gets whether the task is done.
/// </summary>
/// <returns>Whether the task is done.</returns>
public virtual bool Done() {
if (!www.isDone) return false;
return true;
}
#else
protected WWW www;
public virtual WWW Start() {
return new WWW(Uri);
/// <summary>
/// Starts the task.
/// </summary>
public virtual void Start() {
www = new WWW(Uri);
}
/// <summary>
/// Gets whether the task is done.
/// </summary>
/// <returns>Whether the task is done.</returns>
public virtual bool Done() {
if (!www.isDone) return false;
return true;
}
public abstract void Done();
#endif
}
/// <summary>
/// A <see cref="NetworkTask" /> that loads a texture.
/// </summary>
public class LoadTextureTask : NetworkTask {
/// <summary>
/// Creates an instance of the <see cref="LoadTextureTask" /> class.
/// </summary>
/// <param name="uri">The URI of the resource.</param>
/// <param name="callback">The callback function upon load complete.</param>
public LoadTextureTask(string uri, Action<bool, Texture2D> callback) : base(uri) {
Callback = callback;
}
/// <summary>
/// The callback function upon load complete.
/// </summary>
public Action<bool, Texture2D> Callback { get; private set; }
#if UNITY_5_4_OR_NEWER
DownloadHandlerTexture handler;
/// <inheritdoc />
public override void Start() {
handler = new DownloadHandlerTexture();
www = new UnityWebRequest(Uri, "GET", handler, null);
www.SendWebRequest();
}
/// <inheritdoc />
public override bool Done() {
if (!handler.isDone) return false;
if (handler.texture != null) {
if (!www.isDone) return false;
if (handler.isDone && handler.texture != null) {
var buffer = handler.texture;
/*var result = new Texture2D(buffer.width, buffer.height, buffer.format, true);
if (SystemInfo.copyTextureSupport.HasFlag(CopyTextureSupport.Basic)) {
@@ -119,17 +199,21 @@ namespace Cryville.Common.Unity {
return true;
}
#else
public override void PostProcess(WWW www) {
/// <inheritdoc />
public override bool Done() {
if (!www.isDone) return false;
bool succeeded = string.IsNullOrEmpty(www.error);
if (succeeded) {
var buffer = www.texture;
var result = new Texture2D(buffer.width, buffer.height, buffer.format, true);
/*var result = new Texture2D(buffer.width, buffer.height, buffer.format, true);
result.SetPixels(buffer.GetPixels());
result.Apply(true, true);
Texture2D.Destroy(buffer);
Callback(true, result);
Callback(true, result);*/
Callback(true, buffer);
}
else Callback(false, null);
return true;
}
#endif
}

View File

@@ -3,16 +3,27 @@ using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace Cryville.Common.Unity.UI {
/// <summary>
/// A <see cref="ILayoutElement" /> that takes the length of one axis to compute the preferred length of the other axis with respect to a aspect ratio.
/// </summary>
public class AspectRatioLayoutElement : UIBehaviour, ILayoutElement {
[SerializeField]
[Tooltip("The aspect ratio. Width divided by height.")]
private float m_aspectRatio = 1;
/// <summary>
/// The aspect ratio. Width divided by height.
/// </summary>
public float AspectRatio {
get { return m_aspectRatio; }
set { SetProperty(ref m_aspectRatio, value); }
}
[SerializeField]
[Tooltip("Whether to compute the length of the y axis.")]
private bool m_isVertical = false;
/// <summary>
/// Whether to compute the length of the y axis.
/// </summary>
public bool IsVertical {
get { return m_isVertical; }
set { SetProperty(ref m_isVertical, value); }
@@ -29,26 +40,35 @@ namespace Cryville.Common.Unity.UI {
LayoutRebuilder.MarkLayoutForRebuild(transform as RectTransform);
}
/// <inheritdoc />
public float minWidth {
get {
return m_isVertical ? 0 : (transform as RectTransform).rect.height * m_aspectRatio;
}
}
/// <inheritdoc />
public float preferredWidth { get { return minWidth; } }
/// <inheritdoc />
public float flexibleWidth { get { return 0; } }
/// <inheritdoc />
public float minHeight {
get {
return m_isVertical ? (transform as RectTransform).rect.width / m_aspectRatio : 0;
}
}
/// <inheritdoc />
public float preferredHeight { get { return minHeight; } }
/// <inheritdoc />
public float flexibleHeight { get { return 0; } }
/// <inheritdoc />
public int layoutPriority { get { return 1; } }
/// <inheritdoc />
public void CalculateLayoutInputHorizontal() { }
/// <inheritdoc />
public void CalculateLayoutInputVertical() { }
protected override void OnDidApplyAnimationProperties() {

View File

@@ -1,9 +1,16 @@
using UnityEngine;
namespace Cryville.Common.Unity.UI {
/// <summary>
/// A <see cref="DockLayoutGroup" /> that sets the aspect ratio of the docking element.
/// </summary>
public sealed class DockAspectRatioLayoutGroup : DockLayoutGroup {
[SerializeField]
[Tooltip("The aspect ratio of the docking element.")]
private float m_dockAspectRatio = 1;
/// <summary>
/// The aspect ratio of the docking element.
/// </summary>
public float DockAspectRatio {
get { return m_dockAspectRatio; }
set { base.SetProperty(ref m_dockAspectRatio, value); }

View File

@@ -2,30 +2,60 @@
using UnityEngine.UI;
namespace Cryville.Common.Unity.UI {
/// <summary>
/// A <see cref="LayoutGroup" /> that docks its first child element to one side.
/// </summary>
public abstract class DockLayoutGroup : LayoutGroup {
/// <summary>
/// The dock side.
/// </summary>
public enum Side {
/// <summary>
/// Top.
/// </summary>
Top = 0,
/// <summary>
/// Right.
/// </summary>
Right = 1,
/// <summary>
/// Bottom.
/// </summary>
Bottom = 2,
/// <summary>
/// Left.
/// </summary>
Left = 3,
}
[SerializeField]
[Tooltip("The docking side of the first child element.")]
private Side m_side;
/// <summary>
/// The docking side of the first child element.
/// </summary>
public Side DockSide {
get { return m_side; }
set { base.SetProperty(ref m_side, value); }
}
[SerializeField]
[Tooltip("The slide index. The children slide along the cross axis.")]
private float m_slideIndex;
/// <summary>
/// The slide index. The children slide along the axis.
/// </summary>
public float SlideIndex {
get { return m_slideIndex; }
set { base.SetProperty(ref m_slideIndex, value); }
}
/// <inheritdoc />
public sealed override void CalculateLayoutInputHorizontal() { base.CalculateLayoutInputHorizontal(); }
/// <inheritdoc />
public sealed override void CalculateLayoutInputVertical() { }
/// <inheritdoc />
public sealed override void SetLayoutHorizontal() { SetChildrenAlongAxis(0); }
/// <inheritdoc />
public sealed override void SetLayoutVertical() { SetChildrenAlongAxis(1); }
private float GetSlidePosition(float groupHeight, float dockHeight) {
@@ -68,6 +98,11 @@ namespace Cryville.Common.Unity.UI {
}
}
/// <summary>
/// Gets the length of the first child element along the axis.
/// </summary>
/// <param name="groupSize">The size of the layout group.</param>
/// <returns></returns>
protected abstract float GetDockElementSize(Vector2 groupSize);
}
}

View File

@@ -1,9 +1,16 @@
using UnityEngine;
namespace Cryville.Common.Unity.UI {
/// <summary>
/// A <see cref="DockLayoutGroup" /> that sets the occupied ratio of the docking element.
/// </summary>
public sealed class DockOccupiedRatioLayoutGroup : DockLayoutGroup {
[SerializeField]
[Tooltip("The occupied ratio of the docking element.")]
private float m_dockOccupiedRatio = 1;
/// <summary>
/// The occupied ratio of the docking element.
/// </summary>
public float DockOccupiedRatio {
get { return m_dockOccupiedRatio; }
set { base.SetProperty(ref m_dockOccupiedRatio, value); }

View File

@@ -2,11 +2,17 @@
using UnityEngine.UI;
namespace Cryville.Common.Unity.UI {
/// <summary>
/// Fits the size of a <see cref="GridLayoutGroup" /> with its cells.
/// </summary>
[RequireComponent(typeof(GridLayoutGroup))]
public class GridLayoutSizeFitter : MonoBehaviour {
RectTransform rectTransform;
GridLayoutGroup gridLayoutGroup;
Canvas canvas;
/// <summary>
/// The item count per line.
/// </summary>
public int GroupItemCount = 3;
#pragma warning disable IDE0051

View File

@@ -10,6 +10,7 @@ namespace Cryville.Common.Unity.UI {
[ExecuteInEditMode]
public class ImageSliced3 : MaskableGraphic {
[SerializeField]
[Tooltip("The sliced sprite.")]
private Sprite m_sprite;
/// <summary>
/// The sliced sprite.
@@ -45,6 +46,7 @@ namespace Cryville.Common.Unity.UI {
DiagonalRight = 5,
}
[SerializeField]
[Tooltip("The mode how a sliced image is generated when it is too compact.")]
private CompactMode m_compact;
/// <summary>
/// The mode how a sliced image is generated when it is too compact.

View File

@@ -2,9 +2,16 @@
using UnityEngine.UI;
namespace Cryville.Common.Unity.UI {
/// <summary>
/// Fits the length of an axis of an element with respect to the children count and the shared aspect ratio.
/// </summary>
public class LayoutAspectRatioFitter : MonoBehaviour {
[SerializeField]
[Tooltip("The aspect ratio per element.")]
private float m_aspectRatioPerElement = 1;
/// <summary>
/// The aspect ratio per element.
/// </summary>
public float AspectRatioPerElement {
get { return m_aspectRatioPerElement; }
set { m_aspectRatioPerElement = value; }

View File

@@ -2,16 +2,27 @@
using UnityEngine.UI;
namespace Cryville.Common.Unity.UI {
/// <summary>
/// A non-interactive <see cref="Slider" /> that has an internal tweening behaviour.
/// </summary>
public class ProgressBar : Slider {
[SerializeField][Range(0f, 1f)]
[Tooltip("The tweening parameter.")]
float m_smooth = 0;
/// <summary>
/// The tweening parameter.
/// </summary>
public float Smooth {
get { return m_smooth; }
set { m_smooth = value; }
}
[SerializeField]
[Tooltip("The target value.")]
float m_targetValue;
/// <summary>
/// Gets the current displayed value or sets the target value.
/// </summary>
public override float value {
get { return base.value; }
set { m_targetValue = value; }

View File

@@ -3,61 +3,84 @@ using UnityEngine;
using UnityEngine.UI;
namespace Cryville.Common.Unity.UI {
/// <summary>
/// A handler for loading an item.
/// </summary>
/// <param name="index">The zero-based index of the item.</param>
/// <param name="gameObject">The game object for the item instantiated from the item template.</param>
/// <returns></returns>
public delegate bool LoadItemHandler(int index, GameObject gameObject);
/// <summary>
/// A scrollable grid that dynamically loads its items.
/// </summary>
public sealed class ScrollableItemGrid : MonoBehaviour {
[SerializeField]
[Tooltip("The item template.")]
private GameObject m_itemTemplate;
/// <summary>
/// The item template.
/// </summary>
public GameObject ItemTemplate {
get { return m_itemTemplate; }
set { m_itemTemplate = value; OnTemplateUpdate(); }
}
public Func<int, GameObject, bool> LoadItem { private get; set; }
public enum Corner {
UpperLeft = 0,
UpperRight = 1,
LowerLeft = 2,
LowerRight = 3,
}
[SerializeField]
private Corner m_startCorner; // TODO
public Corner StartCorner {
get { return m_startCorner; }
set { m_startCorner = value; OnFrameUpdate(); }
}
/// <summary>
/// The handler for loading an item.
/// </summary>
public LoadItemHandler LoadItem { private get; set; }
/// <summary>
/// Axis.
/// </summary>
public enum Axis {
Horizontal = 0, Vertical = 1,
/// <summary>
/// Horizontal (x) axis.
/// </summary>
Horizontal = 0,
/// <summary>
/// Vertical (y) axis.
/// </summary>
Vertical = 1,
}
[SerializeField]
[Tooltip("The main axis.")]
private Axis m_startAxis;
/// <summary>
/// The main axis.
/// </summary>
public Axis StartAxis {
get { return m_startAxis; }
set { m_startAxis = value; OnFrameUpdate(); }
}
[SerializeField]
private Vector2 m_spacing; // TODO
public Vector2 Spacing {
get { return m_spacing; }
set { m_spacing = value; OnFrameUpdate(); }
}
[SerializeField]
[Tooltip("The item count.")]
private int m_itemCount = 3;
/// <summary>
/// The item count.
/// </summary>
public int ItemCount {
get { return m_itemCount; }
set { m_itemCount = value; OnRefresh(); }
}
[SerializeField]
[Tooltip("The item count per line.")]
private int m_lineItemCount = 3;
/// <summary>
/// The item count per line.
/// </summary>
public int LineItemCount {
get { return m_lineItemCount; }
set { m_lineItemCount = value; OnFrameUpdate(); }
}
[SerializeField]
[Tooltip("The length of the cross axis per line.")]
private float m_lineHeight = 100;
/// <summary>
/// The length of the cross axis per line.
/// </summary>
public float LineHeight {
get { return m_lineHeight; }
set { m_lineHeight = value; OnFrameUpdate(); }
@@ -78,6 +101,9 @@ namespace Cryville.Common.Unity.UI {
);
}
}
/// <summary>
/// The maximum count of visible lines.
/// </summary>
public int VisibleLines {
get {
return Mathf.CeilToInt(VisibleSize.y / m_lineHeight) + 1;