#if UNITY_EDITOR using System; using System.Collections.Generic; using System.IO; using System.Text.RegularExpressions; using UnityEditor; using UnityEngine; using UnitySettingsProviderAttribute = UnityEditor.SettingsProviderAttribute; using UnitySettingsProvider = UnityEditor.SettingsProvider; using FishNet.Configuring; using System.Linq; namespace FishNet.Editing.PrefabCollectionGenerator { internal static class SettingsProvider { private static readonly Regex SlashRegex = new Regex(@"[\\//]"); private static PrefabGeneratorConfigurations _settings; private static GUIContent _folderIcon; private static GUIContent _deleteIcon; private static Vector2 _scrollVector; private static bool _showFolders; [UnitySettingsProvider] private static UnitySettingsProvider Create() { return new UnitySettingsProvider("Project/Fish-Networking/Prefab Objects Generator", SettingsScope.Project) { label = "Prefab Objects Generator", guiHandler = OnGUI, keywords = new string[] { "Fish", "Networking", "Prefab", "Objects", "Generator", }, }; } private static void OnGUI(string searchContext) { if (_settings == null) _settings = Configuration.Configurations.PrefabGenerator; if (_folderIcon == null) _folderIcon = EditorGUIUtility.IconContent("d_FolderOpened Icon"); if (_deleteIcon == null) _deleteIcon = EditorGUIUtility.IconContent("P4_DeletedLocal"); EditorGUI.BeginChangeCheck(); GUIStyle scrollViewStyle = new GUIStyle() { padding = new RectOffset(10, 10, 10, 10), }; _scrollVector = EditorGUILayout.BeginScrollView(_scrollVector, scrollViewStyle); _settings.Enabled = EditorGUILayout.Toggle(ObjectNames.NicifyVariableName(nameof(_settings.Enabled)), _settings.Enabled); _settings.LogToConsole = EditorGUILayout.Toggle(ObjectNames.NicifyVariableName(nameof(_settings.LogToConsole)), _settings.LogToConsole); _settings.FullRebuild = EditorGUILayout.Toggle(ObjectNames.NicifyVariableName(nameof(_settings.FullRebuild)), _settings.FullRebuild); _settings.SaveChanges = EditorGUILayout.Toggle(ObjectNames.NicifyVariableName(nameof(_settings.SaveChanges)), _settings.SaveChanges); GUILayoutOption iconWidthConstraint = GUILayout.MaxWidth(32.0f); GUILayoutOption iconHeightConstraint = GUILayout.MaxHeight(EditorGUIUtility.singleLineHeight); EditorGUILayout.BeginHorizontal(); string oldAssetPath = _settings.DefaultPrefabObjectsPath; string newAssetPath = EditorGUILayout.DelayedTextField(ObjectNames.NicifyVariableName(nameof(_settings.DefaultPrefabObjectsPath)), oldAssetPath); if (GUILayout.Button(_folderIcon, iconWidthConstraint, iconHeightConstraint)) { if (TrySaveFilePathInsideAssetsFolder(null, Application.dataPath, "DefaultPrefabObjects", "asset", out string result)) newAssetPath = result; else EditorWindow.focusedWindow.ShowNotification(new GUIContent($"{ObjectNames.NicifyVariableName(nameof(_settings.DefaultPrefabObjectsPath))} must be inside the Assets folder.")); } if (!newAssetPath.Equals(oldAssetPath, StringComparison.OrdinalIgnoreCase)) { if (newAssetPath.StartsWith($"Assets{Path.DirectorySeparatorChar}", StringComparison.OrdinalIgnoreCase)) { if (File.Exists(newAssetPath)) { EditorWindow.focusedWindow.ShowNotification(new GUIContent("Another asset already exists at the new path.")); } else { Generator.IgnorePostProcess = true; if (File.Exists(oldAssetPath)) AssetDatabase.MoveAsset(oldAssetPath, newAssetPath); _settings.DefaultPrefabObjectsPath = newAssetPath; Generator.IgnorePostProcess = false; } } else { EditorWindow.focusedWindow.ShowNotification(new GUIContent($"{ObjectNames.NicifyVariableName(nameof(_settings.DefaultPrefabObjectsPath))} must be inside the Assets folder.")); } } EditorGUILayout.EndHorizontal(); int currentSearchScope = _settings.SearchScope; SearchScopeType searchScopeType = (SearchScopeType)EditorGUILayout.EnumPopup(ValueToSearchScope(_settings.SearchScope)); _settings.SearchScope = (int)searchScopeType; SearchScopeType ValueToSearchScope(int value) => (SearchScopeType)value; if (_settings.SearchScope == (int)SearchScopeType.EntireProject) { EditorGUILayout.HelpBox("Searching the entire project for prefabs can become very slow. Consider switching the search scope to specific folders instead.", MessageType.Warning); if (GUILayout.Button("Switch")) _settings.SearchScope = (int)SearchScopeType.SpecificFolders; } //If search scope changed then update prefabs. if (currentSearchScope != _settings.SearchScope && (SearchScopeType)_settings.SearchScope == SearchScopeType.EntireProject) Generator.GenerateFull(); List folders = null; string foldersName = null; if (_settings.SearchScope == (int)SearchScopeType.EntireProject) { folders = _settings.ExcludedFolders; foldersName = ObjectNames.NicifyVariableName(nameof(_settings.ExcludedFolders)); } else if (_settings.SearchScope == (int)SearchScopeType.SpecificFolders) { folders = _settings.IncludedFolders; foldersName = ObjectNames.NicifyVariableName(nameof(_settings.IncludedFolders)); } string folderName = foldersName.Substring(0, foldersName.Length - 1); if ((_showFolders = EditorGUILayout.Foldout(_showFolders, $"{foldersName} ({folders.Count})")) && folders != null) { EditorGUI.indentLevel++; for (int i = 0; i < folders.Count; i++) { EditorGUILayout.BeginHorizontal(); string oldFolder = folders[i]; string newFolder = SlashRegex.Replace(EditorGUILayout.DelayedTextField(oldFolder), Path.DirectorySeparatorChar.ToString()); if (!newFolder.Equals(oldFolder, StringComparison.OrdinalIgnoreCase)) { if (newFolder.StartsWith($"Assets{Path.DirectorySeparatorChar}", StringComparison.OrdinalIgnoreCase)) folders[i] = newFolder; else EditorWindow.focusedWindow.ShowNotification(new GUIContent($"{folderName} must be inside the Assets folder.")); } if (GUILayout.Button(_folderIcon, iconWidthConstraint, iconHeightConstraint)) { if (TryOpenFolderPathInsideAssetsFolder(null, Application.dataPath, null, out string result)) folders[i] = result; else EditorWindow.focusedWindow.ShowNotification(new GUIContent($"{folderName} must be inside the Assets folder.")); } if (GUILayout.Button(_deleteIcon, iconWidthConstraint, iconHeightConstraint)) folders.RemoveAt(i); EditorGUILayout.EndHorizontal(); } EditorGUI.indentLevel--; if (_settings.SearchScope == (int)SearchScopeType.SpecificFolders) EditorGUILayout.HelpBox("You can include subfolders by appending an asterisk (*) to a path.", MessageType.None); if (GUILayout.Button("Browse")) { if (TryOpenFolderPathInsideAssetsFolder(null, Application.dataPath, null, out string result)) { folders.Add(result); } else { EditorWindow.focusedWindow.ShowNotification(new GUIContent($"{folderName} must be inside the Assets folder.")); } } } if (EditorGUI.EndChangeCheck()) Configuration.Configurations.Write(true); if (GUILayout.Button("Generate")) Generator.GenerateFull(); EditorGUILayout.HelpBox("Consider pressing 'Generate' after changing the settings.", MessageType.Info); EditorGUILayout.EndScrollView(); } private static bool TrySaveFilePathInsideAssetsFolder(string title, string directory, string name, string extension, out string result) { result = null; string selectedPath = EditorUtility.SaveFilePanel(title, directory, name, extension); if (selectedPath.StartsWith(Application.dataPath, StringComparison.OrdinalIgnoreCase)) { result = SlashRegex.Replace(selectedPath.Remove(0, Path.GetDirectoryName(Application.dataPath).Length + 1), Path.DirectorySeparatorChar.ToString()); return true; } return false; } private static bool TryOpenFolderPathInsideAssetsFolder(string title, string folder, string name, out string result) { result = null; string selectedPath = EditorUtility.OpenFolderPanel(title, folder, name); if (selectedPath.StartsWith(Application.dataPath, StringComparison.OrdinalIgnoreCase)) { result = SlashRegex.Replace(selectedPath.Remove(0, Path.GetDirectoryName(Application.dataPath).Length + 1), Path.DirectorySeparatorChar.ToString()); return true; } return false; } } } #endif