Init
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a3eaecc6a5f4f974092a4aae01bb5b48
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace QFSW.QC.Actions
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts an async Task into an action.
|
||||
/// </summary>
|
||||
public class Async : ICommandAction
|
||||
{
|
||||
private readonly Task _task;
|
||||
|
||||
public bool IsFinished => _task.IsCompleted ||
|
||||
_task.IsCanceled ||
|
||||
_task.IsFaulted;
|
||||
public bool StartsIdle => false;
|
||||
|
||||
/// <param name="task">The async Task to convert.</param>
|
||||
public Async(Task task)
|
||||
{
|
||||
_task = task;
|
||||
}
|
||||
|
||||
public void Start(ActionContext context) { }
|
||||
|
||||
public void Finalize(ActionContext context)
|
||||
{
|
||||
if (_task.IsFaulted)
|
||||
{
|
||||
throw _task.Exception.InnerException;
|
||||
}
|
||||
if (_task.IsCanceled)
|
||||
{
|
||||
throw new TaskCanceledException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an async Task into an action.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The return type of the Task to convert.</typeparam>
|
||||
public class Async<T> : ICommandAction
|
||||
{
|
||||
private readonly Task<T> _task;
|
||||
private readonly Action<T> _onResult;
|
||||
|
||||
public bool IsFinished => _task.IsCompleted ||
|
||||
_task.IsCanceled ||
|
||||
_task.IsFaulted;
|
||||
public bool StartsIdle => false;
|
||||
|
||||
/// <param name="task">The async Task to convert.</param>
|
||||
/// <param name="onResult">The action to invoke when the Task completes.</param>
|
||||
public Async(Task<T> task, Action<T> onResult)
|
||||
{
|
||||
_task = task;
|
||||
_onResult = onResult;
|
||||
}
|
||||
|
||||
public void Start(ActionContext context) { }
|
||||
|
||||
public void Finalize(ActionContext context)
|
||||
{
|
||||
if (_task.IsFaulted)
|
||||
{
|
||||
throw _task.Exception.InnerException;
|
||||
}
|
||||
if (_task.IsCanceled)
|
||||
{
|
||||
throw new TaskCanceledException();
|
||||
}
|
||||
|
||||
_onResult(_task.Result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aed3880bc1532c7418a020825bd88a2b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using QFSW.QC.Utilities;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QFSW.QC.Actions
|
||||
{
|
||||
/// <summary>
|
||||
/// Give the user a selection of choices which can be made by using the arrow keys and enter key.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the choices.</typeparam>
|
||||
public class Choice<T> : Composite
|
||||
{
|
||||
/// <summary>
|
||||
/// Configuration for the Choice action.
|
||||
/// </summary>
|
||||
public struct Config
|
||||
{
|
||||
public string ItemFormat;
|
||||
public string Delimiter;
|
||||
public Color SelectedColor;
|
||||
|
||||
public static readonly Config Default = new Config
|
||||
{
|
||||
ItemFormat = "{0} [{1}]",
|
||||
Delimiter = " ",
|
||||
SelectedColor = Color.green
|
||||
};
|
||||
}
|
||||
|
||||
/// <param name="choices">The choices to select between.</param>
|
||||
/// <param name="onSelect">Action to invoke when a selection is made.</param>
|
||||
public Choice(IEnumerable<T> choices, Action<T> onSelect)
|
||||
: this(choices, onSelect, Config.Default)
|
||||
{ }
|
||||
|
||||
/// <param name="choices">The choices to select between.</param>
|
||||
/// <param name="onSelect">Action to invoke when a selection is made.</param>
|
||||
/// <param name="config">The configuration to be used.</param>
|
||||
public Choice(IEnumerable<T> choices, Action<T> onSelect, Config config)
|
||||
: base(Generate(choices, onSelect, config))
|
||||
{ }
|
||||
|
||||
private static IEnumerator<ICommandAction> Generate(IEnumerable<T> choices, Action<T> onSelect, Config config)
|
||||
{
|
||||
QuantumConsole console = null;
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
IReadOnlyList<T> choiceList = choices as IReadOnlyList<T> ?? choices.ToList();
|
||||
KeyCode key = KeyCode.None;
|
||||
int choice = 0;
|
||||
|
||||
yield return new GetContext(ctx => console = ctx.Console);
|
||||
|
||||
ICommandAction DrawRow()
|
||||
{
|
||||
builder.Clear();
|
||||
for (int i = 0; i < choiceList.Count; i++)
|
||||
{
|
||||
string item = console.Serialize(choiceList[i]);
|
||||
builder.Append(i == choice
|
||||
? string.Format(config.ItemFormat, item, 'x').ColorText(config.SelectedColor)
|
||||
: string.Format(config.ItemFormat, item, ' '));
|
||||
|
||||
if (i != choiceList.Count - 1)
|
||||
{
|
||||
builder.Append(config.Delimiter);
|
||||
}
|
||||
}
|
||||
|
||||
return new Value(builder.ToString());
|
||||
}
|
||||
|
||||
yield return DrawRow();
|
||||
while (key != KeyCode.Return)
|
||||
{
|
||||
yield return new GetKey(k => key = k);
|
||||
|
||||
switch (key)
|
||||
{
|
||||
case KeyCode.LeftArrow: choice--; break;
|
||||
case KeyCode.RightArrow: choice++; break;
|
||||
case KeyCode.DownArrow: choice++; break;
|
||||
case KeyCode.UpArrow: choice--; break;
|
||||
}
|
||||
|
||||
choice = (choice + choiceList.Count) % choiceList.Count;
|
||||
yield return new RemoveLog();
|
||||
yield return DrawRow();
|
||||
}
|
||||
|
||||
onSelect(choiceList[choice]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 629e5579947e6da4688f17d834a20b56
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QFSW.QC.Actions
|
||||
{
|
||||
/// <summary>
|
||||
/// Combines a sequence of actions into a single action.
|
||||
/// </summary>
|
||||
public class Composite : ICommandAction
|
||||
{
|
||||
private ActionContext _context;
|
||||
private readonly IEnumerator<ICommandAction> _actions;
|
||||
|
||||
public bool IsFinished => _actions.Execute(_context) == ActionState.Complete;
|
||||
public bool StartsIdle => false;
|
||||
|
||||
/// <param name="actions">The sequence of actions to create the composite from.</param>
|
||||
public Composite(IEnumerator<ICommandAction> actions)
|
||||
{
|
||||
_actions = actions;
|
||||
}
|
||||
|
||||
/// <param name="actions">The sequence of actions to create the composite from.</param>
|
||||
public Composite(IEnumerable<ICommandAction> actions) : this(actions.GetEnumerator())
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Start(ActionContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public void Finalize(ActionContext context) { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3ecb8c263614be7489f996de15278bf5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3dba2d4b65cfdad428d18b1bd91c54e7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace QFSW.QC
|
||||
{
|
||||
/// <summary>
|
||||
/// The context that an action is being invoked on.
|
||||
/// </summary>
|
||||
public struct ActionContext
|
||||
{
|
||||
public QuantumConsole Console;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 16c86e1b478f19a4fa02d70bd8aafcde
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,53 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QFSW.QC
|
||||
{
|
||||
public static class ActionExecuter
|
||||
{
|
||||
/// <summary>
|
||||
/// Executes an action command until it becomes idle.
|
||||
/// </summary>
|
||||
/// <param name="action">The action command to execute.</param>
|
||||
/// <param name="context">The context that the command is being executed on.</param>
|
||||
/// <returns>The current state of the action command.</returns>
|
||||
public static ActionState Execute(this IEnumerator<ICommandAction> action, ActionContext context)
|
||||
{
|
||||
ActionState state = ActionState.Running;
|
||||
bool idle = false;
|
||||
|
||||
void MoveNext()
|
||||
{
|
||||
if (action.MoveNext())
|
||||
{
|
||||
action.Current?.Start(context);
|
||||
idle = action.Current?.StartsIdle ?? false;
|
||||
}
|
||||
else
|
||||
{
|
||||
idle = true;
|
||||
state = ActionState.Complete;
|
||||
action.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
while (!idle)
|
||||
{
|
||||
if (action.Current == null)
|
||||
{
|
||||
MoveNext();
|
||||
}
|
||||
else if (action.Current.IsFinished)
|
||||
{
|
||||
action.Current.Finalize(context);
|
||||
MoveNext();
|
||||
}
|
||||
else
|
||||
{
|
||||
idle = true;
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cc98ecd40616cc142a69f2c94957435f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace QFSW.QC
|
||||
{
|
||||
/// <summary>
|
||||
/// The execution state of an action.
|
||||
/// </summary>
|
||||
public enum ActionState
|
||||
{
|
||||
Unknown = 0,
|
||||
Running = 1,
|
||||
Complete = 2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c68b189efbe61cc41af4263794f5b1b4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,31 @@
|
||||
namespace QFSW.QC
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an action that can be yielded in commands.
|
||||
/// </summary>
|
||||
public interface ICommandAction
|
||||
{
|
||||
/// <summary>
|
||||
/// Starts the action.
|
||||
/// </summary>
|
||||
/// <param name="context">The context that the action is being executed on.</param>
|
||||
void Start(ActionContext context);
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes the action. Should not be called unless <c>IsFinished</c> is true.
|
||||
/// </summary>
|
||||
/// <param name="context">The context that the action is being executed on.</param>
|
||||
void Finalize(ActionContext context);
|
||||
|
||||
/// <summary>
|
||||
/// If the action has finished. Should not be called before <c>Start</c>.
|
||||
/// </summary>
|
||||
bool IsFinished { get; }
|
||||
|
||||
/// <summary>
|
||||
/// If the action should start off idle, causing the execution to suspend until executed again.
|
||||
/// It is recommended to make this <c>false</c> if the action should be instant.
|
||||
/// </summary>
|
||||
bool StartsIdle { get; }
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8ad6ee627978b454d9938922d0e5415c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
|
||||
namespace QFSW.QC.Actions
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom action implemented via delegates.
|
||||
/// For more complex actions it is usually recommended to create a new action implementing <c>ICommandAction</c>.
|
||||
/// </summary>
|
||||
public class Custom : ICommandAction
|
||||
{
|
||||
private readonly Func<bool> _isFinished;
|
||||
private readonly Func<bool> _startsIdle;
|
||||
private readonly Action<ActionContext> _start;
|
||||
private readonly Action<ActionContext> _finalize;
|
||||
|
||||
public Custom(
|
||||
Func<bool> isFinished,
|
||||
Func<bool> startsIdle,
|
||||
Action<ActionContext> start,
|
||||
Action<ActionContext> finalize
|
||||
)
|
||||
{
|
||||
_isFinished = isFinished;
|
||||
_startsIdle = startsIdle;
|
||||
_start = start;
|
||||
_finalize = finalize;
|
||||
}
|
||||
|
||||
public bool IsFinished => _isFinished();
|
||||
public bool StartsIdle => _startsIdle();
|
||||
|
||||
public void Start(ActionContext context) { _start(context); }
|
||||
public void Finalize(ActionContext context) { _finalize(context); }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b9bd021bf2e291447a56d4cede936d56
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
|
||||
namespace QFSW.QC.Actions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the <c>ActionContext</c> that the command is currently being invoked on.
|
||||
/// </summary>
|
||||
public class GetContext : ICommandAction
|
||||
{
|
||||
private readonly Action<ActionContext> _onContext;
|
||||
|
||||
public bool IsFinished => true;
|
||||
public bool StartsIdle => false;
|
||||
|
||||
/// <param name="onContext">Action to invoke when the context is retrieved.</param>
|
||||
public GetContext(Action<ActionContext> onContext)
|
||||
{
|
||||
_onContext = onContext;
|
||||
}
|
||||
|
||||
public void Start(ActionContext context) { }
|
||||
|
||||
public void Finalize(ActionContext context)
|
||||
{
|
||||
_onContext(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a2b0f8e4c27c89e4caac865a57f52f99
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QFSW.QC.Actions
|
||||
{
|
||||
/// <summary>
|
||||
/// Waits for any key to be pressed and returns the key via the given delegate.
|
||||
/// </summary>
|
||||
public class GetKey : ICommandAction
|
||||
{
|
||||
private KeyCode _key;
|
||||
private readonly Action<KeyCode> _onKey;
|
||||
private static readonly KeyCode[] KeyCodes = Enum.GetValues(typeof(KeyCode))
|
||||
.Cast<KeyCode>()
|
||||
.Where(k => (int)k < (int)KeyCode.Mouse0)
|
||||
.ToArray();
|
||||
|
||||
public bool IsFinished
|
||||
{
|
||||
get
|
||||
{
|
||||
_key = GetCurrentKeyDown();
|
||||
return _key != KeyCode.None;
|
||||
}
|
||||
}
|
||||
|
||||
public bool StartsIdle => true;
|
||||
|
||||
/// <param name="onKey">The action to perform when a key is pressed.</param>
|
||||
public GetKey(Action<KeyCode> onKey)
|
||||
{
|
||||
_onKey = onKey;
|
||||
}
|
||||
|
||||
private KeyCode GetCurrentKeyDown()
|
||||
{
|
||||
return KeyCodes.FirstOrDefault(InputHelper.GetKeyDown);
|
||||
}
|
||||
|
||||
public void Start(ActionContext context) { }
|
||||
|
||||
public void Finalize(ActionContext context)
|
||||
{
|
||||
_onKey(_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3a0a962a9335e7e408af971ed79d4e8e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
|
||||
namespace QFSW.QC.Actions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the next line of text entered into the console as a user
|
||||
/// response instead of invoking it as a command.
|
||||
/// </summary>
|
||||
public class ReadLine : ICommandAction
|
||||
{
|
||||
private readonly Action<string> _getInput;
|
||||
private readonly ResponseConfig _config;
|
||||
private QuantumConsole _console;
|
||||
private string _response;
|
||||
|
||||
public bool IsFinished => _response != null;
|
||||
|
||||
public bool StartsIdle => true;
|
||||
|
||||
/// <param name="getInput">A delegate which returns the input entered by the user.</param>
|
||||
/// <param name="config">The config to provide the response flow with.</param>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public ReadLine(Action<string> getInput, ResponseConfig config)
|
||||
{
|
||||
// validate
|
||||
if (getInput == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(getInput));
|
||||
}
|
||||
|
||||
// set fields
|
||||
_getInput = getInput;
|
||||
_config = config;
|
||||
_console = null;
|
||||
_response = null;
|
||||
}
|
||||
|
||||
/// <param name="getInput">A delegate which returns the input entered by the user.</param>
|
||||
/// <param name="config">The config to provide the response flow with.</param>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public ReadLine(Action<string> getInput) : this(getInput, ResponseConfig.Default)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Finalize(ActionContext context)
|
||||
{
|
||||
_getInput(_response); // push value to the caller
|
||||
}
|
||||
|
||||
public void Start(ActionContext context)
|
||||
{
|
||||
_response = null; // reset flag
|
||||
_console = context.Console;
|
||||
_console.BeginResponse(OnResponseSubmittedHandler, _config);
|
||||
}
|
||||
|
||||
private void OnResponseSubmittedHandler(string response)
|
||||
{
|
||||
_response = response; // changes IsFinished flag
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7cda4247941153d44bdace0c87133c6d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QFSW.QC.Actions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the next line of text entered into the console as a user response
|
||||
/// and parses it to a value of the specified type.
|
||||
/// </summary>
|
||||
public class ReadValue<T> : Composite
|
||||
{
|
||||
private static readonly QuantumParser Parser = new QuantumParser();
|
||||
|
||||
/// <param name="getValue">A delegate which returns the parsed value entered by the user.</param>
|
||||
/// <param name="config">The config to provide the response flow with.</param>
|
||||
public ReadValue(Action<T> getValue, ResponseConfig config)
|
||||
: base(Generate(getValue, config))
|
||||
{ }
|
||||
|
||||
/// <param name="getValue">A delegate which returns the parsed value entered by the user.</param>
|
||||
public ReadValue(Action<T> getValue)
|
||||
: this(getValue, ResponseConfig.Default)
|
||||
{ }
|
||||
|
||||
private static IEnumerator<ICommandAction> Generate(Action<T> getValue, ResponseConfig config)
|
||||
{
|
||||
string line = default;
|
||||
yield return new ReadLine(t => line = t, config);
|
||||
|
||||
T value = Parser.Parse<T>(line);
|
||||
getValue(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 13f674045cd08b947b6cc784b98e45f3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace QFSW.QC.Actions
|
||||
{
|
||||
/// <summary>
|
||||
/// Removes the most recent log from the console.
|
||||
/// </summary>
|
||||
public class RemoveLog : ICommandAction
|
||||
{
|
||||
public bool IsFinished => true;
|
||||
public bool StartsIdle => false;
|
||||
|
||||
public void Start(ActionContext context) { }
|
||||
|
||||
public void Finalize(ActionContext context)
|
||||
{
|
||||
context.Console.RemoveLogTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a4e97d9251aaa994aa83824d9bccfd59
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace QFSW.QC.Actions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gradually types a message to the console.
|
||||
/// </summary>
|
||||
public class Typewriter : Composite
|
||||
{
|
||||
/// <summary>
|
||||
/// Configuration for the Typewriter action.
|
||||
/// </summary>
|
||||
public struct Config
|
||||
{
|
||||
public enum ChunkType
|
||||
{
|
||||
Character,
|
||||
Word,
|
||||
Line
|
||||
}
|
||||
|
||||
public float PrintInterval;
|
||||
public ChunkType Chunks;
|
||||
|
||||
public static readonly Config Default = new Config
|
||||
{
|
||||
PrintInterval = 0f,
|
||||
Chunks = ChunkType.Character
|
||||
};
|
||||
}
|
||||
|
||||
private static readonly Regex WhiteRegex = new Regex(@"(?<=[\s+])", RegexOptions.Compiled);
|
||||
private static readonly Regex LineRegex = new Regex(@"(?<=[\n+])", RegexOptions.Compiled);
|
||||
|
||||
/// <param name="message">The message to display to the console.</param>
|
||||
public Typewriter(string message)
|
||||
: this(message, Config.Default)
|
||||
{ }
|
||||
|
||||
/// <param name="message">The message to display to the console.</param>
|
||||
/// <param name="config">The configuration to be used.</param>
|
||||
public Typewriter(string message, Config config)
|
||||
: base(Generate(message, config))
|
||||
{ }
|
||||
|
||||
private static IEnumerator<ICommandAction> Generate(string message, Config config)
|
||||
{
|
||||
string[] chunks;
|
||||
switch (config.Chunks)
|
||||
{
|
||||
case Config.ChunkType.Character: chunks = message.Select(c => c.ToString()).ToArray(); break;
|
||||
case Config.ChunkType.Word: chunks = WhiteRegex.Split(message); break;
|
||||
case Config.ChunkType.Line: chunks = LineRegex.Split(message); break;
|
||||
default: throw new ArgumentException($"Chunk type {config.Chunks} is not supported.");
|
||||
}
|
||||
|
||||
for (int i = 0; i < chunks.Length; i++)
|
||||
{
|
||||
yield return new WaitRealtime(config.PrintInterval);
|
||||
yield return new Value(chunks[i], i == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ea886313beb68ed45b9b597a71c3514a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,31 @@
|
||||
namespace QFSW.QC.Actions
|
||||
{
|
||||
/// <summary>
|
||||
/// Serializes and logs a value to the console.
|
||||
/// </summary>
|
||||
public class Value : ICommandAction
|
||||
{
|
||||
private readonly object _value;
|
||||
private readonly bool _newline;
|
||||
|
||||
public bool IsFinished => true;
|
||||
public bool StartsIdle => false;
|
||||
|
||||
/// <param name="value">The value to log to the console.</param>
|
||||
/// <param name="newline">If the value should be logged on a new line.</param>
|
||||
public Value(object value, bool newline = true)
|
||||
{
|
||||
_value = value;
|
||||
_newline = newline;
|
||||
}
|
||||
|
||||
public void Start(ActionContext context) { }
|
||||
|
||||
public void Finalize(ActionContext context)
|
||||
{
|
||||
QuantumConsole console = context.Console;
|
||||
string serialized = _value as string ?? console.Serialize(_value);
|
||||
console.LogToConsole(serialized, _newline);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aa35d5d3d1cdc54429fbcd0a8754323f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,30 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace QFSW.QC.Actions
|
||||
{
|
||||
/// <summary>
|
||||
/// Waits for the given amount of seconds using scaled time.
|
||||
/// </summary>
|
||||
public class Wait : ICommandAction
|
||||
{
|
||||
private float _startTime;
|
||||
private readonly float _duration;
|
||||
|
||||
public bool IsFinished => Time.time >= _startTime + _duration;
|
||||
public bool StartsIdle => true;
|
||||
|
||||
/// <param name="seconds">The duration to wait in seconds.</param>
|
||||
public Wait(float seconds)
|
||||
{
|
||||
_duration = seconds;
|
||||
}
|
||||
|
||||
public void Start(ActionContext ctx)
|
||||
{
|
||||
_startTime = Time.time;
|
||||
}
|
||||
|
||||
public void Finalize(ActionContext ctx) { }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: da3e71593087b0145b4c7416865c73ff
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace QFSW.QC.Actions
|
||||
{
|
||||
/// <summary>
|
||||
/// Waits until the next frame.
|
||||
/// </summary>
|
||||
public class WaitFrame : WaitRealtime
|
||||
{
|
||||
public WaitFrame() : base(0)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ec7bf5a05072b33469812750790bff62
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,16 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace QFSW.QC.Actions
|
||||
{
|
||||
/// <summary>
|
||||
/// Waits until the given key is pressed.
|
||||
/// </summary>
|
||||
public class WaitKey : WaitUntil
|
||||
{
|
||||
/// <param name="key">The key to wait for.</param>
|
||||
public WaitKey(KeyCode key) : base(() => InputHelper.GetKeyDown(key))
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 67f025b392a312e45bb0ed80959d5cd1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,30 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace QFSW.QC.Actions
|
||||
{
|
||||
/// <summary>
|
||||
/// Waits for the given amount of seconds using real time.
|
||||
/// </summary>
|
||||
public class WaitRealtime : ICommandAction
|
||||
{
|
||||
private float _startTime;
|
||||
private readonly float _duration;
|
||||
|
||||
public bool IsFinished => Time.realtimeSinceStartup >= _startTime + _duration;
|
||||
public bool StartsIdle => true;
|
||||
|
||||
/// <param name="seconds">The duration to wait for in seconds.</param>
|
||||
public WaitRealtime(float seconds)
|
||||
{
|
||||
_duration = seconds;
|
||||
}
|
||||
|
||||
public void Start(ActionContext ctx)
|
||||
{
|
||||
_startTime = Time.realtimeSinceStartup;
|
||||
}
|
||||
|
||||
public void Finalize(ActionContext ctx) { }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 32cadea9087bc5d4682ac8455fde1fd4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace QFSW.QC.Actions
|
||||
{
|
||||
/// <summary>
|
||||
/// Waits until the given condition is met.
|
||||
/// </summary>
|
||||
public class WaitUntil : WaitWhile
|
||||
{
|
||||
/// <param name="condition">The condition to wait on.</param>
|
||||
public WaitUntil(Func<bool> condition) : base(() => !condition())
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 10b1ff66cd0d51d4a9363f7ad3477830
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace QFSW.QC.Actions
|
||||
{
|
||||
/// <summary>
|
||||
/// Waits while the given condition is met.
|
||||
/// </summary>
|
||||
public class WaitWhile : ICommandAction
|
||||
{
|
||||
private readonly Func<bool> _condition;
|
||||
|
||||
public bool IsFinished => !_condition();
|
||||
public bool StartsIdle => true;
|
||||
|
||||
/// <param name="condition">The condition to wait on.</param>
|
||||
public WaitWhile(Func<bool> condition)
|
||||
{
|
||||
_condition = condition;
|
||||
}
|
||||
|
||||
|
||||
public void Start(ActionContext context) { }
|
||||
public void Finalize(ActionContext context) { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8050f33217e30684a9a0ea5dfefdfd45
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8f98c28cf2a7c934db3f256bd3921741
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QFSW.QC
|
||||
{
|
||||
/// <summary>
|
||||
/// Marks the associated method as a command, allowing it to be loaded by the QuantumConsoleProcessor. This means it will be usable as a command from a Quantum Console.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true, Inherited = false)]
|
||||
public sealed class CommandAttribute : Attribute
|
||||
{
|
||||
public readonly string Alias;
|
||||
public readonly string Description;
|
||||
public readonly Platform SupportedPlatforms;
|
||||
public readonly MonoTargetType MonoTarget;
|
||||
public readonly bool Valid = true;
|
||||
|
||||
private static readonly char[] _bannedAliasChars = new char[] { ' ', '(', ')', '{', '}', '[', ']', '<', '>' };
|
||||
|
||||
public CommandAttribute([CallerMemberName] string aliasOverride = "", Platform supportedPlatforms = Platform.AllPlatforms, MonoTargetType targetType = MonoTargetType.Single)
|
||||
{
|
||||
Alias = aliasOverride;
|
||||
MonoTarget = targetType;
|
||||
SupportedPlatforms = supportedPlatforms;
|
||||
|
||||
for (int i = 0; i < _bannedAliasChars.Length; i++)
|
||||
{
|
||||
if (Alias.Contains(_bannedAliasChars[i]))
|
||||
{
|
||||
string errorMessage = $"Development Processor Error: Command with alias '{Alias}' contains the char '{_bannedAliasChars[i]}' which is banned. Unexpected behaviour may occur.";
|
||||
Debug.LogError(errorMessage);
|
||||
Valid = false;
|
||||
throw new ArgumentException(errorMessage, nameof(aliasOverride));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public CommandAttribute(string aliasOverride, MonoTargetType targetType, Platform supportedPlatforms = Platform.AllPlatforms) : this(aliasOverride, supportedPlatforms, targetType) { }
|
||||
|
||||
public CommandAttribute(string aliasOverride, string description, Platform supportedPlatforms = Platform.AllPlatforms, MonoTargetType targetType = MonoTargetType.Single) : this(aliasOverride, supportedPlatforms, targetType)
|
||||
{
|
||||
Description = description;
|
||||
}
|
||||
|
||||
public CommandAttribute(string aliasOverride, string description, MonoTargetType targetType, Platform supportedPlatforms = Platform.AllPlatforms) : this(aliasOverride, description, supportedPlatforms, targetType) { }
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8115824be443344f29ef0da5a1c05a3b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
|
||||
namespace QFSW.QC
|
||||
{
|
||||
/// <summary>Provides a command with a description. If the [Command] attribute already provides a description, that will supersede this one. Useful for when you have several [Command]s on a single method.</summary>
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
|
||||
public sealed class CommandDescriptionAttribute : Attribute
|
||||
{
|
||||
public readonly string Description;
|
||||
public readonly bool Valid;
|
||||
|
||||
public CommandDescriptionAttribute(string description)
|
||||
{
|
||||
Description = description;
|
||||
Valid = !string.IsNullOrWhiteSpace(description);
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e52ff948b143dc54684640418a70f825
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
|
||||
namespace QFSW.QC
|
||||
{
|
||||
/// <summary>Provides a command paremeter with a description.</summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
|
||||
public sealed class CommandParameterDescriptionAttribute : Attribute
|
||||
{
|
||||
public readonly string Description;
|
||||
public readonly bool Valid;
|
||||
|
||||
public CommandParameterDescriptionAttribute(string description)
|
||||
{
|
||||
Description = description;
|
||||
Valid = !string.IsNullOrWhiteSpace(description);
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6c171fb328ed92146bd061b07d446a1a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace QFSW.QC
|
||||
{
|
||||
/// <summary>Determines which platforms the command is available on. Supersedes platform availability determined in the [Command].</summary>
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
|
||||
public sealed class CommandPlatformAttribute : Attribute
|
||||
{
|
||||
public readonly Platform SupportedPlatforms;
|
||||
|
||||
public CommandPlatformAttribute(Platform supportedPlatforms)
|
||||
{
|
||||
SupportedPlatforms = supportedPlatforms;
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6d0cd481e1faecd4a8159c22114d7d9a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QFSW.QC
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a prefix that will be prepended to all commands made within this class. Works recursively with sub-classes.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Assembly, AllowMultiple = true, Inherited = false)]
|
||||
public sealed class CommandPrefixAttribute : Attribute
|
||||
{
|
||||
public readonly string Prefix;
|
||||
public readonly bool Valid = true;
|
||||
|
||||
private static readonly char[] _bannedAliasChars = { ' ', '(', ')', '{', '}', '[', ']', '<', '>' };
|
||||
|
||||
public CommandPrefixAttribute([CallerMemberName] string prefixName = "")
|
||||
{
|
||||
Prefix = prefixName;
|
||||
foreach (var c in _bannedAliasChars)
|
||||
{
|
||||
if (Prefix.Contains(c))
|
||||
{
|
||||
string errorMessage = $"Development Processor Error: Command prefix '{Prefix}' contains the char '{c}' which is banned. Unexpected behaviour may occurr.";
|
||||
Debug.LogError(errorMessage);
|
||||
|
||||
Valid = false;
|
||||
throw new ArgumentException(errorMessage, nameof(prefixName));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e8b3bb27784b8544785af220e6f8b39a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace QFSW.QC
|
||||
{
|
||||
/// <summary>
|
||||
/// Instructs QC to ignore this entity when scanning the code base for commands.
|
||||
/// This can be used to optimise QCs loading times in large codebases when there are large entities that do not have any commands present.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
|
||||
public sealed class QcIgnoreAttribute : Attribute { }
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f9509f39d8a98d44784d6efc252ed5d0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace QFSW.QC
|
||||
{
|
||||
public enum AutoScrollOptions
|
||||
{
|
||||
Never = 0,
|
||||
OnInvoke = 1,
|
||||
Always = 2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1fd10c1eb5013b648963ed0e1cdbd869
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,309 @@
|
||||
using QFSW.QC.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using QFSW.QC.Internal;
|
||||
|
||||
namespace QFSW.QC
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains the full data about a command and provides an execution point for invoking the command.
|
||||
/// </summary>
|
||||
public class CommandData
|
||||
{
|
||||
public readonly string CommandName;
|
||||
public readonly string CommandDescription;
|
||||
public readonly string CommandSignature;
|
||||
public readonly string ParameterSignature;
|
||||
public readonly string GenericSignature;
|
||||
|
||||
public readonly ParameterInfo[] MethodParamData;
|
||||
public readonly Type[] ParamTypes;
|
||||
public readonly Type[] GenericParamTypes;
|
||||
public readonly MethodInfo MethodData;
|
||||
public readonly MonoTargetType MonoTarget;
|
||||
|
||||
private readonly object[] _defaultParameters;
|
||||
|
||||
public bool IsGeneric => GenericParamTypes.Length > 0;
|
||||
public bool IsStatic => MethodData.IsStatic;
|
||||
public bool HasDescription => !string.IsNullOrWhiteSpace(CommandDescription);
|
||||
public int ParamCount => ParamTypes.Length - _defaultParameters.Length;
|
||||
|
||||
public Type[] MakeGenericArguments(params Type[] genericTypeArguments)
|
||||
{
|
||||
if (genericTypeArguments.Length != GenericParamTypes.Length)
|
||||
{
|
||||
throw new ArgumentException("Incorrect number of generic substitution types were supplied.");
|
||||
}
|
||||
|
||||
Dictionary<string, Type> substitutionTable = new Dictionary<string, Type>();
|
||||
for (int i = 0; i < genericTypeArguments.Length; i++)
|
||||
{
|
||||
substitutionTable.Add(GenericParamTypes[i].Name, genericTypeArguments[i]);
|
||||
}
|
||||
|
||||
Type[] types = new Type[ParamTypes.Length];
|
||||
for (int i = 0; i < types.Length; i++)
|
||||
{
|
||||
if (ParamTypes[i].ContainsGenericParameters)
|
||||
{
|
||||
Type substitution = ConstructGenericType(ParamTypes[i], substitutionTable);
|
||||
types[i] = substitution;
|
||||
}
|
||||
else
|
||||
{
|
||||
types[i] = ParamTypes[i];
|
||||
}
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
private Type ConstructGenericType(Type genericType, Dictionary<string, Type> substitutionTable)
|
||||
{
|
||||
if (!genericType.ContainsGenericParameters) { return genericType; }
|
||||
if (substitutionTable.ContainsKey(genericType.Name)) { return substitutionTable[genericType.Name]; }
|
||||
if (genericType.IsArray) { return ConstructGenericType(genericType.GetElementType(), substitutionTable).MakeArrayType(); }
|
||||
if (genericType.IsGenericType)
|
||||
{
|
||||
Type baseType = genericType.GetGenericTypeDefinition();
|
||||
Type[] typeArguments = genericType.GetGenericArguments();
|
||||
for (int i = 0; i < typeArguments.Length; i++)
|
||||
{
|
||||
typeArguments[i] = ConstructGenericType(typeArguments[i], substitutionTable);
|
||||
}
|
||||
|
||||
return baseType.MakeGenericType(typeArguments);
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Could not construct the generic type {genericType}");
|
||||
}
|
||||
|
||||
public object Invoke(object[] paramData, Type[] genericTypeArguments)
|
||||
{
|
||||
// For MonoTargetType.Argument, need to use the first argument as the invocation target
|
||||
// and then forward the rest as normal
|
||||
int paramDataStart = 0;
|
||||
int paramDataLength = paramData.Length;
|
||||
if (MonoTarget == MonoTargetType.Argument || MonoTarget == MonoTargetType.ArgumentMulti)
|
||||
{
|
||||
paramDataStart++;
|
||||
paramDataLength--;
|
||||
}
|
||||
|
||||
int numArguments = paramDataLength + _defaultParameters.Length;
|
||||
object[] arguments = new object[numArguments];
|
||||
|
||||
// Copy supplied argument data and default arguments to create final argument set to forward to method
|
||||
Array.Copy(paramData, paramDataStart, arguments, 0, paramDataLength);
|
||||
Array.Copy(_defaultParameters, 0, arguments, paramDataLength, _defaultParameters.Length);
|
||||
|
||||
MethodInfo invokingMethod = GetInvokingMethod(genericTypeArguments);
|
||||
|
||||
if (IsStatic)
|
||||
{
|
||||
return invokingMethod.Invoke(null, arguments);
|
||||
}
|
||||
|
||||
// For MonoTargetType.Argument, use the first argument as the target
|
||||
// Otherwise, get invocation targets like normal
|
||||
IEnumerable<object> targets = MonoTarget switch
|
||||
{
|
||||
MonoTargetType.Argument => paramData[0].Yield(),
|
||||
MonoTargetType.ArgumentMulti => paramData[0] as IEnumerable<object>,
|
||||
_ => GetInvocationTargets(invokingMethod)
|
||||
};
|
||||
|
||||
return InvocationTargetFactory.InvokeOnTargets(invokingMethod, targets, arguments);
|
||||
}
|
||||
|
||||
protected virtual IEnumerable<object> GetInvocationTargets(MethodInfo invokingMethod)
|
||||
{
|
||||
return InvocationTargetFactory.FindTargets(invokingMethod.DeclaringType, MonoTarget);
|
||||
}
|
||||
|
||||
private MethodInfo GetInvokingMethod(Type[] genericTypeArguments)
|
||||
{
|
||||
if (!IsGeneric)
|
||||
{
|
||||
return MethodData;
|
||||
}
|
||||
|
||||
T WrapConstruction<T>(Func<T> f)
|
||||
{
|
||||
try
|
||||
{
|
||||
return f();
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
throw new ArgumentException($"Supplied generic parameters did not satisfy the generic constraints imposed by '{CommandName}'");
|
||||
}
|
||||
}
|
||||
|
||||
Type declaringType = MethodData.DeclaringType;
|
||||
MethodInfo method = MethodData;
|
||||
|
||||
if (declaringType.IsGenericTypeDefinition)
|
||||
{
|
||||
int typeCount = declaringType.GetGenericArguments().Length;
|
||||
|
||||
Type[] genericTypes = genericTypeArguments
|
||||
.Take(typeCount)
|
||||
.ToArray();
|
||||
|
||||
genericTypeArguments = genericTypeArguments
|
||||
.Skip(typeCount)
|
||||
.ToArray();
|
||||
|
||||
declaringType = WrapConstruction(() => declaringType.MakeGenericType(genericTypes));
|
||||
method = method.RebaseMethod(declaringType);
|
||||
}
|
||||
|
||||
return genericTypeArguments.Length == 0
|
||||
? method
|
||||
: WrapConstruction(() => method.MakeGenericMethod(genericTypeArguments));
|
||||
}
|
||||
|
||||
private string BuildPrefix(Type declaringType)
|
||||
{
|
||||
List<string> prefixes = new List<string>();
|
||||
Assembly assembly = declaringType.Assembly;
|
||||
|
||||
void AddPrefixes(IEnumerable<CommandPrefixAttribute> prefixAttributes, string defaultName)
|
||||
{
|
||||
foreach (CommandPrefixAttribute prefixAttribute in prefixAttributes.Reverse())
|
||||
{
|
||||
if (prefixAttribute.Valid)
|
||||
{
|
||||
string prefix = prefixAttribute.Prefix;
|
||||
if (string.IsNullOrWhiteSpace(prefix)) { prefix = defaultName; }
|
||||
|
||||
prefixes.Add(prefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (declaringType != null)
|
||||
{
|
||||
IEnumerable<CommandPrefixAttribute> typePrefixes = declaringType.GetCustomAttributes<CommandPrefixAttribute>();
|
||||
AddPrefixes(typePrefixes, declaringType.Name);
|
||||
|
||||
declaringType = declaringType.DeclaringType;
|
||||
}
|
||||
|
||||
IEnumerable<CommandPrefixAttribute> assemblyPrefixes = assembly.GetCustomAttributes<CommandPrefixAttribute>();
|
||||
AddPrefixes(assemblyPrefixes, assembly.GetName().Name);
|
||||
|
||||
return string.Join("", prefixes.Reversed());
|
||||
}
|
||||
|
||||
private string BuildGenericSignature(Type[] genericParamTypes)
|
||||
{
|
||||
if (genericParamTypes.Length == 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
IEnumerable<string> names = genericParamTypes.Select(x => x.Name);
|
||||
return $"<{string.Join(", ", names)}>";
|
||||
}
|
||||
|
||||
private string BuildParameterSignature(ParameterInfo[] methodParams, int defaultParameterCount)
|
||||
{
|
||||
string signature = string.Empty;
|
||||
for (int i = 0; i < methodParams.Length - defaultParameterCount; i++)
|
||||
{
|
||||
signature += $"{(i == 0 ? string.Empty : " ")}{methodParams[i].Name}";
|
||||
}
|
||||
|
||||
return signature;
|
||||
}
|
||||
|
||||
private Type[] BuildGenericParamTypes(MethodInfo method, Type declaringType)
|
||||
{
|
||||
List<Type> types = new List<Type>();
|
||||
|
||||
if (declaringType.IsGenericTypeDefinition)
|
||||
{
|
||||
types.AddRange(declaringType.GetGenericArguments());
|
||||
}
|
||||
|
||||
if (method.IsGenericMethodDefinition)
|
||||
{
|
||||
types.AddRange(method.GetGenericArguments());
|
||||
}
|
||||
|
||||
return types.ToArray();
|
||||
}
|
||||
|
||||
public CommandData(MethodInfo methodData, string commandName, MonoTargetType monoTarget, int defaultParameterCount = 0)
|
||||
{
|
||||
CommandName = commandName;
|
||||
MethodData = methodData;
|
||||
MonoTarget = monoTarget;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(commandName))
|
||||
{
|
||||
CommandName = methodData.Name;
|
||||
}
|
||||
|
||||
Type declaringType = methodData.DeclaringType;
|
||||
|
||||
string prefix = BuildPrefix(declaringType);
|
||||
CommandName = $"{prefix}{CommandName}";
|
||||
|
||||
// Add a dummy parameter used for parsing the invoking target if required
|
||||
List<ParameterInfo> parameters = methodData.GetParameters().ToList();
|
||||
if (MonoTarget == MonoTargetType.Argument)
|
||||
{
|
||||
parameters.Insert(0, new DummyParameter(methodData.DeclaringType, "target", 0));
|
||||
}
|
||||
else if (MonoTarget == MonoTargetType.ArgumentMulti)
|
||||
{
|
||||
parameters.Insert(0, new DummyParameter(methodData.DeclaringType.MakeArrayType(), "targets", 0));
|
||||
}
|
||||
|
||||
MethodParamData = parameters.ToArray();
|
||||
ParamTypes = MethodParamData
|
||||
.Select(x => x.ParameterType)
|
||||
.ToArray();
|
||||
|
||||
_defaultParameters = new object[defaultParameterCount];
|
||||
for (int i = 0; i < defaultParameterCount; i++)
|
||||
{
|
||||
int j = MethodParamData.Length - defaultParameterCount + i;
|
||||
_defaultParameters[i] = MethodParamData[j].DefaultValue;
|
||||
}
|
||||
|
||||
GenericParamTypes = BuildGenericParamTypes(methodData, declaringType);
|
||||
|
||||
ParameterSignature = BuildParameterSignature(MethodParamData, defaultParameterCount);
|
||||
GenericSignature = BuildGenericSignature(GenericParamTypes);
|
||||
CommandSignature = ParamCount > 0
|
||||
? $"{CommandName}{GenericSignature} {ParameterSignature}"
|
||||
: $"{CommandName}{GenericSignature}";
|
||||
}
|
||||
|
||||
public CommandData(MethodInfo methodData, MonoTargetType monoTarget, int defaultParameterCount = 0)
|
||||
: this(methodData, methodData.Name, monoTarget, defaultParameterCount)
|
||||
{ }
|
||||
|
||||
public CommandData(MethodInfo methodData, CommandAttribute commandAttribute, int defaultParameterCount = 0)
|
||||
: this(methodData, commandAttribute.Alias, commandAttribute.MonoTarget, defaultParameterCount)
|
||||
{
|
||||
CommandDescription = commandAttribute.Description;
|
||||
}
|
||||
|
||||
public CommandData(MethodInfo methodData, CommandAttribute commandAttribute, CommandDescriptionAttribute descriptionAttribute, int defaultParameterCount = 0)
|
||||
: this(methodData, commandAttribute, defaultParameterCount)
|
||||
{
|
||||
if ((descriptionAttribute?.Valid ?? false) && string.IsNullOrWhiteSpace(commandAttribute.Description))
|
||||
{
|
||||
CommandDescription = descriptionAttribute.Description;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5e34b97732540ee4d9b37cd16f6b78dc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 48497173fbbd4884682d42192fdb2df6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,149 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QFSW.QC.Comparators
|
||||
{
|
||||
public class AlphanumComparator : IComparer<string>
|
||||
{
|
||||
private const int MaxStackSize = 512;
|
||||
|
||||
public unsafe int Compare(string x, string y)
|
||||
{
|
||||
if (x == null) { return 0; }
|
||||
if (y == null) { return 0; }
|
||||
|
||||
int len1 = x.Length;
|
||||
int len2 = y.Length;
|
||||
|
||||
if (len1 + len2 + 2 <= MaxStackSize)
|
||||
{
|
||||
char* buffer1 = stackalloc char[len1 + 1];
|
||||
char* buffer2 = stackalloc char[len2 + 1];
|
||||
|
||||
return Compare(x, buffer1, len1, y, buffer2, len2);
|
||||
}
|
||||
else
|
||||
{
|
||||
char[] buffer1 = new char[len1 + 1];
|
||||
char[] buffer2 = new char[len2 + 1];
|
||||
|
||||
fixed (char* ptr1 = buffer1)
|
||||
fixed (char* ptr2 = buffer2)
|
||||
{
|
||||
return Compare(x, ptr1, len1, y, ptr2, len2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe int Compare(string x, char* buffer1, int len1, string y, char* buffer2, int len2)
|
||||
{
|
||||
int marker1 = 0;
|
||||
int marker2 = 0;
|
||||
|
||||
while (marker1 < len1 && marker2 < len2)
|
||||
{
|
||||
char ch1 = x[marker1];
|
||||
char ch2 = y[marker2];
|
||||
|
||||
int loc1 = 0;
|
||||
int loc2 = 0;
|
||||
|
||||
do
|
||||
{
|
||||
buffer1[loc1++] = ch1;
|
||||
marker1++;
|
||||
|
||||
if (marker1 < len1)
|
||||
{
|
||||
ch1 = x[marker1];
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
} while (char.IsDigit(ch1) == char.IsDigit(buffer1[0]));
|
||||
|
||||
do
|
||||
{
|
||||
buffer2[loc2++] = ch2;
|
||||
marker2++;
|
||||
|
||||
if (marker2 < len2)
|
||||
{
|
||||
ch2 = y[marker2];
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
} while (char.IsDigit(ch2) == char.IsDigit(buffer2[0]));
|
||||
|
||||
//null terminate buffers
|
||||
buffer1[loc1] = buffer2[loc2] = (char)0;
|
||||
|
||||
int result;
|
||||
if (char.IsDigit(buffer1[0]) && char.IsDigit(buffer2[0]))
|
||||
{
|
||||
int chunk1 = ParseInt(buffer1);
|
||||
int chunk2 = ParseInt(buffer2);
|
||||
result = chunk1 - chunk2;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = CompareStrings(buffer1, buffer2);
|
||||
}
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return len1 - len2;
|
||||
}
|
||||
|
||||
private unsafe int ParseInt(char* buffer)
|
||||
{
|
||||
int acc = 0;
|
||||
|
||||
while (*buffer != 0)
|
||||
{
|
||||
acc *= 10;
|
||||
acc += *buffer++ - '0';
|
||||
}
|
||||
|
||||
return acc;
|
||||
}
|
||||
|
||||
private unsafe int CompareStrings(char* buffer1, char* buffer2)
|
||||
{
|
||||
int index = 0;
|
||||
while (buffer1[index] != 0 && buffer2[index] != 0)
|
||||
{
|
||||
char c1 = buffer1[index];
|
||||
char c2 = buffer2[index++];
|
||||
|
||||
if (c1 > c2)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if (c1 < c2)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer1[index] != 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if (buffer2[index] != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f5a6b857ac27fa44b0213d2a5f85538
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d2efd1c3b1ea14940bbea68283e5ae22
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,37 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QFSW.QC.Containers
|
||||
{
|
||||
public struct ArraySingle<T> : IReadOnlyList<T>
|
||||
{
|
||||
private readonly T _data;
|
||||
|
||||
public ArraySingle(T data)
|
||||
{
|
||||
_data = data;
|
||||
}
|
||||
|
||||
public T this[int index] => _data;
|
||||
|
||||
public int Count => 1;
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
yield return _data;
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
yield return _data;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ArraySingleExtensions
|
||||
{
|
||||
public static ArraySingle<T> AsArraySingle<T>(this T data)
|
||||
{
|
||||
return new ArraySingle<T>(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e0a7bee164207c044847125f649d72e5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,53 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QFSW.QC.Containers
|
||||
{
|
||||
public struct StringContainer : IReadOnlyList<char>
|
||||
{
|
||||
private readonly string _str;
|
||||
|
||||
public StringContainer(string str)
|
||||
{
|
||||
_str = str;
|
||||
}
|
||||
|
||||
public char this[int index] => _str[index];
|
||||
|
||||
public int Count => _str.Length;
|
||||
|
||||
public IEnumerator<char> GetEnumerator()
|
||||
{
|
||||
for (int i = 0; i < _str.Length; i++)
|
||||
{
|
||||
yield return _str[i];
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
for (int i = 0; i < _str.Length; i++)
|
||||
{
|
||||
yield return _str[i];
|
||||
}
|
||||
}
|
||||
|
||||
public static implicit operator StringContainer(string str)
|
||||
{
|
||||
return new StringContainer(str);
|
||||
}
|
||||
|
||||
public static implicit operator string(StringContainer str)
|
||||
{
|
||||
return str._str;
|
||||
}
|
||||
}
|
||||
|
||||
public static class StringContainerExtensions
|
||||
{
|
||||
public static StringContainer AsIReadOnlyList(this string str)
|
||||
{
|
||||
return str;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b6fe5a46a82b32946a28567aefce78c0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6189a8da57915c848abf41c6ae6fc7f4
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QFSW.QC.Editor
|
||||
{
|
||||
public class DataEntryPopup : PopupWindowContent
|
||||
{
|
||||
private readonly string _title;
|
||||
private readonly string _btnName;
|
||||
|
||||
private string _data;
|
||||
private string _errors;
|
||||
private bool _success;
|
||||
|
||||
private GUIStyle _errorStyle;
|
||||
private GUIStyle _successStyle;
|
||||
|
||||
private readonly Action<string> _submitCallback;
|
||||
|
||||
public DataEntryPopup(string title, string btnName, Action<string> SubmitCallback)
|
||||
{
|
||||
CreateStyles();
|
||||
_title = title;
|
||||
_btnName = btnName;
|
||||
_submitCallback = SubmitCallback;
|
||||
}
|
||||
|
||||
public override Vector2 GetWindowSize() { return new Vector2(500, 100); }
|
||||
|
||||
private void CreateStyles()
|
||||
{
|
||||
_errorStyle = new GUIStyle(EditorStyles.wordWrappedLabel);
|
||||
_errorStyle.normal.textColor = new Color(1, 0, 0);
|
||||
|
||||
_successStyle = new GUIStyle(EditorStyles.wordWrappedLabel);
|
||||
_successStyle.normal.textColor = new Color(0, 0.5f, 0);
|
||||
}
|
||||
|
||||
public override void OnGUI(Rect rect)
|
||||
{
|
||||
_data = EditorGUILayout.TextField(_title, _data);
|
||||
GUI.enabled = !string.IsNullOrWhiteSpace(_data);
|
||||
if (GUILayout.Button(_btnName))
|
||||
{
|
||||
try
|
||||
{
|
||||
_submitCallback(_data);
|
||||
_success = true;
|
||||
_errors = "";
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_errors = e.Message;
|
||||
_success = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(_errors)) { EditorGUILayout.LabelField(_errors, _errorStyle); }
|
||||
else if (_success) { EditorGUILayout.LabelField("Success!", _successStyle); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5eaadd4a6e56548a0bac180051d85654
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,95 @@
|
||||
using QFSW.QC.QGUI;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QFSW.QC.Editor
|
||||
{
|
||||
public static class EditorHelpers
|
||||
{
|
||||
private struct SupportItem
|
||||
{
|
||||
public string Name;
|
||||
public string Tooltip;
|
||||
public string Url;
|
||||
}
|
||||
|
||||
private static readonly SupportItem[] _supportItems =
|
||||
{
|
||||
new SupportItem
|
||||
{
|
||||
Name = "Docs",
|
||||
Tooltip = "Official and up to date documentation for Quantum Console.",
|
||||
Url = "https://qfsw.co.uk/docs/QC/"
|
||||
},
|
||||
new SupportItem
|
||||
{
|
||||
Name = "Email",
|
||||
Tooltip = "Email address for support and other inquiries.",
|
||||
Url = "mailto:support@qfsw.co.uk"
|
||||
},
|
||||
new SupportItem
|
||||
{
|
||||
Name = "Discord",
|
||||
Tooltip = "Discord server for customer support, WIPs and more.",
|
||||
Url = "https://discord.gg/g8SJ7X6"
|
||||
},
|
||||
new SupportItem
|
||||
{
|
||||
Name = "Twitter",
|
||||
Tooltip = "Get in touch or show off what you've made with QC.",
|
||||
Url = "https://twitter.com/QFSW1024"
|
||||
},
|
||||
new SupportItem
|
||||
{
|
||||
Name = "Review",
|
||||
Tooltip = "Leave a review to share your opinion and support Quantum Console!",
|
||||
Url = "https://assetstore.unity.com/packages/tools/utilities/quantum-console-211046#reviews"
|
||||
},
|
||||
new SupportItem
|
||||
{
|
||||
Name = "Survey",
|
||||
Tooltip = "A short survey to help me get feedback on Quantum Console and prioritize what needs the most focus.",
|
||||
Url = "https://forms.gle/TZbpg1t6hc6sypZA9"
|
||||
}
|
||||
};
|
||||
|
||||
private static Rect[] _supportItemRects = new Rect[_supportItems.Length];
|
||||
|
||||
public static void DrawBanner(Texture2D banner, float sizeMultiplier = 1f)
|
||||
{
|
||||
if (banner)
|
||||
{
|
||||
sizeMultiplier = Mathf.Clamp01(sizeMultiplier);
|
||||
Rect bannerRect = GUILayoutUtility.GetRect(0.0f, 0.0f);
|
||||
bannerRect.height = Screen.width / EditorGUIUtility.pixelsPerPoint * banner.height / banner.width;
|
||||
bannerRect.x += bannerRect.width * (1 - sizeMultiplier) / 2;
|
||||
bannerRect.width *= sizeMultiplier;
|
||||
bannerRect.height *= sizeMultiplier;
|
||||
|
||||
GUILayout.Space(bannerRect.height);
|
||||
GUI.Label(bannerRect, banner);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawSupportRow()
|
||||
{
|
||||
LayoutController layout = new LayoutController(EditorGUILayout.GetControlRect());
|
||||
layout.SpliceRow(_supportItemRects.Length, ref _supportItemRects);
|
||||
|
||||
for (int i = 0; i < _supportItems.Length; i++)
|
||||
{
|
||||
SupportItem item = _supportItems[i];
|
||||
if (GUI.Button(_supportItemRects[i], new GUIContent(item.Name, item.Tooltip)))
|
||||
{
|
||||
Application.OpenURL(item.Url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawHeader(Texture2D banner)
|
||||
{
|
||||
DrawBanner(banner);
|
||||
DrawSupportRow();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b2819424558646f42af68616ef5e127a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,42 @@
|
||||
using QFSW.QC.QGUI;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QFSW.QC.Editor
|
||||
{
|
||||
[CustomPropertyDrawer(typeof(ModifierKeyCombo), true)]
|
||||
public class ModifierKeyComboEditor : PropertyDrawer
|
||||
{
|
||||
private readonly GUIContent _shiftLabel = new GUIContent("shift");
|
||||
private readonly GUIContent _altLabel = new GUIContent("alt");
|
||||
private readonly GUIContent _ctrlLabel = new GUIContent("ctrl");
|
||||
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
LayoutController layout = new LayoutController(position);
|
||||
EditorGUI.BeginProperty(layout.CurrentRect, label, property);
|
||||
|
||||
const float boolWidth = 10;
|
||||
bool enableState = GUI.enabled;
|
||||
float boolLabelWidth = QGUILayout.GetMaxContentSize(EditorStyles.label, _shiftLabel, _altLabel, _ctrlLabel).x;
|
||||
|
||||
SerializedProperty key = property.FindPropertyRelative("Key");
|
||||
SerializedProperty ctrl = property.FindPropertyRelative("Ctrl");
|
||||
SerializedProperty alt = property.FindPropertyRelative("Alt");
|
||||
SerializedProperty shift = property.FindPropertyRelative("Shift");
|
||||
|
||||
GUI.enabled &= ((KeyCode)key.enumValueIndex) != KeyCode.None;
|
||||
EditorGUI.LabelField(layout.ReserveHorizontalReversed(boolLabelWidth), _shiftLabel);
|
||||
EditorGUI.PropertyField(layout.ReserveHorizontalReversed(boolWidth), shift, GUIContent.none);
|
||||
EditorGUI.LabelField(layout.ReserveHorizontalReversed(boolLabelWidth), _altLabel);
|
||||
EditorGUI.PropertyField(layout.ReserveHorizontalReversed(boolWidth), alt, GUIContent.none);
|
||||
EditorGUI.LabelField(layout.ReserveHorizontalReversed(boolLabelWidth), _ctrlLabel);
|
||||
EditorGUI.PropertyField(layout.ReserveHorizontalReversed(boolWidth), ctrl, GUIContent.none);
|
||||
|
||||
GUI.enabled = enableState;
|
||||
EditorGUI.PropertyField(layout.CurrentRect, key, label);
|
||||
|
||||
EditorGUI.EndProperty();
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a359df86554203048b60947f59468558
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,47 @@
|
||||
using QFSW.QC.Utilities;
|
||||
using System;
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QFSW.QC.Editor
|
||||
{
|
||||
public class QCInspectorBase : UnityEditor.Editor
|
||||
{
|
||||
const string ROOT_PATH = "Source";
|
||||
protected string BannerName => "BannerEditor.png";
|
||||
protected Texture2D Banner { get; private set; }
|
||||
|
||||
protected T LoadAssetInSource<T>(string assetName, string root) where T : UnityEngine.Object
|
||||
{
|
||||
MonoScript src = MonoScript.FromScriptableObject(this);
|
||||
string srcPath = AssetDatabase.GetAssetPath(src);
|
||||
string dirPath = Path.GetDirectoryName(srcPath);
|
||||
string[] pathParts = dirPath.Split(new string[] { root }, StringSplitOptions.None);
|
||||
string rootPath = string.Join(root, pathParts.SkipLast()) + root;
|
||||
string[] files = Directory.GetFiles(rootPath, assetName, SearchOption.AllDirectories);
|
||||
|
||||
if (files.Length > 0)
|
||||
{
|
||||
string bannerPath = files[0];
|
||||
return AssetDatabase.LoadAssetAtPath<T>(bannerPath);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
if (!Banner)
|
||||
{
|
||||
Banner = LoadAssetInSource<Texture2D>(BannerName, ROOT_PATH);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
EditorHelpers.DrawHeader(Banner);
|
||||
base.OnInspectorGUI();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 117a477a3c59db341907364ce3923b02
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "QFSW.QC.Editor",
|
||||
"references": [
|
||||
"QFSW.QC",
|
||||
"QFSW.QC.Editor.Tools",
|
||||
"QFSW.QC.QGUI",
|
||||
"Unity.TextMeshPro"
|
||||
],
|
||||
"optionalUnityReferences": [],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": []
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 50b2bdd4833ec8043a7227258a04ccb7
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 979ba78ee0b3f154ab44f1295d48ce61
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace QFSW.QC.QGUI
|
||||
{
|
||||
public interface IGUIItem
|
||||
{
|
||||
void DrawGUI(LayoutController layout);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 506cdc118faa64bd7b040644b69c623b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,140 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QFSW.QC.QGUI
|
||||
{
|
||||
public class LayoutController
|
||||
{
|
||||
public static float HorizontalPadding => 4;
|
||||
public static float RowPadding => EditorGUIUtility.standardVerticalSpacing;
|
||||
public static float RowHeight => EditorGUIUtility.singleLineHeight;
|
||||
|
||||
public bool IsValid
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_currentRect.width < 0) { return false; }
|
||||
if (_currentRect.height < 0) { return false; }
|
||||
if (_currentRect.x < TotalDrawRect.x) { return false; }
|
||||
if (_currentRect.y < TotalDrawRect.y) { return false; }
|
||||
if (_currentRect.x + _currentRect.width > TotalDrawRect.x + TotalDrawRect.width) { return false; }
|
||||
if (_currentRect.y + _currentRect.height > TotalDrawRect.y + TotalDrawRect.height) { return false; }
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public Rect TotalDrawRect { get; }
|
||||
public Rect CurrentRect => _currentRect;
|
||||
|
||||
private Rect _currentRect;
|
||||
|
||||
public LayoutController(Rect drawRect)
|
||||
{
|
||||
TotalDrawRect = drawRect;
|
||||
_currentRect = drawRect;
|
||||
_currentRect.height = RowHeight;
|
||||
}
|
||||
|
||||
public Rect BeginNewLine()
|
||||
{
|
||||
_currentRect.y += RowPadding + RowHeight;
|
||||
_currentRect.x = TotalDrawRect.x;
|
||||
_currentRect.width = TotalDrawRect.width;
|
||||
return _currentRect;
|
||||
}
|
||||
|
||||
public Rect ReserveHorizontal(float width)
|
||||
{
|
||||
Rect drawRect = _currentRect;
|
||||
drawRect.width = width;
|
||||
drawRect.width -= HorizontalPadding;
|
||||
|
||||
_currentRect.x += width;
|
||||
_currentRect.width -= width;
|
||||
|
||||
return drawRect;
|
||||
}
|
||||
|
||||
public Rect ReserveHorizontalPercentage(float widthPercentage)
|
||||
{
|
||||
float width = _currentRect.width * widthPercentage;
|
||||
return ReserveHorizontal(width);
|
||||
}
|
||||
|
||||
public Rect ReserveHorizontalReversed(float width)
|
||||
{
|
||||
Rect drawRect = _currentRect;
|
||||
drawRect.x += drawRect.width;
|
||||
drawRect.x -= width;
|
||||
drawRect.width = width;
|
||||
|
||||
_currentRect.width -= HorizontalPadding;
|
||||
_currentRect.width -= width;
|
||||
|
||||
return drawRect;
|
||||
}
|
||||
|
||||
public Rect ReserveHorizontalReversedPercentage(float widthPercentage)
|
||||
{
|
||||
float width = _currentRect.width * widthPercentage;
|
||||
return ReserveHorizontalReversed(width);
|
||||
}
|
||||
|
||||
public Rect ResizeRectHeight(Rect rect, float height)
|
||||
{
|
||||
rect.y += (rect.height - height) / 2;
|
||||
rect.height = height;
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
public Rect ReserveHorizontal(float width, float height)
|
||||
{
|
||||
return ResizeRectHeight(ReserveHorizontal(width), height);
|
||||
}
|
||||
|
||||
public Rect ReserveHorizontalReversed(float width, float height)
|
||||
{
|
||||
return ResizeRectHeight(ReserveHorizontalReversed(width), height);
|
||||
}
|
||||
|
||||
public Rect ReserveSquare()
|
||||
{
|
||||
return ReserveHorizontal(RowHeight);
|
||||
}
|
||||
|
||||
public Rect ReserveSquareReversed()
|
||||
{
|
||||
return ReserveHorizontalReversed(RowHeight);
|
||||
}
|
||||
|
||||
public Rect ReserveAuto(GUIContent content, GUIStyle style)
|
||||
{
|
||||
Vector2 size = style.CalcSize(content);
|
||||
return ReserveHorizontal(size.x, size.y);
|
||||
}
|
||||
|
||||
public Rect ReserveAutoReversed(GUIContent content, GUIStyle style)
|
||||
{
|
||||
Vector2 size = style.CalcSize(content);
|
||||
return ReserveHorizontalReversed(size.x, size.y);
|
||||
}
|
||||
|
||||
public void SpliceRow(int colCount, ref Rect[] rects)
|
||||
{
|
||||
float width = _currentRect.width / colCount;
|
||||
for (int i = 0; i < rects.Length; i++)
|
||||
{
|
||||
rects[i] = ReserveHorizontal(width);
|
||||
}
|
||||
}
|
||||
|
||||
public Rect[] SpliceRow(int colCount)
|
||||
{
|
||||
Rect[] rects = new Rect[colCount];
|
||||
SpliceRow(colCount, ref rects);
|
||||
return rects;
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 481da05e0c09175429ec2db6589454a5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "QFSW.QC.QGUI",
|
||||
"references": [],
|
||||
"optionalUnityReferences": [],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": []
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2c8115fef53c440a1873e4077e45b326
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QFSW.QC.QGUI
|
||||
{
|
||||
public static class QGUILayout
|
||||
{
|
||||
public static T EnumPopup<T>(T selected, params GUILayoutOption[] options) where T : Enum
|
||||
{
|
||||
return (T)EditorGUILayout.EnumPopup(selected, options);
|
||||
}
|
||||
|
||||
public static T EnumPopup<T>(GUIContent content, T selected, params GUILayoutOption[] options) where T : Enum
|
||||
{
|
||||
return (T)EditorGUILayout.EnumPopup(content, selected, options);
|
||||
}
|
||||
|
||||
public static T EnumFlagsField<T>(GUIContent content, T enumValue, params GUILayoutOption[] options) where T : Enum
|
||||
{
|
||||
return (T)EditorGUILayout.EnumFlagsField(content, enumValue, options);
|
||||
}
|
||||
|
||||
public static bool ButtonAuto(GUIContent content, GUIStyle style)
|
||||
{
|
||||
Vector2 size = style.CalcSize(content);
|
||||
return GUILayout.Button(content, style, GUILayout.Width(size.x));
|
||||
}
|
||||
|
||||
public static bool ButtonAuto(LayoutController layout, GUIContent content, GUIStyle style)
|
||||
{
|
||||
Rect rect = layout.ReserveAuto(content, style);
|
||||
return GUI.Button(rect, content, style);
|
||||
}
|
||||
|
||||
public static Vector2 GetMaxContentSize(GUIStyle style, params GUIContent[] contents)
|
||||
{
|
||||
Vector2 maxSize = new Vector2();
|
||||
foreach (GUIContent content in contents)
|
||||
{
|
||||
Vector2 size = style.CalcSize(content);
|
||||
maxSize.x = Mathf.Max(maxSize.x, size.x);
|
||||
maxSize.y = Mathf.Max(maxSize.y, size.y);
|
||||
}
|
||||
|
||||
return maxSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: df613967b4fdf486d9a4e932822cec60
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,267 @@
|
||||
using QFSW.QC.Editor.Tools;
|
||||
using QFSW.QC.QGUI;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QFSW.QC.Editor
|
||||
{
|
||||
[CustomEditor(typeof(QuantumConsole), true)]
|
||||
public class QuantumConsoleInspector : QCInspectorBase
|
||||
{
|
||||
private QuantumConsole QCInstance;
|
||||
|
||||
private SerializedProperty _themeProperty;
|
||||
private SerializedProperty _keyConfigProperty;
|
||||
private SerializedProperty _localizationProperty;
|
||||
|
||||
private SerializedProperty _verboseLoggingProperty;
|
||||
private SerializedProperty _verboseErrorsProperty;
|
||||
private SerializedProperty _loggingLevelProperty;
|
||||
|
||||
private SerializedProperty _openOnLogLevelProperty;
|
||||
private SerializedProperty _supportedStateProperty;
|
||||
private SerializedProperty _autoScrollProperty;
|
||||
private SerializedProperty _interceptDebugProperty;
|
||||
private SerializedProperty _interceptInactiveProperty;
|
||||
private SerializedProperty _prependTimestampsProperty;
|
||||
private SerializedProperty _activateOnStartupProperty;
|
||||
private SerializedProperty _initialiseOnStartupProperty;
|
||||
private SerializedProperty _focusOnActivateProperty;
|
||||
private SerializedProperty _closeOnSubmitProperty;
|
||||
private SerializedProperty _singletonModeProperty;
|
||||
private SerializedProperty _inputProperty;
|
||||
private SerializedProperty _inputPlaceholderProperty;
|
||||
private SerializedProperty _logProperty;
|
||||
private SerializedProperty _containerProperty;
|
||||
private SerializedProperty _scrollRectProperty;
|
||||
private SerializedProperty _suggestionProperty;
|
||||
private SerializedProperty _popupProperty;
|
||||
private SerializedProperty _popupTextProperty;
|
||||
private SerializedProperty _jobCounterTextProperty;
|
||||
private SerializedProperty _jobCounterRectProperty;
|
||||
private SerializedProperty _panelsProperty;
|
||||
|
||||
private SerializedProperty _commandHistoryProperty;
|
||||
private SerializedProperty _commandHistorySizeProperty;
|
||||
private SerializedProperty _commandHistoryDuplicatesProperty;
|
||||
private SerializedProperty _commandHistoryAdjacentDuplicatesProperty;
|
||||
|
||||
private SerializedProperty _enableAutocompleteProperty;
|
||||
private SerializedProperty _usePopupProperty;
|
||||
private SerializedProperty _maxSuggestionProperty;
|
||||
private SerializedProperty _popupOrderProperty;
|
||||
private SerializedProperty _fuzzyProperty;
|
||||
private SerializedProperty _caseSensitiveProperty;
|
||||
private SerializedProperty _collapseSuggestionOverloadsProperty;
|
||||
|
||||
private SerializedProperty _showCurrentJobsProperty;
|
||||
private SerializedProperty _blockOnAsyncProperty;
|
||||
private SerializedProperty _maxStoredLogsProperty;
|
||||
private SerializedProperty _maxLogSizeProperty;
|
||||
private SerializedProperty _showInitLogsProperty;
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
QCInstance = (QuantumConsole)target;
|
||||
QCInstance.OnStateChange += Repaint;
|
||||
|
||||
FetchSerializedProperties();
|
||||
}
|
||||
|
||||
private void FetchSerializedProperties()
|
||||
{
|
||||
_themeProperty = serializedObject.FindProperty("_theme");
|
||||
_keyConfigProperty = serializedObject.FindProperty("_keyConfig");
|
||||
_localizationProperty = serializedObject.FindProperty("_localization");
|
||||
|
||||
_verboseLoggingProperty = serializedObject.FindProperty("_verboseLogging");
|
||||
_verboseErrorsProperty = serializedObject.FindProperty("_verboseErrors");
|
||||
_loggingLevelProperty = serializedObject.FindProperty("_loggingLevel");
|
||||
_openOnLogLevelProperty = serializedObject.FindProperty("_openOnLogLevel");
|
||||
_supportedStateProperty = serializedObject.FindProperty("_supportedState");
|
||||
_autoScrollProperty = serializedObject.FindProperty("_autoScroll");
|
||||
_interceptDebugProperty = serializedObject.FindProperty("_interceptDebugLogger");
|
||||
_interceptInactiveProperty = serializedObject.FindProperty("_interceptWhilstInactive");
|
||||
_prependTimestampsProperty = serializedObject.FindProperty("_prependTimestamps");
|
||||
_activateOnStartupProperty = serializedObject.FindProperty("_activateOnStartup");
|
||||
_initialiseOnStartupProperty = serializedObject.FindProperty("_initialiseOnStartup");
|
||||
_focusOnActivateProperty = serializedObject.FindProperty("_focusOnActivate");
|
||||
_closeOnSubmitProperty = serializedObject.FindProperty("_closeOnSubmit");
|
||||
_singletonModeProperty = serializedObject.FindProperty("_singletonMode");
|
||||
_containerProperty = serializedObject.FindProperty("_containerRect");
|
||||
_scrollRectProperty = serializedObject.FindProperty("_scrollRect");
|
||||
_popupProperty = serializedObject.FindProperty("_suggestionPopupRect");
|
||||
_jobCounterRectProperty = serializedObject.FindProperty("_jobCounterRect");
|
||||
_panelsProperty = serializedObject.FindProperty("_panels");
|
||||
_commandHistoryProperty = serializedObject.FindProperty("_storeCommandHistory");
|
||||
_commandHistoryDuplicatesProperty = serializedObject.FindProperty("_storeDuplicateCommands");
|
||||
_commandHistoryAdjacentDuplicatesProperty = serializedObject.FindProperty("_storeAdjacentDuplicateCommands");
|
||||
_commandHistorySizeProperty = serializedObject.FindProperty("_commandHistorySize");
|
||||
_showCurrentJobsProperty = serializedObject.FindProperty("_showCurrentJobs");
|
||||
_blockOnAsyncProperty = serializedObject.FindProperty("_blockOnAsync");
|
||||
_enableAutocompleteProperty = serializedObject.FindProperty("_enableAutocomplete");
|
||||
_usePopupProperty = serializedObject.FindProperty("_showPopupDisplay");
|
||||
_maxSuggestionProperty = serializedObject.FindProperty("_maxSuggestionDisplaySize");
|
||||
_popupOrderProperty = serializedObject.FindProperty("_suggestionDisplayOrder");
|
||||
_fuzzyProperty = serializedObject.FindProperty("_useFuzzySearch");
|
||||
_caseSensitiveProperty = serializedObject.FindProperty("_caseSensitiveSearch");
|
||||
_collapseSuggestionOverloadsProperty = serializedObject.FindProperty("_collapseSuggestionOverloads");
|
||||
_maxStoredLogsProperty = serializedObject.FindProperty("_maxStoredLogs");
|
||||
_maxLogSizeProperty = serializedObject.FindProperty("_maxLogSize");
|
||||
_showInitLogsProperty = serializedObject.FindProperty("_showInitLogs");
|
||||
|
||||
_inputProperty = serializedObject.FindProperty("_consoleInput");
|
||||
_inputPlaceholderProperty = serializedObject.FindProperty("_inputPlaceholderText");
|
||||
_logProperty = serializedObject.FindProperty("_consoleLogText");
|
||||
_suggestionProperty = serializedObject.FindProperty("_consoleSuggestionText");
|
||||
_popupTextProperty = serializedObject.FindProperty("_suggestionPopupText");
|
||||
_jobCounterTextProperty = serializedObject.FindProperty("_jobCounterText");
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
QCInstance.OnStateChange -= Repaint;
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
EditorHelpers.DrawHeader(Banner);
|
||||
|
||||
if (QuantumConsoleProcessor.TableGenerated || QuantumConsoleProcessor.TableIsGenerating)
|
||||
{
|
||||
EditorGUILayout.LabelField("Quantum Console Processor Information", EditorStyles.miniBoldLabel);
|
||||
if (QuantumConsoleProcessor.TableIsGenerating) { EditorGUILayout.LabelField("Command Table Generating...", EditorStyles.miniLabel); }
|
||||
EditorGUILayout.LabelField($"Commands Loaded: {QuantumConsoleProcessor.LoadedCommandCount}", EditorStyles.miniLabel);
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
|
||||
EditorGUILayout.LabelField(new GUIContent("General Settings", "All general and basic settings for the Quantum Console."), EditorStyles.boldLabel);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PropertyField(_themeProperty, new GUIContent("Theme", "QuantumTheme to use for this Quantum Console."));
|
||||
if (_themeProperty.objectReferenceValue)
|
||||
{
|
||||
GUIContent applyBtnContent = new GUIContent("Apply", "Forces an application of the theme now allowing you to see any GUI changes it would make");
|
||||
if (QGUILayout.ButtonAuto(applyBtnContent, EditorStyles.miniButton))
|
||||
{
|
||||
Undo.RecordObject(QCInstance, "Applied a theme to the Quantum Console");
|
||||
QCInstance.ApplyTheme((QuantumTheme)_themeProperty.objectReferenceValue, true);
|
||||
PrefabUtil.RecordPrefabInstancePropertyModificationsFullyRecursive(QCInstance.gameObject);
|
||||
EditorUtility.SetDirty(QCInstance);
|
||||
}
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.PropertyField(_keyConfigProperty, new GUIContent("Key Configuration", "Key configuration for the various keyboard shortcuts used by Quantum Console."));
|
||||
EditorGUILayout.PropertyField(_localizationProperty, new GUIContent("Localization", "Localization configuration for the various messages displayed by Quantum Console."));
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PropertyField(_supportedStateProperty, new GUIContent("Enabled", "On which build/editor states should the console be enabled on"));
|
||||
ShowSceneViewToggle();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.PropertyField(_activateOnStartupProperty, new GUIContent("Activate on Startup", "If the Quantum Console should be shown and activated on startup."));
|
||||
if (!_activateOnStartupProperty.boolValue)
|
||||
{
|
||||
EditorGUILayout.PropertyField(_initialiseOnStartupProperty, new GUIContent("Initialise on Startup", "If the Quantum Console should be initialised on startup in the background."));
|
||||
}
|
||||
|
||||
EditorGUILayout.PropertyField(_focusOnActivateProperty, new GUIContent(
|
||||
"Focus on Activate", "If the input field should automatically be focused when Quantum Console is activated.")
|
||||
);
|
||||
|
||||
EditorGUILayout.PropertyField(_closeOnSubmitProperty, new GUIContent("Close on Submit", "If the Quantum Console should be hidden and closed when a command is submitted and invoked."));
|
||||
EditorGUILayout.PropertyField(_singletonModeProperty, new GUIContent("Singleton", "Forces the console into singleton mode. " +
|
||||
"This means the console will be made scene persistent and will not be destroyed when new scenes are loaded. " +
|
||||
"Additionally, only one instance of the console will be allowed to exist, and it will be accessible via QuantumConsole.Instance"));
|
||||
EditorGUILayout.PropertyField(_verboseErrorsProperty, new GUIContent("Verbose Errors", "If errors caused by the Quantum Console Processor or commands should be logged in verbose mode."));
|
||||
EditorGUILayout.PropertyField(_autoScrollProperty, new GUIContent("Autoscroll", "Determine if and when the console should autoscroll."));
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.LabelField(new GUIContent("Debug Interception", "All settings relating to the interception of Unity's Debug class."), EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(_interceptDebugProperty, new GUIContent("Intercept Debug Messages", "If the Quantum Console should intercept and display messages from the Unity Debug logging."));
|
||||
if (_interceptDebugProperty.boolValue)
|
||||
{
|
||||
EditorGUILayout.PropertyField(_interceptInactiveProperty, new GUIContent("Intercept Whilst Inactive", "If the Quantum Console should continue to intercept messages whilst inactive."));
|
||||
EditorGUILayout.PropertyField(_prependTimestampsProperty, new GUIContent("Enable Timestamps", "If the timestamp of the log message should be prepended."));
|
||||
EditorGUILayout.PropertyField(_loggingLevelProperty, new GUIContent("Logging Level", "The minimum log severity required to intercept and display the log."));
|
||||
EditorGUILayout.PropertyField(_verboseLoggingProperty, new GUIContent("Verbose Logging", "The minimum log severity required to use verbose logging."));
|
||||
EditorGUILayout.PropertyField(_openOnLogLevelProperty, new GUIContent("Open Console", "The minimum log severity required to open the console."));
|
||||
}
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.LabelField(new GUIContent("Async Settings", "All settings related to async commands."), EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(_showCurrentJobsProperty, new GUIContent("Show Current Jobs", "Shows a popup counter with the currently executing async commands."));
|
||||
EditorGUILayout.PropertyField(_blockOnAsyncProperty, new GUIContent("Block on Execute", "Blocks the Quantum Console from being used until the current async command has finished."));
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.LabelField(new GUIContent("Autocomplete Settings", "Settings relating to autocomplete and suggestions in the console using tab."), EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(_enableAutocompleteProperty, new GUIContent("Enable Autocomplete", "If the suggestion and autocomplete system should be enabled."));
|
||||
if (_enableAutocompleteProperty.boolValue)
|
||||
{
|
||||
EditorGUILayout.PropertyField(_fuzzyProperty, new GUIContent("Use Fuzzy Search", "If fuzzy search is disabled, then your current search must match the beginning of the suggestion to be suggested (foo*). If fuzzy search is enabled, it can be anywhere within the suggestion to be suggested (*foo*)."));
|
||||
EditorGUILayout.PropertyField(_caseSensitiveProperty, new GUIContent("Case Sensitive", "If the search should be case sensitive or not."));
|
||||
EditorGUILayout.PropertyField(_collapseSuggestionOverloadsProperty, new GUIContent("Collapse Overloads",
|
||||
"If multiple overloads of the same suggestion should be collapsed into a single suggestion with optional elements where possible." +
|
||||
"\nFor example, the following" +
|
||||
"\ncommand arg0" +
|
||||
"\ncommand arg0 arg1" +
|
||||
"\nWill become" +
|
||||
"\ncommand arg0 [arg1]"));
|
||||
|
||||
EditorGUILayout.PropertyField(_usePopupProperty, new GUIContent("Show Popup Display", "If enabled, a popup display will be shown containing potential auto completions as you type."));
|
||||
if (_usePopupProperty.boolValue)
|
||||
{
|
||||
EditorGUILayout.PropertyField(_maxSuggestionProperty, new GUIContent("Max Suggestion Count", "The maximum number of suggestions to display in the popup. Set to -1 for unlimited."));
|
||||
EditorGUILayout.PropertyField(_popupOrderProperty, new GUIContent("Suggestion Popup Order", "The sort direction used when displaying suggestions to the popup display."));
|
||||
}
|
||||
}
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.LabelField(new GUIContent("Command History", "Settings relating to storing previous commands so that they can be easily accessed with the arrow keys."), EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(_commandHistoryProperty, new GUIContent("Store Previous Commands", "If previous commands should be stored, allowing them to be accessed with the arrow keys."));
|
||||
if (_commandHistoryProperty.boolValue)
|
||||
{
|
||||
EditorGUILayout.PropertyField(_commandHistoryDuplicatesProperty, new GUIContent("Allow Duplicates", "Store commands into the history even if they have already appeared."));
|
||||
if (_commandHistoryDuplicatesProperty.boolValue) { EditorGUILayout.PropertyField(_commandHistoryAdjacentDuplicatesProperty, new GUIContent("Allow Adjacent Duplicates", "Store commands in the history even if they are adjacent duplicates (i.e same command multiple times in a row).")); }
|
||||
_commandHistorySizeProperty.intValue = Mathf.Max(-1, EditorGUILayout.IntField(new GUIContent("Max Size", "The maximum size of the command history buffer; exceeding this size will cause the oldest commands to be removed to make space. Set to -1 for unlimited."), _commandHistorySizeProperty.intValue));
|
||||
}
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.LabelField(new GUIContent("Advanced Settings", "Advanced settings such as buffer sizes."), EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(_maxStoredLogsProperty, new GUIContent("Maximum Stored Logs", "Maximum number of logtraces to store before discarding old logs. Set to -1 for unlimited."));
|
||||
EditorGUILayout.PropertyField(_maxLogSizeProperty, new GUIContent("Maximum Log Size", "Logs exceeding this size will be discarded and an error will be shown. Set to -1 for no maximum size on a single log."));
|
||||
EditorGUILayout.PropertyField(_showInitLogsProperty, new GUIContent("Show Initialization Logs", "Whether the initialization logs should be shown or not."));
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.LabelField(new GUIContent("References", "All the references needed by the Quantum Console"), EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(_containerProperty, new GUIContent("Container Rect", "The top level container rect-transform containing all of the Quantum Console UI elements."));
|
||||
EditorGUILayout.PropertyField(_scrollRectProperty, new GUIContent("Scroll Rect", "(Optional) The scroll rect of the console text, required for auto scrolling."));
|
||||
EditorGUILayout.PropertyField(_popupProperty, new GUIContent("Suggestion Popup Display", "Top level transform for the suggestion popup display."));
|
||||
EditorGUILayout.PropertyField(_jobCounterRectProperty, new GUIContent("Job Counter Display", "Top level transform for the job counter display."));
|
||||
|
||||
EditorGUILayout.PropertyField(_inputProperty, new GUIContent("Console Input Field", "The input field used for interfacing with the Quantum Console."));
|
||||
EditorGUILayout.PropertyField(_inputPlaceholderProperty, new GUIContent("Console Input Placeholder", "The placeholder text component for when the input field is not in use."));
|
||||
EditorGUILayout.PropertyField(_popupTextProperty, new GUIContent("Suggestion Popup Text", "Text display for the suggestion popup display."));
|
||||
EditorGUILayout.PropertyField(_logProperty, new GUIContent("Console Log Display", "The text display used as the log output by the Quantum Console."));
|
||||
EditorGUILayout.PropertyField(_suggestionProperty, new GUIContent("Command Suggestion Display", "(optional) If assigned, the Quantum Console will show the paramater signature for suggested commands here."));
|
||||
EditorGUILayout.PropertyField(_jobCounterTextProperty, new GUIContent("Job Counter Text", "Text display for the job counter display."));
|
||||
|
||||
EditorGUILayout.PropertyField(_panelsProperty, new GUIContent("UI Panels", "All panels in the UI to control with the Quantum Theme."), true);
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
private void ShowSceneViewToggle()
|
||||
{
|
||||
RectTransform consoleContainer = (RectTransform)_containerProperty.objectReferenceValue;
|
||||
bool containerFound = consoleContainer;
|
||||
bool containerHidden = containerFound ? consoleContainer.gameObject.activeSelf : false;
|
||||
|
||||
GUI.enabled = containerFound;
|
||||
GUIContent message = new GUIContent(containerFound ? containerHidden ? "Hide Console" : "Show Console" : "Console Missing");
|
||||
if (QGUILayout.ButtonAuto(message, EditorStyles.miniButton)) { consoleContainer.gameObject.SetActive(!consoleContainer.gameObject.activeSelf); }
|
||||
GUI.enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 183567afdaef7164f803d9188375d34b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences:
|
||||
- banner: {fileID: 2800000, guid: 0a00d33b88ebe3f4eb5dfb0d2a1c0726, type: 3}
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QFSW.QC.Editor
|
||||
{
|
||||
[CustomEditor(typeof(QuantumKeyConfig))]
|
||||
public class QuantumKeyConfigInspector : QCInspectorBase
|
||||
{
|
||||
private QuantumKeyConfig _keyConfigInstance;
|
||||
|
||||
private SerializedProperty _submitCommandKeyProperty;
|
||||
private SerializedProperty _hideConsoleKeyProperty;
|
||||
private SerializedProperty _showConsoleKeyProperty;
|
||||
private SerializedProperty _toggleConsoleVisibilityKeyProperty;
|
||||
|
||||
private SerializedProperty _zoomInKeyProperty;
|
||||
private SerializedProperty _zoomOutKeyProperty;
|
||||
private SerializedProperty _dragConsoleKeyProperty;
|
||||
|
||||
private SerializedProperty _selectNextSuggestionKeyProperty;
|
||||
private SerializedProperty _selectPreviousSuggestionKeyProperty;
|
||||
|
||||
private SerializedProperty _nextCommandKeyProperty;
|
||||
private SerializedProperty _previousCommandKeyProperty;
|
||||
|
||||
private SerializedProperty _cancelActionsKeyProperty;
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
_keyConfigInstance = (QuantumKeyConfig)target;
|
||||
|
||||
_submitCommandKeyProperty = serializedObject.FindProperty("SubmitCommandKey");
|
||||
_hideConsoleKeyProperty = serializedObject.FindProperty("HideConsoleKey");
|
||||
_showConsoleKeyProperty = serializedObject.FindProperty("ShowConsoleKey");
|
||||
_toggleConsoleVisibilityKeyProperty = serializedObject.FindProperty("ToggleConsoleVisibilityKey");
|
||||
|
||||
_zoomInKeyProperty = serializedObject.FindProperty("ZoomInKey");
|
||||
_zoomOutKeyProperty = serializedObject.FindProperty("ZoomOutKey");
|
||||
_dragConsoleKeyProperty = serializedObject.FindProperty("DragConsoleKey");
|
||||
|
||||
_selectNextSuggestionKeyProperty = serializedObject.FindProperty("SelectNextSuggestionKey");
|
||||
_selectPreviousSuggestionKeyProperty = serializedObject.FindProperty("SelectPreviousSuggestionKey");
|
||||
|
||||
_nextCommandKeyProperty = serializedObject.FindProperty("NextCommandKey");
|
||||
_previousCommandKeyProperty = serializedObject.FindProperty("PreviousCommandKey");
|
||||
|
||||
_cancelActionsKeyProperty = serializedObject.FindProperty("CancelActionsKey");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
EditorHelpers.DrawHeader(Banner);
|
||||
|
||||
EditorGUILayout.LabelField(new GUIContent("General"), EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(_submitCommandKeyProperty, new GUIContent("Submit Command", "The key to submit and invoke the current console input."));
|
||||
EditorGUILayout.PropertyField(_showConsoleKeyProperty, new GUIContent("Show Console", "The key used to show and activate the console."));
|
||||
EditorGUILayout.PropertyField(_hideConsoleKeyProperty, new GUIContent("Hide Console", "The key used to hide and deactivate the console."));
|
||||
EditorGUILayout.PropertyField(_toggleConsoleVisibilityKeyProperty, new GUIContent("Toggle Console", "The key used to toggle the active and visibility state of the console."));
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.LabelField(new GUIContent("UI"), EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(_zoomInKeyProperty, new GUIContent("Zoom In", "Zooms in the console scaling."));
|
||||
EditorGUILayout.PropertyField(_zoomOutKeyProperty, new GUIContent("Zoom Out", "Zooms out the console scaling."));
|
||||
EditorGUILayout.PropertyField(_dragConsoleKeyProperty, new GUIContent("Drag Console", "Drags the console window with the cursor."));
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.LabelField(new GUIContent("Autocomplete"), EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(_selectNextSuggestionKeyProperty, new GUIContent("Select Next Suggestion", "The key to show and select the next autocomplete suggestion."));
|
||||
EditorGUILayout.PropertyField(_selectPreviousSuggestionKeyProperty, new GUIContent("Select Previous Suggestion", "The key to show and select the previous autocomplete suggestion."));
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.LabelField(new GUIContent("Command History"), EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(_nextCommandKeyProperty, new GUIContent("Select Next Command", "The key to be used to select the next command from the console history."));
|
||||
EditorGUILayout.PropertyField(_previousCommandKeyProperty, new GUIContent("Select Previous Command", "The key to be used to select the previous command from the console history."));
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.LabelField(new GUIContent("Actions"), EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(_cancelActionsKeyProperty, new GUIContent("Cancel Actions", "Cancels any actions currently executing."));
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 37cda8ee039ccf9499ee9ed64f06664a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QFSW.QC.Editor
|
||||
{
|
||||
[CustomEditor(typeof(QuantumLocalization), true)]
|
||||
public class QuantumLocalizationInspector : QCInspectorBase
|
||||
{
|
||||
private QuantumLocalization _localizationInstance;
|
||||
|
||||
private SerializedProperty _loadingProperty;
|
||||
private SerializedProperty _executingAsyncCommandProperty;
|
||||
private SerializedProperty _enterCommandProperty;
|
||||
|
||||
private SerializedProperty _commandErrorProperty;
|
||||
private SerializedProperty _consoleErrorProperty;
|
||||
private SerializedProperty _maxLogSizeExceededProperty;
|
||||
|
||||
private SerializedProperty _initializationProgressProperty;
|
||||
private SerializedProperty _initializationCompleteProperty;
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
_localizationInstance = (QuantumLocalization)target;
|
||||
|
||||
_loadingProperty = serializedObject.FindProperty("Loading");
|
||||
_executingAsyncCommandProperty = serializedObject.FindProperty("ExecutingAsyncCommand");
|
||||
_enterCommandProperty = serializedObject.FindProperty("EnterCommand");
|
||||
|
||||
_commandErrorProperty = serializedObject.FindProperty("CommandError");
|
||||
_consoleErrorProperty = serializedObject.FindProperty("ConsoleError");
|
||||
_maxLogSizeExceededProperty = serializedObject.FindProperty("MaxLogSizeExceeded");
|
||||
|
||||
_initializationProgressProperty = serializedObject.FindProperty("InitializationProgress");
|
||||
_initializationCompleteProperty = serializedObject.FindProperty("InitializationComplete");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
EditorHelpers.DrawHeader(Banner);
|
||||
|
||||
EditorGUILayout.LabelField(new GUIContent("Prompts", "Prompts to display in the input field."), EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(_loadingProperty, new GUIContent("Loading", "Prompt during table generation."));
|
||||
EditorGUILayout.PropertyField(_executingAsyncCommandProperty, new GUIContent("Executing Async Command", "Prompt during blocking async command execution."));
|
||||
EditorGUILayout.PropertyField(_enterCommandProperty, new GUIContent("Enter Command", "Prompt when the console is ready for a command to be entered."));
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.LabelField(new GUIContent("Errors", "Messages and labels around errors."), EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(_commandErrorProperty, new GUIContent("Command Error", "The label to prefix command errors with."));
|
||||
EditorGUILayout.PropertyField(_consoleErrorProperty, new GUIContent("Console Error", "The label to prefix console errors with."));
|
||||
EditorGUILayout.PropertyField(_maxLogSizeExceededProperty, new GUIContent("Max Log Size Exceeded Color",
|
||||
"The error message for the max log size being exceeded." +
|
||||
"\n{0} = Log size" +
|
||||
"\n{0} = Max log size"));
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.LabelField(new GUIContent("Initialization", "Localization around the initialization updates of Quantum Console."), EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(_initializationProgressProperty, new GUIContent("Initialization Progress",
|
||||
"The initialization progress message." +
|
||||
"\n{0} = Loaded command count"));
|
||||
EditorGUILayout.PropertyField(_initializationCompleteProperty, new GUIContent("Initialization Complete",
|
||||
"The initialization completion message."));
|
||||
EditorGUILayout.Space();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 355b72951d691d34198ebba18a22283f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,243 @@
|
||||
using QFSW.QC.Utilities;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QFSW.QC.Editor
|
||||
{
|
||||
[CustomEditor(typeof(QuantumTheme), true)]
|
||||
public class QuantumThemeInspector : QCInspectorBase
|
||||
{
|
||||
private QuantumTheme _themeInstance;
|
||||
|
||||
private SerializedProperty _fontProperty;
|
||||
private SerializedProperty _panelMaterialProperty;
|
||||
private SerializedProperty _panelColorProperty;
|
||||
|
||||
private SerializedProperty _errorColorProperty;
|
||||
private SerializedProperty _warningColorProperty;
|
||||
private SerializedProperty _successColorProperty;
|
||||
private SerializedProperty _selectedSuggestionColorProperty;
|
||||
private SerializedProperty _suggestionColorProperty;
|
||||
private SerializedProperty _commandLogColorProperty;
|
||||
private SerializedProperty _defaultValueColorProperty;
|
||||
|
||||
private SerializedProperty _timestampFormatProperty;
|
||||
private SerializedProperty _commandLogFormatProperty;
|
||||
|
||||
private ReorderableList _typeFormattersListDisplay;
|
||||
private SerializedProperty _typeFormattersProperty;
|
||||
|
||||
private ReorderableList _collectionFormattersListDisplay;
|
||||
private SerializedProperty _collectionFormattersProperty;
|
||||
|
||||
private GUIStyle _centeredMiniLabel;
|
||||
private GUIStyle _centeredLabel;
|
||||
private GUIStyle _centeredTextField;
|
||||
private bool _initialisedStyles;
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
_themeInstance = (QuantumTheme)target;
|
||||
|
||||
_fontProperty = serializedObject.FindProperty("Font");
|
||||
_panelMaterialProperty = serializedObject.FindProperty("PanelMaterial");
|
||||
_panelColorProperty = serializedObject.FindProperty("PanelColor");
|
||||
|
||||
_defaultValueColorProperty = serializedObject.FindProperty("DefaultReturnValueColor");
|
||||
_errorColorProperty = serializedObject.FindProperty("ErrorColor");
|
||||
_warningColorProperty = serializedObject.FindProperty("WarningColor");
|
||||
_successColorProperty = serializedObject.FindProperty("SuccessColor");
|
||||
_selectedSuggestionColorProperty = serializedObject.FindProperty("SelectedSuggestionColor");
|
||||
_suggestionColorProperty = serializedObject.FindProperty("SuggestionColor");
|
||||
_commandLogColorProperty = serializedObject.FindProperty("CommandLogColor");
|
||||
|
||||
_timestampFormatProperty = serializedObject.FindProperty("TimestampFormat");
|
||||
_commandLogFormatProperty = serializedObject.FindProperty("CommandLogFormat");
|
||||
|
||||
_typeFormattersProperty = serializedObject.FindProperty("TypeFormatters");
|
||||
_typeFormattersListDisplay = new ReorderableList(serializedObject, _typeFormattersProperty, true, true, true, true);
|
||||
_typeFormattersListDisplay.onAddCallback = AppendNewTypeFormatter;
|
||||
_typeFormattersListDisplay.drawElementCallback = DrawTypeFormatterInspector;
|
||||
_typeFormattersListDisplay.drawHeaderCallback = DrawTypeFormatterListHeader;
|
||||
|
||||
_collectionFormattersProperty = serializedObject.FindProperty("CollectionFormatters");
|
||||
_collectionFormattersListDisplay = new ReorderableList(serializedObject, _collectionFormattersProperty, true, true, true, true);
|
||||
_collectionFormattersListDisplay.onAddCallback = AppendNewCollectionFormatter;
|
||||
_collectionFormattersListDisplay.drawElementCallback = DrawCollectionFormatterInspector;
|
||||
_collectionFormattersListDisplay.drawHeaderCallback = DrawCollectionFormatterListHeader;
|
||||
}
|
||||
|
||||
private void CreateStyles()
|
||||
{
|
||||
if (!_initialisedStyles)
|
||||
{
|
||||
_initialisedStyles = true;
|
||||
|
||||
_centeredMiniLabel = new GUIStyle(EditorStyles.centeredGreyMiniLabel);
|
||||
_centeredMiniLabel.normal.textColor = EditorGUIUtility.isProSkin ? new Color(0.7f, 0.7f, 0.7f, 1) : new Color(0.3f, 0.3f, 0.3f, 1);
|
||||
|
||||
_centeredLabel = new GUIStyle(EditorStyles.label);
|
||||
_centeredLabel.alignment = TextAnchor.MiddleCenter;
|
||||
_centeredLabel.richText = true;
|
||||
|
||||
_centeredTextField = new GUIStyle(EditorStyles.textField);
|
||||
_centeredTextField.alignment = TextAnchor.MiddleCenter;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
CreateStyles();
|
||||
serializedObject.Update();
|
||||
EditorHelpers.DrawHeader(Banner);
|
||||
|
||||
EditorGUILayout.LabelField(new GUIContent("UI", "Theme customisations for the Quantum Console UI."), EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(_fontProperty, new GUIContent("Font", "The font to be used throughout the Quantum Console."));
|
||||
|
||||
EditorGUILayout.PropertyField(_panelMaterialProperty, new GUIContent("Panel Material", "The material to use in the UI panels. Leave null for default."));
|
||||
EditorGUILayout.PropertyField(_panelColorProperty, new GUIContent("Panel Color", "The color to use in the UI panels."));
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.LabelField(new GUIContent("Colors", "Color customisation for various aspects of the Quantum Console."), EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(_errorColorProperty, new GUIContent("Error Color", "The color to use when formatting errors in the console log."));
|
||||
EditorGUILayout.PropertyField(_warningColorProperty, new GUIContent("Warning Color", "The color to use when formatting warnings in the console log."));
|
||||
EditorGUILayout.PropertyField(_successColorProperty, new GUIContent("Success Color", "The color to use when formatting successful void commands."));
|
||||
EditorGUILayout.PropertyField(_selectedSuggestionColorProperty, new GUIContent("Selected Suggestion Color", "The color to use for the selected suggestion from the suggestion popup display."));
|
||||
EditorGUILayout.PropertyField(_suggestionColorProperty, new GUIContent("Suggestion Signature Color", "The color to use when displaying the paramater signature for suggested commands."));
|
||||
EditorGUILayout.PropertyField(_commandLogColorProperty, new GUIContent("Command Log Color", "The color to use when displaying logged commands in the console log."));
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.LabelField(new GUIContent("Formatting", "Control various formatting within Quantum Console."), EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(_timestampFormatProperty, new GUIContent("Timestamp Format",
|
||||
"The format to use when generating timestamps." +
|
||||
"\n{0} = Hour" +
|
||||
"\n{1} = Minute" +
|
||||
"\n{2} = Second"));
|
||||
EditorGUILayout.PropertyField(_commandLogFormatProperty, new GUIContent("Command Log Format",
|
||||
"The format to use when generating command logs." +
|
||||
"\n{0} = The invoked command"));
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.LabelField(new GUIContent("Return Value Formatting", "Formatting options for the return serialization"), EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(_defaultValueColorProperty, new GUIContent("Default Color", "The default color for return values"));
|
||||
_typeFormattersListDisplay.DoLayoutList();
|
||||
_collectionFormattersListDisplay.DoLayoutList();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
private void DrawTypeFormatterInspector(Rect drawRect, int index, bool isActive, bool isFocused)
|
||||
{
|
||||
float nameWidth = 150f;
|
||||
drawRect.y += 2;
|
||||
|
||||
SerializedProperty currentTypeFormatter = _typeFormattersListDisplay.serializedProperty.GetArrayElementAtIndex(index);
|
||||
SerializedProperty colorProperty = currentTypeFormatter.FindPropertyRelative("Color");
|
||||
|
||||
string typeName = _themeInstance.TypeFormatters[index].Type.GetDisplayName();
|
||||
Rect labelRect = new Rect(drawRect.x, drawRect.y, nameWidth, EditorGUIUtility.singleLineHeight);
|
||||
Rect colorRect = new Rect(drawRect.x + nameWidth, drawRect.y, drawRect.width - nameWidth, EditorGUIUtility.singleLineHeight);
|
||||
EditorGUI.LabelField(labelRect, typeName);
|
||||
EditorGUI.PropertyField(colorRect, colorProperty, new GUIContent());
|
||||
}
|
||||
|
||||
private void DrawTypeFormatterListHeader(Rect drawRect)
|
||||
{
|
||||
EditorGUI.LabelField(new Rect(drawRect.x, drawRect.y, 150, drawRect.height), new GUIContent("Type Formatters", "The different colors that should be used for formatting different type returns."));
|
||||
EditorGUI.LabelField(new Rect((drawRect.x + drawRect.width) / 2 - 40, drawRect.y, 80, drawRect.height), _typeFormattersProperty.arraySize.ToString() + (_typeFormattersProperty.arraySize == 1 ? " Formatter" : " Formatters"), _centeredMiniLabel);
|
||||
}
|
||||
|
||||
private void AppendNewTypeFormatter(ReorderableList listTarget)
|
||||
{
|
||||
Action<string> SubmitCallback = (string data) =>
|
||||
{
|
||||
Type type = QuantumParser.ParseType(data);
|
||||
if (type == null) { throw new ArgumentException($"No type of name '{data}' could be found. Are you missing a namespace?"); }
|
||||
|
||||
Undo.RecordObject(_themeInstance, "Added a new Type Formatter");
|
||||
_themeInstance.TypeFormatters.Add(new TypeColorFormatter(type));
|
||||
EditorUtility.SetDirty(_themeInstance);
|
||||
};
|
||||
|
||||
PopupWindow.Show(new Rect(5, 5, 0, 0), new DataEntryPopup("Type Name", "Create Type Formatter", SubmitCallback));
|
||||
}
|
||||
|
||||
private void DrawCollectionFormatterInspector(Rect drawRect, int index, bool isActive, bool isFocused)
|
||||
{
|
||||
float itemWidth = 40f;
|
||||
float dataWidth = 35f;
|
||||
float padding = 5f;
|
||||
float endPadding = 10f;
|
||||
float nameWidth = drawRect.width - (6 * itemWidth + 3 * dataWidth + 5 * padding + endPadding);
|
||||
drawRect.y += 2;
|
||||
|
||||
SerializedProperty currentCollectionFormatter = _collectionFormattersListDisplay.serializedProperty.GetArrayElementAtIndex(index);
|
||||
SerializedProperty seperatorProperty = currentCollectionFormatter.FindPropertyRelative("SeperatorString");
|
||||
SerializedProperty leftScoperProperty = currentCollectionFormatter.FindPropertyRelative("LeftScoper");
|
||||
SerializedProperty rightScoperProperty = currentCollectionFormatter.FindPropertyRelative("RightScoper");
|
||||
|
||||
string typeName = _themeInstance.CollectionFormatters[index].Type.GetDisplayName();
|
||||
Rect rect = new Rect(drawRect.x, drawRect.y, nameWidth, EditorGUIUtility.singleLineHeight);
|
||||
EditorGUI.LabelField(rect, typeName);
|
||||
rect.x += nameWidth + padding;
|
||||
|
||||
Action<SerializedProperty, float> DrawTextField = (SerializedProperty prop, float width) =>
|
||||
{
|
||||
rect.width = width;
|
||||
prop.stringValue = EditorGUI.TextField(rect, new GUIContent(), prop.stringValue, _centeredTextField);
|
||||
rect.x += width + padding;
|
||||
};
|
||||
|
||||
Action<string, float> DrawLabelField = (string text, float width) =>
|
||||
{
|
||||
rect.width = width;
|
||||
EditorGUI.LabelField(rect, text, _centeredLabel);
|
||||
rect.x += width + padding;
|
||||
};
|
||||
|
||||
string highlightCol = EditorGUIUtility.isProSkin ? "#1fe035" : "#005209";
|
||||
string itemCol = EditorGUIUtility.isProSkin ? "#ff8280" : "#6A0301";
|
||||
string example = $"<b><color={highlightCol}>{leftScoperProperty.stringValue}</color></b>" +
|
||||
$"<color={itemCol}>item1</color><b><color={highlightCol}>{seperatorProperty.stringValue}</color></b>" +
|
||||
$"<color={itemCol}>item2</color><b><color={highlightCol}>{rightScoperProperty.stringValue}</color></b>";
|
||||
|
||||
DrawTextField(leftScoperProperty, dataWidth);
|
||||
DrawLabelField("item1", itemWidth);
|
||||
DrawTextField(seperatorProperty, dataWidth);
|
||||
DrawLabelField("item2", itemWidth);
|
||||
DrawTextField(rightScoperProperty, dataWidth);
|
||||
DrawLabelField("<b>=></b>", itemWidth * 1.5f);
|
||||
DrawLabelField(example, itemWidth * 2.5f);
|
||||
}
|
||||
|
||||
private void DrawCollectionFormatterListHeader(Rect drawRect)
|
||||
{
|
||||
EditorGUI.LabelField(new Rect(drawRect.x, drawRect.y, 150, drawRect.height), new GUIContent("Collection Formatters", "The different strings that should be used for seperating and enclosing collections when serialized."));
|
||||
EditorGUI.LabelField(new Rect((drawRect.x + drawRect.width) / 2 - 40, drawRect.y, 80, drawRect.height), _collectionFormattersProperty.arraySize.ToString() + (_collectionFormattersProperty.arraySize == 1 ? " Formatter" : " Formatters"), _centeredMiniLabel);
|
||||
}
|
||||
|
||||
private void AppendNewCollectionFormatter(ReorderableList listTarget)
|
||||
{
|
||||
Action<string> SubmitCallback = (string data) =>
|
||||
{
|
||||
Type type = QuantumParser.ParseType(data);
|
||||
if (type == null) { throw new ArgumentException($"No type of name '{data}' could be found. Are you missing a namespace?"); }
|
||||
if (!typeof(IEnumerable).IsAssignableFrom(type))
|
||||
{
|
||||
throw new ArgumentException("Collection type must implement IEnumerator");
|
||||
}
|
||||
|
||||
Undo.RecordObject(_themeInstance, "Added a new Collection Formatter");
|
||||
_themeInstance.CollectionFormatters.Add(new CollectionFormatter(type));
|
||||
EditorUtility.SetDirty(_themeInstance);
|
||||
};
|
||||
|
||||
PopupWindow.Show(new Rect(5, 5, 0, 0), new DataEntryPopup("Collection Type Name", "Create Collection Formatter", SubmitCallback));
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c958f403b85740698edbcc10c951bce
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences:
|
||||
- banner: {fileID: 2800000, guid: 0a00d33b88ebe3f4eb5dfb0d2a1c0726, type: 3}
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 248ef14759720fa4a800416d09dc3b23
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,23 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace QFSW.QC.Editor.Tools
|
||||
{
|
||||
public static class PrefabUtil
|
||||
{
|
||||
public static void RecordPrefabInstancePropertyModificationsFullyRecursive(GameObject objRoot)
|
||||
{
|
||||
PrefabUtility.RecordPrefabInstancePropertyModifications(objRoot);
|
||||
foreach (Component comp in objRoot.GetComponents<Component>())
|
||||
{
|
||||
PrefabUtility.RecordPrefabInstancePropertyModifications(comp);
|
||||
}
|
||||
|
||||
for (int i = 0; i < objRoot.transform.childCount; i++)
|
||||
{
|
||||
Transform child = objRoot.transform.GetChild(i);
|
||||
RecordPrefabInstancePropertyModificationsFullyRecursive(child.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8efd6ec275d0caa46920e3c03f6e3925
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user