-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #26 from nasshu2916/feature/artnet-recorder-file-g…
…enerate DmxRecorder の保存先を wildcard で生成できるようにする
- Loading branch information
Showing
15 changed files
with
408 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Globalization; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Text.RegularExpressions; | ||
using UnityEngine; | ||
|
||
namespace ArtNet.Editor.DmxRecorder | ||
{ | ||
internal class Wildcard | ||
{ | ||
private readonly Func<string> _resolver; | ||
|
||
public string Pattern { get; } | ||
public string Label { get; } | ||
|
||
internal Wildcard(string pattern, Func<string> resolver) | ||
{ | ||
Pattern = pattern; | ||
Label = Pattern; | ||
_resolver = resolver; | ||
} | ||
|
||
internal string Resolve() | ||
{ | ||
return _resolver == null ? string.Empty : _resolver(); | ||
} | ||
} | ||
|
||
public static class DefaultWildcard | ||
{ | ||
/// <summary> | ||
/// The Recorder name. | ||
/// </summary> | ||
public static readonly string Recorder = GeneratePattern("Recorder"); | ||
|
||
/// <summary> | ||
/// The date when the recording session started (in the yyyy-MM-dd format). | ||
/// </summary> | ||
public static readonly string Date = GeneratePattern("Date"); | ||
|
||
/// <summary> | ||
/// The time the recording session started (in the 00h00m format). | ||
/// </summary> | ||
public static readonly string Time = GeneratePattern("Time"); | ||
|
||
/// <summary> | ||
/// The take number (which is incremented every time a new session is started). | ||
/// </summary> | ||
public static readonly string Take = GeneratePattern("Take"); | ||
|
||
/// <summary> | ||
/// The file extension of the output format. | ||
/// </summary> | ||
public static readonly string Extension = GeneratePattern("Extension"); | ||
|
||
private static string GeneratePattern(string tag) | ||
{ | ||
return "<" + tag + ">"; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// A class that provides a way to generate names of output files, with support for wildcards. | ||
/// </summary> | ||
[Serializable] | ||
public class FileGenerator | ||
{ | ||
[SerializeField] private string _directory = "Recordings"; | ||
[SerializeField] private string _fileName = DefaultWildcard.Recorder; | ||
|
||
private readonly List<Wildcard> _wildcards; | ||
internal IEnumerable<Wildcard> Wildcards => _wildcards; | ||
|
||
|
||
public string Directory | ||
{ | ||
get => _directory; | ||
set => _directory = value; | ||
} | ||
|
||
public string FileName | ||
{ | ||
get => _fileName; | ||
set => _fileName = value; | ||
} | ||
|
||
internal RecorderSettings RecorderSettings { get; private set; } | ||
|
||
internal FileGenerator(RecorderSettings recorderSettings) | ||
{ | ||
RecorderSettings = recorderSettings; | ||
_wildcards = new List<Wildcard> | ||
{ | ||
new(DefaultWildcard.Recorder, RecorderResolver), | ||
new(DefaultWildcard.Date, DateResolver), | ||
new(DefaultWildcard.Time, TimeResolver), | ||
new(DefaultWildcard.Take, TakeResolver), | ||
new(DefaultWildcard.Extension, ExtensionResolver) | ||
}; | ||
} | ||
|
||
private string RecorderResolver() | ||
{ | ||
return SanitizeInvalidName(RecorderSettings.name); | ||
} | ||
|
||
private static string DateResolver() | ||
{ | ||
var date = DateTime.Now; | ||
return date.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture); | ||
} | ||
|
||
private static string TimeResolver() | ||
{ | ||
var date = DateTime.Now; | ||
return $"{date:HH}h{date:mm}m"; | ||
} | ||
|
||
private string TakeResolver() | ||
{ | ||
return RecorderSettings.Take.ToString("000"); | ||
} | ||
|
||
private string ExtensionResolver() | ||
{ | ||
return RecorderSettings.Extension; | ||
} | ||
|
||
public string AbsolutePath() | ||
{ | ||
return OutputDirectory() + OutputFileName(); | ||
} | ||
|
||
public string AssetsRelativePath() | ||
{ | ||
var path = OutputDirectoryPath(); | ||
return "Assets/" + path + OutputFileName(); | ||
} | ||
|
||
public string OutputDirectory() | ||
{ | ||
var path = OutputDirectoryPath(); | ||
return Application.dataPath + Path.DirectorySeparatorChar + path; | ||
} | ||
|
||
private string OutputDirectoryPath() | ||
{ | ||
var path = ApplyWildcards(Directory); | ||
if (!string.IsNullOrEmpty(path) && !path.EndsWith("/")) | ||
path += "/"; | ||
return path; | ||
} | ||
|
||
public string OutputFileName() | ||
{ | ||
return ApplyWildcards(SanitizeFileName(FileName)) + "." + ExtensionResolver(); | ||
} | ||
|
||
public void CreateDirectory() | ||
{ | ||
var path = OutputDirectory(); | ||
if (!string.IsNullOrEmpty(path) && !System.IO.Directory.Exists(path)) | ||
System.IO.Directory.CreateDirectory(path); | ||
} | ||
|
||
/// <summary> | ||
/// Replaces any invalid path character by "_". | ||
/// </summary> | ||
internal static string SanitizeInvalidName(string fileName) | ||
{ | ||
var invalidChars = Path.GetInvalidFileNameChars(); | ||
return invalidChars.Aggregate(fileName, (current, c) => current.Replace(c, '_')); | ||
} | ||
|
||
/// <summary> | ||
/// Replaces any occurrence of "/" or "\" in file name with "_". | ||
/// </summary> | ||
internal static string SanitizeFileName(string fileName) | ||
{ | ||
return Regex.Replace(fileName, @"[\\|/]", "_"); | ||
} | ||
|
||
private string ApplyWildcards(string str) | ||
{ | ||
if (string.IsNullOrEmpty(str)) | ||
return string.Empty; | ||
|
||
foreach (var w in Wildcards) | ||
{ | ||
str = str.Replace(w.Pattern, w.Resolve()); | ||
} | ||
|
||
return str; | ||
} | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
148 changes: 148 additions & 0 deletions
148
Assets/ArtNet/Editor/DmxRecorder/FileGeneratorDrawer.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
using System.IO; | ||
using UnityEditor; | ||
using UnityEngine; | ||
|
||
namespace ArtNet.Editor.DmxRecorder | ||
{ | ||
[CustomPropertyDrawer(typeof(FileGenerator))] | ||
internal class FileGeneratorDrawer : TargetedPropertyDrawer<FileGenerator> | ||
{ | ||
private static class Styles | ||
{ | ||
internal static readonly GUIContent FileNameLabel = new("File Name", "The name of the file to record to."); | ||
internal static readonly GUIContent AddWildcardButton = new("+ Wildcards", | ||
"Add a wildcard to the file name. Wildcards are replaced with values."); | ||
internal static readonly GUIContent PathSelectButton = new("...", "Select the output location"); | ||
} | ||
|
||
private SerializedProperty _fileName; | ||
private SerializedProperty _directory; | ||
|
||
private static readonly GUIStyle PathPreviewStyle = new(GUI.skin.label) | ||
{ | ||
wordWrap = true, | ||
stretchHeight = true, | ||
stretchWidth = true, | ||
padding = new RectOffset(20, 0, 0, 0), | ||
clipping = TextClipping.Overflow | ||
}; | ||
|
||
private static Texture2D _openPathIcon; | ||
|
||
protected override void Initialize(SerializedProperty property) | ||
{ | ||
if (_openPathIcon == null) _openPathIcon = IconHelper.FolderOpen as Texture2D; | ||
|
||
base.Initialize(property); | ||
|
||
_fileName = property.FindPropertyRelative("_fileName"); | ||
_directory = property.FindPropertyRelative("_directory"); | ||
} | ||
|
||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) | ||
{ | ||
Initialize(property); | ||
|
||
EditorGUI.BeginProperty(position, label, property); | ||
|
||
using (new EditorGUILayout.HorizontalScope()) | ||
{ | ||
EditorGUILayout.PropertyField(_fileName, Styles.FileNameLabel); | ||
|
||
if (GUILayout.Button(Styles.AddWildcardButton, EditorStyles.popup, GUILayout.Width(90))) | ||
{ | ||
GUI.FocusControl(null); | ||
var menu = new GenericMenu(); | ||
|
||
foreach (var w in Target.Wildcards) | ||
{ | ||
var pattern = w.Pattern; | ||
menu.AddItem(new GUIContent(w.Label), | ||
false, | ||
() => | ||
{ | ||
_fileName.stringValue += pattern; | ||
_fileName.serializedObject.ApplyModifiedProperties(); | ||
}); | ||
} | ||
|
||
menu.ShowAsContext(); | ||
} | ||
} | ||
|
||
|
||
using (new EditorGUILayout.HorizontalScope()) | ||
{ | ||
EditorGUILayout.LabelField("Assets Path", GUILayout.Width(EditorGUIUtility.labelWidth)); | ||
|
||
var restRect = GUILayoutUtility.GetRect(GUIContent.none, GUI.skin.textField); | ||
var labelRect = new Rect(restRect.x, restRect.y, 50, restRect.height); | ||
GUI.Label(labelRect, "Assets" + Path.DirectorySeparatorChar, EditorStyles.label); | ||
var directoryInputRect = new Rect( | ||
restRect.x + 55, | ||
restRect.y, | ||
restRect.width - 55, | ||
restRect.height | ||
); | ||
_directory.stringValue = EditorGUI.TextField(directoryInputRect, _directory.stringValue); | ||
|
||
if (GUILayout.Button(Styles.PathSelectButton, EditorStyles.miniButton, GUILayout.Width(30))) | ||
{ | ||
var outputDirectory = Target.OutputDirectory(); | ||
var newDirectory = EditorUtility.OpenFolderPanel("Select Folder", outputDirectory, ""); | ||
GUI.FocusControl(null); | ||
|
||
if (!string.IsNullOrEmpty(newDirectory)) | ||
{ | ||
if (!newDirectory.StartsWith(Application.dataPath)) | ||
{ | ||
EditorUtility.DisplayDialog("Invalid Path", | ||
"Selected path " + newDirectory + " is not within the project's Assets folder.", | ||
"Ok"); | ||
} | ||
else | ||
{ | ||
newDirectory = newDirectory[Application.dataPath.Length..]; | ||
if (newDirectory.StartsWith("/") || newDirectory.StartsWith("\\")) | ||
{ | ||
newDirectory = newDirectory[1..]; | ||
} | ||
|
||
_directory.stringValue = newDirectory; | ||
_directory.serializedObject.ApplyModifiedProperties(); | ||
} | ||
} | ||
|
||
GUIUtility.ExitGUI(); | ||
} | ||
} | ||
|
||
|
||
using (new EditorGUILayout.HorizontalScope()) | ||
{ | ||
EditorGUILayout.PrefixLabel(" "); | ||
|
||
var layoutOptions = new[] | ||
{ | ||
GUILayout.ExpandWidth(true), | ||
GUILayout.ExpandHeight(true) | ||
}; | ||
|
||
var outputPath = Target.AbsolutePath(); | ||
var rect = GUILayoutUtility.GetRect(new GUIContent(outputPath), PathPreviewStyle, layoutOptions); | ||
EditorGUI.SelectableLabel(rect, outputPath, PathPreviewStyle); | ||
|
||
if (GUILayout.Button(_openPathIcon, EditorStyles.miniButton, GUILayout.Width(30))) | ||
{ | ||
var outputDir = Target.OutputDirectory(); | ||
var dir = new DirectoryInfo(outputDir); | ||
if (!dir.Exists) dir.Create(); | ||
|
||
EditorUtility.RevealInFinder(outputDir); | ||
} | ||
} | ||
|
||
EditorGUI.EndProperty(); | ||
} | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.