Upgrade
This commit is contained in:
9
Assets/PlayerPrefsEditor/Editor/Dialogs.meta
Normal file
9
Assets/PlayerPrefsEditor/Editor/Dialogs.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2c61139fc58134242bb4b9e6d9fabdc0
|
||||
folderAsset: yes
|
||||
timeCreated: 1502815237
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
139
Assets/PlayerPrefsEditor/Editor/Dialogs/TextFieldDialog.cs
Normal file
139
Assets/PlayerPrefsEditor/Editor/Dialogs/TextFieldDialog.cs
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 380be3677d2e95144863ee00c051c1f2
|
||||
timeCreated: 1500849296
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
64
Assets/PlayerPrefsEditor/Editor/Dialogs/TextValidator.cs
Normal file
64
Assets/PlayerPrefsEditor/Editor/Dialogs/TextValidator.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4202eaaf18e2e43438f2f3632b252393
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
9
Assets/PlayerPrefsEditor/Editor/Extensions.meta
Normal file
9
Assets/PlayerPrefsEditor/Editor/Extensions.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5c7bb3ee5362c0a40a707ade01e79972
|
||||
folderAsset: yes
|
||||
timeCreated: 1502876479
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
90
Assets/PlayerPrefsEditor/Editor/Extensions/CenterOnWindow.cs
Normal file
90
Assets/PlayerPrefsEditor/Editor/Extensions/CenterOnWindow.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 274db1862ad1a1b4c80a2ed6558e05ec
|
||||
timeCreated: 1502876542
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
9
Assets/PlayerPrefsEditor/Editor/PreferencesEditor.meta
Normal file
9
Assets/PlayerPrefsEditor/Editor/PreferencesEditor.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e93ef5c4e798b034bb024596113459cb
|
||||
folderAsset: yes
|
||||
timeCreated: 1505565882
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2ae9239fbddf12b4099b3cacc5301271
|
||||
timeCreated: 1496684286
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d5d94a5263d6af0478dde8fb08a3dcb7
|
||||
timeCreated: 1500316993
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f54241e622579a145a495df929a9330a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 13c94fa190e7e6f4690cadc347a312aa
|
||||
timeCreated: 1496263475
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9c38f17e357d98d4296b689ae716240b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
109
Assets/PlayerPrefsEditor/Editor/Styles.cs
Normal file
109
Assets/PlayerPrefsEditor/Editor/Styles.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Assets/PlayerPrefsEditor/Editor/Styles.cs.meta
Normal file
12
Assets/PlayerPrefsEditor/Editor/Styles.cs.meta
Normal 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:
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "Unity.PlayerPrefsEditor.Editor",
|
||||
"references": [
|
||||
"Unity.PlayerPrefsEditor.EditorResources"
|
||||
],
|
||||
"optionalUnityReferences": [],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": []
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 516df2812c38a7348b10d202b71bf483
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user