This commit is contained in:
2023-01-22 15:59:13 +05:30
parent fad3593f9f
commit 950c3b29f2
117 changed files with 20168 additions and 79 deletions

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 2c61139fc58134242bb4b9e6d9fabdc0
folderAsset: yes
timeCreated: 1502815237
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,139 @@
using BgTools.Extensions;
using BgTools.Utils;
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace BgTools.Dialogs
{
public class TextFieldDialog : EditorWindow
{
[NonSerialized]
private string resultString = string.Empty;
[NonSerialized]
private Action<string> callback;
[NonSerialized]
private string description;
[NonSerialized]
private List<TextValidator> validatorList = new List<TextValidator>();
[NonSerialized]
private TextValidator errorValidator = null;
public static void OpenDialog(string title, string description, List<TextValidator> validatorList, Action<string> callback, EditorWindow targetWin = null)
{
TextFieldDialog window = ScriptableObject.CreateInstance<TextFieldDialog>();
window.name = "TextFieldDialog '" + title + "'";
window.titleContent = new GUIContent (title);
window.description = description;
window.callback = callback;
window.validatorList = validatorList;
window.position = new Rect(0, 0, 350, 140);
window.ShowUtility();
window.CenterOnWindow(targetWin);
window.Focus();
EditorWindow.FocusWindowIfItsOpen<TextFieldDialog>();
}
void OnGUI()
{
errorValidator = null;
Color defaultColor = GUI.contentColor;
GUILayout.Space(20);
EditorGUILayout.LabelField(description);
GUILayout.Space(20);
GUI.SetNextControlName(name+"_textInput");
resultString = EditorGUILayout.TextField(resultString, GUILayout.ExpandWidth(true));
// GUILayout.Space(20);
GUILayout.FlexibleSpace();
foreach(TextValidator val in validatorList)
{
if (!val.Validate(resultString))
{
errorValidator = val;
break;
}
}
bool lockOkButton = !(errorValidator != null && errorValidator.m_errorType == TextValidator.ErrorType.Error);
GUILayout.BeginHorizontal();
if(errorValidator != null)
{
switch (errorValidator.m_errorType)
{
case TextValidator.ErrorType.Info:
GUI.contentColor = Styles.Colors.Blue;
GUILayout.Box(new GUIContent(ImageManager.Info, errorValidator.m_failureMsg), Styles.icon);
break;
case TextValidator.ErrorType.Warning:
GUI.contentColor = Styles.Colors.Yellow;
GUILayout.Box(new GUIContent(ImageManager.Exclamation, errorValidator.m_failureMsg), Styles.icon);
break;
case TextValidator.ErrorType.Error:
GUI.contentColor = Styles.Colors.Red;
GUILayout.Box(new GUIContent(ImageManager.Exclamation, errorValidator.m_failureMsg), Styles.icon);
break;
}
GUI.contentColor = defaultColor;
}
GUILayout.FlexibleSpace();
if (GUILayout.Button("Cancel", GUILayout.Width(75.0f)))
this.Close();
GUI.enabled = lockOkButton;
if (GUILayout.Button("OK", GUILayout.Width(75.0f)))
{
callback(resultString);
Close();
}
GUI.enabled = true;
GUILayout.EndHorizontal();
GUILayout.Space(20);
// set focus only if element exist
try
{
EditorGUI.FocusTextInControl(name+"_textInput");
}
catch (MissingReferenceException)
{ }
if (Event.current != null && Event.current.isKey)
{
switch (Event.current.keyCode)
{
case KeyCode.Return:
if (lockOkButton)
{
callback(resultString);
Close();
}
break;
case KeyCode.Escape:
Close();
break;
}
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 380be3677d2e95144863ee00c051c1f2
timeCreated: 1500849296
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,64 @@
using System;
using System.Text.RegularExpressions;
namespace BgTools.Dialogs
{
public class TextValidator
{
public enum ErrorType
{
Invalid = -1,
Info = 0,
Warning = 1,
Error = 2
}
[NonSerialized]
public ErrorType m_errorType = ErrorType.Invalid;
[NonSerialized]
private string m_regEx = string.Empty;
[NonSerialized]
private Func<string, bool> m_validationFunction;
[NonSerialized]
public string m_failureMsg = string.Empty;
/// <summary>
/// Validator for TextFieldDialog based on regex.
/// </summary>
/// <param name="errorType">Categorie of the error.</param>
/// <param name="failureMsg">Message that described the reason why the validation fail.</param>
/// <param name="regEx">String with regular expression. It need to describe the valid state.</param>
public TextValidator(ErrorType errorType, string failureMsg, string regEx)
{
m_errorType = errorType;
m_failureMsg = failureMsg;
m_regEx = regEx;
}
/// <summary>
/// Validator for TextFieldDialog based on regex.
/// </summary>
/// <param name="errorType">Categorie of the error.</param>
/// <param name="failureMsg">Message that described the reason why the validation fail.</param>
/// <param name="validationFunction">Function that validate the input. Get the current input as string and need to return a bool. Nedd to return 'false' if the validation fails.</param>
public TextValidator(ErrorType errorType, string failureMsg, Func<string, bool> validationFunction)
{
m_errorType = errorType;
m_failureMsg = failureMsg;
m_validationFunction = validationFunction;
}
public bool Validate(string srcString)
{
if (m_regEx != string.Empty)
return Regex.IsMatch(srcString, m_regEx);
else if (m_validationFunction != null)
return m_validationFunction(srcString);
return false;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4202eaaf18e2e43438f2f3632b252393
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 5c7bb3ee5362c0a40a707ade01e79972
folderAsset: yes
timeCreated: 1502876479
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,90 @@
using System;
using System.Linq;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
namespace BgTools.Extensions
{
public static class Extensions
{
private static Type[] GetAllDerivedTypes(this AppDomain aAppDomain, Type aType)
{
var result = new List<Type>();
var assemblies = aAppDomain.GetAssemblies();
foreach (var assembly in assemblies)
{
var types = assembly.GetTypes();
foreach (Type type in types)
{
if (type.IsSubclassOf(aType))
result.Add(type);
}
}
return result.ToArray();
}
public static Rect GetEditorMainWindowPos(EditorWindow relatedWin = null)
{
var containerWinType = AppDomain.CurrentDomain.GetAllDerivedTypes(typeof(ScriptableObject)).Where(t => t.Name == "ContainerWindow").FirstOrDefault();
if (containerWinType == null)
throw new MissingMemberException("Can't find internal type ContainerWindow. Maybe something has changed inside Unity");
var showModeField = containerWinType.GetField("m_ShowMode", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var positionProperty = containerWinType.GetProperty("position", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
if (showModeField == null || positionProperty == null)
throw new MissingFieldException("Can't find internal fields 'm_ShowMode' or 'position'. Maybe something has changed inside Unity");
var windows = Resources.FindObjectsOfTypeAll(containerWinType);
foreach (var win in windows)
{
var showmode = (int)showModeField.GetValue(win);
// Given window
//if (relatedWin != null && relatedWin.GetInstanceID() == win.GetInstanceID())
//{
// var pos = (Rect)positionProperty.GetValue(win, null);
// return pos;
//}
// Main window
if (showmode == 4)
{
var pos = (Rect)positionProperty.GetValue(win, null);
return pos;
}
}
throw new NotSupportedException("Can't find internal main window. Maybe something has changed inside Unity");
}
/// <summary>
/// Center the EditorWindow in front of the MainUnityWindow (support multi screens).
/// Kept the currend window sizes.
/// </summary>
public static void CenterOnMainWindow(this EditorWindow window)
{
CenterOnWindow(window, null);
}
/// <summary>
/// Center the EditorWindow in front of the given EditorWindow (support multi screens).
/// Kept the currend window sizes.
/// </summary>
/// <param name="relatedWin">Referance window for the positioning.</param>
public static void CenterOnWindow(this EditorWindow window, EditorWindow relatedWin)
{
var main = GetEditorMainWindowPos(relatedWin);
var pos = window.position;
float w = (main.width - pos.width) * 0.5f;
float h = (main.height - pos.height) * 0.5f;
pos.x = main.x + w;
pos.y = main.y + h;
window.position = pos;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 274db1862ad1a1b4c80a2ed6558e05ec
timeCreated: 1502876542
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: e93ef5c4e798b034bb024596113459cb
folderAsset: yes
timeCreated: 1505565882
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,36 @@
namespace BgTools.PlayerPrefsEditor
{
[System.Serializable]
public class PreferenceEntry
{
public enum PrefTypes
{
String = 0,
Int = 1,
Float = 2
}
public PrefTypes m_typeSelection;
public string m_key;
// Need diffrend ones for auto type selection of serilizedProerty
public string m_strValue;
public int m_intValue;
public float m_floatValue;
public string ValueAsString()
{
switch(m_typeSelection)
{
case PrefTypes.String:
return m_strValue;
case PrefTypes.Int:
return m_intValue.ToString();
case PrefTypes.Float:
return m_floatValue.ToString();
default:
return string.Empty;
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 2ae9239fbddf12b4099b3cacc5301271
timeCreated: 1496684286
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,29 @@
using System.Collections.Generic;
using UnityEngine;
namespace BgTools.PlayerPrefsEditor
{
[System.Serializable]
public class PreferenceEntryHolder : ScriptableObject
{
public List<PreferenceEntry> userDefList;
public List<PreferenceEntry> unityDefList;
private void OnEnable()
{
hideFlags = HideFlags.DontSave;
if (userDefList == null)
userDefList = new List<PreferenceEntry>();
if (unityDefList == null)
unityDefList = new List<PreferenceEntry>();
}
public void ClearLists()
{
if (userDefList != null)
userDefList.Clear();
if (unityDefList != null)
unityDefList.Clear();
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: d5d94a5263d6af0478dde8fb08a3dcb7
timeCreated: 1500316993
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,272 @@
using System;
using System.Linq;
#if UNITY_EDITOR_WIN
using Microsoft.Win32;
using System.Text;
#elif UNITY_EDITOR_OSX
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;
#elif UNITY_EDITOR_LINUX
using System.IO;
using System.Xml;
using System.Xml.Linq;
#endif
namespace BgTools.PlayerPrefsEditor
{
public abstract class PreferanceStorageAccessor
{
protected string prefPath;
protected string[] cachedData = new string[0];
protected abstract void FetchKeysFromSystem();
protected PreferanceStorageAccessor(string pathToPrefs)
{
prefPath = pathToPrefs;
}
public string[] GetKeys(bool reloadData = true)
{
if (reloadData || cachedData.Length == 0)
{
FetchKeysFromSystem();
}
return cachedData;
}
public Action PrefEntryChangedDelegate;
protected bool ignoreNextChange = false;
public void IgnoreNextChange()
{
ignoreNextChange = true;
}
protected virtual void OnPrefEntryChanged()
{
if (ignoreNextChange)
{
ignoreNextChange = false;
return;
}
PrefEntryChangedDelegate();
}
public Action StartLoadingDelegate;
public Action StopLoadingDelegate;
public abstract void StartMonitoring();
public abstract void StopMonitoring();
public abstract bool IsMonitoring();
}
#if UNITY_EDITOR_WIN
public class WindowsPrefStorage : PreferanceStorageAccessor
{
RegistryMonitor monitor;
public WindowsPrefStorage(string pathToPrefs) : base(pathToPrefs)
{
monitor = new RegistryMonitor(RegistryHive.CurrentUser, prefPath);
monitor.RegChanged += new EventHandler(OnRegChanged);
}
private void OnRegChanged(object sender, EventArgs e)
{
OnPrefEntryChanged();
}
protected override void FetchKeysFromSystem()
{
cachedData = new string[0];
using (RegistryKey rootKey = Registry.CurrentUser.OpenSubKey(prefPath))
{
if (rootKey != null)
{
cachedData = rootKey.GetValueNames();
rootKey.Close();
}
}
// Clean <key>_h3320113488 nameing
cachedData = cachedData.Select((key) => { return key.Substring(0, key.LastIndexOf("_h", StringComparison.Ordinal)); }).ToArray();
EncodeAnsiInPlace();
}
public override void StartMonitoring()
{
monitor.Start();
}
public override void StopMonitoring()
{
monitor.Stop();
}
public override bool IsMonitoring()
{
return monitor.IsMonitoring;
}
private void EncodeAnsiInPlace()
{
Encoding utf8 = Encoding.UTF8;
Encoding ansi = Encoding.GetEncoding(1252);
for (int i = 0; i < cachedData.Length; i++)
{
cachedData[i] = utf8.GetString(ansi.GetBytes(cachedData[i]));
}
}
}
#elif UNITY_EDITOR_LINUX
public class LinuxPrefStorage : PreferanceStorageAccessor
{
FileSystemWatcher fileWatcher;
public LinuxPrefStorage(string pathToPrefs) : base(Path.Combine(Environment.GetEnvironmentVariable("HOME"), pathToPrefs))
{
fileWatcher = new FileSystemWatcher();
fileWatcher.Path = Path.GetDirectoryName(prefPath);
fileWatcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite;
fileWatcher.Filter = "prefs";
fileWatcher.Changed += OnWatchedFileChanged;
}
protected override void FetchKeysFromSystem()
{
cachedData = new string[0];
if (File.Exists(prefPath))
{
XmlReaderSettings settings = new XmlReaderSettings();
XmlReader reader = XmlReader.Create(prefPath, settings);
XDocument doc = XDocument.Load(reader);
cachedData = doc.Element("unity_prefs").Elements().Select((e) => e.Attribute("name").Value).ToArray();
}
}
public override void StartMonitoring()
{
fileWatcher.EnableRaisingEvents = true;
}
public override void StopMonitoring()
{
fileWatcher.EnableRaisingEvents = false;
}
public override bool IsMonitoring()
{
return fileWatcher.EnableRaisingEvents;
}
private void OnWatchedFileChanged(object source, FileSystemEventArgs e)
{
OnPrefEntryChanged();
}
}
#elif UNITY_EDITOR_OSX
public class MacPrefStorage : PreferanceStorageAccessor
{
private FileSystemWatcher fileWatcher;
private DirectoryInfo prefsDirInfo;
private String prefsFileNameWithoutExtension;
public MacPrefStorage(string pathToPrefs) : base(Path.Combine(Environment.GetEnvironmentVariable("HOME"), pathToPrefs))
{
prefsDirInfo = new DirectoryInfo(Path.GetDirectoryName(prefPath));
prefsFileNameWithoutExtension = Path.GetFileNameWithoutExtension(prefPath);
fileWatcher = new FileSystemWatcher();
fileWatcher.Path = Path.GetDirectoryName(prefPath);
fileWatcher.NotifyFilter = NotifyFilters.LastWrite;
fileWatcher.Filter = Path.GetFileName(prefPath);
// MAC delete the old and create a new file instead of updating
fileWatcher.Created += OnWatchedFileChanged;
}
protected override void FetchKeysFromSystem()
{
// Workaround to avoid incomplete tmp phase from MAC OS
foreach (FileInfo info in prefsDirInfo.GetFiles())
{
// Check if tmp PlayerPrefs file exist
if (info.FullName.Contains(prefsFileNameWithoutExtension) && !info.FullName.EndsWith(".plist"))
{
StartLoadingDelegate();
return;
}
}
StopLoadingDelegate();
cachedData = new string[0];
if (File.Exists(prefPath))
{
string fixedPrefsPath = prefPath.Replace("\"", "\\\"").Replace("'", "\\'").Replace("`", "\\`");
var cmdStr = string.Format(@"-p '{0}'", fixedPrefsPath);
string stdOut = String.Empty;
string errOut = String.Empty;
var process = new System.Diagnostics.Process();
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = "plutil";
process.StartInfo.Arguments = cmdStr;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.OutputDataReceived += new DataReceivedEventHandler((sender, evt) => { stdOut += evt.Data + "\n"; });
process.ErrorDataReceived += new DataReceivedEventHandler((sender, evt) => { errOut += evt.Data + "\n"; });
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
MatchCollection matches = Regex.Matches(stdOut, @"(?: "")(.*)(?:"" =>.*)");
cachedData = matches.Cast<Match>().Select((e) => e.Groups[1].Value).ToArray();
}
}
public override void StartMonitoring()
{
fileWatcher.EnableRaisingEvents = true;
}
public override void StopMonitoring()
{
fileWatcher.EnableRaisingEvents = false;
}
public override bool IsMonitoring()
{
return fileWatcher.EnableRaisingEvents;
}
private void OnWatchedFileChanged(object source, FileSystemEventArgs e)
{
OnPrefEntryChanged();
}
}
#endif
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f54241e622579a145a495df929a9330a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,700 @@
using UnityEngine;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEditorInternal;
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using BgTools.Utils;
using BgTools.Dialogs;
#if (UNITY_EDITOR_LINUX || UNITY_EDITOR_OSX)
using System.Text;
using System.Globalization;
#endif
namespace BgTools.PlayerPrefsEditor
{
public class PreferencesEditorWindow : EditorWindow
{
#region ErrorValues
private readonly int ERROR_VALUE_INT = int.MinValue;
private readonly string ERROR_VALUE_STR = "<bgTool_error_24072017>";
#endregion //ErrorValues
private enum PreferencesEntrySortOrder
{
None = 0,
Asscending = 1,
Descending = 2
}
private static string pathToPrefs = String.Empty;
private static string platformPathPrefix = @"~";
private string[] userDef;
private string[] unityDef;
private bool showSystemGroup = false;
private PreferencesEntrySortOrder sortOrder = PreferencesEntrySortOrder.None;
private SerializedObject serializedObject;
private ReorderableList userDefList;
private ReorderableList unityDefList;
private SerializedProperty[] userDefListCache = new SerializedProperty[0];
private PreferenceEntryHolder prefEntryHolder;
private Vector2 scrollPos;
private float relSpliterPos;
private bool moveSplitterPos = false;
private PreferanceStorageAccessor entryAccessor;
private MySearchField searchfield;
private string searchTxt;
private int loadingSpinnerFrame;
private bool updateView = false;
private bool monitoring = false;
private bool showLoadingIndicatorOverlay = false;
private readonly List<TextValidator> prefKeyValidatorList = new List<TextValidator>()
{
new TextValidator(TextValidator.ErrorType.Error, @"Invalid character detected. Only letters, numbers, space and ,.;:<>_|!§$%&/()=?*+~#-]+$ are allowed", @"(^$)|(^[a-zA-Z0-9 ,.;:<>_|!§$%&/()=?*+~#-]+$)"),
new TextValidator(TextValidator.ErrorType.Warning, @"The given key already exist. The existing entry would be overwritten!", (key) => { return !PlayerPrefs.HasKey(key); })
};
#if UNITY_EDITOR_LINUX
private readonly char[] invalidFilenameChars = { '"', '\\', '*', '/', ':', '<', '>', '?', '|' };
#elif UNITY_EDITOR_OSX
private readonly char[] invalidFilenameChars = { '$', '%', '&', '\\', '/', ':', '<', '>', '|', '~' };
#endif
[MenuItem("Tools/BG Tools/PlayerPrefs Editor", false, 1)]
static void ShowWindow()
{
PreferencesEditorWindow window = EditorWindow.GetWindow<PreferencesEditorWindow>(false, "Prefs Editor");
window.minSize = new Vector2(270.0f, 300.0f);
window.name = "Prefs Editor";
//window.titleContent = EditorGUIUtility.IconContent("SettingsIcon"); // Icon
window.Show();
}
private void OnEnable()
{
#if UNITY_EDITOR_WIN
pathToPrefs = @"SOFTWARE\Unity\UnityEditor\" + PlayerSettings.companyName + @"\" + PlayerSettings.productName;
platformPathPrefix = @"<CurrentUser>";
entryAccessor = new WindowsPrefStorage(pathToPrefs);
#elif UNITY_EDITOR_OSX
pathToPrefs = @"Library/Preferences/unity." + MakeValidFileName(PlayerSettings.companyName) + "." + MakeValidFileName(PlayerSettings.productName) + ".plist";
entryAccessor = new MacPrefStorage(pathToPrefs);
entryAccessor.StartLoadingDelegate = () => { showLoadingIndicatorOverlay = true; };
entryAccessor.StopLoadingDelegate = () => { showLoadingIndicatorOverlay = false; };
#elif UNITY_EDITOR_LINUX
pathToPrefs = @".config/unity3d/" + MakeValidFileName(PlayerSettings.companyName) + "/" + MakeValidFileName(PlayerSettings.productName) + "/prefs";
entryAccessor = new LinuxPrefStorage(pathToPrefs);
#endif
entryAccessor.PrefEntryChangedDelegate = () => { updateView = true; };
monitoring = EditorPrefs.GetBool("BGTools.PlayerPrefsEditor.WatchingForChanges", true);
if(monitoring)
entryAccessor.StartMonitoring();
sortOrder = (PreferencesEntrySortOrder) EditorPrefs.GetInt("BGTools.PlayerPrefsEditor.SortOrder", 0);
searchfield = new MySearchField();
searchfield.DropdownSelectionDelegate = () => { PrepareData(); };
// Fix for serialisation issue of static fields
if (userDefList == null)
{
InitReorderedList();
PrepareData();
}
}
// Handel view updates for monitored changes
// Necessary to avoid main thread access issue
private void Update()
{
if (showLoadingIndicatorOverlay)
{
loadingSpinnerFrame = (int)Mathf.Repeat(Time.realtimeSinceStartup * 10, 11.99f);
PrepareData();
Repaint();
}
if (updateView)
{
updateView = false;
PrepareData();
Repaint();
}
}
private void OnDisable()
{
entryAccessor.StopMonitoring();
}
private void InitReorderedList()
{
if (prefEntryHolder == null)
{
var tmp = Resources.FindObjectsOfTypeAll<PreferenceEntryHolder>();
if (tmp.Length > 0)
{
prefEntryHolder = tmp[0];
}
else
{
prefEntryHolder = ScriptableObject.CreateInstance<PreferenceEntryHolder>();
}
}
if (serializedObject == null)
{
serializedObject = new SerializedObject(prefEntryHolder);
}
userDefList = new ReorderableList(serializedObject, serializedObject.FindProperty("userDefList"), false, true, true, true);
unityDefList = new ReorderableList(serializedObject, serializedObject.FindProperty("unityDefList"), false, true, false, false);
relSpliterPos = EditorPrefs.GetFloat("BGTools.PlayerPrefsEditor.RelativeSpliterPosition", 100 / position.width);
userDefList.drawHeaderCallback = (Rect rect) =>
{
EditorGUI.LabelField(rect, "User defined");
};
userDefList.drawElementBackgroundCallback = OnDrawElementBackgroundCallback;
userDefList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
{
SerializedProperty element = GetUserDefListElementAtIndex(index, userDefList.serializedProperty);
SerializedProperty key = element.FindPropertyRelative("m_key");
SerializedProperty type = element.FindPropertyRelative("m_typeSelection");
SerializedProperty value;
// Load only necessary type
switch ((PreferenceEntry.PrefTypes)type.enumValueIndex)
{
case PreferenceEntry.PrefTypes.Float:
value = element.FindPropertyRelative("m_floatValue");
break;
case PreferenceEntry.PrefTypes.Int:
value = element.FindPropertyRelative("m_intValue");
break;
case PreferenceEntry.PrefTypes.String:
value = element.FindPropertyRelative("m_strValue");
break;
default:
value = element.FindPropertyRelative("This should never happen");
break;
}
float spliterPos = relSpliterPos * rect.width;
rect.y += 2;
EditorGUI.BeginChangeCheck();
string prefKeyName = key.stringValue;
EditorGUI.LabelField(new Rect(rect.x, rect.y, spliterPos - 1, EditorGUIUtility.singleLineHeight), new GUIContent(prefKeyName, prefKeyName));
GUI.enabled = false;
EditorGUI.EnumPopup(new Rect(rect.x + spliterPos + 1, rect.y, 60, EditorGUIUtility.singleLineHeight), (PreferenceEntry.PrefTypes)type.enumValueIndex);
GUI.enabled = !showLoadingIndicatorOverlay;
switch ((PreferenceEntry.PrefTypes)type.enumValueIndex)
{
case PreferenceEntry.PrefTypes.Float:
EditorGUI.DelayedFloatField(new Rect(rect.x + spliterPos + 62, rect.y, rect.width - spliterPos - 60, EditorGUIUtility.singleLineHeight), value, GUIContent.none);
break;
case PreferenceEntry.PrefTypes.Int:
EditorGUI.DelayedIntField(new Rect(rect.x + spliterPos + 62, rect.y, rect.width - spliterPos - 60, EditorGUIUtility.singleLineHeight), value, GUIContent.none);
break;
case PreferenceEntry.PrefTypes.String:
EditorGUI.DelayedTextField(new Rect(rect.x + spliterPos + 62, rect.y, rect.width - spliterPos - 60, EditorGUIUtility.singleLineHeight), value, GUIContent.none);
break;
}
if (EditorGUI.EndChangeCheck())
{
entryAccessor.IgnoreNextChange();
switch ((PreferenceEntry.PrefTypes)type.enumValueIndex)
{
case PreferenceEntry.PrefTypes.Float:
PlayerPrefs.SetFloat(key.stringValue, value.floatValue);
break;
case PreferenceEntry.PrefTypes.Int:
PlayerPrefs.SetInt(key.stringValue, value.intValue);
break;
case PreferenceEntry.PrefTypes.String:
PlayerPrefs.SetString(key.stringValue, value.stringValue);
break;
}
PlayerPrefs.Save();
}
};
userDefList.onRemoveCallback = (ReorderableList l) =>
{
userDefList.ReleaseKeyboardFocus();
unityDefList.ReleaseKeyboardFocus();
string prefKey = l.serializedProperty.GetArrayElementAtIndex(l.index).FindPropertyRelative("m_key").stringValue;
if (EditorUtility.DisplayDialog("Warning!", $"Are you sure you want to delete this entry from PlayerPrefs?\n\nEntry: {prefKey}", "Yes", "No"))
{
entryAccessor.IgnoreNextChange();
PlayerPrefs.DeleteKey(prefKey);
PlayerPrefs.Save();
ReorderableList.defaultBehaviours.DoRemoveButton(l);
PrepareData();
GUIUtility.ExitGUI();
}
};
userDefList.onAddDropdownCallback = (Rect buttonRect, ReorderableList l) =>
{
var menu = new GenericMenu();
foreach (PreferenceEntry.PrefTypes type in Enum.GetValues(typeof(PreferenceEntry.PrefTypes)))
{
menu.AddItem(new GUIContent(type.ToString()), false, () =>
{
TextFieldDialog.OpenDialog("Create new property", "Key for the new property:", prefKeyValidatorList, (key) => {
entryAccessor.IgnoreNextChange();
switch (type)
{
case PreferenceEntry.PrefTypes.Float:
PlayerPrefs.SetFloat(key, 0.0f);
break;
case PreferenceEntry.PrefTypes.Int:
PlayerPrefs.SetInt(key, 0);
break;
case PreferenceEntry.PrefTypes.String:
PlayerPrefs.SetString(key, string.Empty);
break;
}
PlayerPrefs.Save();
PrepareData();
Focus();
}, this);
});
}
menu.ShowAsContext();
};
unityDefList.drawElementBackgroundCallback = OnDrawElementBackgroundCallback;
unityDefList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
{
var element = unityDefList.serializedProperty.GetArrayElementAtIndex(index);
SerializedProperty key = element.FindPropertyRelative("m_key");
SerializedProperty type = element.FindPropertyRelative("m_typeSelection");
SerializedProperty value;
// Load only necessary type
switch ((PreferenceEntry.PrefTypes)type.enumValueIndex)
{
case PreferenceEntry.PrefTypes.Float:
value = element.FindPropertyRelative("m_floatValue");
break;
case PreferenceEntry.PrefTypes.Int:
value = element.FindPropertyRelative("m_intValue");
break;
case PreferenceEntry.PrefTypes.String:
value = element.FindPropertyRelative("m_strValue");
break;
default:
value = element.FindPropertyRelative("This should never happen");
break;
}
float spliterPos = relSpliterPos * rect.width;
rect.y += 2;
GUI.enabled = false;
string prefKeyName = key.stringValue;
EditorGUI.LabelField(new Rect(rect.x, rect.y, spliterPos - 1, EditorGUIUtility.singleLineHeight), new GUIContent(prefKeyName, prefKeyName));
EditorGUI.EnumPopup(new Rect(rect.x + spliterPos + 1, rect.y, 60, EditorGUIUtility.singleLineHeight), (PreferenceEntry.PrefTypes)type.enumValueIndex);
switch ((PreferenceEntry.PrefTypes)type.enumValueIndex)
{
case PreferenceEntry.PrefTypes.Float:
EditorGUI.DelayedFloatField(new Rect(rect.x + spliterPos + 62, rect.y, rect.width - spliterPos - 60, EditorGUIUtility.singleLineHeight), value, GUIContent.none);
break;
case PreferenceEntry.PrefTypes.Int:
EditorGUI.DelayedIntField(new Rect(rect.x + spliterPos + 62, rect.y, rect.width - spliterPos - 60, EditorGUIUtility.singleLineHeight), value, GUIContent.none);
break;
case PreferenceEntry.PrefTypes.String:
EditorGUI.DelayedTextField(new Rect(rect.x + spliterPos + 62, rect.y, rect.width - spliterPos - 60, EditorGUIUtility.singleLineHeight), value, GUIContent.none);
break;
}
GUI.enabled = !showLoadingIndicatorOverlay;
};
unityDefList.drawHeaderCallback = (Rect rect) =>
{
EditorGUI.LabelField(rect, "Unity defined");
};
}
private void OnDrawElementBackgroundCallback(Rect rect, int index, bool isActive, bool isFocused)
{
if (Event.current.type == EventType.Repaint)
{
ReorderableList.defaultBehaviours.elementBackground.Draw(rect, false, isActive, isActive, isFocused);
}
Rect spliterRect = new Rect(rect.x + relSpliterPos * rect.width, rect.y, 2, rect.height);
EditorGUIUtility.AddCursorRect(spliterRect, MouseCursor.ResizeHorizontal);
if (Event.current.type == EventType.MouseDown && spliterRect.Contains(Event.current.mousePosition))
{
moveSplitterPos = true;
}
if(moveSplitterPos)
{
if (Event.current.mousePosition.x > 100 && Event.current.mousePosition.x<rect.width - 120)
{
relSpliterPos = Event.current.mousePosition.x / rect.width;
Repaint();
}
}
if (Event.current.type == EventType.MouseUp)
{
moveSplitterPos = false;
EditorPrefs.SetFloat("BGTools.PlayerPrefsEditor.RelativeSpliterPosition", relSpliterPos);
}
}
void OnGUI()
{
// Need to catch 'Stack empty' error on linux
try
{
if (showLoadingIndicatorOverlay)
{
GUI.enabled = false;
}
Color defaultColor = GUI.contentColor;
if (!EditorGUIUtility.isProSkin)
{
GUI.contentColor = Styles.Colors.DarkGray;
}
GUILayout.BeginVertical();
GUILayout.BeginHorizontal(EditorStyles.toolbar);
EditorGUI.BeginChangeCheck();
searchTxt = searchfield.OnToolbarGUI(searchTxt);
if (EditorGUI.EndChangeCheck())
{
PrepareData(false);
}
GUILayout.FlexibleSpace();
EditorGUIUtility.SetIconSize(new Vector2(14.0f, 14.0f));
GUIContent sortOrderContent;
switch (sortOrder)
{
case PreferencesEntrySortOrder.Asscending:
sortOrderContent = new GUIContent(ImageManager.SortAsscending, "Ascending sorted");
break;
case PreferencesEntrySortOrder.Descending:
sortOrderContent = new GUIContent(ImageManager.SortDescending, "Descending sorted");
break;
case PreferencesEntrySortOrder.None:
default:
sortOrderContent = new GUIContent(ImageManager.SortDisabled, "Not sorted");
break;
}
if (GUILayout.Button(sortOrderContent, EditorStyles.toolbarButton))
{
sortOrder++;
if((int) sortOrder >= Enum.GetValues(typeof(PreferencesEntrySortOrder)).Length)
{
sortOrder = 0;
}
EditorPrefs.SetInt("BGTools.PlayerPrefsEditor.SortOrder", (int) sortOrder);
PrepareData(false);
}
GUIContent watcherContent = (entryAccessor.IsMonitoring()) ? new GUIContent(ImageManager.Watching, "Watching changes") : new GUIContent(ImageManager.NotWatching, "Not watching changes");
if (GUILayout.Button(watcherContent, EditorStyles.toolbarButton))
{
monitoring = !monitoring;
EditorPrefs.SetBool("BGTools.PlayerPrefsEditor.WatchingForChanges", monitoring);
if (monitoring)
entryAccessor.StartMonitoring();
else
entryAccessor.StopMonitoring();
Repaint();
}
if (GUILayout.Button(new GUIContent(ImageManager.Refresh, "Refresh"), EditorStyles.toolbarButton))
{
PlayerPrefs.Save();
PrepareData();
}
if (GUILayout.Button(new GUIContent(ImageManager.Trash, "Delete all"), EditorStyles.toolbarButton))
{
if (EditorUtility.DisplayDialog("Warning!", "Are you sure you want to delete ALL entries from PlayerPrefs?\n\nUse with caution! Unity defined keys are affected too.", "Yes", "No"))
{
PlayerPrefs.DeleteAll();
PrepareData();
GUIUtility.ExitGUI();
}
}
EditorGUIUtility.SetIconSize(new Vector2(0.0f, 0.0f));
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Box(ImageManager.GetOsIcon(), Styles.icon);
GUILayout.TextField(platformPathPrefix + Path.DirectorySeparatorChar + pathToPrefs, GUILayout.MinWidth(200));
GUILayout.EndHorizontal();
scrollPos = GUILayout.BeginScrollView(scrollPos);
serializedObject.Update();
userDefList.DoLayoutList();
serializedObject.ApplyModifiedProperties();
GUILayout.FlexibleSpace();
showSystemGroup = EditorGUILayout.Foldout(showSystemGroup, new GUIContent("Show System"));
if (showSystemGroup)
{
unityDefList.DoLayoutList();
}
GUILayout.EndScrollView();
GUILayout.EndVertical();
GUI.enabled = true;
if (showLoadingIndicatorOverlay)
{
GUILayout.BeginArea(new Rect(position.size.x * 0.5f - 30, position.size.y * 0.5f - 25, 60, 50), GUI.skin.box);
GUILayout.FlexibleSpace();
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
GUILayout.Box(ImageManager.SpinWheelIcons[loadingSpinnerFrame], Styles.icon);
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
GUILayout.Label("Loading");
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
GUILayout.FlexibleSpace();
GUILayout.EndArea();
}
GUI.contentColor = defaultColor;
}
catch (InvalidOperationException)
{ }
}
private void PrepareData(bool reloadKeys = true)
{
prefEntryHolder.ClearLists();
LoadKeys(out userDef, out unityDef, reloadKeys);
CreatePrefEntries(userDef, ref prefEntryHolder.userDefList);
CreatePrefEntries(unityDef, ref prefEntryHolder.unityDefList);
// Clear cache
userDefListCache = new SerializedProperty[prefEntryHolder.userDefList.Count];
}
private void CreatePrefEntries(string[] keySource, ref List<PreferenceEntry> listDest)
{
if (!string.IsNullOrEmpty(searchTxt) && searchfield.SearchMode == MySearchField.SearchModePreferencesEditorWindow.Key)
{
keySource = keySource.Where((keyEntry) => keyEntry.ToLower().Contains(searchTxt.ToLower())).ToArray();
}
foreach (string key in keySource)
{
var entry = new PreferenceEntry();
entry.m_key = key;
string s = PlayerPrefs.GetString(key, ERROR_VALUE_STR);
if (s != ERROR_VALUE_STR)
{
entry.m_strValue = s;
entry.m_typeSelection = PreferenceEntry.PrefTypes.String;
listDest.Add(entry);
continue;
}
float f = PlayerPrefs.GetFloat(key, float.NaN);
if (!float.IsNaN(f))
{
entry.m_floatValue = f;
entry.m_typeSelection = PreferenceEntry.PrefTypes.Float;
listDest.Add(entry);
continue;
}
int i = PlayerPrefs.GetInt(key, ERROR_VALUE_INT);
if (i != ERROR_VALUE_INT)
{
entry.m_intValue = i;
entry.m_typeSelection = PreferenceEntry.PrefTypes.Int;
listDest.Add(entry);
continue;
}
}
if (!string.IsNullOrEmpty(searchTxt) && searchfield.SearchMode == MySearchField.SearchModePreferencesEditorWindow.Value)
{
listDest = listDest.Where((preferenceEntry) => preferenceEntry.ValueAsString().ToLower().Contains(searchTxt.ToLower())).ToList<PreferenceEntry>();
}
switch(sortOrder)
{
case PreferencesEntrySortOrder.Asscending:
listDest.Sort((PreferenceEntry x, PreferenceEntry y) => { return x.m_key.CompareTo(y.m_key); });
break;
case PreferencesEntrySortOrder.Descending:
listDest.Sort((PreferenceEntry x, PreferenceEntry y) => { return y.m_key.CompareTo(x.m_key); });
break;
}
}
private void LoadKeys(out string[] userDef, out string[] unityDef, bool reloadKeys)
{
string[] keys = entryAccessor.GetKeys(reloadKeys);
//keys.ToList().ForEach( e => { Debug.Log(e); } );
// Seperate keys int unity defined and user defined
Dictionary<bool, List<string>> groups = keys
.GroupBy( (key) => key.StartsWith("unity.") || key.StartsWith("UnityGraphicsQuality") )
.ToDictionary( (g) => g.Key, (g) => g.ToList() );
unityDef = (groups.ContainsKey(true)) ? groups[true].ToArray() : new string[0];
userDef = (groups.ContainsKey(false)) ? groups[false].ToArray() : new string[0];
}
private SerializedProperty GetUserDefListElementAtIndex(int index, SerializedProperty ListProperty)
{
UnityEngine.Assertions.Assert.IsTrue(ListProperty.isArray, "Given 'ListProperts' is not type of array");
if (userDefListCache[index] == null)
{
userDefListCache[index] = ListProperty.GetArrayElementAtIndex(index);
}
return userDefListCache[index];
}
#if (UNITY_EDITOR_LINUX || UNITY_EDITOR_OSX)
private string MakeValidFileName(string unsafeFileName)
{
string normalizedFileName = unsafeFileName.Trim().Normalize(NormalizationForm.FormD);
StringBuilder stringBuilder = new StringBuilder();
// We need to use a TextElementEmumerator in order to support UTF16 characters that may take up more than one char(case 1169358)
TextElementEnumerator charEnum = StringInfo.GetTextElementEnumerator(normalizedFileName);
while (charEnum.MoveNext())
{
string c = charEnum.GetTextElement();
if (c.Length == 1 && invalidFilenameChars.Contains(c[0]))
{
stringBuilder.Append('_');
continue;
}
UnicodeCategory unicodeCategory = CharUnicodeInfo.GetUnicodeCategory(c, 0);
if (unicodeCategory != UnicodeCategory.NonSpacingMark)
stringBuilder.Append(c);
}
return stringBuilder.ToString().Normalize(NormalizationForm.FormC);
}
#endif
}
}
public class MySearchField : SearchField
{
public enum SearchModePreferencesEditorWindow { Key, Value }
public SearchModePreferencesEditorWindow SearchMode { get; private set; }
public Action DropdownSelectionDelegate;
public new string OnGUI(
Rect rect,
string text,
GUIStyle style,
GUIStyle cancelButtonStyle,
GUIStyle emptyCancelButtonStyle)
{
style.padding.left = 17;
Rect ContextMenuRect = new Rect(rect.x, rect.y, 10, rect.height);
// Add interactive area
EditorGUIUtility.AddCursorRect(ContextMenuRect, MouseCursor.Text);
if (Event.current.type == EventType.MouseDown && ContextMenuRect.Contains(Event.current.mousePosition))
{
void OnDropdownSelection(object parameter)
{
SearchMode = (SearchModePreferencesEditorWindow) Enum.Parse(typeof(SearchModePreferencesEditorWindow), parameter.ToString());
DropdownSelectionDelegate();
}
GenericMenu menu = new GenericMenu();
foreach(SearchModePreferencesEditorWindow EnumIt in Enum.GetValues(typeof(SearchModePreferencesEditorWindow)))
{
String EnumName = Enum.GetName(typeof(SearchModePreferencesEditorWindow), EnumIt);
menu.AddItem(new GUIContent(EnumName), SearchMode == EnumIt, OnDropdownSelection, EnumName);
}
menu.DropDown(rect);
}
// Render original search field
String result = base.OnGUI(rect, text, style, cancelButtonStyle, emptyCancelButtonStyle);
// Render additional images
GUIStyle ContexMenuOverlayStyle = GUIStyle.none;
ContexMenuOverlayStyle.contentOffset = new Vector2(9, 5);
GUI.Box(new Rect(rect.x, rect.y, 5, 5), EditorGUIUtility.IconContent("d_ProfilerTimelineDigDownArrow@2x"), ContexMenuOverlayStyle);
if (!HasFocus() && String.IsNullOrEmpty(text))
{
GUI.enabled = false;
GUI.Label(new Rect(rect.x + 14, rect.y, 40, rect.height), Enum.GetName(typeof(SearchModePreferencesEditorWindow), SearchMode));
GUI.enabled = true;
}
ContexMenuOverlayStyle.contentOffset = new Vector2();
return result;
}
public new string OnToolbarGUI(string text, params GUILayoutOption[] options) => this.OnToolbarGUI(GUILayoutUtility.GetRect(29f, 200f, 18f, 18f, EditorStyles.toolbarSearchField, options), text);
public new string OnToolbarGUI(Rect rect, string text) => this.OnGUI(rect, text, EditorStyles.toolbarSearchField, EditorStyles.toolbarButton, EditorStyles.toolbarButton);
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 13c94fa190e7e6f4690cadc347a312aa
timeCreated: 1496263475
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,364 @@
/*
* Thanks to gr0ss for the inspiration.
*
* https://github.com/gr0ss/RegistryMonitor
*
* 11/08/2019
*/
using System;
using System.ComponentModel;
using System.IO;
using System.Threading;
using System.Runtime.InteropServices;
using Microsoft.Win32;
namespace BgTools.PlayerPrefsEditor
{
public class RegistryMonitor : IDisposable
{
#region P/Invoke
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern int RegOpenKeyEx(IntPtr hKey, string subKey, uint options, int samDesired, out IntPtr phkResult);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern int RegNotifyChangeKeyValue(IntPtr hKey, bool bWatchSubtree, RegChangeNotifyFilter dwNotifyFilter, IntPtr hEvent, bool fAsynchronous);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern int RegCloseKey(IntPtr hKey);
private const int KEY_QUERY_VALUE = 0x0001;
private const int KEY_NOTIFY = 0x0010;
private const int STANDARD_RIGHTS_READ = 0x00020000;
private static readonly IntPtr HKEY_CLASSES_ROOT = new IntPtr(unchecked((int)0x80000000));
private static readonly IntPtr HKEY_CURRENT_USER = new IntPtr(unchecked((int)0x80000001));
private static readonly IntPtr HKEY_LOCAL_MACHINE = new IntPtr(unchecked((int)0x80000002));
private static readonly IntPtr HKEY_USERS = new IntPtr(unchecked((int)0x80000003));
private static readonly IntPtr HKEY_PERFORMANCE_DATA = new IntPtr(unchecked((int)0x80000004));
private static readonly IntPtr HKEY_CURRENT_CONFIG = new IntPtr(unchecked((int)0x80000005));
private static readonly IntPtr HKEY_DYN_DATA = new IntPtr(unchecked((int)0x80000006));
#endregion
#region Event handling
/// <summary>
/// Occurs when the specified registry key has changed.
/// </summary>
public event EventHandler RegChanged;
/// <summary>
/// Raises the <see cref="RegChanged"/> event.
/// </summary>
/// <remarks>
/// <p>
/// <b>OnRegChanged</b> is called when the specified registry key has changed.
/// </p>
/// <note type="inheritinfo">
/// When overriding <see cref="OnRegChanged"/> in a derived class, be sure to call
/// the base class's <see cref="OnRegChanged"/> method.
/// </note>
/// </remarks>
protected virtual void OnRegChanged()
{
EventHandler handler = RegChanged;
if (handler != null)
handler(this, null);
}
/// <summary>
/// Occurs when the access to the registry fails.
/// </summary>
public event ErrorEventHandler Error;
/// <summary>
/// Raises the <see cref="Error"/> event.
/// </summary>
/// <param name="e">The <see cref="Exception"/> which occured while watching the registry.</param>
/// <remarks>
/// <p>
/// <b>OnError</b> is called when an exception occurs while watching the registry.
/// </p>
/// <note type="inheritinfo">
/// When overriding <see cref="OnError"/> in a derived class, be sure to call
/// the base class's <see cref="OnError"/> method.
/// </note>
/// </remarks>
protected virtual void OnError(Exception e)
{
ErrorEventHandler handler = Error;
if (handler != null)
handler(this, new ErrorEventArgs(e));
}
#endregion
#region Private member variables
private IntPtr _registryHive;
private string _registrySubName;
private object _threadLock = new object();
private Thread _thread;
private bool _disposed = false;
private ManualResetEvent _eventTerminate = new ManualResetEvent(false);
private RegChangeNotifyFilter _regFilter = RegChangeNotifyFilter.Key | RegChangeNotifyFilter.Attribute | RegChangeNotifyFilter.Value | RegChangeNotifyFilter.Security;
#endregion
/// <summary>
/// Initializes a new instance of the <see cref="RegistryMonitor"/> class.
/// </summary>
/// <param name="registryKey">The registry key to monitor.</param>
public RegistryMonitor(RegistryKey registryKey)
{
InitRegistryKey(registryKey.Name);
}
/// <summary>
/// Initializes a new instance of the <see cref="RegistryMonitor"/> class.
/// </summary>
/// <param name="name">The name.</param>
public RegistryMonitor(string name)
{
if (name == null || name.Length == 0)
throw new ArgumentNullException("name");
InitRegistryKey(name);
}
/// <summary>
/// Initializes a new instance of the <see cref="RegistryMonitor"/> class.
/// </summary>
/// <param name="registryHive">The registry hive.</param>
/// <param name="subKey">The sub key.</param>
public RegistryMonitor(RegistryHive registryHive, string subKey)
{
InitRegistryKey(registryHive, subKey);
}
/// <summary>
/// Disposes this object.
/// </summary>
public void Dispose()
{
Stop();
_disposed = true;
GC.SuppressFinalize(this);
}
/// <summary>
/// Gets or sets the <see cref="RegChangeNotifyFilter">RegChangeNotifyFilter</see>.
/// </summary>
public RegChangeNotifyFilter RegChangeNotifyFilter
{
get { return _regFilter; }
set
{
lock (_threadLock)
{
if (IsMonitoring)
throw new InvalidOperationException("Monitoring thread is already running");
_regFilter = value;
}
}
}
#region Initialization
private void InitRegistryKey(RegistryHive hive, string name)
{
switch (hive)
{
case RegistryHive.ClassesRoot:
_registryHive = HKEY_CLASSES_ROOT;
break;
case RegistryHive.CurrentConfig:
_registryHive = HKEY_CURRENT_CONFIG;
break;
case RegistryHive.CurrentUser:
_registryHive = HKEY_CURRENT_USER;
break;
case RegistryHive.DynData:
_registryHive = HKEY_DYN_DATA;
break;
case RegistryHive.LocalMachine:
_registryHive = HKEY_LOCAL_MACHINE;
break;
case RegistryHive.PerformanceData:
_registryHive = HKEY_PERFORMANCE_DATA;
break;
case RegistryHive.Users:
_registryHive = HKEY_USERS;
break;
default:
throw new InvalidEnumArgumentException("hive", (int)hive, typeof(RegistryHive));
}
_registrySubName = name;
}
private void InitRegistryKey(string name)
{
string[] nameParts = name.Split('\\');
switch (nameParts[0])
{
case "HKEY_CLASSES_ROOT":
case "HKCR":
_registryHive = HKEY_CLASSES_ROOT;
break;
case "HKEY_CURRENT_USER":
case "HKCU":
_registryHive = HKEY_CURRENT_USER;
break;
case "HKEY_LOCAL_MACHINE":
case "HKLM":
_registryHive = HKEY_LOCAL_MACHINE;
break;
case "HKEY_USERS":
_registryHive = HKEY_USERS;
break;
case "HKEY_CURRENT_CONFIG":
_registryHive = HKEY_CURRENT_CONFIG;
break;
default:
_registryHive = IntPtr.Zero;
throw new ArgumentException("The registry hive '" + nameParts[0] + "' is not supported", "value");
}
_registrySubName = String.Join("\\", nameParts, 1, nameParts.Length - 1);
}
#endregion
/// <summary>
/// <b>true</b> if this <see cref="RegistryMonitor"/> object is currently monitoring;
/// otherwise, <b>false</b>.
/// </summary>
public bool IsMonitoring
{
get { return _thread != null; }
}
/// <summary>
/// Start monitoring.
/// </summary>
public void Start()
{
if (_disposed)
throw new ObjectDisposedException(null, "This instance is already disposed");
lock (_threadLock)
{
if (!IsMonitoring)
{
_eventTerminate.Reset();
_thread = new Thread(new ThreadStart(MonitorThread)) { IsBackground = true };
_thread.Start();
}
}
}
/// <summary>
/// Stops the monitoring thread.
/// </summary>
public void Stop()
{
if (_disposed)
throw new ObjectDisposedException(null, "This instance is already disposed");
lock (_threadLock)
{
Thread thread = _thread;
if (thread != null)
{
_eventTerminate.Set();
thread.Join();
}
}
}
private void MonitorThread()
{
try
{
ThreadLoop();
}
catch (Exception e)
{
OnError(e);
}
_thread = null;
}
private void ThreadLoop()
{
IntPtr registryKey;
int result = RegOpenKeyEx(_registryHive, _registrySubName, 0, STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | KEY_NOTIFY, out registryKey);
if (result != 0)
{
throw new Win32Exception(result);
}
try
{
AutoResetEvent _eventNotify = new AutoResetEvent(false);
WaitHandle[] waitHandles = new WaitHandle[] { _eventNotify, _eventTerminate };
while (!_eventTerminate.WaitOne(0, true))
{
result = RegNotifyChangeKeyValue(registryKey, true, _regFilter, _eventNotify.SafeWaitHandle.DangerousGetHandle(), true);
if (result != 0)
{
throw new Win32Exception(result);
}
if (WaitHandle.WaitAny(waitHandles) == 0)
{
OnRegChanged();
}
}
}
finally
{
if (registryKey != IntPtr.Zero)
{
RegCloseKey(registryKey);
}
}
}
}
/// <summary>
/// Filter for notifications reported by <see cref="RegistryMonitor"/>.
/// </summary>
[Flags]
public enum RegChangeNotifyFilter
{
/// <summary>Notify the caller if a subkey is added or deleted.</summary>
Key = 1,
/// <summary>Notify the caller of changes to the attributes of the key,
/// such as the security descriptor information.</summary>
Attribute = 2,
/// <summary>Notify the caller of changes to a value of the key. This can
/// include adding or deleting a value, or changing an existing value.</summary>
Value = 4,
/// <summary>Notify the caller of changes to the security descriptor
/// of the key.</summary>
Security = 8,
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9c38f17e357d98d4296b689ae716240b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,109 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace BgTools.Utils
{
public class Styles
{
#region Colors
public class Colors {
public static Color DarkGray = new Color(0.09f, 0.09f, 0.09f);
public static Color LightGray = new Color(0.65f, 0.65f, 0.65f);
public static Color Red = new Color(1.00f, 0.00f, 0.00f);
public static Color Yellow = new Color(1.00f, 1.00f, 0.00f);
public static Color Blue = new Color(0.00f, 0.63f, 0.99f);
}
#endregion // Colors
#region Texture manager
static Dictionary<long, Texture2D> mTextures = new Dictionary<long, Texture2D>();
public static Texture2D GetTexture(long pColorRGBA)
{
if (mTextures.ContainsKey(pColorRGBA) && mTextures[pColorRGBA] != null)
return mTextures[pColorRGBA];
Color32 c = GetColor(pColorRGBA);
var texture = new Texture2D(4, 4);
for (int x = 0; x < 4; x++)
for (int y = 0; y < 4; y++)
texture.SetPixel(x, y, c);
texture.Apply();
texture.Compress(true);
mTextures[pColorRGBA] = texture;
return texture;
}
private static Color32 GetColor(long pColorRGBA)
{
byte r = (byte)((pColorRGBA & 0xff000000) >> 24);
byte g = (byte)((pColorRGBA & 0xff0000) >> 16);
byte b = (byte)((pColorRGBA & 0xff00) >> 8);
byte a = (byte)((pColorRGBA & 0xff));
Color32 c = new Color32(r, g, b, a);
return c;
}
#endregion Texture manager
static GUIStyle mHSeparator;
private static GUIStyle hSeparator
{
get
{
if (mHSeparator == null)
{
mHSeparator = new GUIStyle();
mHSeparator.alignment = TextAnchor.MiddleCenter;
mHSeparator.stretchWidth = true;
mHSeparator.fixedHeight = 1;
mHSeparator.margin = new RectOffset(20, 20, 5, 5);
mHSeparator.normal.background = (EditorGUIUtility.isProSkin) ? GetTexture(0xb5b5b5ff) : GetTexture(0x000000ff);
}
return mHSeparator;
}
}
public static void HorizontalSeparator()
{
GUILayout.Label("", hSeparator);
}
static GUIStyle Icon;
public static GUIStyle icon
{
get
{
if (Icon == null)
{
Icon = new GUIStyle();
Icon.fixedWidth = 15.0f;
Icon.fixedHeight = 15.0f;
Icon.margin = new RectOffset(2, 2, 2, 2);
}
return Icon;
}
}
static GUIStyle MiniButton;
public static GUIStyle miniButton
{
get
{
if (MiniButton == null)
{
MiniButton = new GUIStyle(GUI.skin.button);
MiniButton.fixedWidth = 15.0f;
MiniButton.fixedHeight = 15.0f;
MiniButton.margin = new RectOffset(2, 2, 2, 2);
MiniButton.padding = new RectOffset(2, 2, 2, 2);
}
return MiniButton;
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: be53f59c705f7434a9d6581d0746990f
timeCreated: 1496670894
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,16 @@
{
"name": "Unity.PlayerPrefsEditor.Editor",
"references": [
"Unity.PlayerPrefsEditor.EditorResources"
],
"optionalUnityReferences": [],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": []
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 516df2812c38a7348b10d202b71bf483
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: