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

@@ -1,12 +1,30 @@
using System;
namespace Cryville.Common {
/// <summary>
/// Represents a cancellable asynchronized task.
/// </summary>
/// <typeparam name="T"></typeparam>
public class AsyncDelivery<T> {
/// <summary>
/// The delegate to cancel the task.
/// </summary>
public Action CancelSource { private get; set; }
/// <summary>
/// The delegate to call on task completion.
/// </summary>
public Action<bool, T> Destination { private get; set; }
/// <summary>
/// Delivers the result to the destination.
/// </summary>
/// <param name="succeeded">Whether the task has succeeded.</param>
/// <param name="result">The result.</param>
public void Deliver(bool succeeded, T result) {
if (Destination != null) Destination(succeeded, result);
}
/// <summary>
/// Cancels the task.
/// </summary>
public void Cancel() {
if (CancelSource != null) CancelSource();
}

View File

@@ -1,22 +1,42 @@
using System;
using System.IO;
using System.Text;
namespace Cryville.Common {
/// <summary>
/// Provides a set of <see langword="static" /> methods related to file system and IO.
/// </summary>
public static class IOExtensions {
/// <summary>
/// Gets a subdirectory of a directory. The subdirectory is created if it does not exist.
/// </summary>
/// <param name="dir">The parent directory.</param>
/// <param name="name">The name of the subdirectory.</param>
/// <returns></returns>
public static DirectoryInfo GetSubdirectory(this DirectoryInfo dir, string name) {
var l1 = dir.GetDirectories(name);
if (l1.Length == 0) return dir.CreateSubdirectory(name);
else return l1[0];
}
/// <summary>
/// Reads a string length-prefixed with a <see cref="System.UInt16" />.
/// </summary>
/// <param name="reader">The binary reader.</param>
/// <param name="encoding">The encoding of the string.</param>
/// <returns>The string read from the reader.</returns>
public static string ReadUInt16String(this BinaryReader reader, Encoding encoding = null) {
if (encoding == null) encoding = Encoding.UTF8;
var len = reader.ReadUInt16();
byte[] buffer = reader.ReadBytes(len);
return encoding.GetString(buffer);
}
/// <summary>
/// Writes a string length-prefixed with a <see cref="System.UInt16" />.
/// </summary>
/// <param name="writer">The binary writer.</param>
/// <param name="value">The string to write by the writer.</param>
/// <param name="encoding">The encoding of the string.</param>
public static void WriteUInt16String(this BinaryWriter writer, string value, Encoding encoding = null) {
if (encoding == null) encoding = Encoding.UTF8;
byte[] buffer = encoding.GetBytes(value);

View File

@@ -1,23 +1,42 @@
using Ionic.Zip;
using System;
using System.Collections.Generic;
using System.IO;
namespace Cryville.Common {
/// <summary>
/// A logger.
/// </summary>
public abstract class Logger {
static readonly Dictionary<string, Logger> Instances = new Dictionary<string, Logger>();
static readonly Dictionary<string, StreamWriter> Files = new Dictionary<string, StreamWriter>();
static string logPath = null;
/// <summary>
/// Sets the path where the log files shall be stored.
/// </summary>
/// <param name="path">The path.</param>
public static void SetLogPath(string path) {
logPath = path;
var dir = new DirectoryInfo(path);
if (!dir.Exists) Directory.CreateDirectory(dir.FullName);
}
/// <summary>
/// Logs to the specified logger.
/// </summary>
/// <param name="key">The key of the logger.</param>
/// <param name="level">The severity level.</param>
/// <param name="module">The module that is logging.</param>
/// <param name="format">The format string.</param>
/// <param name="args">The arguments for formatting.</param>
public static void Log(string key, int level, string module, string format, params object[] args) {
if (!Instances.ContainsKey(key)) return;
Instances[key].Log(level, module, string.Format(format, args));
if (Files.ContainsKey(key)) Files[key].WriteLine("[{0:O}] [{1}] <{2}> {3}", DateTime.UtcNow, level, module, string.Format(format, args));
}
/// <summary>
/// Adds a created logger to the shared logger manager.
/// </summary>
/// <param name="key">The key of the logger.</param>
/// <param name="logger">The logger.</param>
public static void Create(string key, Logger logger) {
Instances[key] = logger;
if (logPath != null) {
@@ -26,37 +45,65 @@ namespace Cryville.Common {
};
}
}
/// <summary>
/// Closes all loggers and related file streams.
/// </summary>
public static void Close() {
Instances.Clear();
foreach (var f in Files) f.Value.Dispose();
Files.Clear();
}
/// <summary>
/// Logs to the logger.
/// </summary>
/// <param name="level">The severity level.</param>
/// <param name="module">The module that is logging.</param>
/// <param name="msg">The message.</param>
public virtual void Log(int level, string module, string msg) { }
}
/// <summary>
/// A <see cref="Logger" /> that calls a callback function on log.
/// </summary>
public class InstantLogger : Logger {
readonly Action<int, string, string> callback;
/// <summary>
/// Creates an instance of the <see cref="InstantLogger" /> class.
/// </summary>
/// <param name="callback">The callback function.</param>
/// <exception cref="ArgumentNullException"><paramref name="callback" /> is <see langword="null" />.</exception>
public InstantLogger(Action<int, string, string> callback) {
if (callback == null)
throw new ArgumentNullException("callback");
this.callback = callback;
}
/// <inheritdoc />
public override void Log(int level, string module, string msg) {
base.Log(level, module, msg);
callback(level, module, msg);
}
}
/// <summary>
/// A <see cref="Logger" /> that buffers the logs for enumeration.
/// </summary>
public class BufferedLogger : Logger {
readonly List<LogEntry> buffer = new List<LogEntry>();
/// <summary>
/// Creates an instance of the <see cref="BufferedLogger" /> class.
/// </summary>
public BufferedLogger() { }
/// <inheritdoc />
public override void Log(int level, string module, string msg) {
base.Log(level, module, msg);
lock (buffer) {
buffer.Add(new LogEntry(level, module, msg));
}
}
/// <summary>
/// Enumerates the buffered logs.
/// </summary>
/// <param name="callback">The callback function to receive the logs.</param>
public void Enumerate(Action<int, string, string> callback) {
lock (buffer) {
foreach (var i in buffer) {
@@ -67,7 +114,7 @@ namespace Cryville.Common {
}
}
public struct LogEntry {
struct LogEntry {
public int level;
public string module;
public string msg;

View File

@@ -1,18 +1,38 @@
namespace Cryville.Common.Math {
/// <summary>
/// Represents a column vector of vector type <typeparamref name="T" />.
/// </summary>
/// <typeparam name="T">The vector type of the elements.</typeparam>
public class ColumnVector<T> {
readonly T[] content;
/// <summary>
/// The size of the vector.
/// </summary>
public int Size {
get;
private set;
}
/// <summary>
/// Creates a column vector with specified size.
/// </summary>
/// <param name="size">The size of the vector.</param>
public ColumnVector(int size) {
content = new T[size];
Size = size;
}
/// <summary>
/// Creates a column vector from an array.
/// </summary>
/// <param name="c">The array.</param>
public ColumnVector(T[] c) {
Size = c.Length;
content = c;
}
/// <summary>
/// Gets or sets the element at the specified index.
/// </summary>
/// <param name="i">The zero-based index of the element to get or set.</param>
/// <returns>The element at the specified index.</returns>
public T this[int i] {
get {
return content[i];
@@ -21,12 +41,24 @@ namespace Cryville.Common.Math {
content[i] = value;
}
}
/// <summary>
/// Performs dot operation with a <see cref="System.Single" /> column vector.
/// </summary>
/// <param name="lhs">The lefthand column vector.</param>
/// <param name="o">The vector operator.</param>
/// <returns>The result of the dot operation.</returns>
public T Dot(ColumnVector<float> lhs, IVectorOperator<T> o) {
T res = default(T);
for (var i = 0; i < Size; i++)
res = o.Add(res, o.ScalarMultiply(lhs[i], content[i]));
return res;
}
/// <summary>
/// Creates a <see cref="System.Single" /> column vector and fills it with polynomial coefficients.
/// </summary>
/// <param name="size">The size of the column vector.</param>
/// <param name="num">The base number.</param>
/// <returns>A <see cref="System.Single" /> column vector filled with polynomial coefficients.</returns>
public static ColumnVector<float> WithPolynomialCoefficients(int size, float num) {
var m = new ColumnVector<float>(size);
for (var i = 0; i < size; i++)

View File

@@ -1,6 +1,22 @@
namespace Cryville.Common.Math {
/// <summary>
/// Provides a set of operators for vector type <typeparamref name="T" />.
/// </summary>
/// <typeparam name="T">The vector type.</typeparam>
public interface IVectorOperator<T> {
/// <summary>
/// Adds two vectors.
/// </summary>
/// <param name="lhs">Lefthand vector.</param>
/// <param name="rhs">Righthand vector.</param>
/// <returns>The sum of the two vectors.</returns>
T Add(T lhs, T rhs);
/// <summary>
/// Multiplies a vector with a number.
/// </summary>
/// <param name="lhs">The number.</param>
/// <param name="rhs">The vector.</param>
/// <returns>The product of the number and the vector.</returns>
T ScalarMultiply(float lhs, T rhs);
}
}

View File

@@ -1,18 +1,41 @@
namespace Cryville.Common.Math {
/// <summary>
/// Represents a square matrix.
/// </summary>
public class SquareMatrix {
readonly float[,] content;
/// <summary>
/// The size of the matrix.
/// </summary>
public int Size {
get;
private set;
}
/// <summary>
/// Creates a square matrix with the specified size.
/// </summary>
/// <param name="size">The size of the matrix.</param>
public SquareMatrix(int size) {
content = new float[size, size];
Size = size;
}
/// <summary>
/// Gets or sets the element at the specified index.
/// </summary>
/// <param name="r">The zero-based row index.</param>
/// <param name="c">The zero-based column index.</param>
/// <returns>The element at the specified index.</returns>
public float this[int r, int c] {
get { return content[r, c]; }
set { content[r, c] = value; }
}
/// <summary>
/// Eliminates the square matrix against a column vector.
/// </summary>
/// <typeparam name="T">The vector type.</typeparam>
/// <param name="v">The column vector.</param>
/// <param name="o">The column operator.</param>
/// <returns>The column vector eliminated.</returns>
public ColumnVector<T> Eliminate<T>(ColumnVector<T> v, IVectorOperator<T> o) {
int s = Size;
float[,] d = (float[,])content.Clone();
@@ -48,6 +71,11 @@
}
return new ColumnVector<T>(res);
}
/// <summary>
/// Creates a square matrix and fills it with polynomial coefficients.
/// </summary>
/// <param name="size">The size of the square matrix.</param>
/// <returns>A square matrix filled with polynomial coefficients.</returns>
public static SquareMatrix WithPolynomialCoefficients(int size) {
var m = new SquareMatrix(size);
for (var r = 0; r < size; r++) {

View File

@@ -4,16 +4,36 @@ using System.Collections.Generic;
using System.Linq;
namespace Cryville.Common {
/// <summary>
/// Provides a set of <see langword="static" /> methods for refletion.
/// </summary>
public static class ReflectionHelper {
static readonly Type[] emptyTypeArray = {};
/// <summary>
/// Gets the parameterless constructor of a type.
/// </summary>
/// <param name="type">The type.</param>
/// <returns>The parameterless constructor of the type.</returns>
public static ConstructorInfo GetEmptyConstructor(Type type) {
return type.GetConstructor(emptyTypeArray);
}
static readonly object[] emptyObjectArray = {};
/// <summary>
/// Invokes the parameterless constructor of a type and returns the result.
/// </summary>
/// <param name="type">The type.</param>
/// <returns>The created instance.</returns>
public static object InvokeEmptyConstructor(Type type) {
return GetEmptyConstructor(type).Invoke(emptyObjectArray);
}
/// <summary>
/// Tries to find a member with the specified attribute type in a type.
/// </summary>
/// <typeparam name="T">The attribute type.</typeparam>
/// <param name="t">The type containing the member with the specified attribute type.</param>
/// <param name="mi">The member.</param>
/// <returns>Whether the member is found.</returns>
public static bool TryFindMemberWithAttribute<T>(Type t, out MemberInfo mi) where T : Attribute {
try {
mi = FindMemberWithAttribute<T>(t);
@@ -24,6 +44,13 @@ namespace Cryville.Common {
return false;
}
}
/// <summary>
/// Finds a member with the specified attribute type in a type.
/// </summary>
/// <typeparam name="T">The attribute type.</typeparam>
/// <param name="type">The type containing the member with the specified attribute type.</param>
/// <returns></returns>
/// <exception cref="MissingMemberException">The member is not found or multiple members are found.</exception>
public static MemberInfo FindMemberWithAttribute<T>(Type type) where T : Attribute {
var mil = type.FindMembers(
MemberTypes.Field | MemberTypes.Property,
@@ -36,10 +63,22 @@ namespace Cryville.Common {
return mil[0];
}
/// <summary>
/// Gets whether a type is a <see cref="Dictionary{TKey, TValue}" />.
/// </summary>
/// <param name="type">The type.</param>
/// <returns>Whether the type is a <see cref="Dictionary{TKey, TValue}" />.</returns>
public static bool IsGenericDictionary(Type type) {
return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>);
}
/// <summary>
/// Gets the member from a type with the specified name.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="name">The name of the member.</param>
/// <returns>The member.</returns>
/// <exception cref="MissingMemberException">The member is not found or multiple members are found.</exception>
public static MemberInfo GetMember(Type type, string name) {
var mil = type.GetMember(
name,
@@ -50,16 +89,29 @@ namespace Cryville.Common {
throw new MissingMemberException(type.Name, name);
return mil[0];
}
/// <summary>
/// Gets the type of a member.
/// </summary>
/// <param name="mi">The member.</param>
/// <returns>The type of the member.</returns>
/// <exception cref="ArgumentException"><paramref name="mi" /> is not a field or a property.</exception>
public static Type GetMemberType(MemberInfo mi) {
if (mi is FieldInfo)
return ((FieldInfo)mi).FieldType;
if (mi is PropertyInfo)
return ((PropertyInfo)mi).PropertyType;
else
throw new ArgumentException();
throw new ArgumentException("Member is not field or property.");
}
/// <summary>
/// Gets the value of a member of an object.
/// </summary>
/// <param name="mi">The member.</param>
/// <param name="obj">The object.</param>
/// <returns>The value.</returns>
/// <exception cref="ArgumentException"><paramref name="mi" /> is not a field or a property.</exception>
public static object GetValue(MemberInfo mi, object obj) {
if (mi is FieldInfo)
return ((FieldInfo)mi).GetValue(obj);
@@ -68,7 +120,15 @@ namespace Cryville.Common {
else
throw new ArgumentException();
}
/// <summary>
/// Sets the value of a member of an object.
/// </summary>
/// <param name="mi">The member.</param>
/// <param name="obj">The object.</param>
/// <param name="value">The value.</param>
/// <param name="binder">An optional binder to convert the value.</param>
/// <exception cref="ArgumentException"><paramref name="mi" /> is not a field or a property.</exception>
public static void SetValue(MemberInfo mi, object obj, object value, Binder binder = null) {
if (mi is FieldInfo)
((FieldInfo)mi).SetValue(obj, value, BindingFlags.Default, binder, null);
@@ -78,6 +138,11 @@ namespace Cryville.Common {
throw new ArgumentException();
}
/// <summary>
/// Gets all the subclasses of a type in the current app domain.
/// </summary>
/// <typeparam name="T">The type.</typeparam>
/// <returns>An array containing all the subclasses of the type in the current app domain.</returns>
public static Type[] GetSubclassesOf<T>() where T : class {
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
IEnumerable<Type> r = new List<Type>();
@@ -90,6 +155,11 @@ namespace Cryville.Common {
return r.ToArray();
}
/// <summary>
/// Gets a simple name of a type.
/// </summary>
/// <param name="type">The type.</param>
/// <returns>A simple name of the class.</returns>
public static string GetSimpleName(Type type) {
string result = type.Name;
var typeargs = type.GetGenericArguments();

View File

@@ -2,10 +2,24 @@
using System.Text;
namespace Cryville.Common {
/// <summary>
/// Provides a set of <see langword="static" /> methods related to string operations.
/// </summary>
public static class StringUtils {
/// <summary>
/// Removes the extension in a file name or file path.
/// </summary>
/// <param name="s">The file name or file path.</param>
/// <returns>The file name or file path with the extension removed.</returns>
public static string TrimExt(string s) {
return s.Substring(0, s.LastIndexOf("."));
}
/// <summary>
/// Converts the value of a <see cref="TimeSpan" /> to a human-readable string.
/// </summary>
/// <param name="timeSpan">The time span.</param>
/// <param name="digits">The digit count for seconds.</param>
/// <returns>A human-readable string representing the time span.</returns>
public static string ToString(this TimeSpan timeSpan, int digits) {
var b = new StringBuilder();
bool flag = false;

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;