This commit is contained in:
Даниил Заикин
2026-06-17 20:44:53 +03:00
parent 9d153773c2
commit b133e7c656
1880 changed files with 244545 additions and 0 deletions
@@ -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;
}
}
}
@@ -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; }
}
}
@@ -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) { }
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8115824be443344f29ef0da5a1c05a3b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e52ff948b143dc54684640418a70f825
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6c171fb328ed92146bd061b07d446a1a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6d0cd481e1faecd4a8159c22114d7d9a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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));
}
}
}
}
}
@@ -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 { }
}
@@ -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;
}
}
}
}
@@ -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();
}
}
}
@@ -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;
}
}
}
@@ -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": []
}
@@ -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;
}
}
}
@@ -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:
@@ -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();
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 37cda8ee039ccf9499ee9ed64f06664a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -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();
}
}
}
@@ -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));
}
}
}
@@ -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