diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Extensions.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities.meta similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/Extensions.meta rename to Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities.meta diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/AssetDatabaseUtility.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/AssetDatabaseUtility.cs similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/AssetDatabaseUtility.cs rename to Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/AssetDatabaseUtility.cs diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/AssetDatabaseUtility.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/AssetDatabaseUtility.cs.meta similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/AssetDatabaseUtility.cs.meta rename to Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/AssetDatabaseUtility.cs.meta diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/ColorExtensions.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/ColorExtensions.cs new file mode 100644 index 0000000..2bd51f0 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/ColorExtensions.cs @@ -0,0 +1,49 @@ +/* MIT License + +Copyright (c) 2016 Edward Rowe, RedBlueGames + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +namespace RedBlueGames.MulliganRenamer +{ + using UnityEngine; + + /// + /// Extension methods for the Color class + /// + public static class ColorExtensions + { + /// + /// Sets the alpha on the color to the specified value and returns the result + /// + /// Color to modify + /// New alpha value + /// Resulting color + public static Color CreateCopyWithNewAlpha(this Color color, float alpha) + { + var alphaModified = new Color( + color.r, + color.g, + color.b, + alpha); + return alphaModified; + } + } +} diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/ColorExtensions.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/ColorExtensions.cs.meta new file mode 100644 index 0000000..872b896 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/ColorExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e80ef52f824374a3d850849d5318c8d0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/EditorCoroutineUtility.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/EditorCoroutineUtility.cs new file mode 100644 index 0000000..005aee7 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/EditorCoroutineUtility.cs @@ -0,0 +1,50 @@ +namespace RedBlueGames.MulliganRenamer +{ + using System; + using System.Collections; + using UnityEditor; + using UnityEngine; + + public static class EditorCoroutineUtility + { + /// + /// Starts a Coroutine using Unity Editor's update loop. This is useful for EditorWindows + /// which aren't MonoBehaviours and therefore can't use Coroutines. + /// Utility function adapted from https://forum.unity.com/threads/using-unitywebrequest-in-editor-tools.397466/#post-4485181 + /// + /// + /// + public static void StartBackgroundTask(IEnumerator update, Action onEnd = null) + { + EditorApplication.CallbackFunction closureCallback = null; + + closureCallback = () => + { + try + { + if (update.MoveNext() == false) + { + if (onEnd != null) + { + onEnd(); + } + + EditorApplication.update -= closureCallback; + } + } + catch (Exception ex) + { + if (onEnd != null) + { + onEnd(); + } + + Debug.LogException(ex); + EditorApplication.update -= closureCallback; + } + }; + + EditorApplication.update += closureCallback; + } + } +} \ No newline at end of file diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/EditorCoroutineUtility.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/EditorCoroutineUtility.cs.meta new file mode 100644 index 0000000..db4c6fa --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/EditorCoroutineUtility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e2209f9be6a614dc78f4f72ca8a413e6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/GUIControlNameUtility.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/GUIControlNameUtility.cs similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/GUIControlNameUtility.cs rename to Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/GUIControlNameUtility.cs diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/GUIControlNameUtility.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/GUIControlNameUtility.cs.meta similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/GUIControlNameUtility.cs.meta rename to Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/GUIControlNameUtility.cs.meta diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Extensions/GameObjectExtensions.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/GameObjectExtensions.cs similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/Extensions/GameObjectExtensions.cs rename to Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/GameObjectExtensions.cs diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Extensions/GameObjectExtensions.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/GameObjectExtensions.cs.meta similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/Extensions/GameObjectExtensions.cs.meta rename to Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/GameObjectExtensions.cs.meta diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Extensions/ListExtensions.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/ListExtensions.cs similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/Extensions/ListExtensions.cs rename to Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/ListExtensions.cs diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Extensions/ListExtensions.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/ListExtensions.cs.meta similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/Extensions/ListExtensions.cs.meta rename to Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/ListExtensions.cs.meta diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/MulliganEditorGUIUtilities.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/MulliganEditorGUIUtilities.cs new file mode 100644 index 0000000..5a9ccef --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/MulliganEditorGUIUtilities.cs @@ -0,0 +1,187 @@ +/* MIT License + +Copyright (c) 2016 Edward Rowe, RedBlueGames + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +namespace RedBlueGames.MulliganRenamer +{ + using System.Collections.Generic; + using UnityEditor; + using UnityEngine; + + public static class MulliganEditorGUIUtilities + { + /// + /// Draw a DiffLabel, which draws a simple EditorGUILabel populated with the results from a rename op (diff). + /// + /// Rect to draw in + /// The result of a RenameOp, which contains the diffs to render + /// Flag to show the name before the op, instead of the result + /// Style of the DiffLabel + /// Style for the EditorGUILabel itself + public static void DrawDiffLabel(Rect rect, RenameResult renameResult, bool showBefore, DiffLabelStyle resultLabelStyle, GUIStyle style) + { + var labelText = string.Empty; + if (!resultLabelStyle.HideDiff) + { + ApplyBackgroundColorToDiff( + rect, + style, + renameResult, + resultLabelStyle.OperationToShow, + resultLabelStyle.DiffBackgroundColor); + } + + labelText = showBefore ? renameResult.GetOriginalColored(resultLabelStyle.DiffTextColor) : + renameResult.GetResultColored(resultLabelStyle.DiffTextColor); + EditorGUI.LabelField(rect, labelText, style); + } + + private static void ApplyBackgroundColorToDiff( + Rect rect, + GUIStyle style, + RenameResult renameContent, + DiffOperation operationToColor, + Color backgroundColor) + { + if (string.IsNullOrEmpty(renameContent.Original)) + { + return; + } + + // Blocks don't need padding or margin because it's accounted for + // when we measure the total. We only want to know the size of each content block . + var blockStyle = new GUIStyle(style); + blockStyle.margin = new RectOffset(); + blockStyle.padding = new RectOffset(); + + var position = rect; + var allTextSoFar = string.Empty; + foreach (var diff in renameContent) + { + // We want to skip whatever diff we aren't rendering on this column + // (Column 1 shows deletions, Column 2 shows insertions) + if (diff.Operation != operationToColor && diff.Operation != DiffOperation.Equal) + { + continue; + } + + if (diff.Operation == operationToColor) + { + var totalRect = style.CalcSize(new GUIContent(allTextSoFar)); + var blockRect = new Rect(position.x + totalRect.x - style.padding.left, position.y, 0, totalRect.y); + var spaceBlocks = GetConsecutiveBlocksOfToken(diff.Result, ' '); + + foreach (var block in spaceBlocks) + { + var blockSize = blockStyle.CalcSize(new GUIContent(block)); + + blockRect.width = blockSize.x; + var textureWidth = ((blockRect.x + blockRect.width) < (rect.x + rect.width)) + ? blockRect.width + : Mathf.Max(0f, (rect.x + rect.width) - (blockRect.x)); + + var textureRect = new Rect( + blockRect.x, + blockRect.y, + textureWidth, + blockRect.height); + + var textColorTransparent = backgroundColor; + + var oldColor = GUI.color; + GUI.color = textColorTransparent; + GUI.DrawTexture(textureRect, Texture2D.whiteTexture); + GUI.color = oldColor; + + blockRect.x += blockSize.x; + } + } + + allTextSoFar += diff.Result; + } + } + + private static List GetConsecutiveBlocksOfToken(string str, char token) + { + var spaceBlocks = new List(); + var characterStreak = new System.Text.StringBuilder(); + var isTokenBlock = false; + if (str.Length > 0) + { + characterStreak.Append(str[0]); + isTokenBlock = str[0] == token; + } + + for (int i = 1; i < str.Length; ++i) + { + if (isTokenBlock) + { + if (str[i] == token) + { + characterStreak.Append(str[i]); + } + else + { + spaceBlocks.Add(characterStreak.ToString()); + characterStreak = new System.Text.StringBuilder(); + characterStreak.Append(str[i]); + + isTokenBlock = false; + } + } + else + { + if (str[i] == token) + { + spaceBlocks.Add(characterStreak.ToString()); + characterStreak = new System.Text.StringBuilder(); + characterStreak.Append(str[i]); + + isTokenBlock = true; + } + else + { + characterStreak.Append(str[i]); + } + } + } + + if (characterStreak.Length > 0) + { + spaceBlocks.Add(characterStreak.ToString()); + } + + return spaceBlocks; + } + + public class DiffLabelStyle + { + public bool HideDiff { get; set; } + + public DiffOperation OperationToShow { get; set; } + + public Color DiffTextColor { get; set; } + + public Color DiffBackgroundColor { get; set; } + } + } +} \ No newline at end of file diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/MulliganEditorGUIUtilities.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/MulliganEditorGUIUtilities.cs.meta new file mode 100644 index 0000000..e72c988 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/MulliganEditorGUIUtilities.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: eb4eba937515b443dbd5f697b3430cb6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Extensions/ObjectExtensions.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/ObjectExtensions.cs similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/Extensions/ObjectExtensions.cs rename to Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/ObjectExtensions.cs diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Extensions/ObjectExtensions.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/ObjectExtensions.cs.meta similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/Extensions/ObjectExtensions.cs.meta rename to Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/ObjectExtensions.cs.meta diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/RectExtensions.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/RectExtensions.cs similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/RectExtensions.cs rename to Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/RectExtensions.cs diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/RectExtensions.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/RectExtensions.cs.meta similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/RectExtensions.cs.meta rename to Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/RectExtensions.cs.meta diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/RenameResultUtilities.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/RenameResultUtilities.cs new file mode 100644 index 0000000..e2bf12e --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/RenameResultUtilities.cs @@ -0,0 +1,136 @@ +namespace RedBlueGames.MulliganRenamer +{ + using System.Collections; + using System.Text.RegularExpressions; + using System.Collections.Generic; + using UnityEngine; + + /// + /// Static functions that help manipulate or create RenameResults + /// + public static class RenameResultUtilities + { + public delegate string ReplaceMatchFunction(Match match); + + /// + /// Compares two strings and constructs a diff based on their differences. It simply compares character by character, + /// no analysis is done to see if a character could have moved. + /// + /// The original string. Letters in here that aren't in the new string will be deletions. + /// The new string. Letters in here that aren't in the originalString will be insertions. + /// A diff sequence representing changes between the two strings + public static RenameResult GetDiffResultFromStrings(string originalString, string newString) + { + var renameResult = new RenameResult(); + var consecutiveEqualChars = string.Empty; + var longestLength = Mathf.Max(originalString.Length, newString.Length); + for (int i = 0; i < longestLength; ++i) + { + if (i >= newString.Length) + { + // Consolidate the diff with the remainder of the string so that we get a cleaner diff + // (ex: ABC => ABDog comes back as [AB=],[C-],[Dog+] instead of [AB=],[C-],[D+],[og+]) + ConsolidateRemainderOfStringIntoRenameResult(renameResult, originalString, i, DiffOperation.Deletion); + break; + } + else if (i >= originalString.Length) + { + // Consolidate the diff with the remainder of the string so that we get a cleaner diff + // (ex: ABC => ABDog comes back as [AB=],[C-],[Dog+] instead of [AB=],[C-],[D+],[og+]) + ConsolidateRemainderOfStringIntoRenameResult(renameResult, newString, i, DiffOperation.Insertion); + break; + } + else + { + string oldLetter = originalString.Substring(i, 1); + string newLetter = newString.Substring(i, 1); + if (oldLetter.Equals(newLetter)) + { + consecutiveEqualChars = string.Concat(consecutiveEqualChars, oldLetter); + } + else + { + if (!string.IsNullOrEmpty(consecutiveEqualChars)) + { + renameResult.Add(new Diff(consecutiveEqualChars, DiffOperation.Equal)); + consecutiveEqualChars = string.Empty; + } + + renameResult.Add(new Diff(oldLetter, DiffOperation.Deletion)); + renameResult.Add(new Diff(newLetter, DiffOperation.Insertion)); + } + } + } + + if (!string.IsNullOrEmpty(consecutiveEqualChars)) + { + renameResult.Add(new Diff(consecutiveEqualChars, DiffOperation.Equal)); + } + + return renameResult; + } + + /// + /// Creates a RenameResult (Diff) based on regex matches and a replacement function to replace each match. + /// + /// Original string to match against + /// Replacement function, used to replace each match + /// Matches from a regex Matches query + /// A diff of the original and resulting strings (original with Matches replaced using the replacement function) + public static RenameResult CreateDiffFromReplacedMatches(string originalName, ReplaceMatchFunction replacementFunction, MatchCollection matches) + { + var renameResult = new RenameResult(); + var nextMatchStartingIndex = 0; + foreach (System.Text.RegularExpressions.Match match in matches) + { + // Grab the substring before the match + if (nextMatchStartingIndex < match.Index) + { + string before = originalName.Substring(nextMatchStartingIndex, match.Index - nextMatchStartingIndex); + renameResult.Add(new Diff(before, DiffOperation.Equal)); + } + + // Add the match as a deletion + renameResult.Add(new Diff(match.Value, DiffOperation.Deletion)); + + // Add the result as an insertion + var result = replacementFunction.Invoke(match); + if (!string.IsNullOrEmpty(result)) + { + renameResult.Add(new Diff(result, DiffOperation.Insertion)); + } + + nextMatchStartingIndex = match.Index + match.Length; + } + + if (nextMatchStartingIndex < originalName.Length) + { + var lastSubstring = originalName.Substring(nextMatchStartingIndex, originalName.Length - nextMatchStartingIndex); + renameResult.Add(new Diff(lastSubstring, DiffOperation.Equal)); + } + + return renameResult; + } + + private static void ConsolidateRemainderOfStringIntoRenameResult( + RenameResult renameResult, + string originalString, + int indexToStartFrom, + DiffOperation diffOpIfDifferent) + { + var remainderOfOldString = originalString.Substring(indexToStartFrom, originalString.Length - indexToStartFrom); + if (renameResult.Count > 0 && renameResult[renameResult.Count - 1].Operation == diffOpIfDifferent) + { + // last diff in the sequence matches the desired diff for the remainder of the string. Consolidate them + var previousDiff = renameResult[renameResult.Count - 1]; + renameResult[renameResult.Count - 1] = new Diff( + string.Concat(previousDiff.Result, remainderOfOldString), + previousDiff.Operation); + } + else + { + renameResult.Add(new Diff(remainderOfOldString, diffOpIfDifferent)); + } + } + } +} \ No newline at end of file diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/RenameResultUtilities.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/RenameResultUtilities.cs.meta new file mode 100644 index 0000000..8a9413d --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/RenameResultUtilities.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 161a99cb193784a3e94cc1ebdbcc0cbf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/StringUtilities.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/StringUtilities.cs similarity index 86% rename from Assets/RedBlueGames/MulliganRenamer/Editor/StringUtilities.cs rename to Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/StringUtilities.cs index a828aee..b2aaee8 100644 --- a/Assets/RedBlueGames/MulliganRenamer/Editor/StringUtilities.cs +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/StringUtilities.cs @@ -21,11 +21,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + namespace RedBlueGames.MulliganRenamer { - using System.Collections; - using System.Collections.Generic; - using UnityEngine; + using System.Text.RegularExpressions; public static class StringUtilities { @@ -49,5 +48,11 @@ public static string AddCommasBetweenStrings(string[] strings) return fullString; } + + const string HTML_TAG_PATTERN = "<.*?>"; + public static string StripHTML(string inputString, string replaceWith = "") + { + return Regex.Replace(inputString, HTML_TAG_PATTERN, replaceWith); + } } } \ No newline at end of file diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/StringUtilities.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/StringUtilities.cs.meta similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/StringUtilities.cs.meta rename to Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/StringUtilities.cs.meta diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/UniqueList.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/UniqueList.cs similarity index 98% rename from Assets/RedBlueGames/MulliganRenamer/Editor/UniqueList.cs rename to Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/UniqueList.cs index fe1947d..a0c5809 100644 --- a/Assets/RedBlueGames/MulliganRenamer/Editor/UniqueList.cs +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/UniqueList.cs @@ -181,7 +181,10 @@ public void AddRange(List collection) { foreach (var obj in collection) { - this.Add(obj); + if (!this.Contains(obj)) + { + this.Add(obj); + } } } } diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/UniqueList.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/UniqueList.cs.meta similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/UniqueList.cs.meta rename to Assets/RedBlueGames/MulliganRenamer/Editor/ExtensionsAndUtilities/UniqueList.cs.meta diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/GUI.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/GUI.meta new file mode 100644 index 0000000..7640346 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/GUI.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8d65db04978da4837bbff01115f1785d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/ManagePresetsWindow.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/GUI/ManagePresetsWindow.cs similarity index 89% rename from Assets/RedBlueGames/MulliganRenamer/Editor/ManagePresetsWindow.cs rename to Assets/RedBlueGames/MulliganRenamer/Editor/GUI/ManagePresetsWindow.cs index 1e9a652..f9a68c6 100644 --- a/Assets/RedBlueGames/MulliganRenamer/Editor/ManagePresetsWindow.cs +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/GUI/ManagePresetsWindow.cs @@ -53,7 +53,7 @@ public void PopulateWithPresets(List presets) var copySerialized = JsonUtility.ToJson(preset); var copy = JsonUtility.FromJson(copySerialized); this.presetsToDraw.Add(copy); - this.uniqueNames.Add(copy, "Preset " + i); + this.uniqueNames.Add(copy, LocalizationManager.Instance.GetTranslation("preset") + " " + i); } this.reorderableList.list = this.presetsToDraw; @@ -76,7 +76,7 @@ private void OnEnable() private void DrawHeader(Rect rect) { - GUI.Label(rect, "Saved Presets", EditorStyles.label); + GUI.Label(rect, LocalizationManager.Instance.GetTranslation("savedPresets"), EditorStyles.label); } private void DrawElement(Rect rect, int index, bool isActive, bool isFocused) @@ -147,7 +147,7 @@ private void OnGUI() EditorGUILayout.BeginHorizontal(); GUILayout.Space(50.0f); EditorGUILayout.LabelField( - "You have no saved Rename Operation presets. Select 'Save as...' in the \"Presets\" dropdown to create a new preset.", + LocalizationManager.Instance.GetTranslation("errorNoSavedPresets"), EditorStyles.wordWrappedLabel); GUILayout.Space(50.0f); EditorGUILayout.EndHorizontal(); @@ -160,10 +160,13 @@ private void HandleElementRemoved(UnityEditorInternal.ReorderableList list) var indexToRemove = list.index; var elementToDelete = this.presetsToDraw[indexToRemove]; var popupMessage = string.Format( - "Are you sure you want to delete the preset \"{0}\"?", elementToDelete.Name + LocalizationManager.Instance.GetTranslation("areYouSureDelete"), elementToDelete.Name ); - if (EditorUtility.DisplayDialog("Warning", popupMessage, "Delete Preset", "No")) + if (EditorUtility.DisplayDialog(LocalizationManager.Instance.GetTranslation("warning"), + popupMessage, + LocalizationManager.Instance.GetTranslation("deletePreset"), + LocalizationManager.Instance.GetTranslation("no"))) { this.presetsToDraw.RemoveAt(indexToRemove); this.reorderableList.index = 0; diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/ManagePresetsWindow.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/GUI/ManagePresetsWindow.cs.meta similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/ManagePresetsWindow.cs.meta rename to Assets/RedBlueGames/MulliganRenamer/Editor/GUI/ManagePresetsWindow.cs.meta diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/MulliganRenamerPreviewPanel.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/GUI/MulliganRenamerPreviewPanel.cs similarity index 55% rename from Assets/RedBlueGames/MulliganRenamer/Editor/MulliganRenamerPreviewPanel.cs rename to Assets/RedBlueGames/MulliganRenamer/Editor/GUI/MulliganRenamerPreviewPanel.cs index 180a36f..225bf8c 100644 --- a/Assets/RedBlueGames/MulliganRenamer/Editor/MulliganRenamerPreviewPanel.cs +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/GUI/MulliganRenamerPreviewPanel.cs @@ -23,7 +23,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE namespace RedBlueGames.MulliganRenamer { - using System.Collections; + using System; using System.Collections.Generic; using UnityEditor; using UnityEngine; @@ -33,8 +33,13 @@ namespace RedBlueGames.MulliganRenamer /// public class MulliganRenamerPreviewPanel { - private const float PreviewPanelFirstColumnMinSize = 50.0f; + private const float MinColumnWidth = 125f; private const float PreviewRowHeight = 18.0f; + private const float DividerWidth = 1f; + + private const float DividerHotSpotPadding = 3f; + + private const float DeleteButtonWidth = 16.0f; /// /// Event fired when the AddSelectedObjects button is clicked @@ -56,6 +61,8 @@ public class MulliganRenamerPreviewPanel /// public event System.Action ObjectRemovedAtIndex; + public event System.Action ColumnsResized; + /// /// The validate object function, used to determine if objects should be /// allows to be added. @@ -67,6 +74,16 @@ public class MulliganRenamerPreviewPanel private GUIContents guiContents; private GUIStyles guiStyles; + private PreviewPanelContentsLayout contentsLayout; + + private bool blockDivisorClick; + + private bool isResizingFirstDivider; + + private bool isResizingSecondDivider; + + private bool drewEmptyLastFrame = true; + /// /// Gets or sets a value indicating whether this /// /// should disable its AddSelectedObjects button. @@ -88,6 +105,26 @@ public class MulliganRenamerPreviewPanel /// public int NumPreviouslyRenamedObjects { get; set; } + /// + /// Gets or sets the color to use for Text on diff Insertions + /// + public Color InsertionTextColor { get; set; } + + /// + /// Gets or sets the color to use for Text on diff Deletions + /// + public Color DeletionTextColor { get; set; } + + /// + /// Gets or sets the color to use for the background of Text on diff Insertions + /// + public Color InsertionBackgroundColor { get; set; } + + /// + /// Gets or sets the color to use for the background of Text on diff Deletions + /// + public Color DeletionBackgroundColor { get; set; } + public enum ColumnStyle { OriginalAndFinalOnly, @@ -95,96 +132,169 @@ public enum ColumnStyle StepwiseHideFinal } + public enum PreviewRowResult + { + None, + Delete, + } + public MulliganRenamerPreviewPanel() { + // Set colors to obnoxious defaults so that we can tell when defaults are improperly being used + // (Users shouldn't ever see these) + this.InsertionTextColor = Color.green; + this.InsertionBackgroundColor = this.InsertionTextColor.CreateCopyWithNewAlpha(this.InsertionTextColor.a * 0.2f); + this.DeletionTextColor = Color.red; + this.DeletionBackgroundColor = this.DeletionTextColor.CreateCopyWithNewAlpha(this.DeletionTextColor.a * 0.2f); + this.InitializeGUIStyles(); this.InitializeGUIContents(); + + this.contentsLayout = new PreviewPanelContentsLayout(); + + this.contentsLayout.WidthForButtons = DeleteButtonWidth; + this.contentsLayout.MinimumColumnWidth = MinColumnWidth; } - private static bool DrawPreviewRow(Rect rowRect, PreviewRowModel info, PreviewRowStyle style) + /// + /// Refresh the GUIContents of this window. Use this if the language changes, for example + /// + public void RefreshGUIContent() + { + this.InitializeGUIContents(); + } + + private static PreviewRowResult DrawPreviewRow(Rect rowRect, PreviewRowModel info, PreviewRowStyle style) { - bool isDeleteClicked = false; + PreviewRowResult result = PreviewRowResult.None; var oldColor = GUI.color; GUI.color = style.BackgroundColor; GUI.DrawTexture(rowRect, Texture2D.whiteTexture); GUI.color = oldColor; + const float RowButtonsSize = DeleteButtonWidth; + const float InitialXOffset = 4f; + const float ColumnContentPadding = 2.0f; + // Space gives us a bit of padding or else we're just too bunched up to the side var deleteButtonRect = new Rect(rowRect); - deleteButtonRect.x += 4.0f; - deleteButtonRect.width = 16.0f; - deleteButtonRect.height = 16.0f; + deleteButtonRect.x += InitialXOffset; + deleteButtonRect.width = RowButtonsSize; + deleteButtonRect.height = RowButtonsSize; deleteButtonRect.y += Mathf.Max(0, (rowRect.height - deleteButtonRect.height) / 2.0f); var deleteButtonStyle = new GUIStyle(EditorStyles.miniButton); deleteButtonStyle.padding = new RectOffset(); if (GUI.Button(deleteButtonRect, "X", deleteButtonStyle)) { - isDeleteClicked = true; + result = PreviewRowResult.Delete; } + var widthOffset = RowButtonsSize + InitialXOffset; var warningRect = new Rect(deleteButtonRect); - warningRect.y = rowRect.y; - warningRect.x += deleteButtonRect.width; - warningRect.width = 16.0f; - warningRect.height = 16.0f; if (info.WarningIcon != null) { + warningRect.y = rowRect.y; + warningRect.x += deleteButtonRect.width; + warningRect.width = RowButtonsSize; + warningRect.height = RowButtonsSize; + var content = new GUIContent(info.WarningIcon, info.WarningMessage); GUI.Box(warningRect, content, style.IconStyle); + + widthOffset += RowButtonsSize; } var iconRect = new Rect(warningRect); - iconRect.x += warningRect.width; - iconRect.width = 16.0f; - iconRect.height = 16.0f; + iconRect.x += RowButtonsSize; + iconRect.width = RowButtonsSize; + iconRect.height = RowButtonsSize; GUI.Box(iconRect, info.Icon, style.IconStyle); var firstColumnRect = new Rect(iconRect); firstColumnRect.x += iconRect.width; - firstColumnRect.width = style.FirstColumnWidth; + firstColumnRect.width = style.FirstColumnWidth - widthOffset; firstColumnRect.height = rowRect.height; if (style.FirstColumnWidth > 0) { - EditorGUI.LabelField(firstColumnRect, info.NameBeforeStep, style.FirstColumnStyle); + var renameResultStyle = new MulliganEditorGUIUtilities.DiffLabelStyle() + { + HideDiff = !info.IsPreviewingSteps, + OperationToShow = DiffOperation.Deletion, + DiffTextColor = style.FirstColumnDiffTextColor, + DiffBackgroundColor = style.FirstColumnDiffBackgroundColor, + }; + + if (info.IsPreviewingSteps) + { + MulliganEditorGUIUtilities.DrawDiffLabel( + firstColumnRect, + info.RenameResultBefore, + true, + renameResultStyle, + style.FirstColumnStyle); + } + else + { + EditorGUI.LabelField(firstColumnRect, info.NameBeforeStep, style.FirstColumnStyle); + } } var secondColumnRect = new Rect(firstColumnRect); - secondColumnRect.x += firstColumnRect.width; - secondColumnRect.width = style.SecondColumnWidth; + secondColumnRect.x += firstColumnRect.width + DividerWidth + ColumnContentPadding; + secondColumnRect.width = style.SecondColumnWidth - DividerWidth - ColumnContentPadding; secondColumnRect.height = rowRect.height; if (style.SecondColumnWidth > 0) { - EditorGUI.LabelField(secondColumnRect, info.NameAtStep, style.SecondColumnStyle); + var renameResultStyle = new MulliganEditorGUIUtilities.DiffLabelStyle() + { + HideDiff = !info.IsPreviewingSteps, + OperationToShow = DiffOperation.Insertion, + DiffTextColor = style.SecondColumnDiffTextColor, + DiffBackgroundColor = style.SecondColumnDiffBackgroundColor, + }; + + if (info.IsPreviewingSteps) + { + MulliganEditorGUIUtilities.DrawDiffLabel( + secondColumnRect, + info.RenameResultAfter, + false, + renameResultStyle, + style.SecondColumnStyle); + } + else + { + EditorGUI.LabelField(secondColumnRect, info.NameAtStep, style.SecondColumnStyle); + } } var thirdColumnRect = new Rect(secondColumnRect); - thirdColumnRect.x += secondColumnRect.width; - thirdColumnRect.width = style.ThirdColumnWidth; + thirdColumnRect.x += secondColumnRect.width + DividerWidth + ColumnContentPadding; + thirdColumnRect.width = rowRect.width; thirdColumnRect.height = rowRect.height; + if (style.ThirdColumnWidth > 0) { EditorGUI.LabelField(thirdColumnRect, info.FinalName, style.ThirdColumnStyle); } - return isDeleteClicked; + if (GUI.Button(iconRect, "", GUIStyle.none)) + { + EditorGUIUtility.PingObject(info.Object); + } + + return result; } private void InitializeGUIContents() { this.guiContents = new GUIContents(); - this.guiContents.DropPrompt = new GUIContent( - "No objects specified for rename. Drag objects here to rename them, or"); - - this.guiContents.DropPromptHintInsideScroll = new GUIContent( - "Add more objects by dragging them here"); - - this.guiContents.DropPromptHint = new GUIContent( - "Add more objects by dragging them into the above panel"); - - this.guiContents.DropPromptRepeat = new GUIContent( - "To rename more objects, drag them here, or"); + this.guiContents.DropPrompt = new GUIContent(LocalizationManager.Instance.GetTranslation("noObjectsSpecified")); + this.guiContents.DropPromptHintInsideScroll = new GUIContent(LocalizationManager.Instance.GetTranslation("addMoreObjectsDragHere")); + this.guiContents.DropPromptHint = new GUIContent(LocalizationManager.Instance.GetTranslation("addMoreObjectsDragPanel")); + this.guiContents.DropPromptRepeat = new GUIContent(LocalizationManager.Instance.GetTranslation("toRenameMoreObjects")); } private void InitializeGUIStyles() @@ -244,18 +354,14 @@ private void InitializeGUIStyles() this.guiStyles.PreviewScroll = new GUIStyle(styleName); this.guiStyles.PreviewRowBackgroundEven = new Color(0.3f, 0.3f, 0.3f, 0.2f); - - this.guiStyles.InsertionTextColor = new Color32(6, 214, 160, 255); - this.guiStyles.DeletionTextColor = new Color32(239, 71, 111, 255); + this.guiStyles.RenameCompleteTextColor = new Color32(6, 214, 160, 255); } else { this.guiStyles.PreviewScroll = new GUIStyle(EditorStyles.textArea); + this.guiStyles.RenameCompleteTextColor = new Color32(0, 140, 104, 255); this.guiStyles.PreviewRowBackgroundEven = new Color(0.6f, 0.6f, 0.6f, 0.2f); - - this.guiStyles.InsertionTextColor = new Color32(0, 140, 104, 255); - this.guiStyles.DeletionTextColor = new Color32(189, 47, 79, 255); } this.guiStyles.PreviewRowBackgroundOdd = Color.clear; @@ -284,52 +390,76 @@ public Vector2 Draw(Rect previewPanelRect, Vector2 previewPanelScrollPosition, B if (preview.NumObjects == 0) { this.DrawPreviewPanelContentsEmpty(scrollViewRect); + this.drewEmptyLastFrame = true; } else { var scrollLayout = new PreviewPanelLayout(scrollViewRect); - // Show the one that doesn't quite fit by subtracting one - var firstItemIndex = Mathf.Max(Mathf.FloorToInt(previewPanelScrollPosition.y / PreviewRowHeight) - 1, 0); - // Add one for the one that's off screen above, and one for the one below. I think? - var numItems = Mathf.CeilToInt(scrollLayout.ScrollRect.height / PreviewRowHeight) + 2; + var numItemsVisibleInScrollRect = Mathf.CeilToInt(scrollLayout.ScrollRect.height / PreviewRowHeight) + 2; + + var firstItemIndex = 0; + + // If all the items will fit on screen, just use the 0th elemnt as the first in the list, + // or else we won't generate the correct models for each element + if (preview.NumObjects < numItemsVisibleInScrollRect) + { + firstItemIndex = 0; + } + else + { + // Show the one that doesn't quite fit by subtracting one + firstItemIndex = Mathf.Max(Mathf.FloorToInt(previewPanelScrollPosition.y / PreviewRowHeight) - 1, 0); + + // If they supply a scroll position way below the bottom, clamp to the first element visible on the list + // when scrolled to the bottom + firstItemIndex = Mathf.Min(firstItemIndex, preview.NumObjects - numItemsVisibleInScrollRect); + } var previewContents = PreviewPanelContents.CreatePreviewContentsForObjects( preview, firstItemIndex, - numItems, - this.PreviewStepIndexToShow, - this.guiStyles.DeletionTextColor, - this.guiStyles.InsertionTextColor); + numItemsVisibleInScrollRect, + this.PreviewStepIndexToShow); bool shouldShowSecondColumn = this.ColumnsToShow != ColumnStyle.OriginalAndFinalOnly; bool shouldShowThirdColumn = this.ColumnsToShow != ColumnStyle.StepwiseHideFinal; - var contentsLayout = new PreviewPanelContentsLayout( + + // Force Fit on the first draw draw so that the columns get properly sized initially. + // This is to fix the bug where some of the columns would be collapsed until + // we messed with the preview window. + var forceFitContents = this.drewEmptyLastFrame; + this.contentsLayout.ResizeForContents( scrollLayout.ScrollRect, previewContents, + forceFitContents, shouldShowSecondColumn, shouldShowThirdColumn); + var panelStyle = new PreviewPanelStyle(); + panelStyle.InsertionColor = this.InsertionTextColor; + panelStyle.InsertionBackgroundColor = this.InsertionBackgroundColor; + panelStyle.DeletionColor = this.DeletionTextColor; + panelStyle.DeletionBackgroundColor = this.DeletionBackgroundColor; + newScrollPosition = this.DrawPreviewPanelContentsWithItems( scrollLayout, contentsLayout, + panelStyle, previewPanelScrollPosition, - previewContents, - this.PreviewStepIndexToShow, - shouldShowSecondColumn, - shouldShowThirdColumn); + previewContents); var buttonSpacing = 2.0f; var rightPadding = 2.0f; var addSelectedObjectsButtonRect = new Rect(panelFooterToolbar); - addSelectedObjectsButtonRect.width = 150.0f; + addSelectedObjectsButtonRect.width = 200.0f; addSelectedObjectsButtonRect.x = panelFooterToolbar.xMax - rightPadding - addSelectedObjectsButtonRect.width; var removeAllButtonRect = new Rect(addSelectedObjectsButtonRect); removeAllButtonRect.width = 100.0f; removeAllButtonRect.x -= (removeAllButtonRect.width + buttonSpacing); - if (GUI.Button(removeAllButtonRect, "Remove All")) + if (GUI.Button(removeAllButtonRect, LocalizationManager.Instance.GetTranslation("removeAll"))) { if (this.RemoveAllClicked != null) { @@ -347,6 +477,8 @@ public Vector2 Draw(Rect previewPanelRect, Vector2 previewPanelScrollPosition, B hintRect.width = scrollViewRect.width - addSelectedObjectsButtonRect.width - removeAllButtonRect.width - buttonSpacing; EditorGUI.LabelField(hintRect, this.guiContents.DropPromptHint, this.guiStyles.DropPromptHint); } + + this.drewEmptyLastFrame = false; } var draggedObjects = this.GetDraggedObjectsOverRect(scrollViewRect); @@ -384,18 +516,18 @@ private void DrawPreviewPanelContentsEmpty(Rect previewPanelRect) if (showSuccessfulRenameLabels) { var oldColor = GUI.contentColor; - GUI.contentColor = this.guiStyles.InsertionTextColor; + GUI.contentColor = this.guiStyles.RenameCompleteTextColor; string noun; if (this.NumPreviouslyRenamedObjects == 1) { - noun = "Object"; + noun = LocalizationManager.Instance.GetTranslation("object"); } else { - noun = "Objects"; + noun = LocalizationManager.Instance.GetTranslation("objects"); } - var renameSuccessContent = new GUIContent(string.Format("{0} {1} Renamed", this.NumPreviouslyRenamedObjects, noun)); + var renameSuccessContent = new GUIContent(string.Format("{0} {1} {2}", this.NumPreviouslyRenamedObjects, noun, LocalizationManager.Instance.GetTranslation("renamed"))); EditorGUI.LabelField(labelRect, renameSuccessContent, this.guiStyles.RenameSuccessPrompt); GUI.contentColor = oldColor; @@ -412,11 +544,9 @@ private void DrawPreviewPanelContentsEmpty(Rect previewPanelRect) private Vector2 DrawPreviewPanelContentsWithItems( PreviewPanelLayout scrollLayout, PreviewPanelContentsLayout contentsLayout, + PreviewPanelStyle panelStyle, Vector2 previewPanelScrollPosition, - PreviewPanelContents previewContents, - int renameStep, - bool shouldShowSecondColumn, - bool shouldShowThirdColumn) + PreviewPanelContents previewContents) { // WORKAROUND FOR 5.5.5: Somehow you could "scroll" the preview area, even // when there was nothing to scroll. Force it to not think it's scrolled because @@ -426,8 +556,8 @@ private Vector2 DrawPreviewPanelContentsWithItems( previewPanelScrollPosition.x = 0; } - string originalNameColumnHeader = renameStep < 1 ? "Original" : "Before"; - string newNameColumnHeader = "After"; + string originalNameColumnHeader = LocalizationManager.Instance.GetTranslation(previewContents.RenameStepIndex < 1 ? "original" : "before"); + string newNameColumnHeader = LocalizationManager.Instance.GetTranslation("after"); this.DrawPreviewHeader( scrollLayout.HeaderRect, -previewPanelScrollPosition.x, @@ -440,11 +570,11 @@ private Vector2 DrawPreviewPanelContentsWithItems( var newScrollPosition = GUI.BeginScrollView( scrollLayout.ScrollRect, previewPanelScrollPosition, - contentsLayout.Rect); + contentsLayout.ContentsRect); var rowRect = new Rect(scrollLayout.ScrollRect); - rowRect.width = Mathf.Max(contentsLayout.Rect.width, scrollLayout.ScrollRect.width); - this.DrawPreviewRows(rowRect, previewContents, shouldShowSecondColumn, shouldShowThirdColumn); + rowRect.width = Mathf.Max(contentsLayout.ContentsRect.width, scrollLayout.ScrollRect.width); + this.DrawPreviewRows(rowRect, previewContents, contentsLayout, panelStyle); // Add the hint into the scroll view if there's room if (scrollLayout.ContentsFitWithoutAnyScrolling(contentsLayout)) @@ -454,7 +584,7 @@ private Vector2 DrawPreviewPanelContentsWithItems( GUI.EndScrollView(); - this.DrawDividers(newScrollPosition, scrollLayout, contentsLayout, shouldShowThirdColumn); + this.DrawDividers(newScrollPosition, scrollLayout, contentsLayout); GUI.EndGroup(); @@ -510,13 +640,17 @@ private void DrawPreviewHeader( if (thirdColumnWidth > 0.0f) { - EditorGUI.LabelField(thirdColumnRect, "Final Name", EditorStyles.boldLabel); + EditorGUI.LabelField(thirdColumnRect, LocalizationManager.Instance.GetTranslation("finalName"), EditorStyles.boldLabel); } GUI.EndGroup(); } - private void DrawPreviewRows(Rect previewRowsRect, PreviewPanelContents previewContents, bool showSecondColumn, bool showThirdColumn) + private void DrawPreviewRows( + Rect previewRowsRect, + PreviewPanelContents previewContents, + PreviewPanelContentsLayout layout, + PreviewPanelStyle panelStyle) { for (int i = 0; i < previewContents.NumVisibleRows; ++i) { @@ -527,42 +661,50 @@ private void DrawPreviewRows(Rect previewRowsRect, PreviewPanelContents previewC previewRowStyle.FirstColumnStyle = content.NameChangedThisStep ? this.guiStyles.OriginalNameLabelWhenModified : this.guiStyles.OriginalNameLabelUnModified; - previewRowStyle.FirstColumnWidth = previewContents.LongestOriginalNameWidth; + previewRowStyle.FirstColumnWidth = layout.FirstColumnWidth; previewRowStyle.SecondColumnStyle = content.NameChangedThisStep ? this.guiStyles.NewNameLabelModified : this.guiStyles.NewNameLabelUnModified; - previewRowStyle.SecondColumnWidth = showSecondColumn ? previewContents.LongestNewNameWidth : 0.0f; + previewRowStyle.SecondColumnWidth = layout.SecondColumnWidth; previewRowStyle.ThirdColumnStyle = content.NameChangedThisStep ? this.guiStyles.FinalNameLabelWhenModified : this.guiStyles.FinalNameLabelUnModified; - previewRowStyle.ThirdColumnWidth = showThirdColumn ? previewContents.LongestFinalNameWidth : 0.0f; + previewRowStyle.ThirdColumnWidth = layout.ThirdColumnWidth; previewRowStyle.BackgroundColor = i % 2 == 0 ? this.guiStyles.PreviewRowBackgroundEven : this.guiStyles.PreviewRowBackgroundOdd; + previewRowStyle.FirstColumnDiffTextColor = panelStyle.DeletionColor; + previewRowStyle.FirstColumnDiffBackgroundColor = panelStyle.DeletionBackgroundColor; + previewRowStyle.SecondColumnDiffBackgroundColor = panelStyle.InsertionBackgroundColor; + previewRowStyle.SecondColumnDiffTextColor = panelStyle.InsertionColor; + var rowRect = new Rect(previewRowsRect); rowRect.height = PreviewRowHeight; rowRect.y = previewRowsRect.y + (content.IndexInPreview * rowRect.height); - if (DrawPreviewRow(rowRect, content, previewRowStyle)) + switch (DrawPreviewRow(rowRect, content, previewRowStyle)) { - if (this.ObjectRemovedAtIndex != null) - { - this.ObjectRemovedAtIndex.Invoke(i); - } - - break; + case PreviewRowResult.Delete: + if (this.ObjectRemovedAtIndex != null) + { + this.ObjectRemovedAtIndex.Invoke(i); + } + break; + default: + continue; } + + break; } } private void DrawDividers( Vector2 newScrollPosition, PreviewPanelLayout scrollLayout, - PreviewPanelContentsLayout contentsLayout, - bool shouldShowThirdColumn) + PreviewPanelContentsLayout contentsLayout) { // Put dividers in group so that they scroll (horizontally) var dividerGroup = new Rect(scrollLayout.ScrollRect); @@ -590,26 +732,116 @@ private void DrawDividers( } var firstDividerRect = new Rect( - -newScrollPosition.x + contentsLayout.FirstColumnWidth + contentsLayout.IconSize, + -newScrollPosition.x + contentsLayout.FirstColumnWidth + contentsLayout.WidthForButtons, 0.0f, - 1.0f, + DividerWidth, dividerHeight - 1.0f); - GUI.DrawTexture(firstDividerRect, Texture2D.whiteTexture); - if (shouldShowThirdColumn) + // Reset minimum width - we only want it enforced while resizing columns, so that + // it doesn't affect scroll position when downsizing columns. + contentsLayout.MinimumContentWidth = 0; + + var doubleClickedInDivider = false; + if (DrawDividerAndCheckResize(firstDividerRect, this.isResizingFirstDivider, out doubleClickedInDivider)) + { + this.isResizingFirstDivider = true; + } + + if (doubleClickedInDivider) + { + contentsLayout.ResizeFirstColumnToFitContent(); + this.ColumnsResized.Invoke(); + blockDivisorClick = true; + } + else if (this.isResizingFirstDivider && !blockDivisorClick) + { + contentsLayout.MinimumContentWidth = contentsLayout.ContentsRect.width; + + var newWidth = (Event.current.mousePosition.x - contentsLayout.WidthForButtons) + newScrollPosition.x; + this.ResizeFirstColumnAndRepaint(contentsLayout, newWidth); + } + + if (contentsLayout.IsShowingThirdColumn) { var secondDividerRect = new Rect(firstDividerRect); secondDividerRect.x += contentsLayout.SecondColumnWidth; - GUI.DrawTexture(secondDividerRect, Texture2D.whiteTexture); + + if (DrawDividerAndCheckResize(secondDividerRect, this.isResizingSecondDivider, out doubleClickedInDivider)) + { + this.isResizingSecondDivider = true; + } + + if (doubleClickedInDivider) + { + contentsLayout.ResizeSecondColumnToFitContent(); + this.ColumnsResized.Invoke(); + blockDivisorClick = true; + } + else if (this.isResizingSecondDivider && !blockDivisorClick) + { + contentsLayout.MinimumContentWidth = contentsLayout.ContentsRect.width; + + // When the second column is hidden, the second divider separates the 1st and 3rd columns, so resize first column + if (contentsLayout.IsShowingSecondColumn) + { + var newWidth = Event.current.mousePosition.x - contentsLayout.FirstColumnWidth - contentsLayout.WidthForButtons + newScrollPosition.x; + this.ResizeSecondColumnAndRepaint(contentsLayout, newWidth); + } + else + { + var newWidth = Event.current.mousePosition.x - contentsLayout.WidthForButtons + newScrollPosition.x; + this.ResizeFirstColumnAndRepaint(contentsLayout, newWidth); + } + } + } + + if (Event.current.rawType == EventType.MouseUp) + { + blockDivisorClick = false; + this.isResizingFirstDivider = false; + this.isResizingSecondDivider = false; } GUI.color = oldColor; } + private void ResizeFirstColumnAndRepaint(PreviewPanelContentsLayout contentsLayout, float newSize) + { + contentsLayout.ChangeFirstColumnWidth(newSize); + this.ColumnsResized.Invoke(); + } + + private void ResizeSecondColumnAndRepaint(PreviewPanelContentsLayout contentsLayout, float newSize) + { + contentsLayout.ChangeSecondColumnWidth(newSize); + this.ColumnsResized.Invoke(); + } + + private bool DrawDividerAndCheckResize(Rect rect, bool alreadyDragging, out bool doubleClicked) + { + GUI.DrawTexture(rect, Texture2D.whiteTexture); + rect.width += DividerWidth; + if (alreadyDragging) + { + rect.width += 2000; + rect.x -= rect.width / 2f; + } + + // Expand hot spot so that it's easier to click than how it looks (line is too fat if we don't have a hot spot) + var hotSpot = new Rect(rect); + hotSpot.width += DividerHotSpotPadding * 2; + hotSpot.x -= DividerHotSpotPadding; + EditorGUIUtility.AddCursorRect(hotSpot, MouseCursor.ResizeHorizontal); + + doubleClicked = Event.current.type == EventType.MouseDown && hotSpot.Contains(Event.current.mousePosition) && Event.current.clickCount == 2; + + return Event.current.type == EventType.MouseDown && hotSpot.Contains(Event.current.mousePosition); + } + private void DrawAddSelectedObjectsButton(Rect buttonRect) { EditorGUI.BeginDisabledGroup(DisableAddSelectedObjectsButton); - if (GUI.Button(buttonRect, "Add Selected Objects")) + if (GUI.Button(buttonRect, LocalizationManager.Instance.GetTranslation("addSelectedObjects"))) { if (this.AddSelectedObjectsClicked != null) { @@ -683,12 +915,12 @@ public PreviewPanelLayout(Rect previewPanelRect) public bool ContentsFitWithoutScrollingHorizontally(PreviewPanelContentsLayout contentsLayout) { - return contentsLayout.Rect.width <= this.ScrollRect.width; + return contentsLayout.ContentsRect.width <= this.ScrollRect.width; } public bool ContentsFitWithoutScrollingVertically(PreviewPanelContentsLayout contentsLayout) { - return contentsLayout.Rect.height + this.HintRect.height <= this.ScrollRect.height; + return contentsLayout.ContentsRect.height + this.HintRect.height <= this.ScrollRect.height; } public bool ContentsFitWithoutAnyScrolling(PreviewPanelContentsLayout contentsLayout) @@ -700,42 +932,175 @@ public bool ContentsFitWithoutAnyScrolling(PreviewPanelContentsLayout contentsLa private class PreviewPanelContentsLayout { - public float IconSize + private float secondColumnWidth; + + private float thirdColumnWidth; + + private float firstColumnContentWidth; + + private float secondColumnContentWidth; + + public float MinimumColumnWidth { get; set; } + + public float WidthForButtons { get; set; } + + public Rect ContentsRect { get; private set; } + + public float FirstColumnWidth { get; private set; } + + public float MinimumContentWidth { get; set; } + + public float SecondColumnWidth { get { - // For now we just use a flat 48 icon size. - return 48.0f; + if (!this.IsShowingSecondColumn) + { + return 0; + } + + return this.secondColumnWidth; } } - public Rect Rect { get; private set; } + public float ThirdColumnWidth + { + get + { + if (!this.IsShowingThirdColumn) + { + return 0; + } - public float FirstColumnWidth { get; private set; } + return this.thirdColumnWidth; + } + } - public float SecondColumnWidth { get; private set; } + public bool IsShowingSecondColumn { get; private set; } - public float ThirdColumnWidth { get; private set; } + public bool IsShowingThirdColumn { get; private set; } - public PreviewPanelContentsLayout(Rect scrollRect, PreviewPanelContents previewContents, bool shouldShowSecondColumn, bool shouldShowThirdColumn) + public void ResizeForContents(Rect scrollRect, PreviewPanelContents previewContents, bool forceResizeToFitContents, bool shouldShowSecondColumn, bool shouldShowThirdColumn) { - this.FirstColumnWidth = previewContents.LongestOriginalNameWidth; - this.SecondColumnWidth = shouldShowSecondColumn ? previewContents.LongestNewNameWidth : 0.0f; - this.ThirdColumnWidth = shouldShowThirdColumn ? previewContents.LongestFinalNameWidth : 0.0f; + // Store off First and Second column content width for divider double clicking + this.firstColumnContentWidth = previewContents.LongestOriginalNameWidth; + this.secondColumnContentWidth = previewContents.LongestNewNameWidth; + + // Don't let columns be so big we can't see all of them. + float maxStartingWidth = scrollRect.width * 0.3f; + const float initialContentPadding = 10; + + if (forceResizeToFitContents) + { + // Use 20% of the window unless we need more to show the contents and can have more. + var desiredWidth = Mathf.Max(scrollRect.width * 0.20f, previewContents.LongestOriginalNameWidth + initialContentPadding); + this.FirstColumnWidth = Mathf.Clamp(desiredWidth, this.MinimumColumnWidth, maxStartingWidth); + } - var totalColumnWidth = this.FirstColumnWidth + this.SecondColumnWidth + this.ThirdColumnWidth; + if (!shouldShowThirdColumn || forceResizeToFitContents) + { + if (shouldShowThirdColumn) + { + // Use 20% of the window unless we need more to show the contents and can have more. + var desiredWidth = Mathf.Max(scrollRect.width * 0.20f, previewContents.LongestNewNameWidth + initialContentPadding); + this.secondColumnWidth = Mathf.Clamp(desiredWidth, this.MinimumColumnWidth, maxStartingWidth); + } + else + { + // When there's no third column, we should expand the second column to fill the window + this.secondColumnWidth = Mathf.Max(this.secondColumnContentWidth, this.MinimumColumnWidth); + } + } + else + { + // Leave second column alone since its size is being managed by the user + } + + this.IsShowingSecondColumn = shouldShowSecondColumn; + + // We always size the last column (whether it's the second or third) to fit contents, so that the window + // gets a scrollbar. Otherwise they have to expand the window to see the contents. + if (shouldShowThirdColumn) + { + this.thirdColumnWidth = Mathf.Max(previewContents.LongestFinalNameWidth, this.MinimumColumnWidth); + } + + this.IsShowingThirdColumn = shouldShowThirdColumn; var rect = new Rect(scrollRect); + + // Stretch the height of the scroll contents to extend past the containing scroll rect (so that it scrolls if it needs to), + // or be within it if it doesn't (so that it won't scroll) rect.height = PreviewRowHeight * previewContents.TotalNumRows; - rect.width = totalColumnWidth + this.IconSize; - this.Rect = rect; + + var contentWidth = this.FirstColumnWidth + this.SecondColumnWidth + this.ThirdColumnWidth + this.WidthForButtons; + + if (forceResizeToFitContents) + { + this.MinimumContentWidth = contentWidth; + } + + var clampedWidth = Mathf.Max(contentWidth, this.MinimumContentWidth); + rect.width = clampedWidth; + + this.ContentsRect = rect; + } + + public void ChangeFirstColumnWidth(float width, bool keepSecondColumnWidthFixed = false) + { + var startingWidth = this.FirstColumnWidth; + this.FirstColumnWidth = Mathf.Max(width, this.MinimumColumnWidth); + var delta = this.FirstColumnWidth - startingWidth; + + // When we resize the first column and the second column is hidden, we can't push its width cause it's hidden. + if (this.IsShowingSecondColumn && !keepSecondColumnWidthFixed) + { + this.secondColumnWidth -= delta; + + if (this.SecondColumnWidth < this.MinimumColumnWidth) + { + this.secondColumnWidth = this.MinimumColumnWidth; + } + } + } + + public void ChangeSecondColumnWidth(float width) + { + this.secondColumnWidth = Mathf.Max(width, MinimumColumnWidth); + + if (width < MinimumColumnWidth) + { + var desiredShrinkWidth = MinimumColumnWidth - width; + this.FirstColumnWidth = Mathf.Max(this.FirstColumnWidth - desiredShrinkWidth, MinimumColumnWidth); + this.secondColumnWidth = MinimumColumnWidth; + } + } + + public void ResizeFirstColumnToFitContent() + { + var desiredWidth = this.firstColumnContentWidth + this.WidthForButtons; + this.ChangeFirstColumnWidth(desiredWidth, true); + } + + public void ResizeSecondColumnToFitContent() + { + this.ChangeSecondColumnWidth(this.secondColumnContentWidth - this.WidthForButtons); } } - private class PreviewPanelContents + /// + /// Aesthetic options for the entire preview panel itself + /// + private class PreviewPanelStyle { - private const float MinColumnWidth = 150.0f; + public Color DeletionColor { get; set; } + public Color InsertionColor { get; set; } + public Color DeletionBackgroundColor { get; set; } + public Color InsertionBackgroundColor { get; set; } + } + private class PreviewPanelContents + { public float LongestOriginalNameWidth { get; private set; } public float LongestNewNameWidth { get; private set; } @@ -754,6 +1119,8 @@ public int NumVisibleRows private PreviewRowModel[] PreviewRowInfos { get; set; } + public int RenameStepIndex { get; set; } + public PreviewRowModel this[int index] { get @@ -765,7 +1132,7 @@ public PreviewRowModel this[int index] else { throw new System.IndexOutOfRangeException( - "Trying to access PreviewRowModel at index that is out of bounds. Index: " + index); + LocalizationManager.Instance.GetTranslation("errorTryingToAccessModel") + index); } } } @@ -774,11 +1141,11 @@ public static PreviewPanelContents CreatePreviewContentsForObjects( BulkRenamePreview preview, int firstPreviewIndex, int numObjectsToShow, - int stepIndex, - Color deletionColor, - Color insertionColor) + int stepIndex) { var previewPanelContents = new PreviewPanelContents(); + previewPanelContents.RenameStepIndex = stepIndex; + var numVisibleObjects = Mathf.Min(numObjectsToShow, preview.NumObjects); previewPanelContents.PreviewRowInfos = new PreviewRowModel[numVisibleObjects]; @@ -787,19 +1154,11 @@ public static PreviewPanelContents CreatePreviewContentsForObjects( var info = new PreviewRowModel(); var indexOfVisibleObject = firstPreviewIndex + j; var previewForIndex = preview.GetPreviewAtIndex(indexOfVisibleObject); - var originalName = stepIndex >= 0 && stepIndex < preview.NumSteps ? - previewForIndex.RenameResultSequence.GetNameBeforeAtStep(stepIndex, deletionColor) : - previewForIndex.RenameResultSequence.OriginalName; - info.NameBeforeStep = originalName; - - var nameAtStep = stepIndex >= 0 && stepIndex < preview.NumSteps ? - previewForIndex.RenameResultSequence.GetNewNameAtStep(stepIndex, insertionColor) : - previewForIndex.RenameResultSequence.NewName; - info.NameAtStep = nameAtStep; - - info.FinalName = previewForIndex.RenameResultSequence.NewName; + info.RenameResultSequence = previewForIndex.RenameResultSequence; + info.RenameStepIndex = stepIndex; info.Icon = previewForIndex.ObjectToRename.GetEditorIcon(); + info.Object = previewForIndex.ObjectToRename; if (previewForIndex.HasWarnings || preview.WillRenameCollideWithExistingAsset(previewForIndex)) { @@ -810,7 +1169,7 @@ public static PreviewPanelContents CreatePreviewContentsForObjects( } else { - info.WarningMessage = "New name matches an existing file or another renamed object."; + info.WarningMessage = LocalizationManager.Instance.GetTranslation("warningNewNameMatchesExisting"); } } else @@ -820,6 +1179,8 @@ public static PreviewPanelContents CreatePreviewContentsForObjects( } info.IndexInPreview = indexOfVisibleObject; + info.FirstElement = j == 0; + info.LastElement = j == (numVisibleObjects - 1); previewPanelContents.PreviewRowInfos[j] = info; } @@ -868,11 +1229,11 @@ private static string GetWarningMessageForRenamePreview(RenamePreview preview) { if (preview.HasInvalidEmptyFinalName) { - return "Asset has blank name."; + return LocalizationManager.Instance.GetTranslation("assetBlankName"); } else if (preview.FinalNameContainsInvalidCharacters) { - return "Name includes invalid characters (usually symbols such as ?.,)."; + return LocalizationManager.Instance.GetTranslation("nameIncludeInvalidCharacter"); } else { @@ -883,19 +1244,98 @@ private static string GetWarningMessageForRenamePreview(RenamePreview preview) private struct PreviewRowModel { + public UnityEngine.Object Object { get; set; } + public Texture Icon { get; set; } public Texture WarningIcon { get; set; } public string WarningMessage { get; set; } - public string NameBeforeStep { get; set; } + public string FinalName + { + get + { + return this.RenameResultSequence.NewName; + } + } - public string NameAtStep { get; set; } + public int IndexInPreview { get; set; } - public string FinalName { get; set; } + public bool LastElement { get; set; } - public int IndexInPreview { get; set; } + public bool FirstElement { get; set; } + + public RenameResultSequence RenameResultSequence { get; set; } + public int RenameStepIndex { get; set; } + + public bool IsPreviewingSteps + { + get + { + return this.RenameStepIndex >= 0 && this.RenameStepIndex < this.RenameResultSequence.NumSteps; + } + } + + public string NameBeforeStep + { + get + { + if (this.RenameStepIndex >= 0 && this.RenameStepIndex < this.RenameResultSequence.NumSteps) + { + return this.RenameResultSequence.GetRenameResultBeforeStep(this.RenameStepIndex).Original; + } + else + { + return this.RenameResultSequence.OriginalName; + } + } + } + + public string NameAtStep + { + get + { + if (this.RenameStepIndex >= 0 && this.RenameStepIndex < this.RenameResultSequence.NumSteps) + { + return this.RenameResultSequence.GetRenameResultForStep(this.RenameStepIndex).Result; + } + else + { + return this.RenameResultSequence.NewName; + } + } + } + + public RenameResult RenameResultBefore + { + get + { + if (this.RenameStepIndex >= 0 && this.RenameStepIndex < this.RenameResultSequence.NumSteps) + { + return this.RenameResultSequence.GetRenameResultBeforeStep(this.RenameStepIndex); + } + else + { + return RenameResult.Empty; + } + } + } + + public RenameResult RenameResultAfter + { + get + { + if (this.RenameStepIndex >= 0 && this.RenameStepIndex < this.RenameResultSequence.NumSteps) + { + return this.RenameResultSequence.GetRenameResultForStep(this.RenameStepIndex); + } + else + { + return RenameResult.Empty; + } + } + } public bool NameChangedThisStep { @@ -923,6 +1363,14 @@ private struct PreviewRowStyle public float ThirdColumnWidth { get; set; } public Color BackgroundColor { get; set; } + + public Color FirstColumnDiffTextColor { get; set; } + + public Color SecondColumnDiffTextColor { get; set; } + + public Color FirstColumnDiffBackgroundColor { get; set; } + + public Color SecondColumnDiffBackgroundColor { get; set; } } private class GUIStyles @@ -959,9 +1407,7 @@ private class GUIStyles public Color PreviewRowBackgroundEven { get; set; } - public Color InsertionTextColor { get; set; } - - public Color DeletionTextColor { get; set; } + public Color RenameCompleteTextColor { get; set; } } private class GUIContents @@ -975,4 +1421,4 @@ private class GUIContents public GUIContent DropPromptHintInsideScroll { get; set; } } } -} \ No newline at end of file +} diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/MulliganRenamerPreviewPanel.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/GUI/MulliganRenamerPreviewPanel.cs.meta similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/MulliganRenamerPreviewPanel.cs.meta rename to Assets/RedBlueGames/MulliganRenamer/Editor/GUI/MulliganRenamerPreviewPanel.cs.meta diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/MulliganRenamerWindow.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/GUI/MulliganRenamerWindow.cs similarity index 87% rename from Assets/RedBlueGames/MulliganRenamer/Editor/MulliganRenamerWindow.cs rename to Assets/RedBlueGames/MulliganRenamer/Editor/GUI/MulliganRenamerWindow.cs index 1623890..50b7ac7 100644 --- a/Assets/RedBlueGames/MulliganRenamer/Editor/MulliganRenamerWindow.cs +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/GUI/MulliganRenamerWindow.cs @@ -32,13 +32,12 @@ namespace RedBlueGames.MulliganRenamer /// /// Tool that tries to allow renaming mulitple selections by parsing similar substrings /// - public class MulliganRenamerWindow : EditorWindow + public class MulliganRenamerWindow : EditorWindow, IHasCustomMenu { - private const string VersionString = "1.6.0"; + private const string VersionString = "1.7.0"; private const string WindowMenuPath = "Window/Red Blue/Mulligan Renamer"; private const string RenameOpsEditorPrefsKey = "RedBlueGames.MulliganRenamer.RenameOperationsToApply"; - private const string UserPreferencesPrefKey = "RedBlueGames.MulliganRenamer.UserPreferences"; private const string PreviewModePrefixKey = "RedBlueGames.MulliganRenamer.IsPreviewStepModePreference"; private const float OperationPanelWidth = 350.0f; @@ -153,12 +152,21 @@ private bool NeedsReview } [MenuItem(WindowMenuPath, false)] - private static void ShowWindow() + public static MulliganRenamerWindow ShowWindow() { - var bulkRenamerWindow = EditorWindow.GetWindow(false, "Mulligan Renamer", true); + return EditorWindow.GetWindow(false, "Mulligan Renamer", true); + } - // When they launch via right click, we immediately load the objects in. + public static void ShowWindowWithSelectedObjects() + { + var bulkRenamerWindow = ShowWindow(); bulkRenamerWindow.LoadSelectedObjects(); + + // Fix Issue #235. RenamePreview is cached OnEnable and Application Update, + // so when GUI draws this frame there's actually no preview, yet, so the + // "Add Selected Objects" (Empty) preview panel flickers for a frame. + bulkRenamerWindow.CacheBulkRenamerPreview(); + bulkRenamerWindow.CacheValidSelectedObjects(); } private static bool ObjectIsRenamable(UnityEngine.Object obj) @@ -216,6 +224,19 @@ private static bool DrawPreviewBreadcrumb(Rect rect, PreviewBreadcrumbOptions br return selected; } + /// + /// Add the menu items for the generic Context Menu + /// + /// + public void AddItemsToMenu(GenericMenu menu) + { + menu.AddItem( + new GUIContent( + LocalizationManager.Instance.GetTranslation("preferences")), + false, + () => this.ShowPreferencesWindowForCurrentUnityVersion()); + } + private void OnEnable() { AssetPreview.SetPreviewTextureCacheSize(100); @@ -245,6 +266,8 @@ private void OnEnable() EditorApplication.update += this.CacheBulkRenamerPreview; EditorApplication.update += this.CacheValidSelectedObjects; + LocalizationManager.Instance.LanguageChanged += this.HandleLanguageChanged; + // Sometimes, GUI happens before Editor Update, so also cache a preview now. this.CacheBulkRenamerPreview(); this.CacheValidSelectedObjects(); @@ -270,6 +293,7 @@ private void InitializePreviewPanel() this.previewPanel.RemoveAllClicked += this.HandleRemoveAllObjectsClicked; this.previewPanel.AddSelectedObjectsClicked += this.HandleAddSelectedObjectsClicked; this.previewPanel.ObjectRemovedAtIndex += this.HandleObjectRemoved; + this.previewPanel.ColumnsResized += this.Repaint; } private void HandleObjectsDroppedOverPreviewArea(UnityEngine.Object[] objects) @@ -293,6 +317,14 @@ private void HandleObjectRemoved(int index) this.ObjectsToRename.RemoveAt(index); } + private void HandleLanguageChanged() + { + if (this.previewPanel != null) + { + this.previewPanel.RefreshGUIContent(); + } + } + private void OnDisable() { this.SaveUserPreferences(); @@ -313,6 +345,8 @@ private void OnDisable() Selection.selectionChanged -= this.Repaint; EditorApplication.update -= this.CacheBulkRenamerPreview; EditorApplication.update -= this.CacheValidSelectedObjects; + + LocalizationManager.Instance.LanguageChanged -= this.HandleLanguageChanged; } private void CacheRenameOperationPrototypes() @@ -343,6 +377,9 @@ private void CacheRenameOperationPrototypes() this.RenameOperationDrawerBindingPrototypes.Add( new RenameOperationDrawerBinding(new ToCamelCaseOperation(), new ToCamelCaseOperationDrawer())); + this.RenameOperationDrawerBindingPrototypes.Add( + new RenameOperationDrawerBinding(new AdjustNumberingOperation(), new AdjustNumberingOperationDrawer())); + this.RenameOperationDrawerBindingPrototypes.Add( new RenameOperationDrawerBinding(new TrimCharactersOperation(), new TrimCharactersOperationDrawer())); @@ -409,8 +446,6 @@ private void OnGUI() this.position.height - toolbarRect.height - footerHeight); this.DrawOperationsPanel(operationPanelRect); - this.FocusForcedFocusControl(); - var previewPanelPadding = new RectOffset(1, 1, -1, 0); var previewPanelRect = new Rect( operationPanelRect.width + previewPanelPadding.left, @@ -444,12 +479,15 @@ private void OnGUI() renameButtonSize.x, renameButtonSize.y); - if (GUI.Button(renameButtonRect, "Rename")) + if (GUI.Button(renameButtonRect, LocalizationManager.Instance.GetTranslation("rename"))) { var popupMessage = string.Concat( - "Some objects have warnings and will not be renamed. Do you want to rename the other objects in the group?"); + LocalizationManager.Instance.GetTranslation("renameWarningNotRenamed")); var renamesHaveNoWarnings = !BulkRenamePreview.HasWarnings; - if (renamesHaveNoWarnings || EditorUtility.DisplayDialog("Warning", popupMessage, "Rename", "Cancel")) + if (renamesHaveNoWarnings || EditorUtility.DisplayDialog(LocalizationManager.Instance.GetTranslation("warning"), + popupMessage, + LocalizationManager.Instance.GetTranslation("rename"), + LocalizationManager.Instance.GetTranslation("cancel"))) { var undoGroupBeforeRename = Undo.GetCurrentGroup(); try @@ -465,11 +503,9 @@ private void OnGUI() catch (System.OperationCanceledException e) { var errorMessage = string.Concat( - "Sorry, some objects failed to rename. Something went wrong with Mulligan." + - "Please report a bug (see UserManual for details). This rename operation will be automatically undone", - "\n\nException: ", + LocalizationManager.Instance.GetTranslation("failToRenameMulligan"), e.Message); - if (EditorUtility.DisplayDialog("Error", errorMessage, "Ok")) + if (EditorUtility.DisplayDialog(LocalizationManager.Instance.GetTranslation("error"), errorMessage, "Ok")) { Undo.RevertAllDownToGroup(undoGroupBeforeRename); } @@ -493,17 +529,7 @@ private void OnGUI() EditorGUIUtility.singleLineHeight); EditorGUI.LabelField(copyrightRect, this.guiContents.CopyrightLabel, this.guiStyles.CopyrightLabel); - // Issue #115 - Workaround to force focus to stay with whatever widget it was previously on... - var focusedControl = GUI.GetNameOfFocusedControl(); - if (string.IsNullOrEmpty(focusedControl)) - { - GUI.FocusControl(this.LastFocusedControlName); - EditorGUI.FocusTextInControl(this.LastFocusedControlName); - } - else - { - this.LastFocusedControlName = GUI.GetNameOfFocusedControl(); - } + this.FocusForcedFocusControl(); } private void DrawToolbar(Rect toolbarRect) @@ -520,8 +546,8 @@ private void DrawToolbar(Rect toolbarRect) this.DrawBreadcrumbs(this.IsShowingPreviewSteps, breadcrumbRect); EditorGUI.BeginDisabledGroup(this.NumRenameOperations <= 1); - var buttonText = "Preview Steps"; - var previewButtonSize = new Vector2(100.0f, toolbarRect.height); + var buttonText = LocalizationManager.Instance.GetTranslation("previewSteps"); + var previewButtonSize = new Vector2(150.0f, toolbarRect.height); var previewButtonPosition = new Vector2(toolbarRect.xMax - previewButtonSize.x, toolbarRect.y); var toggleRect = new Rect(previewButtonPosition, previewButtonSize); this.IsPreviewStepModePreference = GUI.Toggle(toggleRect, this.IsPreviewStepModePreference, buttonText, "toolbarbutton"); @@ -571,7 +597,7 @@ private void DrawBreadcrumbs(bool isShowingPreviewSteps, Rect rect) else { var breadcrumbeOptions = - new PreviewBreadcrumbOptions() { Heading = "Result", HighlightColor = Color.clear, Enabled = true, UseLeftStyle = true }; + new PreviewBreadcrumbOptions() { Heading = LocalizationManager.Instance.GetTranslation("result"), HighlightColor = Color.clear, Enabled = true, UseLeftStyle = true }; var breadcrumbSize = breadcrumbeOptions.SizeForContent; breadcrumbSize.y = rect.height; DrawPreviewBreadcrumb(new Rect(rect.position, breadcrumbSize), breadcrumbeOptions); @@ -622,7 +648,7 @@ private void DrawOperationsPanel(Rect operationPanelRect) buttonRect.height = buttonSize.y; buttonRect.width = buttonSize.x; - if (GUI.Button(buttonRect, "Add Operation")) + if (GUI.Button(buttonRect, LocalizationManager.Instance.GetTranslation("addOperation"))) { // Add enums to the menu var menu = new GenericMenu(); @@ -648,15 +674,24 @@ private void DrawOperationsPanelHeader(Rect headerRect) headerLabelRect.x += 2.0f; headerLabelRect.width -= 2.0f; - var headerLabel = "Rename Operations"; + var headerLabel = LocalizationManager.Instance.GetTranslation("renameOperation"); var renameOpsLabel = new GUIContent(headerLabel); EditorGUI.LabelField(headerLabelRect, renameOpsLabel, headerLabelStyle); + var preferencesButtonRect = new Rect(headerRect); + preferencesButtonRect.width = 80.0f; + preferencesButtonRect.x = headerRect.width - preferencesButtonRect.width; + + if (GUI.Button(preferencesButtonRect, LocalizationManager.Instance.GetTranslation("preferences"), EditorStyles.toolbarButton)) + { + this.ShowPreferencesWindowForCurrentUnityVersion(); + } + var presetButtonsRect = new Rect(headerRect); - presetButtonsRect.width = 60.0f; - presetButtonsRect.x = headerRect.width - presetButtonsRect.width; + presetButtonsRect.width = 80.0f; + presetButtonsRect.x = headerRect.width - presetButtonsRect.width - preferencesButtonRect.width; var useDebugPresets = Event.current.shift; - if (GUI.Button(presetButtonsRect, "Presets", EditorStyles.toolbarDropDown)) + if (GUI.Button(presetButtonsRect, LocalizationManager.Instance.GetTranslation("presets"), EditorStyles.toolbarDropDown)) { var menu = new GenericMenu(); var savedPresetNames = new string[this.ActivePreferences.SavedPresets.Count]; @@ -677,21 +712,36 @@ private void DrawOperationsPanelHeader(Rect headerRect) } menu.AddSeparator(string.Empty); - menu.AddItem(new GUIContent("Save As..."), false, () => this.ShowSavePresetWindow()); - menu.AddItem(new GUIContent("Manage Presets..."), false, () => this.ShowManagePresetsWindow()); + menu.AddItem(new GUIContent(LocalizationManager.Instance.GetTranslation("saveAs")), false, () => this.ShowSavePresetWindow()); + menu.AddItem(new GUIContent(LocalizationManager.Instance.GetTranslation("managePresets")), false, () => this.ShowManagePresetsWindow()); if (useDebugPresets) { menu.AddItem(new GUIContent("DEBUG - Delete UserPrefs"), false, () => { - this.ActivePreferences = new MulliganUserPreferences(); + this.ActivePreferences.ResetToDefaults(); this.SaveUserPreferences(); }); + menu.AddItem(new GUIContent("DEBUG - Reload Languages"), false, () => + { + LocalizationManager.Instance.Initialize(); + }); } menu.ShowAsContext(); } } + private void ShowPreferencesWindowForCurrentUnityVersion() + { + // I used an API for preferences that was introduced in 2018.3 (SettingsProvider). + // Draw a custom window for older versions +#if UNITY_2018_3_OR_NEWER + SettingsService.OpenUserPreferences(MulliganSettingsProvider.Path); +#else + MulliganUserPreferencesWindow.ShowWindow(); +#endif + } + private void DrawRenameOperations(Rect operationsRect) { // Store the op before buttons are pressed because buttons change focus @@ -736,7 +786,7 @@ private void DrawRenameOperations(Rect operationsRect) case RenameOperationSortingButtonEvent.Delete: { - var removingFocusedOperation = focusedOpBeforeButtonPresses == currentElement; + var removingFocusedOperation = focusedOpBeforeButtonPresses == currentElement.Operation; this.RenameOperationsToApplyWithBindings.RemoveAt(i); saveOpsToPreferences = true; @@ -763,9 +813,7 @@ private void DrawRenameOperations(Rect operationsRect) default: { - Debug.LogError(string.Format( - "RenamerWindow found Unrecognized ListButtonEvent [{0}] in OnGUI. Add a case to handle this event.", - buttonClickEvent)); + Debug.LogError(string.Format(LocalizationManager.Instance.GetTranslation("errorUnrecognizedListButton"), buttonClickEvent)); return; } } @@ -787,10 +835,7 @@ private void OnAddRenameOperationConfirmed(object operation) var operationDrawerBinding = operation as RenameOperationDrawerBinding; if (operationDrawerBinding == null) { - throw new System.ArgumentException( - "MulliganRenamerWindow tried to add a new RenameOperation using a type that is not a subclass of RenameOperationDrawerBinding." + - " Operation type: " + - operation.GetType().ToString()); + throw new System.ArgumentException(LocalizationManager.Instance.GetTranslation("errorAddNewOpNotSub"), operation.GetType().ToString()); } this.AddRenameOperation(operationDrawerBinding); @@ -844,6 +889,10 @@ private void DrawPreviewPanel(Rect previewPanelRect, BulkRenamePreview bulkRenam this.previewPanel.ColumnsToShow = columnStyle; this.previewPanel.DisableAddSelectedObjectsButton = this.ValidSelectedObjects.Count == 0; + this.previewPanel.InsertionTextColor = this.ActivePreferences.InsertionTextColor; + this.previewPanel.InsertionBackgroundColor = this.ActivePreferences.InsertionBackgroundColor; + this.previewPanel.DeletionTextColor = this.ActivePreferences.DeletionTextColor; + this.previewPanel.DeletionBackgroundColor = this.ActivePreferences.DeletionBackgroundColor; this.previewPanelScrollPosition = this.previewPanel.Draw(previewPanelRect, this.previewPanelScrollPosition, bulkRenamePreview); } @@ -856,11 +905,11 @@ private void DrawReviewPrompt(Rect rect) color = new AddStringOperationDrawer().HighlightColor; if (RBPackageSettings.IsGitHubRelease) { - reviewPrompt = "Thank you very much for supporting Mulligan!"; + reviewPrompt = string.Format("{0}", LocalizationManager.Instance.GetTranslation("thankYouForSupport")); } else { - reviewPrompt = "Thank you for reviewing Mulligan!"; + reviewPrompt = string.Format("{0}", LocalizationManager.Instance.GetTranslation("thankYouForReview")); } } else @@ -869,15 +918,11 @@ private void DrawReviewPrompt(Rect rect) if (RBPackageSettings.IsGitHubRelease) { - reviewPrompt = "Thank you for using Mulligan! " + - "If you've found it useful, please consider supporting its development by " + - "purchasing it from the Asset Store. Thanks!"; + reviewPrompt = string.Format("{0}", LocalizationManager.Instance.GetTranslation("thankYouForUsing")); } else { - reviewPrompt = "Thank you for purchasing Mulligan! " + - "If you've found it useful, please consider leaving a review on the Asset Store. " + - "The store is very competitive and every review helps to stand out. Thanks!"; + reviewPrompt = string.Format("{0}", LocalizationManager.Instance.GetTranslation("thankYouForPurchasing")); } } @@ -911,7 +956,7 @@ private void DrawReviewBanner(Rect rect, Color color, string prompt, bool showBu labelRect.width = (buttonRect.x - rect.x) - (buttonPaddingLR + labelPaddingL); GUI.Label(labelRect, prompt, reviewStyle); - if (showButton && GUI.Button(buttonRect, "Open Asset Store")) + if (showButton && GUI.Button(buttonRect, LocalizationManager.Instance.GetTranslation("openAssetStore"))) { this.ActivePreferences.HasConfirmedReviewPrompt = true; Application.OpenURL("https://assetstore.unity.com/packages/slug/99843"); @@ -936,7 +981,7 @@ private void ShowSavePresetWindow() savePresetPosition.x = this.position.x + (this.position.width / 2.0f); savePresetPosition.y = this.position.y + (this.position.height / 2.0f); this.activeSavePresetWindow = - EditorWindow.GetWindowWithRect(savePresetPosition, true, "Save Preset", true); + EditorWindow.GetWindowWithRect(savePresetPosition, true, LocalizationManager.Instance.GetTranslation("savePreset"), true); this.activeSavePresetWindow.minSize = windowMinSize; this.activeSavePresetWindow.maxSize = new Vector2(windowMinSize.x * 2.0f, windowMinSize.y); this.activeSavePresetWindow.SetName(this.CurrentPresetName); @@ -964,7 +1009,7 @@ private void ShowManagePresetsWindow() } var existingWindow = this.activePresetManagementWindow; - this.activePresetManagementWindow = EditorWindow.GetWindow(true, "Manage Presets", true); + this.activePresetManagementWindow = EditorWindow.GetWindow(true, LocalizationManager.Instance.GetTranslation("managePresets"), true); this.activePresetManagementWindow.PopulateWithPresets(this.ActivePreferences.SavedPresets); // Only subscribe if it's a new, previously unopened window. @@ -1001,36 +1046,23 @@ private void SaveUserPreferences() var operationSequence = this.GetCurrentRenameOperationSequence(); this.ActivePreferences.PreviousSequence = operationSequence; - EditorPrefs.SetString(UserPreferencesPrefKey, JsonUtility.ToJson(this.ActivePreferences)); + this.ActivePreferences.SaveToEditorPrefs(); } private void LoadUserPreferences() { var oldSerializedOps = EditorPrefs.GetString(RenameOpsEditorPrefsKey, string.Empty); + this.ActivePreferences = MulliganUserPreferences.LoadOrCreatePreferences(); + if (!string.IsNullOrEmpty(oldSerializedOps)) { + this.ActivePreferences.ResetColorsToDefault(EditorGUIUtility.isProSkin); + // Update operations to the new preferences - this.ActivePreferences = new MulliganUserPreferences() - { - PreviousSequence = RenameOperationSequence.FromString(oldSerializedOps) - }; + this.ActivePreferences.PreviousSequence = RenameOperationSequence.FromString(oldSerializedOps); EditorPrefs.DeleteKey(RenameOpsEditorPrefsKey); } - else - { - var serializedPreferences = EditorPrefs.GetString(UserPreferencesPrefKey, string.Empty); - - if (!string.IsNullOrEmpty(serializedPreferences)) - { - var loadedPreferences = JsonUtility.FromJson(serializedPreferences); - this.ActivePreferences = loadedPreferences; - } - else - { - this.ActivePreferences = new MulliganUserPreferences(); - } - } this.LoadOperationSequence(this.ActivePreferences.PreviousSequence); var originPreset = this.ActivePreferences.FindSavedPresetWithName(this.ActivePreferences.LastUsedPresetName); @@ -1179,9 +1211,9 @@ private void AddObjectsToRename(ICollection objectsToAdd) gameObjects.Sort((x, y) => ((GameObject)x).GetHierarchySorting().CompareTo(((GameObject)y).GetHierarchySorting())); assets.Sort((x, y) => - { - return EditorUtility.NaturalCompare(x.name, y.name); - }); + { + return EditorUtility.NaturalCompare(x.name, y.name); + }); this.ObjectsToRename.AddRange(assets); this.ObjectsToRename.AddRange(gameObjects); diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/MulliganRenamerWindow.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/GUI/MulliganRenamerWindow.cs.meta similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/MulliganRenamerWindow.cs.meta rename to Assets/RedBlueGames/MulliganRenamer/Editor/GUI/MulliganRenamerWindow.cs.meta diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/GUI/MulliganUserPreferencesWindow.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/GUI/MulliganUserPreferencesWindow.cs new file mode 100644 index 0000000..7ede9a0 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/GUI/MulliganUserPreferencesWindow.cs @@ -0,0 +1,262 @@ +/* MIT License + +Copyright (c) 2016 Edward Rowe, RedBlueGames + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +namespace RedBlueGames.MulliganRenamer +{ + using UnityEditor; + using UnityEngine; + + /// + /// Handles drawing the PreferencesWindow in a generic way that works both in the + /// SettingsProvider version of Preferences (native Unity Preferences menu), as well as + /// a custom window for older versions. + /// + public class MulliganUserPreferencesWindow : EditorWindow + { + private static GUIStyle sampleDiffLabelStyle; + + private const float LabelWidth = 200.0f; + + private const float MaxWidth = 550.0f; + + private MulliganUserPreferences activePreferences; + + private LanguageRetriever languageRetriever; + + private static GUIStyle SampleDiffLabelStyle + { + get + { + if (sampleDiffLabelStyle == null) + { + sampleDiffLabelStyle = new GUIStyle(EditorStyles.boldLabel) { richText = true }; + } + + return sampleDiffLabelStyle; + } + } + + public static MulliganUserPreferencesWindow ShowWindow() + { + return EditorWindow.GetWindow( + true, + LocalizationManager.Instance.GetTranslation("preferenceWindowTitle"), + true); + } + + private void OnEnable() + { + // Note that Enable is not called when opened as Preference item (via SettingsProvider api) + // We implement it for old versions of Unity that just use a traditional EditorWindow for settings + this.activePreferences = MulliganUserPreferences.LoadOrCreatePreferences(); + this.languageRetriever = new LanguageRetriever(); + } + + private void OnGUI() + { + DrawPreferences(this.activePreferences, this.languageRetriever); + } + + /// + /// Draw the Preferences using Unity GUI framework. + /// + /// Preferences to draw and update + public static void DrawPreferences(MulliganUserPreferences preferences, LanguageRetriever languageRetriever) + { + // I override LabelWidth (and MaxWidth) just to look more like Unity's native preferences + EditorGUIUtility.labelWidth = LabelWidth; + + var prefsChanged = false; + EditorGUILayout.BeginHorizontal(GUILayout.MaxWidth(MaxWidth)); + var newLanguage = DrawLanguageDropdown(LocalizationManager.Instance.CurrentLanguage); + if (newLanguage != LocalizationManager.Instance.CurrentLanguage) + { + LocalizationManager.Instance.ChangeLanguage(newLanguage.Key); + } + + DrawUpdateLanguagesButton(languageRetriever); + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.Space(); + EditorGUILayout.Space(); + + GUILayout.Label(LocalizationManager.Instance.GetTranslation("preferencesDiffLabel"), EditorStyles.boldLabel); + + EditorGUI.BeginChangeCheck(); + preferences.InsertionTextColor = EditorGUILayout.ColorField( + LocalizationManager.Instance.GetTranslation("preferencesInsertionText"), + preferences.InsertionTextColor, + GUILayout.MaxWidth(MaxWidth)); + preferences.InsertionBackgroundColor = EditorGUILayout.ColorField( + LocalizationManager.Instance.GetTranslation("preferencesInsertionBackground"), + preferences.InsertionBackgroundColor, + GUILayout.MaxWidth(MaxWidth)); + EditorGUILayout.Space(); + DrawSampleDiffLabel(true, preferences); + EditorGUILayout.Space(); + + EditorGUILayout.Space(); + preferences.DeletionTextColor = EditorGUILayout.ColorField( + LocalizationManager.Instance.GetTranslation("preferencesDeletionText"), + preferences.DeletionTextColor, + GUILayout.MaxWidth(MaxWidth)); + preferences.DeletionBackgroundColor = EditorGUILayout.ColorField( + LocalizationManager.Instance.GetTranslation("preferencesDeletionBackground"), + preferences.DeletionBackgroundColor, + GUILayout.MaxWidth(MaxWidth)); + EditorGUILayout.Space(); + DrawSampleDiffLabel(false, preferences); + + if (EditorGUI.EndChangeCheck()) + { + prefsChanged = true; + } + + if (GUILayout.Button(LocalizationManager.Instance.GetTranslation("preferencesReset"), GUILayout.Width(150))) + { + preferences.ResetColorsToDefault(EditorGUIUtility.isProSkin); + prefsChanged = true; + } + + if (prefsChanged) + { + preferences.SaveToEditorPrefs(); + } + } + + private static void DrawUpdateLanguagesButton(LanguageRetriever retriever) + { + EditorGUI.BeginDisabledGroup(!retriever.IsDoneUpdating); + var useDebugPresets = Event.current.shift; + var buttonText = LocalizationManager.Instance.GetTranslation("updateLanguages"); + if (useDebugPresets) + { + buttonText = string.Concat(buttonText, "*"); + } + + if (GUILayout.Button(buttonText)) + { + retriever.UpdateLanguages(useDebugPresets); + } + + EditorGUI.EndDisabledGroup(); + } + + private static Language DrawLanguageDropdown(Language currentLanguage) + { + var content = new GUIContent( + LocalizationManager.Instance.GetTranslation("language"), + LocalizationManager.Instance.GetTranslation(" languageTooltip")); + var languages = new GUIContent[LocalizationManager.Instance.AllLanguages.Count]; + for (int i = 0; i < LocalizationManager.Instance.AllLanguages.Count; ++i) + { + var language = LocalizationManager.Instance.AllLanguages[i]; + languages[i] = new GUIContent(language.Name); + } + + var currentLanguageIndex = GetLanguageIndex(currentLanguage); + if (currentLanguageIndex >= 0 && currentLanguageIndex < LocalizationManager.Instance.AllLanguages.Count) + { + var newIndex = EditorGUILayout.Popup(content, currentLanguageIndex, languages, GUILayout.MaxWidth(MaxWidth)); + return LocalizationManager.Instance.AllLanguages[newIndex]; + } + else + { + Debug.Log("Can't draw LanguageDropdown as the CurrentLanguage was not found in LocalizationManager." + + " Restarting Unity should fix this. This should not happen in production, please report it on GitHub issues."); + return LocalizationManager.Instance.CurrentLanguage; + } + } + + private static int GetLanguageIndex(Language language) + { + var currentLanguageIndex = -1; + for (int i = 0; i < LocalizationManager.Instance.AllLanguages.Count; ++i) + { + if (LocalizationManager.Instance.AllLanguages[i].Key == language.Key) + { + currentLanguageIndex = i; + break; + } + } + + return currentLanguageIndex; + } + + private static void DrawSampleDiffLabel(bool insertion, MulliganUserPreferences preferences) + { + EditorGUILayout.BeginHorizontal(); + + EditorGUILayout.LabelField(string.Empty, GUILayout.MaxWidth(LabelWidth)); + var rect = EditorGUILayout.GetControlRect(); + if (insertion) + { + DrawSampleInsertionLabel(rect, preferences); + } + else + { + DrawSampleDeletionLabel(rect, preferences); + } + + EditorGUILayout.EndHorizontal(); + } + + private static void DrawSampleInsertionLabel(Rect rect, MulliganUserPreferences preferences) + { + var diffLabelStyle = new MulliganEditorGUIUtilities.DiffLabelStyle() + { + HideDiff = false, + OperationToShow = DiffOperation.Insertion, + DiffBackgroundColor = preferences.InsertionBackgroundColor, + DiffTextColor = preferences.InsertionTextColor, + }; + + var renameResult = new RenameResult(); + renameResult.Add(new Diff(LocalizationManager.Instance.GetTranslation("exampleThisIs") + " ", DiffOperation.Equal)); + renameResult.Add(new Diff(LocalizationManager.Instance.GetTranslation("exampleSampleText"), DiffOperation.Insertion)); + renameResult.Add(new Diff(" " + LocalizationManager.Instance.GetTranslation("exampleWithWords") + " ", DiffOperation.Equal)); + renameResult.Add(new Diff(LocalizationManager.Instance.GetTranslation("exampleInserted"), DiffOperation.Insertion)); + + MulliganEditorGUIUtilities.DrawDiffLabel(rect, renameResult, false, diffLabelStyle, SampleDiffLabelStyle); + } + + private static void DrawSampleDeletionLabel(Rect rect, MulliganUserPreferences preferences) + { + var diffLabelStyle = new MulliganEditorGUIUtilities.DiffLabelStyle() + { + HideDiff = false, + OperationToShow = DiffOperation.Deletion, + DiffBackgroundColor = preferences.DeletionBackgroundColor, + DiffTextColor = preferences.DeletionTextColor, + }; + + var renameResult = new RenameResult(); + renameResult.Add(new Diff(LocalizationManager.Instance.GetTranslation("exampleThisIs") + " ", DiffOperation.Equal)); + renameResult.Add(new Diff(LocalizationManager.Instance.GetTranslation("exampleSampleText"), DiffOperation.Deletion)); + renameResult.Add(new Diff(" " + LocalizationManager.Instance.GetTranslation("exampleWithWords") + " ", DiffOperation.Equal)); + renameResult.Add(new Diff(LocalizationManager.Instance.GetTranslation("exampleDeleted"), DiffOperation.Deletion)); + + MulliganEditorGUIUtilities.DrawDiffLabel(rect, renameResult, true, diffLabelStyle, SampleDiffLabelStyle); + } + } +} \ No newline at end of file diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/GUI/MulliganUserPreferencesWindow.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/GUI/MulliganUserPreferencesWindow.cs.meta new file mode 100644 index 0000000..900a749 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/GUI/MulliganUserPreferencesWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 93b10462e969e40d8b47590156c26a8a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/SavePresetWindow.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/GUI/SavePresetWindow.cs similarity index 87% rename from Assets/RedBlueGames/MulliganRenamer/Editor/SavePresetWindow.cs rename to Assets/RedBlueGames/MulliganRenamer/Editor/GUI/SavePresetWindow.cs index 0eb1710..6bc5deb 100644 --- a/Assets/RedBlueGames/MulliganRenamer/Editor/SavePresetWindow.cs +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/GUI/SavePresetWindow.cs @@ -93,17 +93,20 @@ private void OnGUI() } EditorGUI.BeginDisabledGroup(!IsNameValid(this.enteredName)); - if (GUILayout.Button("Save") || (hitEnter && IsNameValid(this.enteredName))) + if (GUILayout.Button(LocalizationManager.Instance.GetTranslation("save")) || (hitEnter && IsNameValid(this.enteredName))) { var saveAndClose = false; if (this.existingPresetNames.Contains(this.enteredName)) { var popupMessage = string.Format( - "A preset named \"{0}\" already exists. Do you want to replace it?", + LocalizationManager.Instance.GetTranslation("errorPresetNameAlreadyExists"), this.enteredName ); - saveAndClose = EditorUtility.DisplayDialog("Warning", popupMessage, "Replace", "No"); + saveAndClose = EditorUtility.DisplayDialog(LocalizationManager.Instance.GetTranslation("warning"), + popupMessage, + LocalizationManager.Instance.GetTranslation("replace"), + LocalizationManager.Instance.GetTranslation("no")); } else { diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/SavePresetWindow.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/GUI/SavePresetWindow.cs.meta similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/SavePresetWindow.cs.meta rename to Assets/RedBlueGames/MulliganRenamer/Editor/GUI/SavePresetWindow.cs.meta diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Localization.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Localization.meta new file mode 100644 index 0000000..4e69c35 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Localization.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fa739c9dbd3dc439ebf1a98eb5eeee23 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Localization/Language.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/Localization/Language.cs new file mode 100644 index 0000000..84ed46f --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Localization/Language.cs @@ -0,0 +1,95 @@ +/* MIT License + +Copyright (c) 2019 Murillo Pugliesi Lopes, https://github.com/Mukarillo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +namespace RedBlueGames.MulliganRenamer +{ + using System.Linq; + using System.Collections.Generic; + using UnityEngine; + + /// + /// Class responsible for holding all the translated elements + /// you can get a translated content by its key calling Get method + /// + [System.Serializable] + public class Language + { + // These are assigned through Unity's serialization / deserialization. + // So we Ignore warning about Unassigned field. +#pragma warning disable 0649 + [SerializeField] + private string name; + + [SerializeField] + private string key; + + [SerializeField] + private int version; + + [SerializeField] + private List elements; +#pragma warning restore 0649 + + /// + /// The Version of the language, used to determine whether or not data has been updated. + /// This should be incremented when changes are made. + /// + public int Version + { + get + { + return this.version; + } + } + + public string Name + { + get + { + return name; + } + } + + public string Key + { + get + { + return key; + } + } + + public List Elements + { + get + { + return elements; + } + } + + public string GetValue(string key) + { + var language = elements.Find(x => x.Key.Equals(key)); + return language != null ? language.Value : "A0"; + } + } +} \ No newline at end of file diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Localization/Language.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Localization/Language.cs.meta new file mode 100644 index 0000000..30399a5 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Localization/Language.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 165dbc8119f144c919148d4e67fc18ee +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Localization/LanguageBookmarks.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/Localization/LanguageBookmarks.cs new file mode 100644 index 0000000..0974e5d --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Localization/LanguageBookmarks.cs @@ -0,0 +1,48 @@ +/* MIT License + +Copyright (c) 2016 Edward Rowe, RedBlueGames + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +namespace RedBlueGames.MulliganRenamer +{ + using System.Collections.Generic; + using UnityEngine; + public class LanguageBookmarks + { + // These are assigned through Unity's serialization / deserialization. + // So we Ignore warning about Unassigned field. +#pragma warning disable 0649 + [SerializeField] + private List languageUrls; +#pragma warning restore 0649 + + /// + /// + /// + public List LanguageUrls + { + get + { + return this.languageUrls; + } + } + } +} \ No newline at end of file diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Localization/LanguageBookmarks.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Localization/LanguageBookmarks.cs.meta new file mode 100644 index 0000000..183874b --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Localization/LanguageBookmarks.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 58d93b52a9da64460b9a3c44dbc7224e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Localization/LanguageRetriever.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/Localization/LanguageRetriever.cs new file mode 100644 index 0000000..b5a49f6 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Localization/LanguageRetriever.cs @@ -0,0 +1,239 @@ +/* MIT License + +Copyright (c) 2020 Edward Rowe + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +namespace RedBlueGames.MulliganRenamer +{ + using System.Collections; + using System.Collections.Generic; + using UnityEditor; + using UnityEngine; + + /// + /// This class is responsible for getting, or retrieving, the up to date languages from the web. + /// + public class LanguageRetriever + { + private static readonly string BookmarksURIRelease = "https://raw.githubusercontent.com/redbluegames/unity-mulligan-renamer/master/LanguageBookmarks.json"; + + private static readonly string BookmarksURIStaging = "https://raw.githubusercontent.com/redbluegames/unity-mulligan-renamer/staging/LanguageBookmarks.json"; + + public bool IsDoneUpdating { get; private set; } + + public LanguageRetriever() + { + this.IsDoneUpdating = true; + } + + public void UpdateLanguages(bool useStagingLink = false) + { + EditorCoroutineUtility.StartBackgroundTask(this.UpdateLanguagesAsync(useStagingLink), this.HandleUpdateComplete); + } + + private IEnumerator UpdateLanguagesAsync(bool useStagingLink = false) + { + EditorUtility.DisplayProgressBar( + LocalizationManager.Instance.GetTranslation("languageUpdateProgressTitle"), + LocalizationManager.Instance.GetTranslation("languageUpdateProgressMessage1"), + 0.0f); + this.IsDoneUpdating = false; + + LanguageBookmarks bookmarks = null; + { + var bookmarkRetriever = new JSONRetrieverWeb(useStagingLink ? BookmarksURIStaging : BookmarksURIRelease); + var bookmarkFetchOp = bookmarkRetriever.GetJSON(3); + while (bookmarkFetchOp.Status == AsyncStatus.Pending) + { + yield return null; + } + + if (bookmarkFetchOp.Status != AsyncStatus.Success) + { + ShowDisplayDialogForFailedOp(bookmarkFetchOp); + yield break; + } + + bookmarks = bookmarkFetchOp.ResultData; + } + + var languages = new List(); + { + for (int i = 0; i < bookmarks.LanguageUrls.Count; ++i) + { + var url = bookmarks.LanguageUrls[i]; + var uri = new System.Uri(bookmarks.LanguageUrls[i]); + string filename = System.IO.Path.GetFileName(uri.LocalPath); + + // Add one because we finished downloading Bookmarks. + var percentComplete = (i + 1) / (float)(bookmarks.LanguageUrls.Count + 1); + EditorUtility.DisplayProgressBar( + LocalizationManager.Instance.GetTranslation("languageUpdateProgressTitle"), + string.Format( + LocalizationManager.Instance.GetTranslation( + "languageUpdateDownloadingLanguages"), + filename), + percentComplete); + + var languageRetriever = new JSONRetrieverWeb(url); + var languageFetchOp = languageRetriever.GetJSON(3); + while (languageFetchOp.Status == AsyncStatus.Pending) + { + yield return null; + } + + if (languageFetchOp.Status != AsyncStatus.Success) + { + ShowDisplayDialogForFailedOp(languageFetchOp); + yield break; + } + + languages.Add(languageFetchOp.ResultData); + } + } + + EditorUtility.DisplayProgressBar( + LocalizationManager.Instance.GetTranslation("languageUpdateProgressTitle"), + LocalizationManager.Instance.GetTranslation("languageUpdateSavingChanges"), + 1.0f); + EditorUtility.ClearProgressBar(); + + var reports = LocalizationManager.Instance.AddOrUpdateLanguages(languages); + EditorUtility.DisplayDialog( + LocalizationManager.Instance.GetTranslation("languageUpdateProgressTitleSuccess"), + BuildDisplayStringForReport(reports), + LocalizationManager.Instance.GetTranslation("ok")); + } + + private static void ShowDisplayDialogForFailedOp(AsyncOp op) + { + var message = BuildDisplayStringForAsyncOp(op); + EditorUtility.ClearProgressBar(); + EditorUtility.DisplayDialog( + LocalizationManager.Instance.GetTranslation("languageUpdateProgressTitleFail"), + message, + LocalizationManager.Instance.GetTranslation("ok")); + } + + private static string BuildDisplayStringForAsyncOp(AsyncOp op) + { + string message = string.Empty; + if (op.Status == AsyncStatus.Timeout) + { + message = LocalizationManager.Instance.GetTranslation("languageUpdateTimeout"); + } + else if (op.Status == AsyncStatus.Failed) + { + message = string.Format( + LocalizationManager.Instance.GetTranslation("languageUpdateFail"), + op.FailureCode, + op.FailureMessage); + } + else + { + // Nothing to display for success or otherwise + } + + return message; + } + + private static string BuildDisplayStringForReport(List reports) + { + var updatedStringBuilder = new System.Text.StringBuilder(); + var addedLanguageStringBuilder = new System.Text.StringBuilder(); + var unchangedStringBuilder = new System.Text.StringBuilder(); + foreach (var report in reports) + { + if (report.Result == LocalizationManager.LanguageUpdateReport.UpdateResult.Updated) + { + if (updatedStringBuilder.Length > 0) + { + updatedStringBuilder.AppendLine(); + } + + updatedStringBuilder.AppendFormat( + string.Format( + LocalizationManager.Instance.GetTranslation("languageUpdated"), + report.Language.Name, + report.PreviousVersion, + report.NewVersion)); + } + else if (report.Result == LocalizationManager.LanguageUpdateReport.UpdateResult.Added) + { + if (addedLanguageStringBuilder.Length > 0) + { + addedLanguageStringBuilder.AppendLine(); + } + + addedLanguageStringBuilder.AppendFormat( + string.Format( + LocalizationManager.Instance.GetTranslation("languageAdded"), + report.Language.Name)); + } + else + { + if (unchangedStringBuilder.Length > 0) + { + unchangedStringBuilder.AppendLine(); + } + + unchangedStringBuilder.AppendFormat( + string.Format( + LocalizationManager.Instance.GetTranslation("languageUnchanged"), + report.Language.Name)); + } + } + + var message = new System.Text.StringBuilder(); + if (addedLanguageStringBuilder.Length > 0) + { + message.Append(addedLanguageStringBuilder); + } + + if (updatedStringBuilder.Length > 0) + { + if (message.Length > 0) + { + message.AppendLine(); + } + + message.Append(updatedStringBuilder); + } + + if (message.Length == 0) + { + message.Append(LocalizationManager.Instance.GetTranslation("languageAllUpToDate")); + } + else + { + message.AppendLine(); + message.Append(unchangedStringBuilder); + } + + return message.ToString(); + } + + private void HandleUpdateComplete() + { + this.IsDoneUpdating = true; + } + } +} \ No newline at end of file diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Localization/LanguageRetriever.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Localization/LanguageRetriever.cs.meta new file mode 100644 index 0000000..82de59e --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Localization/LanguageRetriever.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9f2a074bcf1db48299f7d89f04cb6db3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Localization/LocalizationManager.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/Localization/LocalizationManager.cs new file mode 100644 index 0000000..37f7eaf --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Localization/LocalizationManager.cs @@ -0,0 +1,281 @@ +/* MIT License + +Copyright (c) 2019 Murillo Pugliesi Lopes, https://github.com/Mukarillo, +and Edward Rowe. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +namespace RedBlueGames.MulliganRenamer +{ + using System; + using System.Collections.Generic; + using System.Linq; + using UnityEditor; + using UnityEngine; + + /// + /// Manages the loading and serving of languages. Use this to get translated strings + /// for the user's current language. + /// + public class LocalizationManager + { + private static LocalizationManager _Instance; + + private const string LanguagePrefKey = "RedBlueGames.MulliganRenamer.Locale"; + + private const string LanguageFoldername = "MulliganLanguages"; + + public event System.Action LanguageChanged; + + public static LocalizationManager Instance + { + get + { + if (_Instance == null) + { + _Instance = new LocalizationManager(); + } + + return _Instance; + } + } + + public Language CurrentLanguage + { + get + { + return this.currentLanguage; + } + } + + public List AllLanguages + { + get + { + return this.allLanguages; + } + } + + private Language currentLanguage; + private List allLanguages; + + private LocalizationManager() + { + this.Initialize(); + } + + /// + /// Gets all the languages stored in the project for Mulligan + /// + /// A List of LocaleLanguages, loaded from disk. + public static List LoadAllLanguages() + { + var loadedLanguages = new List(); + var jsons = Resources.LoadAll(LanguageFoldername); + foreach (var json in jsons) + { + var language = JsonUtility.FromJson(json.text); + if (!string.IsNullOrEmpty(language.Key)) + { + loadedLanguages.Add(language); + } + } + + // We may want control over the sorting, instead of just using the order they + // are loaded in Resources.LoadAll + SortLanguages(loadedLanguages); + + return loadedLanguages; + } + + /// + /// (Re)Initialize the LocaleManager. This loads the languages and sets the language to English, if unset. + /// + public void Initialize() + { + this.CacheAllLanguages(); + this.ChangeLanguage(EditorPrefs.GetString(LanguagePrefKey, "en")); + } + /// + /// Change the current Locale so that Translations are of the new, specified languages + /// + /// LanguageKey to change to + public void ChangeLanguage(string languageKey) + { + var language = this.allLanguages.FirstOrDefault(x => x.Key == languageKey); + if (language != null) + { + EditorPrefs.SetString(LanguagePrefKey, languageKey); + this.currentLanguage = language; + + if (this.LanguageChanged != null) + { + this.LanguageChanged.Invoke(); + } + } + } + + /// + /// Adds new languages and update existing ones that are new or newer versions, using the specified languages. + /// + /// Languages to update + public List AddOrUpdateLanguages(IEnumerable languages) + { + // Need to reload the languages in the LocalizationManager so that we compare + // the new languages against up to date ones. For example, if the user deletes a + // language or adds their own in the same session. Mostly this is just a use-case in testing. + this.Initialize(); + + var languageUpdateReports = new List(); + + foreach (var language in languages) + { + var report = LocalizationManager.Instance.UpdateLanguage(language); + languageUpdateReports.Add(report); + } + + // Resort the languages in case they got reshuffled + SortLanguages(this.allLanguages); + + return languageUpdateReports; + } + + /// + /// Get the translated string for the specified key in the current language. + /// + /// Key whose value we will retrieve + /// The value stored at the key in the current language + public string GetTranslation(string languageKey) + { + if (this.currentLanguage == null) + { + throw new Exception("Current Language is not set"); + } + + return this.currentLanguage.GetValue(languageKey); + } + + private void CacheAllLanguages() + { + this.allLanguages = LoadAllLanguages(); + } + + private LanguageUpdateReport UpdateLanguage(Language newLanguage) + { + var report = new LanguageUpdateReport(); + report.Language = newLanguage; + Language existingLanguage = this.allLanguages.FirstOrDefault((l) => l.Key == newLanguage.Key); + if (existingLanguage == null) + { + report.Result = LanguageUpdateReport.UpdateResult.Added; + this.SaveLanguageToDisk(newLanguage); + } + else if (newLanguage.Version > existingLanguage.Version) + { + report.Result = LanguageUpdateReport.UpdateResult.Updated; + report.PreviousVersion = existingLanguage.Version; + report.NewVersion = newLanguage.Version; + this.SaveLanguageToDisk(newLanguage); + } + else + { + report.Result = LanguageUpdateReport.UpdateResult.NoChange; + } + + // newLanguage is a new language instance, even if it's the same "language", so + // we need to update the reference for the CurrentLanguage to the new one. + // Note this is not really "changing languages" so we don't fire the callback or update PrefKey + if (this.CurrentLanguage.Key == newLanguage.Key) + { + this.currentLanguage = newLanguage; + } + + return report; + } + + private void SaveLanguageToDisk(Language language) + { + var directory = GetPathToLanguages(); + var json = JsonUtility.ToJson(language, true); + var filename = string.Concat(language.Key, ".json"); + var path = System.IO.Path.Combine(directory, filename); + + System.IO.File.WriteAllText(path, json); + AssetDatabase.ImportAsset(path, ImportAssetOptions.Default); + + // We need to unload the language if it exists so that we don't have two versions loaded + this.UnloadLanguage(language); + + this.allLanguages.Add(language); + } + + private void UnloadLanguage(Language language) + { + for (int i = this.allLanguages.Count - 1; i >= 0; --i) + { + if (this.allLanguages[i].Key == language.Key) + { + this.allLanguages.RemoveAt(i); + } + } + } + + private static void SortLanguages(List languages) + { + languages.Sort(CompareLangauges); + } + + private static int CompareLangauges(Language languageA, Language languageB) + { + return UnityEditor.EditorUtility.NaturalCompare(languageA.Key, languageB.Key); + } + + private static string GetPathToLanguages() + { + var jsons = Resources.LoadAll(LanguageFoldername); + if (jsons == null || jsons.Length == 0) + { + // This would happen if a user deletes all their languages. + return string.Empty; + } + + var pathToFirstLanguage = AssetDatabase.GetAssetPath(jsons[0]); + return System.IO.Path.GetDirectoryName(pathToFirstLanguage); + } + + public class LanguageUpdateReport + { + public Language Language { get; set; } + + public UpdateResult Result { get; set; } + + public int PreviousVersion { get; set; } + + public int NewVersion { get; set; } + + public enum UpdateResult + { + NoChange, + Updated, + Added + } + } + } +} \ No newline at end of file diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Localization/LocalizationManager.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Localization/LocalizationManager.cs.meta new file mode 100644 index 0000000..1531203 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Localization/LocalizationManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b93a3df81a055407cb0aeeabc8283d56 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Localization/LocalizedString.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/Localization/LocalizedString.cs new file mode 100644 index 0000000..a6f5567 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Localization/LocalizedString.cs @@ -0,0 +1,60 @@ +/* MIT License + +Copyright (c) 2019 Murillo Pugliesi Lopes, https://github.com/Mukarillo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +namespace RedBlueGames.MulliganRenamer +{ + using UnityEngine; + + /// + /// Class responsible for holding the data for localized text + /// + [System.Serializable] + public class LocalizedString + { + // These are assigned through Unity's serialization / deserialization. + // So we Ignore warning about Unassigned field. + #pragma warning disable 0649 + [SerializeField] + private string key; + + [SerializeField] + private string value; + #pragma warning restore 0649 + + public string Key + { + get + { + return key; + } + } + + public string Value + { + get + { + return value; + } + } + } +} diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Localization/LocalizedString.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Localization/LocalizedString.cs.meta new file mode 100644 index 0000000..49b68c0 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Localization/LocalizedString.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: da4176387c48e4cc8b88f3b28b909aa2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Networking.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Networking.meta new file mode 100644 index 0000000..3f3f2b3 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Networking.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d3be638cad9b748a9b92d42193d1e2ea +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Networking/AsyncOp.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/Networking/AsyncOp.cs new file mode 100644 index 0000000..c9821c3 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Networking/AsyncOp.cs @@ -0,0 +1,126 @@ +/* MIT License + +Copyright (c) 2016 Edward Rowe, RedBlueGames + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// The following #define can be uncommented to force a minimal delay forcing AsyncOps +// to return "Pending". This is useful for testing in-editor where most async ops are +// actually synchronous/instant. + +// #define DELAY_ASYNCOP + +namespace RedBlueGames.MulliganRenamer +{ + using System.Collections; + using UnityEngine; + + /// + /// AsyncOperation provides a way to query when an asynchronous operation has completed + /// and what the result was. + /// + public class AsyncOp + { + /* Consts, Fields ========================================================================================================= */ + + private AsyncStatus status; + +#if DELAY_ASYNCOP + private int pendingDelayCounter; +#endif + + /* Constructors, Enums ==================================================================================================== */ + + /// + /// Constructs a pending asynchronous operation. + /// + public AsyncOp() + { + this.Status = AsyncStatus.Pending; + } + + /// + /// Constructs the asynchronous operations. Status can be set directly if the + /// operation is synchronous on a certain platform. + /// + public AsyncOp(AsyncStatus status) + { + this.Status = status; + } + + /// + /// The current status of the operation. + /// + public AsyncStatus Status + { +#if DELAY_ASYNCOP + get + { + return (++this.pendingDelayCounter < 250) ? AsyncStatus.Pending : this.status; + } +#else + get + { + return this.status; + } +#endif + set + { + this.status = value; + } + } + + public string FailureCode { get; set; } + + public string FailureMessage { get; set; } + + public IEnumerator WaitForResult(float timeout, System.Action timeoutCallback) + { + var startTime = Time.realtimeSinceStartup; + while (this.Status == AsyncStatus.Pending) + { + if (Time.realtimeSinceStartup - startTime > timeout) + { + if (timeoutCallback != null) + { + timeoutCallback.Invoke(); + } + + break; + } + + yield return null; + } + } + } + + /// + /// A derived that allows for extra result information to be provided to the caller. + /// + /// The type of result information to provide. + public class AsyncOp : AsyncOp + { + public AsyncOp() : base() { } + + public AsyncOp(AsyncStatus status) : base(status) { } + + public T ResultData { get; set; } + } +} \ No newline at end of file diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Networking/AsyncOp.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Networking/AsyncOp.cs.meta new file mode 100644 index 0000000..0535a43 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Networking/AsyncOp.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2afa8daae8986480d930d7525b89a7b3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Networking/AsyncStatus.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/Networking/AsyncStatus.cs new file mode 100644 index 0000000..f233655 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Networking/AsyncStatus.cs @@ -0,0 +1,71 @@ +/* MIT License + +Copyright (c) 2016 Edward Rowe, RedBlueGames + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +namespace RedBlueGames.MulliganRenamer +{ + /// + /// The status of an asynchronous operation. Custom AsyncStatus singleton instances + /// can be created to denote specific types of failure. A generic Failure status is + /// also included below but note that it may not be the only type of failure assigned. + /// + public sealed class AsyncStatus + { + public AsyncStatus(string description) { this.Description = description; } + + /// + /// The description of this status. + /// + public readonly string Description; + + /// + /// Status result for async operations that have not completed. + /// + public static readonly AsyncStatus Pending = new AsyncStatus("Pending"); + + /// + /// Status result for successfully completed async operations. + /// + public static readonly AsyncStatus Success = new AsyncStatus("Success"); + + /// + /// Status result for canceled async operations. + /// + public static readonly AsyncStatus Canceled = new AsyncStatus("Canceled"); + + /// + /// Status result for a timedout async operations. + /// + public static readonly AsyncStatus Timeout = new AsyncStatus("Timeout"); + + /// + /// Status result for a failed async operations. Note that other custom failure + /// types may be created, so *avoid* (op == AsyncOp.Failed) style checks. + /// + public static readonly AsyncStatus Failed = new AsyncStatus("Failed"); + + public override string ToString() + { + return this.Description; + } + } +} \ No newline at end of file diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Networking/AsyncStatus.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Networking/AsyncStatus.cs.meta new file mode 100644 index 0000000..c1466ba --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Networking/AsyncStatus.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 70afa1a09539a46aeadf26cbbdbe0037 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Networking/IWebRequest.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/Networking/IWebRequest.cs new file mode 100644 index 0000000..c7718fd --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Networking/IWebRequest.cs @@ -0,0 +1,51 @@ +/* MIT License + +Copyright (c) 2016 Edward Rowe, RedBlueGames + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +namespace RedBlueGames.MulliganRenamer +{ + using System; + + /// + /// Defines the functionality needed for requesting Text from the web. + /// This is used to code JSONRetriever to a testable interface. + /// + + public interface IWebRequest : IDisposable + { + void SendWebRequest(); + + int Timeout { get; set; } + + bool IsDone { get; } + + bool IsNetworkError { get; } + + bool IsHttpError { get; } + + bool IsTimeout { get; } + + string ErrorText { get; } + + string DownloadedText { get; } + } +} \ No newline at end of file diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Networking/IWebRequest.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Networking/IWebRequest.cs.meta new file mode 100644 index 0000000..e2ea73b --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Networking/IWebRequest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 64d55f1c146014391a64366816df3439 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Networking/JSONRetrieverWeb.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/Networking/JSONRetrieverWeb.cs new file mode 100644 index 0000000..c238086 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Networking/JSONRetrieverWeb.cs @@ -0,0 +1,114 @@ +/* MIT License + +Copyright (c) 2016 Edward Rowe, RedBlueGames + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +namespace RedBlueGames.MulliganRenamer +{ + using System.Collections; + using System; + using UnityEngine; + + /// + /// Get a JSON object from a specified address using the specified IWebRequest + /// + /// Type of the JSON object to contstruct after fetching the file. + public class JSONRetrieverWeb + { + public static readonly string ErrorCodeNetworkError = "Network Error"; + public static readonly string ErrorCodeHttpError = "Http Error"; + public static readonly string ErrorCodeInvalidJsonFormat = "Invalid JSON format"; + + private IWebRequest requester; + + private AsyncOp outstandingOp; + + public JSONRetrieverWeb(string uri) : this(UnityWebRequestWrapper.Get(uri)) + { + Uri uriResult; + bool result = Uri.TryCreate(uri, UriKind.Absolute, out uriResult) + && (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps); + if (!result) + { + throw new System.ArgumentException("Invalid URI Format."); + } + } + + public JSONRetrieverWeb(IWebRequest requester) + { + this.requester = requester; + } + + /// + /// Request the JSON from the web using the initialized uri. + /// + /// Timeout for the web request, which returns AsyncStatus of Timeout. + /// An AsyncOp. Query this for the status of the operation and for its results. + public AsyncOp GetJSON(int timeout) + { + this.outstandingOp = new AsyncOp(); + EditorCoroutineUtility.StartBackgroundTask(this.Post(requester, timeout)); + return this.outstandingOp; + } + + private IEnumerator Post(IWebRequest requester, int timeout) + { + using (this.requester) + { + requester.Timeout = timeout; + + requester.SendWebRequest(); + while (!requester.IsDone) + { + yield return null; + } + + if (requester.IsTimeout) + { + this.outstandingOp.Status = AsyncStatus.Timeout; + yield break; + } + + if (requester.IsNetworkError || requester.IsHttpError) + { + this.outstandingOp.Status = AsyncStatus.Failed; + this.outstandingOp.FailureCode = requester.IsHttpError ? ErrorCodeHttpError : ErrorCodeNetworkError; + this.outstandingOp.FailureMessage = requester.ErrorText; + yield break; + } + + this.outstandingOp.Status = AsyncStatus.Success; + + try + { + var json = JsonUtility.FromJson(requester.DownloadedText); + this.outstandingOp.ResultData = json; + } + catch (System.ArgumentException e) + { + this.outstandingOp.Status = AsyncStatus.Failed; + this.outstandingOp.FailureCode = ErrorCodeInvalidJsonFormat; + this.outstandingOp.FailureMessage = e.Message; + } + } + } + } +} \ No newline at end of file diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Networking/JSONRetrieverWeb.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Networking/JSONRetrieverWeb.cs.meta new file mode 100644 index 0000000..4f350dc --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Networking/JSONRetrieverWeb.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3be2c00468c274bdbb45dd75d1918b17 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Networking/UnityWebRequestWrapper.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/Networking/UnityWebRequestWrapper.cs new file mode 100644 index 0000000..a48c574 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Networking/UnityWebRequestWrapper.cs @@ -0,0 +1,127 @@ +/* MIT License + +Copyright (c) 2020 Edward Rowe + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +namespace RedBlueGames.MulliganRenamer +{ + using System; + using UnityEngine.Networking; + + /// + /// This Wrapper is used so that we can better test JSONRetrievers by coding + /// to a common interface. The wrapper exposes only the functionality we need + /// for JSONRetrievers, which is defined in IWebRequest. + /// + public class UnityWebRequestWrapper : IWebRequest, IDisposable + { + private UnityWebRequest webRequest; + + public int Timeout + { + get + { + return this.webRequest.timeout; + } + + set + { + this.webRequest.timeout = value; + } + } + + public bool IsTimeout + { + get + { + return this.webRequest.isNetworkError && this.webRequest.error == "Request timeout"; + } + } + + public bool IsDone + { + get + { + return this.webRequest.isDone; + } + } + + public bool IsNetworkError + { + get + { + return this.webRequest.isNetworkError; + } + } + + public bool IsHttpError + { + get + { + return this.webRequest.isHttpError; + } + } + + public string ErrorText + { + get + { + return this.webRequest.error; + } + } + + public string DownloadedText + { + get + { + return this.webRequest.downloadHandler.text; + } + } + + /// + /// Create an instance ready to go for a GET request. This mirror's UnityWebRequest's API. + /// + /// Address for the GET + /// The object that can be used to execute the web request + public static UnityWebRequestWrapper Get(string uri) + { + return new UnityWebRequestWrapper(uri); + } + + private UnityWebRequestWrapper(string uri) + { + this.webRequest = UnityWebRequest.Get(uri); + } + + /// + /// Asynchronously send the request to the web. Query IsDone to see if it's complete. + /// + public void SendWebRequest() + { + this.webRequest.SendWebRequest(); + } + + public void Dispose() + { + this.webRequest.Dispose(); + } + } +} \ No newline at end of file diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Networking/UnityWebRequestWrapper.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Networking/UnityWebRequestWrapper.cs.meta new file mode 100644 index 0000000..b33d8d2 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Networking/UnityWebRequestWrapper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3067e9f7317df4229aab03d38957c449 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/AddStringOperationDrawer.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/AddStringOperationDrawer.cs index a211698..b3c9d34 100644 --- a/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/AddStringOperationDrawer.cs +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/AddStringOperationDrawer.cs @@ -36,7 +36,7 @@ public override string MenuDisplayPath { get { - return "Add/Prefix or Suffix"; + return GetOperationPath("add", "prefixOrSuffix"); } } @@ -48,7 +48,7 @@ public override string HeadingLabel { get { - return "Add Prefix or Suffix"; + return LocalizationManager.Instance.GetTranslation("addPrefixOrSuffix"); } } @@ -72,7 +72,7 @@ public override string ControlToFocus { get { - return "Prefix"; + return LocalizationManager.Instance.GetTranslation("prefix"); } } @@ -92,16 +92,16 @@ protected override float GetPreferredHeightForContents() /// The prefix of the control to assign to the control names protected override void DrawContents(Rect operationRect, int controlPrefix) { - GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, "Prefix")); + GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, LocalizationManager.Instance.GetTranslation("prefix"))); this.RenameOperation.Prefix = EditorGUI.TextField( operationRect.GetSplitVertical(1, 2, LineSpacing), - "Prefix", + LocalizationManager.Instance.GetTranslation("prefix"), this.RenameOperation.Prefix); - GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, "Suffix")); + GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, LocalizationManager.Instance.GetTranslation("suffix"))); this.RenameOperation.Suffix = EditorGUI.TextField( operationRect.GetSplitVertical(2, 2, LineSpacing), - "Suffix", + LocalizationManager.Instance.GetTranslation("suffix"), this.RenameOperation.Suffix); } } diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/AddStringSequenceOperationDrawer.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/AddStringSequenceOperationDrawer.cs index 3b852c0..d411509 100644 --- a/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/AddStringSequenceOperationDrawer.cs +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/AddStringSequenceOperationDrawer.cs @@ -37,7 +37,7 @@ public override string MenuDisplayPath { get { - return "Add/String Sequence"; + return GetOperationPath("add", "stringSequence"); } } @@ -49,7 +49,7 @@ public override string HeadingLabel { get { - return "Add String Sequence"; + return LocalizationManager.Instance.GetTranslation("addStringSequence"); } } @@ -73,7 +73,7 @@ public override string ControlToFocus { get { - return "Sequence"; + return LocalizationManager.Instance.GetTranslation("sequence"); } } @@ -117,9 +117,11 @@ protected override void DrawContents(Rect operationRect, int controlPrefix) private string[] DrawStringSequenceField(Rect rect, int controlPrefix, string[] stringSequence) { - GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, "Sequence")); + GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, LocalizationManager.Instance.GetTranslation("sequence"))); - var sequenceContent = new GUIContent("Sequence", "The sequence of strings to add, comma separted."); + var sequenceContent = new GUIContent( + LocalizationManager.Instance.GetTranslation("sequence"), + LocalizationManager.Instance.GetTranslation("theSequenceOfStringsToAddCommaSeparted")); var oldSequence = StringUtilities.AddCommasBetweenStrings(stringSequence); var sequenceStrings = oldSequence; var sequenceWithCommas = EditorGUI.TextField( @@ -132,7 +134,9 @@ private string[] DrawStringSequenceField(Rect rect, int controlPrefix, string[] private bool DrawPrependField(Rect rect, int controlPrefix, bool originalPrepend) { - var content = new GUIContent("Add as Prefix", "Add the count to the front of the object's name."); + var content = new GUIContent( + LocalizationManager.Instance.GetTranslation("addAsPrefix"), + LocalizationManager.Instance.GetTranslation("addTheCountToTheFrontOfTheObjectName")); GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, content.text)); return EditorGUI.Toggle(rect, content, originalPrepend); } diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/AdjustNumberingOperationDrawer.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/AdjustNumberingOperationDrawer.cs new file mode 100644 index 0000000..2dd7657 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/AdjustNumberingOperationDrawer.cs @@ -0,0 +1,102 @@ +/* MIT License + +Copyright (c) 2016 Edward Rowe, RedBlueGames + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +namespace RedBlueGames.MulliganRenamer +{ + using UnityEngine; + using UnityEditor; + + public class AdjustNumberingOperationDrawer : RenameOperationDrawer + { + /// + /// Gets the path that's displayed when this rename op is used in the Add Op menu. + /// + /// The display path. + public override string MenuDisplayPath + { + get + { + return LocalizationManager.Instance.GetTranslation("modify") + "/" + LocalizationManager.Instance.GetTranslation("adjustNumbers"); + } + } + + /// + /// Gets the heading label for the Rename Operation. + /// + /// The heading label. + public override string HeadingLabel + { + get + { + return LocalizationManager.Instance.GetTranslation("adjustNumbers"); + } + } + + /// + /// Gets the color to use for highlighting the operation. + /// + /// The color of the highlight. + public override Color32 HighlightColor + { + get + { + return this.ModifyColor; + } + } + + /// + /// Gets the name of the control to focus when this operation is focused + /// + /// The name of the control to focus. + public override string ControlToFocus + { + get + { + return LocalizationManager.Instance.GetTranslation("offset"); + } + } + + /// + /// Gets the preferred height for the contents of the operation. + /// This allows inherited operations to specify their height. + /// + /// The preferred height for contents. + protected override float GetPreferredHeightForContents() + { + return this.CalculateGUIHeightForLines(1); + } + + /// + /// Draws the contents of the Rename Op. + /// + /// The prefix of the control to assign to the control names + protected override void DrawContents(Rect operationRect, int controlPrefix) + { + GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, LocalizationManager.Instance.GetTranslation("offset"))); + this.RenameOperation.Offset = EditorGUI.IntField( + operationRect.GetSplitVertical(1, 1, LineSpacing), + LocalizationManager.Instance.GetTranslation("offset"), + this.RenameOperation.Offset); + } + } +} \ No newline at end of file diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/AdjustNumberingOperationDrawer.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/AdjustNumberingOperationDrawer.cs.meta new file mode 100644 index 0000000..aed3468 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/AdjustNumberingOperationDrawer.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 60813a4ed56d1425e9d44d7966e374e2 +timeCreated: 1523819846 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/ChangeCaseOperationDrawer.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/ChangeCaseOperationDrawer.cs index 9f22e4d..992a13e 100644 --- a/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/ChangeCaseOperationDrawer.cs +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/ChangeCaseOperationDrawer.cs @@ -36,7 +36,7 @@ public override string MenuDisplayPath { get { - return "Modify/To Upper or Lowercase"; + return GetOperationPath("modify", "toUpperOrLowercase"); } } @@ -48,7 +48,7 @@ public override string HeadingLabel { get { - return "To Upper or Lowercase"; + return LocalizationManager.Instance.GetTranslation("toUpperOrLowercase"); } } @@ -72,7 +72,7 @@ public override string ControlToFocus { get { - return "To Uppercase"; + return LocalizationManager.Instance.GetTranslation("toUppercase"); } } @@ -94,15 +94,22 @@ protected override void DrawContents(Rect operationRect, int controlPrefix) { var singleLineRect = operationRect.GetSplitVertical(1, 2, LineSpacing); - var casingLabel = new GUIContent("New Casing", "The desired casing for the new name."); - GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, "To Uppercase")); - this.RenameOperation.Casing = (ChangeCaseOperation.CasingChange)EditorGUI.EnumPopup( + var casingLabel = new GUIContent( + LocalizationManager.Instance.GetTranslation("newCasing"), + LocalizationManager.Instance.GetTranslation("theDesiredCasingForName")); + GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, LocalizationManager.Instance.GetTranslation("toUppercase"))); + var options = new GUIContent[] {new GUIContent(LocalizationManager.Instance.GetTranslation("Lowercase")), new GUIContent(LocalizationManager.Instance.GetTranslation("Uppercase"))}; + this.RenameOperation.Casing = (ChangeCaseOperation.CasingChange)EditorGUI.Popup( singleLineRect, casingLabel, - this.RenameOperation.Casing); + (int)RenameOperation.Casing, + options); var firstCharOnlyRect = operationRect.GetSplitVertical(2, 2, LineSpacing); - var firstCharToggleLabel = new GUIContent("Only First Character", "Change only the first character's case."); + var firstCharToggleLabel = new GUIContent( + LocalizationManager.Instance.GetTranslation("onlyFirstCharacter"), + LocalizationManager.Instance.GetTranslation("changeOnlyTheFirstCharacterCase")); + GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, LocalizationManager.Instance.GetTranslation("firstCharOnly"))); this.RenameOperation.ChangeFirstCharacterOnly = EditorGUI.Toggle( firstCharOnlyRect, firstCharToggleLabel, diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/CountByLetterOperationDrawer.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/CountByLetterOperationDrawer.cs index 5e6de9f..6d15270 100644 --- a/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/CountByLetterOperationDrawer.cs +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/CountByLetterOperationDrawer.cs @@ -30,10 +30,8 @@ namespace RedBlueGames.MulliganRenamer public class CountByLetterOperationDrawer : RenameOperationDrawer { private readonly GUIContent CustomSequenceContent = new GUIContent( - "Strings", "The strings of letters to add, comma separated. Ex: \"A,B,C\" will append " + - "A, B, and C to the first three objects respectively. After that it will add another " + - "sequence, starting with AA, then AB, then AC, etc."); - + LocalizationManager.Instance.GetTranslation("strings"), + LocalizationManager.Instance.GetTranslation("theStringsOfLettersToAdd")); public CountByLetterOperationDrawer() { @@ -48,7 +46,7 @@ public override string MenuDisplayPath { get { - return "Add/Count By Letter"; + return GetOperationPath("add", "countByLetter"); } } @@ -60,7 +58,7 @@ public override string HeadingLabel { get { - return "Count by Letter"; + return LocalizationManager.Instance.GetTranslation("countByLetter"); } } @@ -84,7 +82,7 @@ public override string ControlToFocus { get { - return "Format"; + return LocalizationManager.Instance.GetTranslation("format"); } } @@ -181,7 +179,9 @@ protected override void DrawContents(Rect operationRect, int controlPrefix) private CountByLetterPresetGUI DrawSequenceSelection(Rect rect, int controlPrefix) { GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, "Format")); - var modeContent = new GUIContent("Format", "Format for the added letters."); + var modeContent = new GUIContent( + LocalizationManager.Instance.GetTranslation("format"), + LocalizationManager.Instance.GetTranslation("formatForTheAddedLetters")); var optionsContent = new GUIContent[this.GUIPresets.Count]; for (int i = 0; i < optionsContent.Length; ++i) { @@ -220,8 +220,9 @@ private int DrawCountFromField(Rect rect, int controlPrefix, int originalIndex) { var weights = new float[] { 0.65f, 0.35f }; var intFieldRect = rect.GetSplitHorizontalWeighted(1, 0.0f, weights); - var content = new GUIContent("Count From", "The value to start counting from. " + - "The string from the sequence at this count will be appended to the first object."); + var content = new GUIContent( + LocalizationManager.Instance.GetTranslation("countFrom"), + LocalizationManager.Instance.GetTranslation("theValueToStartCounting")); GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, content.text)); // Add and subtract 1 so that it displays as 1 based. @@ -234,7 +235,9 @@ private int DrawCountFromField(Rect rect, int controlPrefix, int originalIndex) var labelStyle = new GUIStyle(EditorStyles.label); labelStyle.alignment = TextAnchor.MiddleLeft; var labelRect = rect.GetSplitHorizontalWeighted(2, 0.0f, weights); - EditorGUI.LabelField(labelRect, new GUIContent("Starts with: " + stringToCountFrom), labelStyle); + EditorGUI.LabelField(labelRect, new GUIContent( + LocalizationManager.Instance.GetTranslation("startsWith") + ": " + stringToCountFrom), + labelStyle); EditorGUI.EndDisabledGroup(); } @@ -244,14 +247,18 @@ private int DrawCountFromField(Rect rect, int controlPrefix, int originalIndex) private int DrawIncrementField(Rect rect, int controlPrefix, int originalIncrement) { - var content = new GUIContent("Increment", "The value to add to the count after naming an object."); + var content = new GUIContent( + LocalizationManager.Instance.GetTranslation("increment"), + LocalizationManager.Instance.GetTranslation("theValueToAddToTheCount")); GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, content.text)); return EditorGUI.IntField(rect, content, originalIncrement); } private bool DrawPrependField(Rect rect, int controlPrefix, bool originalPrepend) { - var content = new GUIContent("Add as Prefix", "Add the count to the front of the object's name."); + var content = new GUIContent( + LocalizationManager.Instance.GetTranslation("addAsPrefix"), + LocalizationManager.Instance.GetTranslation("addTheCountToTheFront")); GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, content.text)); return EditorGUI.Toggle(rect, content, originalPrepend); } @@ -260,21 +267,21 @@ private void Initialize() { var uppercasePreset = new CountByLetterPresetGUI() { - DisplayName = "Uppercase Alphabet", + DisplayName = LocalizationManager.Instance.GetTranslation("uppercaseAlphabet"), SequenceToDisplay = "A, B, C...", Preset = CountByLetterOperation.StringPreset.UppercaseAlphabet, }; var lowercasePreset = new CountByLetterPresetGUI() { - DisplayName = "Lowercase Alphabet", + DisplayName = LocalizationManager.Instance.GetTranslation("lowercaseAlphabet"), SequenceToDisplay = "a, b, c...", Preset = CountByLetterOperation.StringPreset.LowercaseAlphabet, }; var customPreset = new CountByLetterPresetGUI() { - DisplayName = "Custom", + DisplayName = LocalizationManager.Instance.GetTranslation("custom"), CountSequence = new string[0], SequenceToDisplay = string.Empty, Preset = CountByLetterOperation.StringPreset.Custom, diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/EnumerateOperationDrawer.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/EnumerateOperationDrawer.cs index 4d9b286..144d901 100644 --- a/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/EnumerateOperationDrawer.cs +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/EnumerateOperationDrawer.cs @@ -42,7 +42,7 @@ public override string MenuDisplayPath { get { - return "Add/Count"; + return GetOperationPath("add", "count"); } } @@ -54,7 +54,7 @@ public override string HeadingLabel { get { - return "Count"; + return LocalizationManager.Instance.GetTranslation("count"); } } @@ -78,7 +78,7 @@ public override string ControlToFocus { get { - return "Format"; + return LocalizationManager.Instance.GetTranslation("format"); } } @@ -137,7 +137,9 @@ private float GetHeightForHelpBox() /// The prefix of the control to assign to the control names protected override void DrawContents(Rect operationRect, int controlPrefix) { - var presetsContent = new GUIContent("Format", "Select a preset format or specify your own format."); + var presetsContent = new GUIContent( + LocalizationManager.Instance.GetTranslation("format"), + LocalizationManager.Instance.GetTranslation("selectPresetFormat")); var names = new List(this.GUIPresets.Count); foreach (var preset in this.GUIPresets) { @@ -167,7 +169,9 @@ protected override void DrawContents(Rect operationRect, int controlPrefix) var selectedPreset = this.GUIPresets[newlySelectedIndex]; EditorGUI.BeginDisabledGroup(selectedPreset.ReadOnly); - var countFormatContent = new GUIContent("Count Format", "The string format to use when adding the Count to the name."); + var countFormatContent = new GUIContent( + LocalizationManager.Instance.GetTranslation("countFormat"), + LocalizationManager.Instance.GetTranslation("theStringFormatToUseWhenAddingTheCountToName")); GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, countFormatContent.text)); if (selectedPreset.ReadOnly) { @@ -203,29 +207,33 @@ protected override void DrawContents(Rect operationRect, int controlPrefix) return; } - var helpBoxMessage = "Invalid Count Format. Typical formats are D1 for one digit with no " + - "leading zeros, D2, for two, etc." + - "\nLookup the String.Format() method for more info on formatting options."; + var helpBoxMessage = LocalizationManager.Instance.GetTranslation("invalidCountFormat"); var helpRect = operationRect.GetSplitVerticalWeighted(++currentLine, LineSpacing, weights); helpRect = helpRect.AddPadding(4, 4, 4, 4); EditorGUI.HelpBox(helpRect, helpBoxMessage, MessageType.Warning); } - var countFromContent = new GUIContent("Count From", "The value to start counting from. The first object will have this number."); + var countFromContent = new GUIContent( + LocalizationManager.Instance.GetTranslation("countFrom"), + LocalizationManager.Instance.GetTranslation("theValueToStartCountingFrom")); GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, countFromContent.text)); this.RenameOperation.StartingCount = EditorGUI.IntField( operationRect.GetSplitVerticalWeighted(++currentLine, LineSpacing, weights), countFromContent, this.RenameOperation.StartingCount); - var incrementContent = new GUIContent("Increment", "The value to add to each object when counting."); + var incrementContent = new GUIContent( + LocalizationManager.Instance.GetTranslation("increment"), + LocalizationManager.Instance.GetTranslation("theValueToAddToEachObjectWhenCounting")); GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, incrementContent.text)); this.RenameOperation.Increment = EditorGUI.IntField( operationRect.GetSplitVerticalWeighted(++currentLine, LineSpacing, weights), incrementContent, this.RenameOperation.Increment); - var prependContent = new GUIContent("Add as Prefix", "Add the count to the front of the object's name."); + var prependContent = new GUIContent( + LocalizationManager.Instance.GetTranslation("addAsPrefix"), + LocalizationManager.Instance.GetTranslation("addTheCountToTheFontOfTheObjectName")); GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, prependContent.text)); this.RenameOperation.Prepend = EditorGUI.Toggle( operationRect.GetSplitVerticalWeighted(++currentLine, LineSpacing, weights), @@ -258,7 +266,7 @@ private void Initialize() var customPreset = new EnumeratePresetGUI() { - DisplayName = "Custom", + DisplayName = LocalizationManager.Instance.GetTranslation("custom"), Preset = EnumerateOperation.CountFormatPreset.Custom, ReadOnly = false }; diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/RemoveCharactersOperationDrawer.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/RemoveCharactersOperationDrawer.cs index 7f1f84f..1e95c83 100644 --- a/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/RemoveCharactersOperationDrawer.cs +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/RemoveCharactersOperationDrawer.cs @@ -42,7 +42,7 @@ public override string MenuDisplayPath { get { - return "Delete/Remove Characters"; + return GetOperationPath("delete", "removeCharacters"); } } @@ -54,7 +54,7 @@ public override string HeadingLabel { get { - return "Remove Characters"; + return LocalizationManager.Instance.GetTranslation("removeCharacters"); } } @@ -78,7 +78,7 @@ public override string ControlToFocus { get { - return "Preset"; + return LocalizationManager.Instance.GetTranslation("preset"); } } @@ -152,7 +152,9 @@ protected override void DrawContents(Rect operationRect, int controlPrefix) numSplits = 3; } - var presetsContent = new GUIContent("Preset", "Select a preset or specify your own characters."); + var presetsContent = new GUIContent( + LocalizationManager.Instance.GetTranslation("preset"), + LocalizationManager.Instance.GetTranslation("selectPresetOrSpecifyCharacters")); var names = new List(this.GUIPresets.Count); foreach (var preset in this.GUIPresets) { @@ -200,14 +202,18 @@ protected override void DrawContents(Rect operationRect, int controlPrefix) } else { - var charactersFieldContent = new GUIContent("Characters to Remove", "All characters that will be removed from the names."); + var charactersFieldContent = new GUIContent( + LocalizationManager.Instance.GetTranslation("charactersToRemove"), + LocalizationManager.Instance.GetTranslation("allCharactersThatWillBeRemoved")); GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, charactersFieldContent.text)); workingOptions.CharactersToRemove = EditorGUI.TextField( operationRect.GetSplitVertical(++currentSplit, numSplits, LineSpacing), charactersFieldContent, this.RenameOperation.CharactersToRemove); - var caseSensitiveToggleContent = new GUIContent("Case Sensitive", "Flag the search to match only the specified case"); + var caseSensitiveToggleContent = new GUIContent( + LocalizationManager.Instance.GetTranslation("caseSensitive"), + LocalizationManager.Instance.GetTranslation("flagTheSearchToMatchCase")); workingOptions.IsCaseSensitive = EditorGUI.Toggle( operationRect.GetSplitVertical(++currentSplit, numSplits, LineSpacing), caseSensitiveToggleContent, @@ -228,31 +234,31 @@ private void Initialize() { var symbolsPreset = new CharacterPresetGUI() { - DisplayName = "Symbols", - ReadOnlyLabel = "Removes special characters (ie. !@#$%^&*)", + DisplayName = LocalizationManager.Instance.GetTranslation("symbols"), + ReadOnlyLabel = LocalizationManager.Instance.GetTranslation("removeSpecialCharacters"), PresetID = RemoveCharactersOperation.PresetID.Symbols, IsReadOnly = true }; var numbersPreset = new CharacterPresetGUI() { - DisplayName = "Numbers", - ReadOnlyLabel = "Removes digits 0-9", + DisplayName = LocalizationManager.Instance.GetTranslation("numbers"), + ReadOnlyLabel = LocalizationManager.Instance.GetTranslation("removeDigits"), PresetID = RemoveCharactersOperation.PresetID.Numbers, IsReadOnly = true }; var whitespacePreset = new CharacterPresetGUI() { - DisplayName = "Whitespace", - ReadOnlyLabel = "Removes whitespace", + DisplayName = LocalizationManager.Instance.GetTranslation("whitespace"), + ReadOnlyLabel = LocalizationManager.Instance.GetTranslation("removesWhitespace"), PresetID = RemoveCharactersOperation.PresetID.Whitespace, IsReadOnly = true }; var customPreset = new CharacterPresetGUI() { - DisplayName = "Custom", + DisplayName = LocalizationManager.Instance.GetTranslation("custom"), PresetID = RemoveCharactersOperation.PresetID.Custom, IsReadOnly = false, ReadOnlyLabel = string.Empty diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/RenameOperationDrawer.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/RenameOperationDrawer.cs index 5b1b7d2..3e2b319 100644 --- a/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/RenameOperationDrawer.cs +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/RenameOperationDrawer.cs @@ -28,7 +28,7 @@ namespace RedBlueGames.MulliganRenamer using UnityEngine; using UnityEditor; - public abstract class RenameOperationDrawer : IRenameOperationDrawer where T: IRenameOperation + public abstract class RenameOperationDrawer : IRenameOperationDrawer where T : IRenameOperation { protected readonly Color32 ReplaceColor = new Color32(17, 138, 178, 255); protected readonly Color32 AddColor = new Color32(6, 214, 160, 255); @@ -88,7 +88,7 @@ public void SetModel(IRenameOperation renameOperationInstance) { // This cast is a *bit* of an assumption, that the passed instance can be // downcasted to a more derived type (T : IRenameOperation) than IRenameOperation. - this.renameOperation = (T) renameOperationInstance; + this.renameOperation = (T)renameOperationInstance; } /// @@ -194,6 +194,17 @@ protected RenameOperationSortingButtonEvent DrawHeaderAndReorderButtons(Rect con return buttonEvent; } + // + /// Returns the path for the operation localized; + /// + /// The path for the operation localized + /// The folder of the operation. + /// The name of the operation + protected string GetOperationPath(string folder, string name) + { + return LocalizationManager.Instance.GetTranslation(folder) + "/" + LocalizationManager.Instance.GetTranslation(name); + } + private RenameOperationSortingButtonEvent DrawReorderingButtons(Rect containingRect, bool disableUpButton, bool disableDownButton) { const string BigUpArrowUnicode = "\u25B2"; diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/ReplaceNameOperationDrawer.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/ReplaceNameOperationDrawer.cs index b21cd3e..5582ede 100644 --- a/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/ReplaceNameOperationDrawer.cs +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/ReplaceNameOperationDrawer.cs @@ -36,7 +36,7 @@ public override string MenuDisplayPath { get { - return "Replace/Rename"; + return GetOperationPath("replace", "rename"); } } @@ -48,7 +48,7 @@ public override string HeadingLabel { get { - return "Rename"; + return LocalizationManager.Instance.GetTranslation("rename"); } } @@ -72,7 +72,7 @@ public override string ControlToFocus { get { - return "New Name"; + return LocalizationManager.Instance.GetTranslation("newName"); } } @@ -95,7 +95,9 @@ protected override void DrawContents(Rect operationRect, int controlPrefix) // Split even a single line GUI so that we properly subtract out spacing var singleLineRect = operationRect.GetSplitVertical(1, 1, LineSpacing); - GUIContent newNameContent = new GUIContent("New Name", "Name to replace the old one with."); + GUIContent newNameContent = new GUIContent( + LocalizationManager.Instance.GetTranslation("newName"), + LocalizationManager.Instance.GetTranslation("nameToReplaceTheOldeOne")); GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, newNameContent.text)); this.RenameOperation.NewName = EditorGUI.TextField(singleLineRect, newNameContent, this.RenameOperation.NewName); } diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/ReplaceStringOperationDrawer.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/ReplaceStringOperationDrawer.cs index 11cd7c6..eebe9c0 100644 --- a/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/ReplaceStringOperationDrawer.cs +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/ReplaceStringOperationDrawer.cs @@ -37,7 +37,7 @@ public override string MenuDisplayPath { get { - return "Replace/Replace String"; + return GetOperationPath("replace", "replaceString"); } } @@ -49,7 +49,7 @@ public override string HeadingLabel { get { - return "Replace String"; + return LocalizationManager.Instance.GetTranslation("replaceString"); } } @@ -73,7 +73,7 @@ public override string ControlToFocus { get { - return "Search String"; + return LocalizationManager.Instance.GetTranslation("searchString"); } } @@ -132,7 +132,9 @@ protected override void DrawContents(Rect operationRect, int controlPrefix) var weightsArray = weights.ToArray(); int currentGUIElement = 0; - var regexToggleContent = new GUIContent("Use Regular Expression", "Match terms using Regular Expressions, terms that allow for powerful pattern matching."); + var regexToggleContent = new GUIContent( + LocalizationManager.Instance.GetTranslation("useRegex"), + LocalizationManager.Instance.GetTranslation("matchTermsUsingRegex")); GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, regexToggleContent.text)); postGUIModel.UseRegex = EditorGUI.Toggle( operationRect.GetSplitVerticalWeighted(++currentGUIElement, LineSpacing, weightsArray), @@ -143,34 +145,36 @@ protected override void DrawContents(Rect operationRect, int controlPrefix) GUIContent replacementContent; if (preGUIModel.UseRegex) { - searchContent = new GUIContent("Match Regex", "Regular Expression to use to match terms."); + searchContent = new GUIContent( + LocalizationManager.Instance.GetTranslation("matchRegex"), + LocalizationManager.Instance.GetTranslation("regexToUseToMatchTerms")); replacementContent = new GUIContent("Replacement Regex", "Regular Expression to use when replacing matched patterns."); } else { searchContent = new GUIContent( - "Search for String", - "Substrings to search for in the filenames. These strings will be replaced by the Replacement String."); + LocalizationManager.Instance.GetTranslation("searchForString"), + LocalizationManager.Instance.GetTranslation("substringsToSeatchInFilenames")); replacementContent = new GUIContent( - "Replace with", - "String to replace matching instances of the Search string."); + LocalizationManager.Instance.GetTranslation("replaceWith"), + LocalizationManager.Instance.GetTranslation("stringToReplaceMatchingInstances")); } - GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, "Search String")); + GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, LocalizationManager.Instance.GetTranslation("searchString"))); postGUIModel.SearchString = EditorGUI.TextField( operationRect.GetSplitVerticalWeighted(++currentGUIElement, LineSpacing, weightsArray), searchContent, preGUIModel.SearchString); - GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, "Replacement String")); + GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, LocalizationManager.Instance.GetTranslation("replacementString"))); postGUIModel.ReplacementString = EditorGUI.TextField( operationRect.GetSplitVerticalWeighted(++currentGUIElement, LineSpacing, weightsArray), replacementContent, preGUIModel.ReplacementString); var caseSensitiveContent = new GUIContent( - "Case Sensitive", - "Search using case sensitivity. Only strings that match the supplied casing will be replaced."); + LocalizationManager.Instance.GetTranslation("caseSensitive"), + LocalizationManager.Instance.GetTranslation("searchUsingCaseSensitivity")); GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, caseSensitiveContent.text)); postGUIModel.SearchIsCaseSensitive = EditorGUI.Toggle( operationRect.GetSplitVerticalWeighted(++currentGUIElement, LineSpacing, weightsArray), @@ -184,14 +188,14 @@ protected override void DrawContents(Rect operationRect, int controlPrefix) { var helpRect = operationRect.GetSplitVerticalWeighted(++currentGUIElement, LineSpacing, weightsArray); helpRect = helpRect.AddPadding(4, 4, 4, 4); - EditorGUI.HelpBox(helpRect, "Match Expression is not a valid Regular Expression.", MessageType.Error); + EditorGUI.HelpBox(helpRect, LocalizationManager.Instance.GetTranslation("matchExpressNotValid"), MessageType.Error); } if (!preGUIModel.ReplacementStringIsValidRegex) { var helpRect = operationRect.GetSplitVerticalWeighted(++currentGUIElement, LineSpacing, weightsArray); helpRect = helpRect.AddPadding(4, 4, 4, 4); - EditorGUI.HelpBox(helpRect, "Replacement Expression is not a valid Regular Expression.", MessageType.Error); + EditorGUI.HelpBox(helpRect, LocalizationManager.Instance.GetTranslation("replacementExpressionNotValid"), MessageType.Error); } } diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/ToCamelCaseOperationDrawer.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/ToCamelCaseOperationDrawer.cs index 87833fb..f129e29 100644 --- a/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/ToCamelCaseOperationDrawer.cs +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/ToCamelCaseOperationDrawer.cs @@ -36,7 +36,7 @@ public override string MenuDisplayPath { get { - return "Modify/To Camel Case"; + return GetOperationPath("modify", "toCamelCase"); } } @@ -48,7 +48,7 @@ public override string HeadingLabel { get { - return "To Camel Case"; + return LocalizationManager.Instance.GetTranslation("toCamelCase"); } } @@ -94,7 +94,9 @@ protected override void DrawContents(Rect operationRect, int controlPrefix) { var singleLineRect = operationRect.GetSplitVertical(1, 2, LineSpacing); - var pascalLabel = new GUIContent("Use Pascal Casing", "Flag to capitalize the first letter of the name (also known as Upper Camel Casing)."); + var pascalLabel = new GUIContent( + LocalizationManager.Instance.GetTranslation("usePascalCasing"), + LocalizationManager.Instance.GetTranslation("flagToCapitalizeTheFirstLetterOfname")); GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, "Pascal")); this.RenameOperation.UsePascal = EditorGUI.Toggle( singleLineRect, @@ -103,8 +105,10 @@ protected override void DrawContents(Rect operationRect, int controlPrefix) ); var delimitersRect = operationRect.GetSplitVertical(2, 2, LineSpacing); - var delimitersLabel = new GUIContent("Delimiter Characters", "The case sensitive characters that indicate the start of a word."); - GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, "Delimiters")); + var delimitersLabel = new GUIContent( + LocalizationManager.Instance.GetTranslation("delimiterCharacters"), + LocalizationManager.Instance.GetTranslation("caseSensitiveCharactersIndicateStart")); + GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, LocalizationManager.Instance.GetTranslation("delimiters"))); this.RenameOperation.DelimiterCharacters = EditorGUI.TextField( delimitersRect, delimitersLabel, diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/TrimCharactersOperationDrawer.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/TrimCharactersOperationDrawer.cs index 8e2d06f..83cd4cc 100644 --- a/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/TrimCharactersOperationDrawer.cs +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/OperationDrawers/TrimCharactersOperationDrawer.cs @@ -36,7 +36,7 @@ public override string MenuDisplayPath { get { - return "Delete/Trim Characters"; + return GetOperationPath("delete", "trimCharacters"); } } @@ -48,7 +48,7 @@ public override string HeadingLabel { get { - return "Trim Characters"; + return LocalizationManager.Instance.GetTranslation("trimCharacters"); } } @@ -72,7 +72,7 @@ public override string ControlToFocus { get { - return "Delete from Front"; + return LocalizationManager.Instance.GetTranslation("deleteFromFront"); } } @@ -92,17 +92,17 @@ protected override float GetPreferredHeightForContents() /// The prefix of the control to assign to the control names protected override void DrawContents(Rect operationRect, int controlPrefix) { - GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, "Delete from Front")); + GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, LocalizationManager.Instance.GetTranslation("deleteFromFront"))); this.RenameOperation.NumFrontDeleteChars = EditorGUI.IntField( operationRect.GetSplitVertical(1, 2, LineSpacing), - "Delete from Front", + LocalizationManager.Instance.GetTranslation("deleteFromFront"), this.RenameOperation.NumFrontDeleteChars); this.RenameOperation.NumFrontDeleteChars = Mathf.Max(0, this.RenameOperation.NumFrontDeleteChars); - GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, "Delete from Back")); + GUI.SetNextControlName(GUIControlNameUtility.CreatePrefixedName(controlPrefix, LocalizationManager.Instance.GetTranslation("deleteFromBack"))); this.RenameOperation.NumBackDeleteChars = EditorGUI.IntField( operationRect.GetSplitVertical(2, 2, LineSpacing), - "Delete from Back", + LocalizationManager.Instance.GetTranslation("deleteFromBack"), this.RenameOperation.NumBackDeleteChars); this.RenameOperation.NumBackDeleteChars = Mathf.Max(0, this.RenameOperation.NumBackDeleteChars); } diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Operations/AdjustNumberingOperation.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/Operations/AdjustNumberingOperation.cs new file mode 100644 index 0000000..6cb2d2c --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Operations/AdjustNumberingOperation.cs @@ -0,0 +1,151 @@ +/* MIT License + +Copyright (c) 2016 Edward Rowe, RedBlueGames + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +namespace RedBlueGames.MulliganRenamer +{ + using System.Collections.Generic; + using System.Text.RegularExpressions; + using UnityEditor; + using UnityEngine; + + /// + /// RenameOperation that changes the case of the characters in the name. + /// + [System.Serializable] + public class AdjustNumberingOperation : IRenameOperation + { + [SerializeField] + private int offset; + + /// + /// Initializes a new instance of the class. + /// + public AdjustNumberingOperation() + { + this.offset = 0; + } + + /// + /// Initializes a new instance of the class by copying another. + /// + /// Operation to copy. + public AdjustNumberingOperation(AdjustNumberingOperation operationToCopy) + { + this.Offset = operationToCopy.Offset; + } + + public int Offset + { + get + { + return this.offset; + } + + set + { + this.offset = value; + } + } + + public bool HasErrors() + { + return false; + } + + /// + /// Clone this instance. + /// + /// A clone of this instance + public IRenameOperation Clone() + { + var clone = new AdjustNumberingOperation(this); + return clone; + } + + /// + /// Rename the specified input, using the relativeCount. + /// + /// Input String to rename. + /// Relative count. This can be used for enumeration. + /// A new string renamed according to the rename operation's rules. + public RenameResult Rename(string input, int relativeCount) + { + if (string.IsNullOrEmpty(input)) + { + return RenameResult.Empty; + } + + var parseNumbersRegex = "([0-9]+)"; + + var regex = new Regex(parseNumbersRegex); + var matches = regex.Matches(input); + + var fullReplacementResult = RenameResultUtilities.CreateDiffFromReplacedMatches(input, this.ReplaceMatch, matches); + var renameResult = RenameResultUtilities.GetDiffResultFromStrings(input, fullReplacementResult.Result); + return renameResult; + } + + private string ReplaceMatch(Match match) + { + try + { + int parsedInt = int.Parse(match.Value); + parsedInt += this.offset; + return parsedInt.ToString(); + } + catch (System.Exception) + { + return match.Value; + } + } + + /// + /// Gets the hash code for the operation + /// + /// A unique hash code from the values + public override int GetHashCode() + { + return this.Offset.GetHashCode(); + } + + /// + /// Returns whether or not this rename operation is equal to another and returns the result. + /// + /// True if the operations are equal.true False otherwise. + public override bool Equals(object obj) + { + var otherAsOp = obj as AdjustNumberingOperation; + if (otherAsOp == null) + { + return false; + } + + if (this.Offset != otherAsOp.Offset) + { + return false; + } + + return true; + } + } +} \ No newline at end of file diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Operations/AdjustNumberingOperation.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Operations/AdjustNumberingOperation.cs.meta new file mode 100644 index 0000000..9ee2ea0 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Operations/AdjustNumberingOperation.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 146fe5a2fa71e4784b18177c93f5ef89 +timeCreated: 1495391168 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Operations/ReplaceStringOperation.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/Operations/ReplaceStringOperation.cs index 8fde77d..6a24c2f 100644 --- a/Assets/RedBlueGames/MulliganRenamer/Editor/Operations/ReplaceStringOperation.cs +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Operations/ReplaceStringOperation.cs @@ -257,10 +257,15 @@ public RenameResult Rename(string input, int relativeCount) return renameResult; } - renameResult = this.CreateDiffFromMatches(input, this.ReplacementString, matches); + renameResult = RenameResultUtilities.CreateDiffFromReplacedMatches(input, this.ReplaceMatch, matches); return renameResult; } + private string ReplaceMatch(Match match) + { + return match.Result(this.ReplacementString); + } + /// /// Gets the hash code for the operation /// @@ -336,40 +341,5 @@ private static bool IsValidRegex(string pattern) return true; } - - private RenameResult CreateDiffFromMatches(string originalName, string replacementRegex, MatchCollection matches) - { - var renameResult = new RenameResult(); - var nextMatchStartingIndex = 0; - foreach (System.Text.RegularExpressions.Match match in matches) - { - // Grab the substring before the match - if (nextMatchStartingIndex < match.Index) - { - string before = originalName.Substring(nextMatchStartingIndex, match.Index - nextMatchStartingIndex); - renameResult.Add(new Diff(before, DiffOperation.Equal)); - } - - // Add the match as a deletion - renameResult.Add(new Diff(match.Value, DiffOperation.Deletion)); - - // Add the result as an insertion - var result = match.Result(replacementRegex); - if (!string.IsNullOrEmpty(result)) - { - renameResult.Add(new Diff(result, DiffOperation.Insertion)); - } - - nextMatchStartingIndex = match.Index + match.Length; - } - - if (nextMatchStartingIndex < originalName.Length) - { - var lastSubstring = originalName.Substring(nextMatchStartingIndex, originalName.Length - nextMatchStartingIndex); - renameResult.Add(new Diff(lastSubstring, DiffOperation.Equal)); - } - - return renameResult; - } } } \ No newline at end of file diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Operations/ToCamelCaseOperation.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/Operations/ToCamelCaseOperation.cs index 9211f31..9924e24 100644 --- a/Assets/RedBlueGames/MulliganRenamer/Editor/Operations/ToCamelCaseOperation.cs +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Operations/ToCamelCaseOperation.cs @@ -171,7 +171,7 @@ public RenameResult Rename(string input, int relativeCount) inputCaseChanged = input; } - var renameResult = this.GetDiffResultFromStrings(input, inputCaseChanged); + var renameResult = RenameResultUtilities.GetDiffResultFromStrings(input, inputCaseChanged); return renameResult; } @@ -221,38 +221,5 @@ private string UpperOrLowerFirstChar(string word, bool toLower) return wordCaseChanged; } - - private RenameResult GetDiffResultFromStrings(string stringA, string stringB) - { - var renameResult = new RenameResult(); - var consecutiveEqualChars = string.Empty; - for (int i = 0; i < stringA.Length; ++i) - { - string oldLetter = stringA.Substring(i, 1); - string newLetter = stringB.Substring(i, 1); - if (oldLetter.Equals(newLetter)) - { - consecutiveEqualChars = string.Concat(consecutiveEqualChars, oldLetter); - } - else - { - if (!string.IsNullOrEmpty(consecutiveEqualChars)) - { - renameResult.Add(new Diff(consecutiveEqualChars, DiffOperation.Equal)); - consecutiveEqualChars = string.Empty; - } - - renameResult.Add(new Diff(oldLetter, DiffOperation.Deletion)); - renameResult.Add(new Diff(newLetter, DiffOperation.Insertion)); - } - } - - if (!string.IsNullOrEmpty(consecutiveEqualChars)) - { - renameResult.Add(new Diff(consecutiveEqualChars, DiffOperation.Equal)); - } - - return renameResult; - } } } \ No newline at end of file diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/PreferencesAndSettings.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/PreferencesAndSettings.meta new file mode 100644 index 0000000..7f6281b --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/PreferencesAndSettings.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c8806b1455e28444293b393c314952b9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/PreferencesAndSettings/MulliganRightClickItems.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/PreferencesAndSettings/MulliganRightClickItems.cs new file mode 100644 index 0000000..c24ad9b --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/PreferencesAndSettings/MulliganRightClickItems.cs @@ -0,0 +1,38 @@ +/* MIT License + +Copyright (c) 2020 Edward Rowe, RedBlueGames + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +namespace RedBlueGames.MulliganRenamer +{ + + using UnityEditor; + + public static class MulliganRightClickItems + { + [MenuItem("GameObject/Rename in Bulk", false, 49)] + [MenuItem("Assets/Rename in Bulk")] + private static void OpenMulliganWindow() + { + RedBlueGames.MulliganRenamer.MulliganRenamerWindow.ShowWindowWithSelectedObjects(); + } + } +} \ No newline at end of file diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/PreferencesAndSettings/MulliganRightClickItems.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/PreferencesAndSettings/MulliganRightClickItems.cs.meta new file mode 100644 index 0000000..fe22e0b --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/PreferencesAndSettings/MulliganRightClickItems.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d36dd7ea56a1949288b2daadf5d02f0c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/PreferencesAndSettings/MulliganSettingsProvider.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/PreferencesAndSettings/MulliganSettingsProvider.cs new file mode 100644 index 0000000..85006b8 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/PreferencesAndSettings/MulliganSettingsProvider.cs @@ -0,0 +1,81 @@ +#if UNITY_2018_3_OR_NEWER +#define MULLIGAN_INCLUDE_PREFS +#endif +/* MIT License + +Copyright (c) 2020 Edward Rowe, RedBlueGames + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +namespace RedBlueGames.MulliganRenamer +{ + using System.Collections.Generic; + using UnityEditor; + + static class MulliganSettingsProvider + { + // Settings providers were added in 2018.3. No support for older versions for now. +#if MULLIGAN_INCLUDE_PREFS + public static string Path + { + get + { + return "Red Blue Games/Mulligan Renamer"; + } + } + + + private static MulliganUserPreferences ActivePreferences; + + private static LanguageRetriever LanguageRetriever; + + [SettingsProvider] + public static SettingsProvider CreateMyCustomSettingsProvider() + { + // First parameter is the path in the Settings window. + // Second parameter is the scope of this setting: it only appears in the Project Settings window. + var provider = new SettingsProvider(Path, SettingsScope.User) + { + // By default the last token of the path is used as display name if no label is provided. + label = LocalizationManager.Instance.GetTranslation("preferencesMenuItem"), + activateHandler = (searchContext, rootElement) => + { + ActivePreferences = MulliganUserPreferences.LoadOrCreatePreferences(); + LanguageRetriever = new LanguageRetriever(); + }, + + // Create the SettingsProvider and initialize its drawing (IMGUI) function in place: + guiHandler = DrawPreferences, + + // Populate the search keywords to enable smart search filtering and label highlighting: + keywords = new HashSet(new[] { "Diff", "Color" }) + }; + + return provider; + } + + private static void DrawPreferences(string searchContext) + { + // Pass in state into the Window since window doesn't have any when opened as a Preference item + MulliganUserPreferencesWindow.DrawPreferences(ActivePreferences, LanguageRetriever); + } +#endif + } +} diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/PreferencesAndSettings/MulliganSettingsProvider.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/PreferencesAndSettings/MulliganSettingsProvider.cs.meta new file mode 100644 index 0000000..5f08d1e --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/PreferencesAndSettings/MulliganSettingsProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f98a4a3154b10491cba9d9b590945ab3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/MulliganUserPreferences.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/PreferencesAndSettings/MulliganUserPreferences.cs similarity index 61% rename from Assets/RedBlueGames/MulliganRenamer/Editor/MulliganUserPreferences.cs rename to Assets/RedBlueGames/MulliganRenamer/Editor/PreferencesAndSettings/MulliganUserPreferences.cs index 9676327..43eb33b 100644 --- a/Assets/RedBlueGames/MulliganRenamer/Editor/MulliganUserPreferences.cs +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/PreferencesAndSettings/MulliganUserPreferences.cs @@ -24,6 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE namespace RedBlueGames.MulliganRenamer { using System.Collections.Generic; + using UnityEditor; using UnityEngine; /// @@ -32,6 +33,10 @@ namespace RedBlueGames.MulliganRenamer [System.Serializable] public class MulliganUserPreferences : ISerializationCallbackReceiver { + private static MulliganUserPreferences LoadedPreferenceInstance; + + private const string UserPreferencesPrefKey = "RedBlueGames.MulliganRenamer.UserPreferences"; + private const int NumberOfSessionsBeforeReviewPrompt = 3; [SerializeField] @@ -53,7 +58,19 @@ public class MulliganUserPreferences : ISerializationCallbackReceiver private bool hasClickedPrompt; [SerializeField] - private bool hasShownThanks; + private Color insertionTextColor; + + [SerializeField] + private Color deletionTextColor; + + [SerializeField] + private Color insertionBackgroundColor; + + [SerializeField] + private Color deletionBackgroundColor; + + [SerializeField] + private bool hasInitializedColors; /// /// Gets or Sets the previously used Sequence of Rename Operations @@ -165,17 +182,108 @@ public bool NeedsReview } } + public Color InsertionTextColor + { + get + { + return this.insertionTextColor; + } + + set + { + this.insertionTextColor = value; + } + } + public Color DeletionTextColor + { + get + { + return this.deletionTextColor; + } + + set + { + this.deletionTextColor = value; + } + } + + public Color InsertionBackgroundColor + { + get + { + return this.insertionBackgroundColor; + } + + set + { + this.insertionBackgroundColor = value; + } + } + public Color DeletionBackgroundColor + { + get + { + return this.deletionBackgroundColor; + } + + set + { + this.deletionBackgroundColor = value; + } + } + /// /// Create a new Instance of MulliganUserPreferences /// - public MulliganUserPreferences() + private MulliganUserPreferences() { - // Default previous sequence to a replace string op just because it's - // most user friendly - this.previousSequence = new RenameOperationSequence(); - this.previousSequence.Add(new ReplaceStringOperation()); + this.ResetAllValuesToDefault(); + } - this.savedPresets = new List(); + /// + /// Loads the user's previous preferences (User specific savedata), or creates a new default one + /// + /// Loaded or newly created Preferences + public static MulliganUserPreferences LoadOrCreatePreferences() + { + // When Preferences got into the mix, we needed to make sure the Mulligan Window and the + // Preferences window referenced the same deserialized preferences, or else they'd compete + // and could stomp eachother's values. So we made it a singleton. + if (LoadedPreferenceInstance != null) + { + return LoadedPreferenceInstance; + } + + var serializedPreferences = EditorPrefs.GetString(UserPreferencesPrefKey, string.Empty); + MulliganUserPreferences prefs = null; + + if (!string.IsNullOrEmpty(serializedPreferences)) + { + var loadedPreferences = JsonUtility.FromJson(serializedPreferences); + prefs = loadedPreferences; + } + else + { + prefs = new MulliganUserPreferences(); + } + + prefs.UpgradePreferences(); + + LoadedPreferenceInstance = prefs; + return prefs; + } + + public void ResetToDefaults() + { + this.ResetAllValuesToDefault(); + } + + /// + /// Save the Preferences to EditorPrefs for loading at another time + /// + public void SaveToEditorPrefs() + { + EditorPrefs.SetString(UserPreferencesPrefKey, JsonUtility.ToJson(this)); } /// @@ -249,5 +357,51 @@ public void OnAfterDeserialize() this.PreviousSequence = RenameOperationSequence.FromString( this.serializedPreviousSequence); } + + public void ResetColorsToDefault(bool useProSkinValues = true) + { + if (useProSkinValues) + { + this.InsertionTextColor = new Color32(6, 214, 160, 255); + this.DeletionTextColor = new Color32(239, 71, 111, 255); + } + else + { + this.InsertionTextColor = new Color32(0, 140, 104, 255); + this.DeletionTextColor = new Color32(189, 47, 79, 255); + } + + this.InsertionBackgroundColor = + this.InsertionTextColor.CreateCopyWithNewAlpha(this.InsertionTextColor.a * 0.2f); + this.DeletionBackgroundColor = + this.DeletionTextColor.CreateCopyWithNewAlpha(this.DeletionTextColor.a * 0.2f); + } + + private void ResetAllValuesToDefault() + { + // Reset all values to default as if this is a new instance + this.lastUsedPresetName = string.Empty; + this.serializedPreviousSequence = string.Empty; + this.numSessionsUsed = 0; + this.hasClickedPrompt = false; + this.ResetColorsToDefault(); + + // Default the previous sequence to a replace string op just because it's + // most user friendly + this.previousSequence = new RenameOperationSequence(); + this.previousSequence.Add(new ReplaceStringOperation()); + + this.savedPresets = new List(); + + } + + private void UpgradePreferences() + { + if (!this.hasInitializedColors) + { + this.ResetColorsToDefault(); + this.hasInitializedColors = true; + } + } } } diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/MulliganUserPreferences.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/PreferencesAndSettings/MulliganUserPreferences.cs.meta similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/MulliganUserPreferences.cs.meta rename to Assets/RedBlueGames/MulliganRenamer/Editor/PreferencesAndSettings/MulliganUserPreferences.cs.meta diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming.meta new file mode 100644 index 0000000..ead074a --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3196933ec3565449f95e55438d76ac11 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/AssetCache.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/AssetCache.cs similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/AssetCache.cs rename to Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/AssetCache.cs diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/AssetCache.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/AssetCache.cs.meta similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/AssetCache.cs.meta rename to Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/AssetCache.cs.meta diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/AssetPostprocessorEvents.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/AssetPostprocessorEvents.cs similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/AssetPostprocessorEvents.cs rename to Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/AssetPostprocessorEvents.cs diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/AssetPostprocessorEvents.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/AssetPostprocessorEvents.cs.meta similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/AssetPostprocessorEvents.cs.meta rename to Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/AssetPostprocessorEvents.cs.meta diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/AssetPostprocessorEventsDispatcher.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/AssetPostprocessorEventsDispatcher.cs similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/AssetPostprocessorEventsDispatcher.cs rename to Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/AssetPostprocessorEventsDispatcher.cs diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/AssetPostprocessorEventsDispatcher.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/AssetPostprocessorEventsDispatcher.cs.meta similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/AssetPostprocessorEventsDispatcher.cs.meta rename to Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/AssetPostprocessorEventsDispatcher.cs.meta diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/BulkRenamePreview.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/BulkRenamePreview.cs similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/BulkRenamePreview.cs rename to Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/BulkRenamePreview.cs diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/BulkRenamePreview.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/BulkRenamePreview.cs.meta similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/BulkRenamePreview.cs.meta rename to Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/BulkRenamePreview.cs.meta diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/BulkRenamer.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/BulkRenamer.cs similarity index 94% rename from Assets/RedBlueGames/MulliganRenamer/Editor/BulkRenamer.cs rename to Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/BulkRenamer.cs index f1c4494..90df1f7 100644 --- a/Assets/RedBlueGames/MulliganRenamer/Editor/BulkRenamer.cs +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/BulkRenamer.cs @@ -84,9 +84,9 @@ public static int ApplyNameDeltas(List objectsAndNewNames, bool gameObjectsToRenameAsGameObjects.Add((GameObject)gameObjectToRename.NamedObject); } - Undo.RecordObjects(gameObjectsToRenameAsGameObjects.ToArray(), "Bulk Rename"); + Undo.RecordObjects(gameObjectsToRenameAsGameObjects.ToArray(), LocalizationManager.Instance.GetTranslation("bulkRename")); - AssetRenameUndoer.RecordAssetRenames("Bulk Rename", objectsAndNewNames); + AssetRenameUndoer.RecordAssetRenames(LocalizationManager.Instance.GetTranslation("bulkRename"), objectsAndNewNames); } // Rename the objects and show a progress bar @@ -268,8 +268,8 @@ private static bool RenamedAssetWillShareNameWithAnotherAsset( private static void UpdateProgressBar(int currentStep, int totalNumSteps) { - var infoString = string.Format("Renaming Object {0} of {1}", currentStep++, totalNumSteps); - EditorUtility.DisplayProgressBar("Renaming...", infoString, currentStep / (float)totalNumSteps); + var infoString = string.Format(LocalizationManager.Instance.GetTranslation("renamingObjectXofY"), currentStep++, totalNumSteps); + EditorUtility.DisplayProgressBar(LocalizationManager.Instance.GetTranslation("renaming")+ "...", infoString, currentStep / (float)totalNumSteps); } private static void SplitObjectsIntoCategories( @@ -331,11 +331,7 @@ private static void RenameAsset(UnityEngine.Object asset, string newName) if (asset.name != newName) { var message = string.Format( - "Asset [{0}] not renamed when trying to RenameAsset in BulkRenamer. " + - "It may have been canceled because the new name was already taken by" + - " an object at the same path. The new name may also have contained " + - "special characters.\n" + - "OriginalPath: {1}, New Name: {1}", + LocalizationManager.Instance.GetTranslation("errorAssetNotBulkRenamed"), asset.name, pathToAsset, newName); diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/BulkRenamer.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/BulkRenamer.cs.meta similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/BulkRenamer.cs.meta rename to Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/BulkRenamer.cs.meta diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Diff.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/Diff.cs similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/Diff.cs rename to Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/Diff.cs diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Diff.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/Diff.cs.meta similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/Diff.cs.meta rename to Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/Diff.cs.meta diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/DiffOperation.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/DiffOperation.cs similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/DiffOperation.cs rename to Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/DiffOperation.cs diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/DiffOperation.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/DiffOperation.cs.meta similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/DiffOperation.cs.meta rename to Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/DiffOperation.cs.meta diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/ObjectNameDelta.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/ObjectNameDelta.cs similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/ObjectNameDelta.cs rename to Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/ObjectNameDelta.cs diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/ObjectNameDelta.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/ObjectNameDelta.cs.meta similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/ObjectNameDelta.cs.meta rename to Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/ObjectNameDelta.cs.meta diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/RenameOperationSequence.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/RenameOperationSequence.cs similarity index 89% rename from Assets/RedBlueGames/MulliganRenamer/Editor/RenameOperationSequence.cs rename to Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/RenameOperationSequence.cs index 4b27790..9eb25e9 100644 --- a/Assets/RedBlueGames/MulliganRenamer/Editor/RenameOperationSequence.cs +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/RenameOperationSequence.cs @@ -38,18 +38,34 @@ public class RenameOperationSequence : IList where T : IRenameOperation { private const string VersionTag = "[Version = 1]"; - private static Dictionary UnversionedOperationSerializedKeys = new Dictionary() + private static bool Initialized = false; + private static Dictionary UnversionedOperationSerializedKeys; + + + private static void Initialize() { - {"Add/Prefix or Suffix", typeof(AddStringOperation)}, - {"Add/String Sequence", typeof(AddStringSequenceOperation)}, - {"Modify/Change Case", typeof(ChangeCaseOperation)}, - {"Add/Count By Letter", typeof(CountByLetterOperation)}, - {"Add/Enumerate", typeof(EnumerateOperation)}, - {"Delete/Remove Characters", typeof(RemoveCharactersOperation)}, - {"Replace/Rename", typeof(ReplaceNameOperation)}, - {"Replace/Replace String", typeof(ReplaceStringOperation)}, - {"Delete/Trim Characters", typeof(TrimCharactersOperation)}, - }; + if (Initialized) return; + + UnversionedOperationSerializedKeys = new Dictionary() + { + {GetOperationPath("add", "prefixOrSuffix"), typeof(AddStringOperation)}, + {GetOperationPath("add", "stringSequence"), typeof(AddStringSequenceOperation)}, + {GetOperationPath("modify", "changeCase"), typeof(ChangeCaseOperation)}, + {GetOperationPath("add", "countByLetter"), typeof(CountByLetterOperation)}, + {GetOperationPath("add", "enumerate"), typeof(EnumerateOperation)}, + {GetOperationPath("delete", "removeCharacters"), typeof(RemoveCharactersOperation)}, + {GetOperationPath("replace", "rename"), typeof(ReplaceNameOperation)}, + {GetOperationPath("replace", "replaceString"), typeof(ReplaceStringOperation)}, + {GetOperationPath("delete", "trimCharacters"), typeof(TrimCharactersOperation)}, + }; + + Initialized = true; + } + + private static string GetOperationPath(string folder, string operationTitle) + { + return LocalizationManager.Instance.GetTranslation(folder) + "/" + LocalizationManager.Instance.GetTranslation(operationTitle); + } private List operationSequence; @@ -317,6 +333,8 @@ public static RenameOperationSequence FromString(string str) private static RenameOperationSequence GetOpsFromPreVersionedString(string str) { + if (!Initialized) Initialize(); + var ops = str.Split(','); var operations = new RenameOperationSequence(); foreach (var op in ops) diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/RenameOperationSequence.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/RenameOperationSequence.cs.meta similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/RenameOperationSequence.cs.meta rename to Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/RenameOperationSequence.cs.meta diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/RenamePreview.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/RenamePreview.cs similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/RenamePreview.cs rename to Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/RenamePreview.cs diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/RenamePreview.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/RenamePreview.cs.meta similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/RenamePreview.cs.meta rename to Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/RenamePreview.cs.meta diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/RenameResult.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/RenameResult.cs similarity index 97% rename from Assets/RedBlueGames/MulliganRenamer/Editor/RenameResult.cs rename to Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/RenameResult.cs index 259059d..6e1e29c 100644 --- a/Assets/RedBlueGames/MulliganRenamer/Editor/RenameResult.cs +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/RenameResult.cs @@ -118,6 +118,19 @@ public string Original } } + public Diff this[int key] + { + get + { + return this.diffs[key]; + } + + set + { + this.diffs[key] = value; + } + } + /// /// Clear the RenameResult. /// diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/RenameResult.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/RenameResult.cs.meta similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/RenameResult.cs.meta rename to Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/RenameResult.cs.meta diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/RenameResultSequence.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/RenameResultSequence.cs similarity index 84% rename from Assets/RedBlueGames/MulliganRenamer/Editor/RenameResultSequence.cs rename to Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/RenameResultSequence.cs index bbcfde6..6d0e571 100644 --- a/Assets/RedBlueGames/MulliganRenamer/Editor/RenameResultSequence.cs +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/RenameResultSequence.cs @@ -84,18 +84,17 @@ public int NumSteps /// /// The original name at the rename step. /// Rename step index. - /// Color for deletions - public string GetNameBeforeAtStep(int stepIndex, Color deletionTagColor) + public RenameResult GetRenameResultBeforeStep(int stepIndex) { if (stepIndex < 0 || stepIndex >= this.NumSteps) { var exception = string.Format( - "Trying to get original name for RenameResultSequence at index that's out of bounds. Index: {0}", + LocalizationManager.Instance.GetTranslation("errorTryintToGetOriginalName"), stepIndex); throw new System.ArgumentException(exception, "stepIndex"); } - return this.OperationResults.DefaultIfEmpty(RenameResult.Empty).ElementAtOrDefault(stepIndex).GetOriginalColored(deletionTagColor); + return this.OperationResults.DefaultIfEmpty(RenameResult.Empty).ElementAtOrDefault(stepIndex); } /// @@ -103,18 +102,17 @@ public string GetNameBeforeAtStep(int stepIndex, Color deletionTagColor) /// /// The new name at step. /// Rename step index. - /// Color for insertions - public string GetNewNameAtStep(int stepIndex, Color insertionTagColor) + public RenameResult GetRenameResultForStep(int stepIndex) { if (stepIndex < 0 || stepIndex >= this.NumSteps) { var exception = string.Format( - "Trying to get original name for RenameResultSequence at index that's out of bounds. Index: {0}", + LocalizationManager.Instance.GetTranslation("errorTryingToGetOriginalNameOutOfBounds"), stepIndex); throw new System.ArgumentException(exception, "stepIndex"); } - return this.OperationResults.DefaultIfEmpty(RenameResult.Empty).ElementAtOrDefault(stepIndex).GetResultColored(insertionTagColor); + return this.OperationResults.DefaultIfEmpty(RenameResult.Empty).ElementAtOrDefault(stepIndex); } } } \ No newline at end of file diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/RenameResultSequence.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/RenameResultSequence.cs.meta similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/RenameResultSequence.cs.meta rename to Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/RenameResultSequence.cs.meta diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/RenameSequencePreset.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/RenameSequencePreset.cs similarity index 94% rename from Assets/RedBlueGames/MulliganRenamer/Editor/RenameSequencePreset.cs rename to Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/RenameSequencePreset.cs index e2be822..af3984c 100644 --- a/Assets/RedBlueGames/MulliganRenamer/Editor/RenameSequencePreset.cs +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/RenameSequencePreset.cs @@ -54,6 +54,9 @@ public RenameOperationSequence OperationSequence { get { + if(this.operationSequence == null) + this.operationSequence = RenameOperationSequence.FromString(this.serializedOperationSequence); + return this.operationSequence; } @@ -77,8 +80,7 @@ public void OnBeforeSerialize() public void OnAfterDeserialize() { - this.operationSequence = RenameOperationSequence.FromString( - this.serializedOperationSequence); + } public override int GetHashCode() diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/RenameSequencePreset.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/RenameSequencePreset.cs.meta similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/RenameSequencePreset.cs.meta rename to Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/RenameSequencePreset.cs.meta diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/SpritesheetRenamer.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/SpritesheetRenamer.cs similarity index 95% rename from Assets/RedBlueGames/MulliganRenamer/Editor/SpritesheetRenamer.cs rename to Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/SpritesheetRenamer.cs index 6ac2038..26bdf63 100644 --- a/Assets/RedBlueGames/MulliganRenamer/Editor/SpritesheetRenamer.cs +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/SpritesheetRenamer.cs @@ -62,8 +62,7 @@ public void AddSpriteForRename(Sprite sprite, string newName) if (!string.IsNullOrEmpty(existingPathToTexture) && pathToSprite != existingPathToTexture) { var exception = string.Format( - "Trying to add Sprite {0} to SpriteRenamer that has a different path to texture " + - "than the other sprites. Received path {1}, expected {2}", + LocalizationManager.Instance.GetTranslation("tryingToAddSpriteToRenamer"), sprite.name, pathToSprite, existingPathToTexture); @@ -74,8 +73,7 @@ public void AddSpriteForRename(Sprite sprite, string newName) if (!System.IO.File.Exists(pathToMeta)) { var exception = string.Format( - "Trying to add Sprite to SpriteRenamer at path {0}, but " + - "no meta file exists at the specified path.", + LocalizationManager.Instance.GetTranslation("errorTryingToAddSpriteToRenamer"), pathToMeta); throw new System.ArgumentException(exception); } diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/SpritesheetRenamer.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/SpritesheetRenamer.cs.meta similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/SpritesheetRenamer.cs.meta rename to Assets/RedBlueGames/MulliganRenamer/Editor/Renaming/SpritesheetRenamer.cs.meta diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Resources.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Resources.meta new file mode 100644 index 0000000..f573172 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Resources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5a17ff510a8fd41caaeaa74d8d39d419 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Resources/MulliganLanguages.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Resources/MulliganLanguages.meta new file mode 100644 index 0000000..800a38f --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Resources/MulliganLanguages.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 50b4d9bd96538495d838d30ee88802bc +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Resources/MulliganLanguages/en.json b/Assets/RedBlueGames/MulliganRenamer/Editor/Resources/MulliganLanguages/en.json new file mode 100644 index 0000000..ac62698 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Resources/MulliganLanguages/en.json @@ -0,0 +1,683 @@ +{ + "name": "English", + "key": "en", + "version": 0, + "elements": [ + { + "key": "rename", + "value": "Rename" + }, + { + "key": "renameWarningNotRenamed", + "value": "Some objects have warnings and will not be renamed. Do you want to rename the other objects in the group?" + }, + { + "key": "warning", + "value": "Warning" + }, + { + "key": "cancel", + "value": "Cancel" + }, + { + "key": "failToRenameMulligan", + "value": "Sorry, some objects failed to rename. Something went wrong with Mulligan. Please report a bug (see UserManual for details). This rename operation will be automatically undone\n\nException: " + }, + { + "key": "error", + "value": "Error" + }, + { + "key": "presets", + "value": "Presets" + }, + { + "key": "result", + "value": "Result" + }, + { + "key": "addOperation", + "value": "Add Operation" + }, + { + "key": "renameOperation", + "value": "Rename Operation" + }, + { + "key": "language", + "value": "Language" + }, + { + "key": "languageTooltip", + "value": "Specifies the language for all text used in Mulligan." + }, + { + "key": "prefix", + "value": "Prefix" + }, + { + "key": "saveAs", + "value": "Save As..." + }, + { + "key": "managePresets", + "value": "Manage Presets" + }, + { + "key": "errorUnrecognizedListButton", + "value": "RenamerWindow found Unrecognized ListButtonEvent [{0}] in OnGUI. Add a case to handle this event." + }, + { + "key": "errorAddNewOpNotSub", + "value": "MulliganRenamerWindow tried to add a new RenameOperation using a type that is not a subclass of RenameOperationDrawerBinding. Operation type: " + }, + { + "key": "thankYouForSupport", + "value": "Thank you very much for supporting Mulligan!" + }, + { + "key": "thankYouForReview", + "value": "Thank you for reviewing Mulligan!" + }, + { + "key": "thankYouForUsing", + "value": "Thank you for using Mulligan! If you've found it useful, please consider supporting its development by purchasing it from the Asset Store. Thanks!" + }, + { + "key": "thankYouForPurchasing", + "value": "Thank you for purchasing Mulligan! If you've found it useful, please consider leaving a review on the Asset Store. The store is very competitive and every review helps to stand out. Thanks!" + }, + { + "key": "openAssetStore", + "value": "Open Asset Store" + }, + { + "key": "savePreset", + "value": "Save Preset" + }, + { + "key": "previewSteps", + "value": "Preview Steps" + }, + { + "key": "noObjectsSpecified", + "value": "No objects specified for rename. Drag objects here to rename them, or" + }, + { + "key": "addMoreObjectsDragPanel", + "value": "Add more objects by dragging them into the above panel" + }, + { + "key": "addMoreObjectsDragHere", + "value": "Add more objects by dragging them here" + }, + { + "key": "toRenameMoreObjects", + "value": "To rename more objects, drag them here, or" + }, + { + "key": "removeAll", + "value": "Remove All" + }, + { + "key": "object", + "value": "Object" + }, + { + "key": "objects", + "value": "Objects" + }, + { + "key": "renamed", + "value": "Renamed" + }, + { + "key": "original", + "value": "Original" + }, + { + "key": "before", + "value": "Before" + }, + { + "key": "after", + "value": "After" + }, + { + "key": "finalName", + "value": "Final Name" + }, + { + "key": "addSelectedObjects", + "value": "Add Selected Objects" + }, + { + "key": "errorTryingToAccessModel", + "value": "Trying to access PreviewRowModel at index that is out of bounds. Index: " + }, + { + "key": "warningNewNameMatchesExisting", + "value": "New name matches an existing file or another renamed object." + }, + { + "key": "assetBlankName", + "value": "Asset has blank name." + }, + { + "key": "nameIncludeInvalidCharacter", + "value": "Name includes invalid characters (usually symbols such as ?.,)." + }, + { + "key": "tryingToAddSpriteToRenamer", + "value": "Trying to add Sprite {0} to SpriteRenamer that has a different path to texture than the other sprites. Received path {1}, expected {2}" + }, + { + "key": "errorTryingToAddSpriteToRenamer", + "value": "Trying to add Sprite to SpriteRenamer at path {0}, but no meta file exists at the specified path." + }, + { + "key": "errorPresetNameAlreadyExists", + "value": "A preset named \"{0}\" already exists. Do you want to replace it?" + }, + { + "key": "no", + "value": "No" + }, + { + "key": "save", + "value": "Save" + }, + { + "key": "errorTryintToGetOriginalName", + "value": "Trying to get original name for RenameResultSequence at index that's out of bounds. Index: {0}" + }, + { + "key": "errorTryingToGetOriginalNameOutOfBounds", + "value": "Trying to get original name for RenameResultSequence at index that's out of bounds. Index: {0}" + }, + { + "key": "add", + "value": "Add" + }, + { + "key": "prefixOrSuffix", + "value": "Prefix or Suffix" + }, + { + "key": "stringSequence", + "value": "String Sequence" + }, + { + "key": "modify", + "value": "Modify" + }, + { + "key": "changeCase", + "value": "Change Case" + }, + { + "key": "countByLetter", + "value": "Count By Letter" + }, + { + "key": "enumerate", + "value": "Enumerate" + }, + { + "key": "delete", + "value": "Delete" + }, + { + "key": "replace", + "value": "Replace" + }, + { + "key": "replaceString", + "value": "Replace String" + }, + { + "key": "trimCharacters", + "value": "Trim Characters" + }, + { + "key": "preset", + "value": "Preset" + }, + { + "key": "savedPresets", + "value": "Saved Presets" + }, + { + "key": "errorNoSavedPresets", + "value": "You have no saved Rename Operation presets. Select 'Save as...' in the \"Presets\" dropdown to create a new preset." + }, + { + "key": "areYouSureDelete", + "value": "Are you sure you want to delete the preset \"{0}\"?" + }, + { + "key": "deletePreset", + "value": "Delete Preset" + }, + { + "key": "removeCharacters", + "value": "Remove Characters" + }, + { + "key": "bulkRename", + "value": "Bulk Rename" + }, + { + "key": "renamingObjectXofY", + "value": "Renaming Object {0} of {1}" + }, + { + "key": "renaming", + "value": "Renaming" + }, + { + "key": "errorAssetNotBulkRenamed", + "value": "Asset [{0}] not renamed when trying to RenameAsset in BulkRenamer. It may have been canceled because the new name was already taken by an object at the same path. The new name may also have contained special characters.\nOriginalPath: {1}, New Name: {2}" + }, + { + "key": "deleteFromFront", + "value": "Delete from Front" + }, + { + "key": "deleteFromBack", + "value": "Delete from Back" + }, + { + "key": "toCamelCase", + "value": "To Camel Case" + }, + { + "key": "usePascalCasing", + "value": "Use Pascal Casing" + }, + { + "key": "flagToCapitalizeTheFirstLetterOfname", + "value": "Flag to capitalize the first letter of the name (also known as Upper Camel Casing)." + }, + { + "key": "delimiterCharacters", + "value": "Delimiter Characters" + }, + { + "key": "caseSensitiveCharactersIndicateStart", + "value": "The case sensitive characters that indicate the start of a word." + }, + { + "key": "delimiters", + "value": "Delimiters" + }, + { + "key": "searchString", + "value": "Search String" + }, + { + "key": "useRegex", + "value": "Use Regular Expression" + }, + { + "key": "matchTermsUsingRegex", + "value": "Match terms using Regular Expressions, terms that allow for powerful pattern matching." + }, + { + "key": "matchRegex", + "value": "Match Regex" + }, + { + "key": "regexToUseToMatchTerms", + "value": "Regular Expression to use to match terms." + }, + { + "key": "searchForString", + "value": "Search for String" + }, + { + "key": "substringsToSeatchInFilenames", + "value": "Substrings to search for in the filenames. These strings will be replaced by the Replacement String." + }, + { + "key": "replaceWith", + "value": "Replace with" + }, + { + "key": "stringToReplaceMatchingInstances", + "value": "String to replace matching instances of the Search string." + }, + { + "key": "caseSensitive", + "value": "Case Sensitive" + }, + { + "key": "searchUsingCaseSensitivity", + "value": "Search using case sensitivity. Only strings that match the supplied casing will be replaced." + }, + { + "key": "matchExpressNotValid", + "value": "Match Expression is not a valid Regular Expression." + }, + { + "key": "replacementExpressionNotValid", + "value": "Replacement Expression is not a valid Regular Expression." + }, + { + "key": "newName", + "value": "New Name" + }, + { + "key": "nameToReplaceTheOldeOne", + "value": "Name to replace the old one with." + }, + { + "key": "selectPresetOrSpecifyCharacters", + "value": "Select a preset or specify your own characters." + }, + { + "key": "charactersToRemove", + "value": "Characters to Remove" + }, + { + "key": "allCharactersThatWillBeRemoved", + "value": "All characters that will be removed from the names." + }, + { + "key": "flagTheSearchToMatchCase", + "value": "Flag the search to match only the specified case" + }, + { + "key": "symbols", + "value": "Symbols" + }, + { + "key": "removeSpecialCharacters", + "value": "Removes special characters (ie. !@#$%^&*)" + }, + { + "key": "numbers", + "value": "Numbers" + }, + { + "key": "removeDigits", + "value": "Removes digits 0-9" + }, + { + "key": "whitespace", + "value": "Whitespace" + }, + { + "key": "removesWhitespace", + "value": "Removes Whitespace" + }, + { + "key": "count", + "value": "Count" + }, + { + "key": "format", + "value": "Format" + }, + { + "key": "selectPresetFormat", + "value": "Select a preset format or specify your own format." + }, + { + "key": "countFormat", + "value": "Count Format" + }, + { + "key": "theStringFormatToUseWhenAddingTheCountToName", + "value": "The string format to use when adding the Count to the name." + }, + { + "key": "invalidCountFormat", + "value": "Invalid Count Format. Typical formats are D1 for one digit with no leading zeros, D2, for two, etc.\nLookup the String.Format() method for more info on formatting options." + }, + { + "key": "countFrom", + "value": "Count From" + }, + { + "key": "theValueToStartCountingFrom", + "value": "The value to start counting from. The first object will have this number." + }, + { + "key": "increment", + "value": "Increment" + }, + { + "key": "theValueToAddToEachObjectWhenCounting", + "value": "The value to add to each object when counting." + }, + { + "key": "addAsPrefix", + "value": "Add As Prefix" + }, + { + "key": "addTheCountToTheFontOfTheObjectName", + "value": "Add the count to the front of the object's name." + }, + { + "key": "custom", + "value": "Custom" + }, + { + "key": "strings", + "value": "Strings" + }, + { + "key": "theStringsOfLettersToAdd", + "value": "The strings of letters to add, comma separated. Ex: \"A,B,C\" will append A, B, and C to the first three objects respectively. After that it will add another sequence, starting with AA, then AB, then AC, etc." + }, + { + "key": "formatForTheAddedLetters", + "value": "Format for the added letters." + }, + { + "key": "theValueToStartCounting", + "value": "The value to start counting from. The string from the sequence at this count will be appended to the first object." + }, + { + "key": "startsWith", + "value": "Starts with" + }, + { + "key": "theValueToAddToTheCount", + "value": "The value to add to the count after naming an object." + }, + { + "key": "addTheCountToTheFront", + "value": "Add the count to the front of the object's name." + }, + { + "key": "uppercaseAlphabet", + "value": "Uppercase Alphabet" + }, + { + "key": "lowercaseAlphabet", + "value": "Lowercase Alphabet" + }, + { + "key": "toUpperOrLowercase", + "value": "To Upper or Lowercase" + }, + { + "key": "toUppercase", + "value": "To Uppercase" + }, + { + "key": "newCasing", + "value": "New Casing" + }, + { + "key": "theDesiredCasingForName", + "value": "The desired casing for the new name." + }, + { + "key": "onlyFirstCharacter", + "value": "Only First Character" + }, + { + "key": "changeOnlyTheFirstCharacterCase", + "value": "Change only the first character's case." + }, + { + "key": "addStringSequence", + "value": "Add String Sequence" + }, + { + "key": "sequence", + "value": "Sequence" + }, + { + "key": "theSequenceOfStringsToAddCommaSeparted", + "value": "The sequence of strings to add, comma separted." + }, + { + "key": "addTheCountToTheFrontOfTheObjectName", + "value": "Add the count to the front of the object's name." + }, + { + "key": "addPrefixOrSuffix", + "value": "Add Prefix or Suffix" + }, + { + "key": "suffix", + "value": "Suffix" + }, + { + "key": "replacementString", + "value": "Replacement String" + }, + { + "key": "preferenceWindowTitle", + "value": "Mulligan Renamer Preferences" + }, + { + "key": "preferencesMenuItem", + "value": "Mulligan Renamer" + }, + { + "key": "preferencesDiffLabel", + "value": "Diff Colors" + }, + { + "key": "preferencesInsertionText", + "value": "Insertion Text" + }, + { + "key": "preferencesInsertionBackground", + "value": "Insertion Background" + }, + { + "key": "preferencesDeletionText", + "value": "Deletion Text" + }, + { + "key": "preferencesDeletionBackground", + "value": "Deletion Background" + }, + { + "key": "preferencesReset", + "value": "Reset to Default" + }, + { + "key": "preferences", + "value": "Preferences" + }, + { + "key": "exampleThisIs", + "value": "This is" + }, + { + "key": "exampleSampleText", + "value": "sample text" + }, + { + "key": "exampleWithWords", + "value": "with words" + }, + { + "key": "exampleInserted", + "value": "inserted" + }, + { + "key": "exampleDeleted", + "value": "deleted" + }, + { + "key": "adjustNumbers", + "value": "Adjust Numbers" + }, + { + "key": "Lowercase", + "value": "Lowercase" + }, + { + "key": "Uppercase", + "value": "Uppercase" + }, + { + "key": "offset", + "value": "Offset" + }, + { + "key": "updateLanguages", + "value": "Update Languages" + }, + { + "key": "languageUpdateProgressTitle", + "value": "Updating Languages" + }, + { + "key": "languageUpdateProgressMessage1", + "value": "Checking for language updates..." + }, + { + "key": "languageUpdateDownloadingLanguages", + "value": "Downloading language {0}..." + }, + { + "key": "languageUpdateSavingChanges", + "value": "Saving Changes..." + }, + { + "key": "languageUpdateProgressTitleSuccess", + "value": "Languages Successfully Updated" + }, + { + "key": "ok", + "value": "OK" + }, + { + "key": "languageUpdateProgressTitleFail", + "value": "Language Update Failed" + }, + { + "key": "languageUpdateTimeout", + "value": "Update failed due to web request timeout. If you have internet, our servers may be down. Please try again later, or report a bug (see UserManual for details) if the issue persists." + }, + { + "key": "languageUpdateFail", + "value": "Update failed. Please report a bug (see UserManual for details). FailCode: {0}, Message: {1}." + }, + { + "key": "languageUpdated", + "value": "Updated {0} from version {1} to {2}" + }, + { + "key": "languageAdded", + "value": "Added {0}." + }, + { + "key": "languageUnchanged", + "value": "{0} is up to date." + }, + { + "key": "languageAllUpToDate", + "value": "All languages are up to date." + } + ] +} \ No newline at end of file diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Resources/MulliganLanguages/en.json.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Resources/MulliganLanguages/en.json.meta new file mode 100644 index 0000000..3339a71 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Resources/MulliganLanguages/en.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: d2b54974ff77e411789a26b24b529178 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Resources/MulliganLanguages/pt.json b/Assets/RedBlueGames/MulliganRenamer/Editor/Resources/MulliganLanguages/pt.json new file mode 100644 index 0000000..25a7794 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Resources/MulliganLanguages/pt.json @@ -0,0 +1,683 @@ +{ + "name": "Portugûes", + "key": "pt", + "version": 0, + "elements": [ + { + "key": "rename", + "value": "Renomear" + }, + { + "key": "renameWarningNotRenamed", + "value": "Alguns objetos possuem alertas e não serão renomeados. Você deseja renomear os outros objetos no grupo?" + }, + { + "key": "warning", + "value": "Alerta" + }, + { + "key": "cancel", + "value": "Cancelar" + }, + { + "key": "failToRenameMulligan", + "value": "Desculpe, o renomeamento de alguns objetos falharam. Algo de errado aconteceu com o Mulligan. Por favor reporte um bug (veja o UserManual para detalhes). Essa operação de renomeação será automaticamente reverida\n\n Erro: " + }, + { + "key": "error", + "value": "Erro" + }, + { + "key": "presets", + "value": "Predefinições" + }, + { + "key": "result", + "value": "Resultado" + }, + { + "key": "addOperation", + "value": "Adicionar Operação" + }, + { + "key": "renameOperation", + "value": "Operação de Renomear" + }, + { + "key": "language", + "value": "Idioma" + }, + { + "key": "languageTooltip", + "value": "Specifies the language for all text used in Mulligan." + }, + { + "key": "prefix", + "value": "Prefixo" + }, + { + "key": "saveAs", + "value": "Salvar como..." + }, + { + "key": "managePresets", + "value": "Gerenciar predefinições" + }, + { + "key": "errorUnrecognizedListButton", + "value": "RenamerWindow achou ListButtonEvent [{0}] não reconhecida no OnGUI. Adicione um case para lidar com esse evento" + }, + { + "key": "errorAddNewOpNotSub", + "value": "MulliganRenamerWindow tentou adicionar uma nova RenameOperation usando um tipo que não é uma subclasse de RenameOperationDrawerBinding. Tipo: " + }, + { + "key": "thankYouForSupport", + "value": "Muito obrigado por suportar Mulligan!" + }, + { + "key": "thankYouForReview", + "value": "Obrigado por avaliar o Mulligan!" + }, + { + "key": "thankYouForUsing", + "value": "Obrigado por usar Mulligan! Se você achou útil, por favor considere suportar o desenvolvimento comprando na Asset Store. Obrigado!" + }, + { + "key": "thankYouForPurchasing", + "value": "Obrigado por comprar o Mulligan! Se você achou útil, por favor considere deixar uma avalição na Asset Store. A loja é muito competitiva e cada avaliação ajuda a sobresair. Obrigado!" + }, + { + "key": "openAssetStore", + "value": "Abrir a Asset Store" + }, + { + "key": "savePreset", + "value": "Salvar Predefinições" + }, + { + "key": "previewSteps", + "value": "Pré-visualização dos passos" + }, + { + "key": "noObjectsSpecified", + "value": "Nenhum objeto específicado para renomear. Arraste objetos para renomea-los, ou" + }, + { + "key": "addMoreObjectsDragPanel", + "value": "Adicione mais objetos arrando-os no painel acima" + }, + { + "key": "addMoreObjectsDragHere", + "value": "Adicione mais objetos arrastando eles aqui" + }, + { + "key": "toRenameMoreObjects", + "value": "Para renomear mais objetos, arraste eles aqui, ou" + }, + { + "key": "removeAll", + "value": "Remover Todos" + }, + { + "key": "object", + "value": "Objeto" + }, + { + "key": "objects", + "value": "Objetos" + }, + { + "key": "renamed", + "value": "Renamed" + }, + { + "key": "original", + "value": "Original" + }, + { + "key": "before", + "value": "Antes" + }, + { + "key": "after", + "value": "Depois" + }, + { + "key": "finalName", + "value": "Nome Final" + }, + { + "key": "addSelectedObjects", + "value": "Adicionar Objetos Selecionados" + }, + { + "key": "errorTryingToAccessModel", + "value": "Tentando acessar PreviewRowModel no index que estão fora dos limites. Index: " + }, + { + "key": "warningNewNameMatchesExisting", + "value": "Novos nomes coincidem um objeto que já existe ou outro objeto renomeado" + }, + { + "key": "assetBlankName", + "value": "O Asset tem um nome em branco" + }, + { + "key": "nameIncludeInvalidCharacter", + "value": "O nome inluí caracteres inválidos (normalmente símbolos como ?.,)." + }, + { + "key": "tryingToAddSpriteToRenamer", + "value": "Tentando adicionar o Sprite {0} para SpriteRenamer que tem um caminho diferente da texture que os outros sprites. Recebeu o caminho {1}, esperando {2}" + }, + { + "key": "errorTryingToAddSpriteToRenamer", + "value": "Tentando adicionar o Sprite para SpriteRenamer ao caminho {0}, mas nenhum arquivo Meta existe no caminho especificado." + }, + { + "key": "errorPresetNameAlreadyExists", + "value": "A predefinicão com nome \"{0}\" já existe. Você deseja substituir?" + }, + { + "key": "no", + "value": "Não" + }, + { + "key": "save", + "value": "Salvar" + }, + { + "key": "errorTryintToGetOriginalName", + "value": "Tentando obter o nome original para RenameResultSequence no index que está fora dos limits. Index: {0}" + }, + { + "key": "errorTryingToGetOriginalNameOutOfBounds", + "value": "Tentando obter o nome original para RenameResultSequence no index que está fora dos limits. Index: {0}" + }, + { + "key": "add", + "value": "Adicionar" + }, + { + "key": "prefixOrSuffix", + "value": "Prefixo ou Sufixo" + }, + { + "key": "stringSequence", + "value": "Sequência de String" + }, + { + "key": "modify", + "value": "Modificar" + }, + { + "key": "changeCase", + "value": "Mudar Case" + }, + { + "key": "countByLetter", + "value": "Contar por Letra" + }, + { + "key": "enumerate", + "value": "Enumerar" + }, + { + "key": "delete", + "value": "Deletar" + }, + { + "key": "replace", + "value": "Substituir" + }, + { + "key": "replaceString", + "value": "Substituir String" + }, + { + "key": "trimCharacters", + "value": "Aparar caracteres" + }, + { + "key": "preset", + "value": "Predefinição" + }, + { + "key": "savedPresets", + "value": "Predefinições Salvas" + }, + { + "key": "errorNoSavedPresets", + "value": "Você não tem nenhuma operação Rename salvas nas predefinições. Selecione 'Salvar como...' no menu \"Predefinições\" para criar uma nova predefinicão" + }, + { + "key": "areYouSureDelete", + "value": "Você tem certeza que quer deletar a predefinicão \"{0}\"" + }, + { + "key": "deletePreset", + "value": "Deletar Predefinição" + }, + { + "key": "removeCharacters", + "value": "Remover Caracteres" + }, + { + "key": "bulkRename", + "value": "Renomear em massa" + }, + { + "key": "renamingObjectXofY", + "value": "Renomeando objeto {0} de {1}" + }, + { + "key": "renaming", + "value": "Renomeando" + }, + { + "key": "errorAssetNotBulkRenamed", + "value": "Asset [{0}] não renomeado quando tentando renomear em massa. Pode ser sido cancelado porque um nove nome já foi pego por um objeto no mesmo caminho. O nome nome pode conter caracteres especiais.\n Caminho original: {1}, Novo caminho: {2}" + }, + { + "key": "deleteFromFront", + "value": "Deletar da frente" + }, + { + "key": "deleteFromBack", + "value": "Deletar de trás" + }, + { + "key": "toCamelCase", + "value": "Para Camel Case" + }, + { + "key": "usePascalCasing", + "value": "Usar Pascal Casing" + }, + { + "key": "flagToCapitalizeTheFirstLetterOfname", + "value": "Sinalize para colocar em maiúscula a primeira letra do nome (também conhecida como Upper Camel Casing)." + }, + { + "key": "delimiterCharacters", + "value": "Caracteres delimitadores" + }, + { + "key": "caseSensitiveCharactersIndicateStart", + "value": "Os caracteres que diferenciam maiúsculas de minúsculas que indicam o início de uma palavra." + }, + { + "key": "delimiters", + "value": "Delimitadores" + }, + { + "key": "searchString", + "value": "Procurar String" + }, + { + "key": "useRegex", + "value": "Usar Expressão regular" + }, + { + "key": "matchTermsUsingRegex", + "value": "Correspondência de termos usando Expressões regulares, termos que permitem uma poderosa correspondência de padrões." + }, + { + "key": "matchRegex", + "value": "Correspondência Regex" + }, + { + "key": "regexToUseToMatchTerms", + "value": "Expressão regular a ser usada para corresponder aos termos." + }, + { + "key": "searchForString", + "value": "Procurar por String" + }, + { + "key": "substringsToSeatchInFilenames", + "value": "Substrings a serem pesquisados ​​nos nomes de arquivos. Essas seqüências serão substituídas pela seqüência de substituição." + }, + { + "key": "replaceWith", + "value": "Substituir com" + }, + { + "key": "stringToReplaceMatchingInstances", + "value": "String para substituir instâncias correspondentes da string de Pesquisa." + }, + { + "key": "caseSensitive", + "value": "Sensível a maiúsculas e minúscula" + }, + { + "key": "searchUsingCaseSensitivity", + "value": "Pesquise usando distinção entre maiúsculas e minúsculas. Somente as strings que correspondem à caixa fornecida serão substituídas." + }, + { + "key": "matchExpressNotValid", + "value": "A expressão de correspondência não é uma expressão regular válida." + }, + { + "key": "replacementExpressionNotValid", + "value": "A expressão de substituição não é uma expressão regular válida." + }, + { + "key": "newName", + "value": "Novo Nome" + }, + { + "key": "nameToReplaceTheOldeOne", + "value": "Nome para substituir o antigo." + }, + { + "key": "selectPresetOrSpecifyCharacters", + "value": "Selecione uma predefinição ou especifique seus próprios caracteres." + }, + { + "key": "charactersToRemove", + "value": "Caracteres para Remover." + }, + { + "key": "allCharactersThatWillBeRemoved", + "value": "Todos os caracteres que serão removidos dos nomes." + }, + { + "key": "flagTheSearchToMatchCase", + "value": "Sinalize a pesquisa para corresponder apenas ao caso especificado." + }, + { + "key": "symbols", + "value": "Símbolos" + }, + { + "key": "removeSpecialCharacters", + "value": "Remove caracteres especiais (ex. !@#$%^&*)" + }, + { + "key": "numbers", + "value": "Números" + }, + { + "key": "removeDigits", + "value": "Remove dígitos 0-9" + }, + { + "key": "whitespace", + "value": "Espaço em branco" + }, + { + "key": "removesWhitespace", + "value": "Remove Espaços em branco" + }, + { + "key": "count", + "value": "Contagem" + }, + { + "key": "format", + "value": "Formato" + }, + { + "key": "selectPresetFormat", + "value": "Selecione um formato predefinido ou especifique seu próprio formato." + }, + { + "key": "countFormat", + "value": "Formato de contagem" + }, + { + "key": "theStringFormatToUseWhenAddingTheCountToName", + "value": "O formato da sequência a ser usada ao adicionar a contagem ao nome." + }, + { + "key": "invalidCountFormat", + "value": "Formato de contagem inválido. Os formatos típicos são D1 para um dígito sem zeros à esquerda, D2, para dois etc.\nProcure o método String.Format () para obter mais informações sobre as opções de formatação." + }, + { + "key": "countFrom", + "value": "Contagem de" + }, + { + "key": "theValueToStartCountingFrom", + "value": "O valor para começar a contar. O primeiro objeto terá esse número." + }, + { + "key": "increment", + "value": "Incremento" + }, + { + "key": "theValueToAddToEachObjectWhenCounting", + "value": "O valor a ser adicionado a cada objeto ao contar." + }, + { + "key": "addAsPrefix", + "value": "Adicionar como prefixo" + }, + { + "key": "addTheCountToTheFontOfTheObjectName", + "value": "Adicione a contagem à frente do nome do objeto." + }, + { + "key": "custom", + "value": "Personalizados" + }, + { + "key": "strings", + "value": "Strings" + }, + { + "key": "theStringsOfLettersToAdd", + "value": "As seqüências de letras a serem adicionadas, separadas por vírgula. Ex: \"A, B, C\" anexará A, B e C aos três primeiros objetos, respectivamente. Depois disso, ele adicionará outra sequência, começando com AA, AB, AC, etc." + }, + { + "key": "formatForTheAddedLetters", + "value": "Formato para as letras adicionadas." + }, + { + "key": "theValueToStartCounting", + "value": "O valor para começar a contar. A sequência da sequência nessa contagem será anexada ao primeiro objeto." + }, + { + "key": "startsWith", + "value": "Começa com" + }, + { + "key": "theValueToAddToTheCount", + "value": "The value to add to the count after naming an object." + }, + { + "key": "addTheCountToTheFront", + "value": "O valor a ser adicionado à contagem após nomear um objeto." + }, + { + "key": "uppercaseAlphabet", + "value": "Alfabeto em Maiúsculas" + }, + { + "key": "lowercaseAlphabet", + "value": "Alfabeto em Minúsculas" + }, + { + "key": "toUpperOrLowercase", + "value": "Para Maiúsculas ou Minúsculas" + }, + { + "key": "toUppercase", + "value": "Para maiúsculas" + }, + { + "key": "newCasing", + "value": "Nova Caixa" + }, + { + "key": "theDesiredCasingForName", + "value": "A Caixa desejada para o novo nome." + }, + { + "key": "onlyFirstCharacter", + "value": "Apenas o Primeiro Caractere" + }, + { + "key": "changeOnlyTheFirstCharacterCase", + "value": "Mude apenas o caso do primeiro caractere." + }, + { + "key": "addStringSequence", + "value": "Adicionar sequência de strings" + }, + { + "key": "sequence", + "value": "Sequência" + }, + { + "key": "theSequenceOfStringsToAddCommaSeparted", + "value": "A sequência de sequências a serem adicionadas, separadas por vírgula." + }, + { + "key": "addTheCountToTheFrontOfTheObjectName", + "value": "Adicione a contagem à frente do nome do objeto." + }, + { + "key": "addPrefixOrSuffix", + "value": "Adicionar prefixo ou sufixo" + }, + { + "key": "suffix", + "value": "Sufixo" + }, + { + "key": "replacementString", + "value": "Substituição de String" + }, + { + "key": "preferenceWindowTitle", + "value": "Preferências do Mulligan Renamer" + }, + { + "key": "preferencesMenuItem", + "value": "Mulligan Renamer" + }, + { + "key": "preferencesDiffLabel", + "value": "Cores de Diff" + }, + { + "key": "preferencesInsertionText", + "value": "Inserção de texto" + }, + { + "key": "preferencesInsertionBackground", + "value": "Inserção de fundo" + }, + { + "key": "preferencesDeletionText", + "value": "Eliminação de texto" + }, + { + "key": "preferencesDeletionBackground", + "value": "Elimninação de fundo" + }, + { + "key": "preferencesReset", + "value": "Restaurar ao padrão" + }, + { + "key": "preferences", + "value": "Preferências" + }, + { + "key": "exampleThisIs", + "value": "Esse é" + }, + { + "key": "exampleSampleText", + "value": "um texto exemplo" + }, + { + "key": "exampleWithWords", + "value": "com palavras" + }, + { + "key": "exampleInserted", + "value": "inseridas" + }, + { + "key": "exampleDeleted", + "value": "eliminadas" + }, + { + "key": "adjustNumbers", + "value": "Ajustar números" + }, + { + "key": "Lowercase", + "value": "Caixa baixa" + }, + { + "key": "Uppercase", + "value": "Caixa alta" + }, + { + "key": "offset", + "value": "Compensar" + }, + { + "key": "updateLanguages", + "value": "Atualizar Idiomas" + }, + { + "key": "languageUpdateProgressTitle", + "value": "Atualizando Idiomas" + }, + { + "key": "languageUpdateProgressMessage1", + "value": "Verificando atualizações de idiomas..." + }, + { + "key": "languageUpdateDownloadingLanguages", + "value": "Baixando idioma {0}..." + }, + { + "key": "languageUpdateSavingChanges", + "value": "Salvando alterações" + }, + { + "key": "languageUpdateProgressTitleSuccess", + "value": "Idioma atualizado com sucesso" + }, + { + "key": "ok", + "value": "OK" + }, + { + "key": "languageUpdateProgressTitleFail", + "value": "Atualização de idioma falhou" + }, + { + "key": "languageUpdateTimeout", + "value": "Atualização falhou devido à um timeout. Se você está conectado à internet, nossos servidores podem estar desligados. Por favor tente novamente mais tarde, ou reporte um bug (olhe o manual do usuário para detalhes) se o erro persistir." + }, + { + "key": "languageUpdateFail", + "value": "Falha na atualização. Por favor reporte um bug (olhe o manual do usuário para detalhes). Código do erro: {0}, Mensagem: {1}" + }, + { + "key": "languageUpdated", + "value": "Atualizando {0} da versão {1} para {2}" + }, + { + "key": "languageAdded", + "value": "{0} adicionada" + }, + { + "key": "languageUnchanged", + "value": "{0} está atualizado" + }, + { + "key": "languageAllUpToDate", + "value": "Todos os idiomas estão atualizados." + } + ] +} \ No newline at end of file diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Resources/MulliganLanguages/pt.json.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Resources/MulliganLanguages/pt.json.meta new file mode 100644 index 0000000..a43fd56 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Resources/MulliganLanguages/pt.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: f27f821a2d0d04689b664084c893f864 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/Undo.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Undo.meta new file mode 100644 index 0000000..40b3015 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Editor/Undo.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c1b403515569e4058bd659fbec8f06e7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/AssetRenameUndoMoment.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/Undo/AssetRenameUndoMoment.cs similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/AssetRenameUndoMoment.cs rename to Assets/RedBlueGames/MulliganRenamer/Editor/Undo/AssetRenameUndoMoment.cs diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/AssetRenameUndoMoment.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Undo/AssetRenameUndoMoment.cs.meta similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/AssetRenameUndoMoment.cs.meta rename to Assets/RedBlueGames/MulliganRenamer/Editor/Undo/AssetRenameUndoMoment.cs.meta diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/AssetRenameUndoer.cs b/Assets/RedBlueGames/MulliganRenamer/Editor/Undo/AssetRenameUndoer.cs similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/AssetRenameUndoer.cs rename to Assets/RedBlueGames/MulliganRenamer/Editor/Undo/AssetRenameUndoer.cs diff --git a/Assets/RedBlueGames/MulliganRenamer/Editor/AssetRenameUndoer.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Editor/Undo/AssetRenameUndoer.cs.meta similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Editor/AssetRenameUndoer.cs.meta rename to Assets/RedBlueGames/MulliganRenamer/Editor/Undo/AssetRenameUndoer.cs.meta diff --git a/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/AdjustNumberingOpTests.cs b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/AdjustNumberingOpTests.cs new file mode 100644 index 0000000..9c415bc --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/AdjustNumberingOpTests.cs @@ -0,0 +1,190 @@ +/* MIT License + +Copyright (c) 2016 Edward Rowe, RedBlueGames + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +namespace RedBlueGames.MulliganRenamer +{ + using NUnit.Framework; + + public class AdjustNumberingOpTests + { + [Test] + public void Rename_NullTarget_ReturnsEmpty() + { + // Arrange + string name = null; + var renameOp = new AdjustNumberingOperation(); + + var expected = RenameResult.Empty; + + // Act + var result = renameOp.Rename(name, 0); + + // Assert + Assert.AreEqual(expected, result); + } + + [Test] + public void Rename_EmptySequence_AddsNothing() + { + // Arrange + string name = "Blah"; + var renameOp = new AdjustNumberingOperation(); + var expected = new RenameResult() { new Diff("Blah", DiffOperation.Equal) }; + + // Act + var result = renameOp.Rename(name, 0); + + // Assert + Assert.AreEqual(expected, result); + } + + [Test] + public void Rename_OnlyNumbersPositiveOffset_Adds() + { + // Arrange + var name = "123"; + var renameOp = new AdjustNumberingOperation(); + renameOp.Offset = 1; + + var expected = new RenameResult() + { + new Diff("12", DiffOperation.Equal), + new Diff("3", DiffOperation.Deletion), + new Diff("4", DiffOperation.Insertion), + }; + + // Act + var result = renameOp.Rename(name, 0); + + // Assert + Assert.AreEqual(expected, result); + } + + [Test] + public void Rename_OnlyNumbersNegativeOffset_Subtracts() + { + // Arrange + var name = "123"; + var renameOp = new AdjustNumberingOperation(); + renameOp.Offset = -1; + + var expected = new RenameResult() + { + new Diff("12", DiffOperation.Equal), + new Diff("3", DiffOperation.Deletion), + new Diff("2", DiffOperation.Insertion), + }; + + // Act + var result = renameOp.Rename(name, 0); + + // Assert + Assert.AreEqual(expected, result); + } + + [Test] + public void Rename_BigNegativeOffset_ShortensString() + { + // Arrange + var name = "12"; + var renameOp = new AdjustNumberingOperation(); + renameOp.Offset = -4; + + var expected = new RenameResult() + { + new Diff("1", DiffOperation.Deletion), + new Diff("8", DiffOperation.Insertion), + new Diff("2", DiffOperation.Deletion), + }; + + // Act + var result = renameOp.Rename(name, 0); + + // Assert + Assert.AreEqual(expected, result); + } + + [Test] + public void Rename_BigPositiveOffset_IncreasesStringLength() + { + // Arrange + var name = "1"; + var renameOp = new AdjustNumberingOperation(); + renameOp.Offset = 24; + + var expected = new RenameResult() + { + new Diff("1", DiffOperation.Deletion), + new Diff("25", DiffOperation.Insertion), + }; + + // Act + var result = renameOp.Rename(name, 0); + + // Assert + Assert.AreEqual(expected, result); + } + + [Test] + public void Rename_OnlyNumbersZeroOffset_DoesNothing() + { + // Arrange + var name = "123"; + var renameOp = new AdjustNumberingOperation(); + renameOp.Offset = 0; + + var expected = new RenameResult() { new Diff("123", DiffOperation.Equal), }; + + // Act + var result = renameOp.Rename(name, 0); + + // Assert + Assert.AreEqual(expected, result); + } + + [Test] + public void Rename_MultipleNumberGroups_AddsToAll() + { + // Arrange + var name = "234abc567"; + var renameOp = new AdjustNumberingOperation(); + renameOp.Offset = 1; + + var expected = new RenameResult() + { + new Diff("23", DiffOperation.Equal), + new Diff("4", DiffOperation.Deletion), + new Diff("5", DiffOperation.Insertion), + new Diff("abc56", DiffOperation.Equal), + new Diff("7", DiffOperation.Deletion), + new Diff("8", DiffOperation.Insertion), + }; + + // Act + var result = renameOp.Rename(name, 0); + + // Assert + Assert.AreEqual(expected, result); + } + } +} \ No newline at end of file diff --git a/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/AdjustNumberingOpTests.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/AdjustNumberingOpTests.cs.meta new file mode 100644 index 0000000..a20ed34 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/AdjustNumberingOpTests.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: c5f858e0061f940fea3ec5870b1170b4 +timeCreated: 1480087188 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/JSONRetrieverTests.cs b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/JSONRetrieverTests.cs new file mode 100644 index 0000000..89bb5dd --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/JSONRetrieverTests.cs @@ -0,0 +1,155 @@ +/* MIT License + +Copyright (c) 2016 Edward Rowe, RedBlueGames + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +namespace RedBlueGames.MulliganRenamer +{ + using System.Collections; + using UnityEngine; + using UnityEngine.TestTools; + using NUnit.Framework; + + public class JSONRetrieverTests + { + [UnityTest] + public IEnumerator GetJSON_ValidURLValidJSON_ReturnsExpected() + { + // Assemble + var simpleJson = new SimpleJson(); + simpleJson.AnInt = 5; + + // Act + var mockWebRequest = new MockWebRequest("{ \"anInt\": 5}"); + var getter = new JSONRetrieverWeb(mockWebRequest); + var op = getter.GetJSON(1); + yield return op.WaitForResult(1.5f, () => Assert.Fail("Test timed out. AsyncOp never returned a Status besides Pending.")); + + Assert.AreEqual(AsyncStatus.Success, op.Status); + Assert.AreEqual(simpleJson.AnInt, op.ResultData.AnInt); + } + + [Test] + public void GetJSON_InvalidURI_ThrowsBadURI() + { + Assert.Throws(() => new JSONRetrieverWeb("InvalidUri")); + } + + [UnityTest] + public IEnumerator GetJSON_InvalidJSON_ThrowsBadJSON() + { + // Assemble + var badJson = "{ \"anIntWithNoValue\" } "; + + // Act + var mockWebRequest = new MockWebRequest(badJson); + var getter = new JSONRetrieverWeb(mockWebRequest); + //Assert.Throws(() => getter.GetJSON(1), "Expected ArgumentException due to invalid Json format."); + var op = getter.GetJSON(1); + yield return op.WaitForResult(1.5f, () => Assert.Fail("Unexpected timeout. Test timed out, expected InvalidJSON exception")); + + Assert.AreEqual(AsyncStatus.Failed, op.Status); + Assert.AreEqual(JSONRetrieverWeb.ErrorCodeInvalidJsonFormat, op.FailureCode); + } + + [UnityTest] + public IEnumerator GetJSON_Timeout_ReportsTimeout() + { + // Assemble + // Act + var mockTimeout = new MockWebRequestTimeout(); + var getter = new JSONRetrieverWeb(mockTimeout); + var op = getter.GetJSON(1); + yield return op.WaitForResult(1.5f, () => Assert.Fail("Unexpected timeout. JsonRetrieverWeb should have sent a timeout, but did not.")); + + Assert.AreEqual(AsyncStatus.Timeout, op.Status); + } + + [UnityTest] + public IEnumerator GetJSON_HttpError_ReportsFail() + { + // Assemble + // Act + var mockHttpError = new MockWebRequestHttpError(); + var getter = new JSONRetrieverWeb(mockHttpError); + var op = getter.GetJSON(1); + yield return op.WaitForResult(1.5f, () => Assert.Fail("Unexpected timeout. JsonRetrieverWeb should have sent a failure, but did not.")); + + Assert.AreEqual(AsyncStatus.Failed, op.Status); + } + + [UnityTest] + public IEnumerator GetJSON_NetworkError_Fails() + { + // Assemble + // Act + var mockError = new MockWebRequestNetworkError(); + var getter = new JSONRetrieverWeb(mockError); + var op = getter.GetJSON(1); + yield return op.WaitForResult(1.5f, () => Assert.Fail("Unexpected timeout. JsonRetrieverWeb should have sent a failure, but did not.")); + + Assert.AreEqual(AsyncStatus.Failed, op.Status); + } + + [UnityTest] + public IEnumerator GetJSON_ReuseRetriever_ReturnsSameResultTwice() + { + // Assemble + var simpleJson = new SimpleJson(); + simpleJson.AnInt = 3; + + // Act + var mockWebRequest = new MockWebRequest("{ \"anInt\": 3}"); + var getter = new JSONRetrieverWeb(mockWebRequest); + var op = getter.GetJSON(1); + yield return op.WaitForResult(1.5f, () => Assert.Fail("Test timed out on first Get. AsyncOp never returned a Status besides Pending.")); + + Assert.AreEqual(AsyncStatus.Success, op.Status); + Assert.AreEqual(simpleJson.AnInt, op.ResultData.AnInt); + + var op2 = getter.GetJSON(1); + yield return op2.WaitForResult(1.5f, () => Assert.Fail("Test timed out on second Get. AsyncOp never returned a Status besides Pending.")); + + Assert.AreEqual(AsyncStatus.Success, op.Status); + Assert.AreEqual(simpleJson.AnInt, op.ResultData.AnInt); + } + + [System.Serializable] + public class SimpleJson + { + [SerializeField] + private int anInt; + + public int AnInt + { + get + { + return this.anInt; + } + + set + { + this.anInt = value; + } + } + } + } +} \ No newline at end of file diff --git a/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/JSONRetrieverTests.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/JSONRetrieverTests.cs.meta new file mode 100644 index 0000000..eb72165 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/JSONRetrieverTests.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 6ae3d0868033f44cc96862343eb1f35b +timeCreated: 1480087188 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/LanguageTests.cs b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/LanguageTests.cs new file mode 100644 index 0000000..0e14195 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/LanguageTests.cs @@ -0,0 +1,111 @@ +/* MIT License + +Copyright (c) 2020 Murillo Pugliesi Lopes, https://github.com/Mukarillo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +namespace RedBlueGames.MulliganRenamer +{ + using System.Collections.Generic; + using NUnit.Framework; + + public class LanguageTests + { + private List allResourceLanguages; + + [SetUp] + public void Init() + { + this.allResourceLanguages = LocalizationManager.LoadAllLanguages(); + } + + [Test] + public void CheckAllLanguages_ForMatchingEnglishElement() + { + var englishLanguage = this.allResourceLanguages.Find(x => x.Key == "en"); + + foreach (var language in this.allResourceLanguages) + { + if (language.Key == "en") + continue; + + foreach (var englishElement in englishLanguage.Elements) + { + Assert.That( + language.Elements.Exists(x => x.Key == englishElement.Key), + "Language " + language.Name + " does not contain key \"" + englishElement.Key + + "\". Please add an element with this key to the language."); + } + } + } + + [Test] + public void CheckAllLanguages_HasValue() + { + foreach (var language in this.allResourceLanguages) + { + foreach (var element in language.Elements) + { + Assert.That(!string.IsNullOrEmpty(element.Value), + "Language " + language.Name + " does not contain a value for key \"" + element.Key + + "\". Please add a value to the language."); + } + } + } + + [Test] + public void AllKeysInProject_ExistInAllLanguages() + { + var allScriptsGUIDs = UnityEditor.AssetDatabase.FindAssets("t:script"); + var keysInUse = new List(); + var regex = new System.Text.RegularExpressions.Regex("LocaleManager.Instance.GetTranslation\\(\"([^\\)]*)\"\\)[,;]"); + foreach (var guid in allScriptsGUIDs) + { + var path = UnityEditor.AssetDatabase.GUIDToAssetPath(guid); + var script = UnityEditor.AssetDatabase.LoadAssetAtPath(path); + var matches = regex.Matches(script.text); + foreach (System.Text.RegularExpressions.Match match in matches) + { + var key = match.Groups[1]; + keysInUse.Add(new ScriptKeyPair() { ScriptName = script.name, Key = key.Value }); + } + } + + foreach (var language in this.allResourceLanguages) + { + foreach (var scriptKeyPair in keysInUse) + { + Assert.That( + language.Elements.Exists(x => x.Key == scriptKeyPair.Key), + "Script \"" + scriptKeyPair.ScriptName + "\" tries to access key \"" + scriptKeyPair.Key + + "\" from Language " + language.Name + " but it could not be found." + + "Please add an element with this key to the language, or remove the GetTranslation call."); + } + } + } + + private class ScriptKeyPair + { + public string ScriptName { get; set; } + + public string Key { get; set; } + } + } +} \ No newline at end of file diff --git a/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/LanguageTests.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/LanguageTests.cs.meta new file mode 100644 index 0000000..07c4f62 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/LanguageTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 27c3707c1736f4624bde07719692ed4c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/LocalizationManagerTests.cs b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/LocalizationManagerTests.cs new file mode 100644 index 0000000..901e2b1 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/LocalizationManagerTests.cs @@ -0,0 +1,71 @@ +/* MIT License + +Copyright (c) 2020 Murillo Pugliesi Lopes, https://github.com/Mukarillo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +namespace RedBlueGames.MulliganRenamer +{ + using NUnit.Framework; + using System; + + public class LocalizationManagerTests + { + private Language languageBeforeTest; + + [SetUp] + public void Init() + { + this.languageBeforeTest = LocalizationManager.Instance.CurrentLanguage; + } + + [TearDown] + public void Cleanup() + { + LocalizationManager.Instance.ChangeLanguage(this.languageBeforeTest.Key); + } + + [Test] + public void ChangeLanguage_GoodKey_Changes() + { + foreach (var language in LocalizationManager.Instance.AllLanguages) + { + LocalizationManager.Instance.ChangeLanguage(language.Key); + + Assert.That( + language.Key.Equals(LocalizationManager.Instance.CurrentLanguage.Key), + "LocalizationManager did not change language to specified language, " + language.Name + "."); + } + } + + [Test] + public void ChangeLanguage_BadKey_DoesNotChange() + { + var oldLanguage = LocalizationManager.Instance.CurrentLanguage; + var receivedLanguageChangedEvent = false; + LocalizationManager.Instance.LanguageChanged += () => receivedLanguageChangedEvent = true; + LocalizationManager.Instance.ChangeLanguage("doesNotExist"); + Assert.AreEqual(oldLanguage, LocalizationManager.Instance.CurrentLanguage, + "LocalizationManager changed language when passed an invalid key. It should remain unchanged."); + Assert.False(receivedLanguageChangedEvent, "LocalizationManager fired off event for langauge changed" + + ", but it should not have changed, or fired the event."); + } + } +} \ No newline at end of file diff --git a/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/LocalizationManagerTests.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/LocalizationManagerTests.cs.meta new file mode 100644 index 0000000..069be8f --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/LocalizationManagerTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a0a490e9094eb45aa9ba01b0d73b684b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/MocksAndUtilities.meta b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/MocksAndUtilities.meta new file mode 100644 index 0000000..0e747ca --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/MocksAndUtilities.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8117a74b8bf7944a992c6152e583fb4d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/MocksAndUtilities/MockWebRequest.cs b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/MocksAndUtilities/MockWebRequest.cs new file mode 100644 index 0000000..803d327 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/MocksAndUtilities/MockWebRequest.cs @@ -0,0 +1,66 @@ +namespace RedBlueGames.MulliganRenamer +{ + using System; + using UnityEngine.Networking; + + public class MockWebRequest : IWebRequest, IDisposable + { + private string mockDownloadText; + + public int Timeout { get; set; } + + public bool IsDone { get; private set; } + + public bool IsNetworkError + { + get + { + return false; + } + } + + public bool IsHttpError + { + get + { + return false; + } + } + + public bool IsTimeout + { + get + { + return false; + } + } + + public string ErrorText + { + get + { + return string.Empty; + } + } + + public string DownloadedText { get; private set; } + + public MockWebRequest(string outputText) + { + this.mockDownloadText = outputText; + + // Must SendWebRequest before we should consider it done. + this.IsDone = false; + } + + public void SendWebRequest() + { + this.DownloadedText = this.mockDownloadText; + this.IsDone = true; + } + + public void Dispose() + { + } + } +} \ No newline at end of file diff --git a/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/MocksAndUtilities/MockWebRequest.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/MocksAndUtilities/MockWebRequest.cs.meta new file mode 100644 index 0000000..28618ba --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/MocksAndUtilities/MockWebRequest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2605f1a6b87d34aed881e353a70eb679 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/MocksAndUtilities/MockWebRequestHttpError.cs b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/MocksAndUtilities/MockWebRequestHttpError.cs new file mode 100644 index 0000000..83547f4 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/MocksAndUtilities/MockWebRequestHttpError.cs @@ -0,0 +1,60 @@ +namespace RedBlueGames.MulliganRenamer +{ + using System; + + public class MockWebRequestHttpError : IWebRequest, IDisposable + { + public int Timeout { get; set; } + + public bool IsDone { get; private set; } + + public bool IsNetworkError + { + get + { + return false; + } + } + + public bool IsHttpError { get; private set; } + + public bool IsTimeout + { + get + { + return false; + } + } + + public string ErrorText + { + get + { + return "Http error message."; + } + } + + public string DownloadedText + { + get + { + return string.Empty; + } + } + + public MockWebRequestHttpError() + { + this.IsHttpError = false; + } + + public void SendWebRequest() + { + this.IsDone = true; + this.IsHttpError = true; + } + + public void Dispose() + { + } + } +} \ No newline at end of file diff --git a/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/MocksAndUtilities/MockWebRequestHttpError.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/MocksAndUtilities/MockWebRequestHttpError.cs.meta new file mode 100644 index 0000000..d3a0d9c --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/MocksAndUtilities/MockWebRequestHttpError.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d1f4555458d414c7b9a343483742afa3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/MocksAndUtilities/MockWebRequestNetworkError.cs b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/MocksAndUtilities/MockWebRequestNetworkError.cs new file mode 100644 index 0000000..bf19d51 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/MocksAndUtilities/MockWebRequestNetworkError.cs @@ -0,0 +1,60 @@ +namespace RedBlueGames.MulliganRenamer +{ + using System; + + public class MockWebRequestNetworkError : IWebRequest, IDisposable + { + public int Timeout { get; set; } + + public bool IsDone { get; private set; } + + public bool IsNetworkError { get; private set; } + + public bool IsHttpError + { + get + { + return false; + } + } + + public bool IsTimeout + { + get + { + return false; + } + } + + public string ErrorText + { + get + { + return "Network error message."; + } + } + + public string DownloadedText + { + get + { + return string.Empty; + } + } + + public MockWebRequestNetworkError() + { + this.IsNetworkError = false; + } + + public void SendWebRequest() + { + this.IsDone = true; + this.IsNetworkError = true; + } + + public void Dispose() + { + } + } +} \ No newline at end of file diff --git a/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/MocksAndUtilities/MockWebRequestNetworkError.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/MocksAndUtilities/MockWebRequestNetworkError.cs.meta new file mode 100644 index 0000000..08d749b --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/MocksAndUtilities/MockWebRequestNetworkError.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a0274b84b7e8e4d6699e5ac7972ef733 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/MocksAndUtilities/MockWebRequestTimeout.cs b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/MocksAndUtilities/MockWebRequestTimeout.cs new file mode 100644 index 0000000..f24d988 --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/MocksAndUtilities/MockWebRequestTimeout.cs @@ -0,0 +1,64 @@ +namespace RedBlueGames.MulliganRenamer +{ + using System; + + public class MockWebRequestTimeout : IWebRequest, IDisposable + { + public int Timeout { get; set; } + + public bool IsDone { get; private set; } + + public bool IsNetworkError + { + get + { + return true; + } + } + + public bool IsHttpError + { + get + { + return false; + } + } + + public bool IsTimeout + { + get + { + return true; + } + } + + public string ErrorText + { + get + { + return "Timeout message"; + } + } + + public string DownloadedText + { + get + { + return string.Empty; + } + } + + public MockWebRequestTimeout() + { + } + + public void SendWebRequest() + { + this.IsDone = true; + } + + public void Dispose() + { + } + } +} \ No newline at end of file diff --git a/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/MocksAndUtilities/MockWebRequestTimeout.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/MocksAndUtilities/MockWebRequestTimeout.cs.meta new file mode 100644 index 0000000..8641aaf --- /dev/null +++ b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/MocksAndUtilities/MockWebRequestTimeout.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 32ecbef92b2f541c3a35d4c230a9e60b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/RBStopwatch.cs b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/MocksAndUtilities/RBStopwatch.cs similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Tests/Editor/RBStopwatch.cs rename to Assets/RedBlueGames/MulliganRenamer/Tests/Editor/MocksAndUtilities/RBStopwatch.cs diff --git a/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/RBStopwatch.cs.meta b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/MocksAndUtilities/RBStopwatch.cs.meta similarity index 100% rename from Assets/RedBlueGames/MulliganRenamer/Tests/Editor/RBStopwatch.cs.meta rename to Assets/RedBlueGames/MulliganRenamer/Tests/Editor/MocksAndUtilities/RBStopwatch.cs.meta diff --git a/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/UniqueListTests.cs b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/UniqueListTests.cs index 1e6fb88..a92d4d9 100644 --- a/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/UniqueListTests.cs +++ b/Assets/RedBlueGames/MulliganRenamer/Tests/Editor/UniqueListTests.cs @@ -83,13 +83,15 @@ public void AddRange_ValidElements_Adds() } [Test] - public void AddRange_Duplicates_Throws() + public void AddRange_Duplicates_IgnoresDuplicate() { // Act var list = new UniqueList(); + list.AddRange(new List() { "Test", "Test" }); + // Assert - Assert.Throws(() => list.AddRange(new List() { "Test", "Test" })); + CollectionAssert.AreEquivalent(new[] { "Test", }, list); } [Test] diff --git a/LanguageBookmarks.json b/LanguageBookmarks.json new file mode 100644 index 0000000..5009f37 --- /dev/null +++ b/LanguageBookmarks.json @@ -0,0 +1,7 @@ +{ + "languageUrls": + [ + "https://raw.githubusercontent.com/redbluegames/unity-mulligan-renamer/staging/Assets/RedBlueGames/MulliganRenamer/Editor/Resources/MulliganLanguages/en.json", + "https://raw.githubusercontent.com/redbluegames/unity-mulligan-renamer/staging/Assets/RedBlueGames/MulliganRenamer/Editor/Resources/MulliganLanguages/pt.json" + ] +} \ No newline at end of file