Compare commits

..

3 Commits

Author SHA1 Message Date
b29bd22db0 Translation 2023-11-21 01:20:27 +01:00
858bfaa0ae WIP 2023-11-20 12:29:56 +01:00
7131c14d99 update 2023-11-11 17:04:30 +01:00
100 changed files with 1528 additions and 8995 deletions

View File

@ -1,4 +1,5 @@
using UnityEngine; using UnityEngine;
using UnityEngine.SceneManagement;
namespace NEG.Utils namespace NEG.Utils
{ {
@ -9,3 +10,4 @@ namespace NEG.Utils
#endif #endif
} }
} }

View File

@ -5,8 +5,7 @@ namespace NEG.Utils.Collections
public static class DictionaryExtensions public static class DictionaryExtensions
{ {
/// <summary> /// <summary>
/// Adds given value to a dictionary if there was no element at given <paramref name="key" />, replaces element with /// Adds given value to a dictionary if there was no element at given <paramref name="key"/>, replaces element with <paramref name="value"> otherwise.
/// <paramref name="value"> otherwise.
/// </summary> /// </summary>
/// <returns>true if element was added, false if it was replaced</returns> /// <returns>true if element was added, false if it was replaced</returns>
public static bool AddOrUpdate<K, V>(this Dictionary<K, V> dict, K key, V value) public static bool AddOrUpdate<K, V>(this Dictionary<K, V> dict, K key, V value)
@ -16,20 +15,24 @@ namespace NEG.Utils.Collections
dict[key] = value; dict[key] = value;
return false; return false;
} }
else
dict.Add(key, value); {
return true; dict.Add(key, value);
return true;
}
} }
/// <summary> /// <summary>
/// Gets a value from the dictionary under a specified key or adds it if did not exist and returns /// Gets a value from the dictionary under a specified key or adds it if did not exist and returns <paramref name="defaultValue"/>.
/// <paramref name="defaultValue" />.
/// </summary> /// </summary>
/// <returns>value under a given <paramref name="key" /> if it exists, <paramref name="defaultValue" /> otherwise</returns> /// <returns>value under a given <paramref name="key"/> if it exists, <paramref name="defaultValue"/> otherwise</returns>
public static V GetOrSetToDefault<K, V>(this Dictionary<K, V> dict, K key, V defaultValue) public static V GetOrSetToDefault<K, V>(this Dictionary<K, V> dict, K key, V defaultValue)
{ {
if (dict.TryGetValue(key, out var value)) return value; if (dict.TryGetValue(key, out V value))
{
return value;
}
dict.Add(key, defaultValue); dict.Add(key, defaultValue);
return defaultValue; return defaultValue;

View File

@ -6,15 +6,20 @@ namespace NEG.Utils
{ {
public static class CoroutineUtils public static class CoroutineUtils
{ {
private static readonly WaitForEndOfFrame WaitForEndOfFrame = new(); private static readonly WaitForEndOfFrame WaitForEndOfFrame = new WaitForEndOfFrame();
public static IEnumerator WaitForFrames(int count) public static IEnumerator WaitForFrames(int count)
{ {
for (int i = 0; i < count; i++) yield return null; for (int i = 0; i < count; i++)
{
yield return null;
}
} }
public static void ActionAfterFrames(this MonoBehaviour mono, int count, Action action) => public static void ActionAfterFrames(this MonoBehaviour mono, int count, Action action)
{
mono.StartCoroutine(ActionAfterFrames(count, action)); mono.StartCoroutine(ActionAfterFrames(count, action));
}
public static IEnumerator ActionAfterFrames(int count, Action action) public static IEnumerator ActionAfterFrames(int count, Action action)
{ {
@ -22,26 +27,19 @@ namespace NEG.Utils
action?.Invoke(); action?.Invoke();
} }
public static void ActionAfterEndOfFrame(this MonoBehaviour mono, Action action) =>
mono.StartCoroutine(ActionAtNextFrame(action));
public static IEnumerator ActionAfterEndOfFrame(Action action) public static IEnumerator ActionAfterEndOfFrame(Action action)
{ {
yield return WaitForEndOfFrame; yield return WaitForEndOfFrame;
action?.Invoke(); action?.Invoke();
} }
public static void ActionAtNextFrame(this MonoBehaviour mono, Action action) => mono.StartCoroutine(ActionAtNextFrame(action));
public static void ActionAtNextFrame(this MonoBehaviour mono, Action action) =>
mono.StartCoroutine(ActionAtNextFrame(action));
public static IEnumerator ActionAtNextFrame(Action action) public static IEnumerator ActionAtNextFrame(Action action)
{ {
yield return null; yield return null;
action?.Invoke(); action?.Invoke();
} }
public static void ActionAfterTime(this MonoBehaviour mono, float time, Action action) => public static void ActionAfterTime(this MonoBehaviour mono, float time, Action action) => mono.StartCoroutine(ActionAfterTime(time, action));
mono.StartCoroutine(ActionAfterTime(time, action));
public static IEnumerator ActionAfterTime(float time, Action action) public static IEnumerator ActionAfterTime(float time, Action action)
{ {

View File

@ -1,8 +1,8 @@
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using UnityEditor;
using UnityEditor.Build;
using UnityEngine; using UnityEngine;
using UnityEditor;
using UnityEditor.Build.Player;
using Debug = UnityEngine.Debug; using Debug = UnityEngine.Debug;
public static class BuildingUtils public static class BuildingUtils
@ -12,9 +12,9 @@ public static class BuildingUtils
[MenuItem("Tools/PrepareForBuild", priority = -10)] [MenuItem("Tools/PrepareForBuild", priority = -10)]
public static void PrepareForBuild() public static void PrepareForBuild()
{ {
var namedBuildTarget = NamedBuildTarget.FromBuildTargetGroup( var namedBuildTarget = UnityEditor.Build.NamedBuildTarget.FromBuildTargetGroup(
BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget)); BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget));
string[] args = PlayerSettings.GetAdditionalCompilerArguments(namedBuildTarget); var args = PlayerSettings.GetAdditionalCompilerArguments(namedBuildTarget);
var argsList = args.ToList(); var argsList = args.ToList();
argsList.Remove(SteamBuildDefine); argsList.Remove(SteamBuildDefine);
PlayerSettings.SetScriptingDefineSymbols(namedBuildTarget, argsList.ToArray()); PlayerSettings.SetScriptingDefineSymbols(namedBuildTarget, argsList.ToArray());
@ -25,9 +25,9 @@ public static class BuildingUtils
{ {
PrepareForBuild(); PrepareForBuild();
var namedBuildTarget = NamedBuildTarget.FromBuildTargetGroup( var namedBuildTarget = UnityEditor.Build.NamedBuildTarget.FromBuildTargetGroup(
BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget)); BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget));
string[] args = PlayerSettings.GetAdditionalCompilerArguments(namedBuildTarget); var args = PlayerSettings.GetAdditionalCompilerArguments(namedBuildTarget);
var argsList = args.ToList(); var argsList = args.ToList();
argsList.Add(SteamBuildDefine); argsList.Add(SteamBuildDefine);
PlayerSettings.SetScriptingDefineSymbols(namedBuildTarget, argsList.ToArray()); PlayerSettings.SetScriptingDefineSymbols(namedBuildTarget, argsList.ToArray());
@ -37,7 +37,7 @@ public static class BuildingUtils
[MenuItem("Tools/Build/Steam/Release")] [MenuItem("Tools/Build/Steam/Release")]
public static void SteamRelease() public static void SteamRelease()
{ {
if (!CanBuild()) if(!CanBuild())
return; return;
IncreaseBuildNumber(); IncreaseBuildNumber();
@ -48,7 +48,7 @@ public static class BuildingUtils
[MenuItem("Tools/Build/Steam/Development")] [MenuItem("Tools/Build/Steam/Development")]
public static void SteamDevelopment() public static void SteamDevelopment()
{ {
if (!CanBuild()) if(!CanBuild())
return; return;
IncreaseBuildNumber(); IncreaseBuildNumber();
@ -57,47 +57,26 @@ public static class BuildingUtils
} }
[MenuItem("Tools/Build/Steam/Demo")]
public static void SteamDemo()
{
if (!CanBuild())
return;
IncreaseBuildNumber();
BuildDemo();
UploadSteam(true);
}
[MenuItem("Tools/Build/All Release")] [MenuItem("Tools/Build/All Release")]
public static void BuildRelease() public static void BuildRelease()
{ {
if (!CanBuild()) if(!CanBuild())
return; return;
BuildWindowsRelease(); BuildWindowsRelease();
BuildLinuxRelease();
} }
[MenuItem("Tools/Build/All Development")] [MenuItem("Tools/Build/All Development")]
public static void BuildDevelopment() public static void BuildDevelopment()
{ {
if (!CanBuild()) if(!CanBuild())
return; return;
BuildWindowsDevelopment(); BuildWindowsDevelopment();
} }
[MenuItem("Tools/Build/All Demo")]
public static void BuildDemo()
{
if (!CanBuild())
return;
BuildWindows(true, new[] { "DEMO" });
BuildLinux(true, new[] { "DEMO" });
}
[MenuItem("Tools/Build/Platform/Windows/x64-Development")] [MenuItem("Tools/Build/Platform/Windows/x64-Development")]
public static void BuildWindowsDevelopment() public static void BuildWindowsDevelopment()
{ {
if (!CanBuild()) if(!CanBuild())
return; return;
BuildWindows(false); BuildWindows(false);
} }
@ -105,20 +84,11 @@ public static class BuildingUtils
[MenuItem("Tools/Build/Platform/Windows/x64-Release")] [MenuItem("Tools/Build/Platform/Windows/x64-Release")]
public static void BuildWindowsRelease() public static void BuildWindowsRelease()
{ {
if (!CanBuild()) if(!CanBuild())
return; return;
BuildWindows(true); BuildWindows(true);
} }
[MenuItem("Tools/Build/Platform/Linux/x64-Release")]
public static void BuildLinuxRelease()
{
if (!CanBuild())
return;
BuildLinux(true);
}
[MenuItem("Tools/Build/Platform/Android/GooglePlay")] [MenuItem("Tools/Build/Platform/Android/GooglePlay")]
public static void BuildGooglePlay() public static void BuildGooglePlay()
{ {
@ -128,7 +98,9 @@ public static class BuildingUtils
var buildPlayerOptions = new BuildPlayerOptions { scenes = new string[EditorBuildSettings.scenes.Length] }; var buildPlayerOptions = new BuildPlayerOptions { scenes = new string[EditorBuildSettings.scenes.Length] };
for (int i = 0; i < EditorBuildSettings.scenes.Length; i++) for (int i = 0; i < EditorBuildSettings.scenes.Length; i++)
{
buildPlayerOptions.scenes[i] = EditorBuildSettings.scenes[i].path; buildPlayerOptions.scenes[i] = EditorBuildSettings.scenes[i].path;
}
buildPlayerOptions.target = BuildTarget.Android; buildPlayerOptions.target = BuildTarget.Android;
buildPlayerOptions.options = BuildOptions.None; buildPlayerOptions.options = BuildOptions.None;
@ -137,13 +109,13 @@ public static class BuildingUtils
BuildPipeline.BuildPlayer(buildPlayerOptions); BuildPipeline.BuildPlayer(buildPlayerOptions);
} }
private static void BuildWindows(bool release, string[] additionalDefines = default) private static void BuildWindows(bool release)
{ {
var buildPlayerOptions = new BuildPlayerOptions { scenes = new string[EditorBuildSettings.scenes.Length] }; var buildPlayerOptions = new BuildPlayerOptions { scenes = new string[EditorBuildSettings.scenes.Length] };
for (int i = 0; i < EditorBuildSettings.scenes.Length; i++) for (int i = 0; i < EditorBuildSettings.scenes.Length; i++)
{
buildPlayerOptions.scenes[i] = EditorBuildSettings.scenes[i].path; buildPlayerOptions.scenes[i] = EditorBuildSettings.scenes[i].path;
}
buildPlayerOptions.extraScriptingDefines = additionalDefines;
buildPlayerOptions.target = BuildTarget.StandaloneWindows64; buildPlayerOptions.target = BuildTarget.StandaloneWindows64;
buildPlayerOptions.options = release ? BuildOptions.None : BuildOptions.Development; buildPlayerOptions.options = release ? BuildOptions.None : BuildOptions.Development;
@ -152,48 +124,26 @@ public static class BuildingUtils
BuildPipeline.BuildPlayer(buildPlayerOptions); BuildPipeline.BuildPlayer(buildPlayerOptions);
} }
private static void BuildLinux(bool release, string[] additionalDefines = default)
{
var buildPlayerOptions = new BuildPlayerOptions { scenes = new string[EditorBuildSettings.scenes.Length] };
for (int i = 0; i < EditorBuildSettings.scenes.Length; i++)
buildPlayerOptions.scenes[i] = EditorBuildSettings.scenes[i].path;
buildPlayerOptions.extraScriptingDefines = additionalDefines;
buildPlayerOptions.target = BuildTarget.StandaloneLinux64;
buildPlayerOptions.options = release ? BuildOptions.None : BuildOptions.Development;
buildPlayerOptions.locationPathName = Application.dataPath +
$"/../../{Application.productName}-Steam/ContentBuilder/content/linux/{Application.productName}.x86_64";
BuildPipeline.BuildPlayer(buildPlayerOptions);
}
private static void IncreaseBuildNumber() private static void IncreaseBuildNumber()
{ {
string[] versionParts = PlayerSettings.bundleVersion.Split('.'); string[] versionParts = PlayerSettings.bundleVersion.Split('.');
if (versionParts.Length != 3 || !int.TryParse(versionParts[2], out int version)) if (versionParts.Length != 3 || !int.TryParse(versionParts[2], out int version)) {
{
Debug.LogError("IncreaseBuildNumber failed to update version " + PlayerSettings.bundleVersion); Debug.LogError("IncreaseBuildNumber failed to update version " + PlayerSettings.bundleVersion);
return; return;
} }
versionParts[2] = (version + 1).ToString(); versionParts[2] = (version + 1).ToString();
PlayerSettings.bundleVersion = string.Join(".", versionParts); PlayerSettings.bundleVersion = string.Join(".", versionParts);
} }
private static void UploadSteam(bool demo = false) private static void UploadSteam()
{ {
string command = string command = $"cd {Application.dataPath}/../../{Application.productName}-Steam/ContentBuilder && run_build.bat";
$"cd {Application.dataPath}/../../{Application.productName}-Steam/ContentBuilder && push_build.bat";
if (demo)
{
command =
$"cd {Application.dataPath}/../../{Application.productName}-Steam/ContentBuilder && push_demo.bat";
}
var processInfo = new ProcessStartInfo("cmd.exe", $"/c {command}") var processInfo = new ProcessStartInfo("cmd.exe", $"/c {command}")
{ {
CreateNoWindow = true, UseShellExecute = false CreateNoWindow = true,
}; UseShellExecute = false
};
var process = Process.Start(processInfo); var process = Process.Start(processInfo);
process.WaitForExit(); process.WaitForExit();
Debug.Log(process.ExitCode); Debug.Log(process.ExitCode);
@ -204,16 +154,15 @@ public static class BuildingUtils
{ {
if (CanBuildUtil()) if (CanBuildUtil())
return true; return true;
Debug.LogError( Debug.LogError("Cannot build with defines set in project, please use PrepareForBuild and wait for scripts recompilation");
"Cannot build with defines set in project, please use PrepareForBuild and wait for scripts recompilation");
return false; return false;
} }
private static bool CanBuildUtil() private static bool CanBuildUtil()
{ {
var namedBuildTarget = NamedBuildTarget.FromBuildTargetGroup( var namedBuildTarget = UnityEditor.Build.NamedBuildTarget.FromBuildTargetGroup(
BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget)); BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget));
string[] args = PlayerSettings.GetAdditionalCompilerArguments(namedBuildTarget); var args = PlayerSettings.GetAdditionalCompilerArguments(namedBuildTarget);
var argsList = args.ToList(); var argsList = args.ToList();
if (argsList.Contains(SteamBuildDefine)) if (argsList.Contains(SteamBuildDefine))

View File

@ -8,19 +8,14 @@ namespace NEG.Utils.Editor.ComponentsAdditionalItems
{ {
[MenuItem("CONTEXT/CanvasScaler/Full HD horizontal", false, 2000)] [MenuItem("CONTEXT/CanvasScaler/Full HD horizontal", false, 2000)]
public static void SetFullHdHorizontal(MenuCommand command) => SetComponent(command, 1920, 1080); public static void SetFullHdHorizontal(MenuCommand command) => SetComponent(command, 1920, 1080);
[MenuItem("CONTEXT/CanvasScaler/Full HD vertical", false, 2000)] [MenuItem("CONTEXT/CanvasScaler/Full HD vertical", false, 2000)]
public static void SetFullHdVertical(MenuCommand command) => SetComponent(command, 1080, 1920); public static void SetFullHdVertical(MenuCommand command) => SetComponent(command, 1080, 1920);
[MenuItem("CONTEXT/CanvasScaler/Full 2k horizontal", false, 2000)] [MenuItem("CONTEXT/CanvasScaler/Full 2k horizontal", false, 2000)]
public static void Set2KHorizontal(MenuCommand command) => SetComponent(command, 2560, 1440); public static void Set2KHorizontal(MenuCommand command) => SetComponent(command, 2560, 1440 );
[MenuItem("CONTEXT/CanvasScaler/Full 2k vertical", false, 2000)] [MenuItem("CONTEXT/CanvasScaler/Full 2k vertical", false, 2000)]
public static void Set2KVertical(MenuCommand command) => SetComponent(command, 1440, 2560); public static void Set2KVertical(MenuCommand command) => SetComponent(command, 1440, 2560);
[MenuItem("CONTEXT/CanvasScaler/Full 4k horizontal", false, 2000)] [MenuItem("CONTEXT/CanvasScaler/Full 4k horizontal", false, 2000)]
public static void Set4KHorizontal(MenuCommand command) => SetComponent(command, 3840, 2160); public static void Set4KHorizontal(MenuCommand command) => SetComponent(command, 3840, 2160);
[MenuItem("CONTEXT/CanvasScaler/Full 4k vertical", false, 2000)] [MenuItem("CONTEXT/CanvasScaler/Full 4k vertical", false, 2000)]
public static void Set4KVertical(MenuCommand command) => SetComponent(command, 2160, 3840); public static void Set4KVertical(MenuCommand command) => SetComponent(command, 2160, 3840);

View File

@ -11,26 +11,31 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using UnityEditor;
using UnityEngine; using UnityEngine;
using Debug = UnityEngine.Debug; using UnityEditor;
namespace TheGamedevGuru namespace TheGamedevGuru
{ {
public class EditorInstanceCreator : EditorWindow public class EditorInstanceCreator : EditorWindow
{ {
private string _extraSubdirectories; string _projectInstanceName;
private bool _includeProjectSettings = true; string _extraSubdirectories;
private string _projectInstanceName; bool _includeProjectSettings = true;
private void OnGUI() [MenuItem("Window/The Gamedev Guru/Editor Instance Creator")]
static void Init()
{
((EditorInstanceCreator)EditorWindow.GetWindow(typeof(EditorInstanceCreator))).Show();
}
void OnGUI()
{ {
if (string.IsNullOrEmpty(_projectInstanceName)) if (string.IsNullOrEmpty(_projectInstanceName))
{
_projectInstanceName = PlayerSettings.productName + "_Slave_1"; _projectInstanceName = PlayerSettings.productName + "_Slave_1";
}
EditorGUILayout.Separator(); EditorGUILayout.Separator();
EditorGUILayout.LabelField("The Gamedev Guru - Project Instance Creator"); EditorGUILayout.LabelField("The Gamedev Guru - Project Instance Creator");
@ -48,20 +53,19 @@ namespace TheGamedevGuru
EditorGUILayout.Separator(); EditorGUILayout.Separator();
if (GUILayout.Button("Create")) if (GUILayout.Button("Create"))
{
CreateProjectInstance(_projectInstanceName, _includeProjectSettings, _extraSubdirectories); CreateProjectInstance(_projectInstanceName, _includeProjectSettings, _extraSubdirectories);
}
if (GUILayout.Button("Help")) if (GUILayout.Button("Help"))
{
Application.OpenURL("https://thegamedev.guru/multiple-unity-editor-instances-within-a-single-project/"); Application.OpenURL("https://thegamedev.guru/multiple-unity-editor-instances-within-a-single-project/");
}
} }
[MenuItem("Window/The Gamedev Guru/Editor Instance Creator")] static void CreateProjectInstance(string projectInstanceName, bool includeProjectSettings, string extraSubdirectories)
private static void Init() => ((EditorInstanceCreator)GetWindow(typeof(EditorInstanceCreator))).Show();
private static void CreateProjectInstance(string projectInstanceName, bool includeProjectSettings,
string extraSubdirectories)
{ {
string targetDirectory = Path.Combine(Directory.GetCurrentDirectory(), ".." + Path.DirectorySeparatorChar, var targetDirectory = Path.Combine(Directory.GetCurrentDirectory(), ".." + Path.DirectorySeparatorChar, projectInstanceName);
projectInstanceName);
Debug.Log(targetDirectory); Debug.Log(targetDirectory);
if (Directory.Exists(targetDirectory)) if (Directory.Exists(targetDirectory))
{ {
@ -71,21 +75,29 @@ namespace TheGamedevGuru
Directory.CreateDirectory(targetDirectory); Directory.CreateDirectory(targetDirectory);
var subdirectories = new List<string> { "Assets", "Packages" }; List<string> subdirectories = new List<string>{"Assets", "Packages"};
if (includeProjectSettings) subdirectories.Add("ProjectSettings"); if (includeProjectSettings)
{
subdirectories.Add("ProjectSettings");
}
foreach (string extraSubdirectory in extraSubdirectories.Split(',')) foreach (var extraSubdirectory in extraSubdirectories.Split(','))
{
subdirectories.Add(extraSubdirectory.Trim()); subdirectories.Add(extraSubdirectory.Trim());
}
foreach (string subdirectory in subdirectories) foreach (var subdirectory in subdirectories)
Process.Start("CMD.exe", GetLinkCommand(subdirectory, targetDirectory)); {
System.Diagnostics.Process.Start("CMD.exe",GetLinkCommand(subdirectory, targetDirectory));
}
EditorUtility.RevealInFinder(targetDirectory + Path.DirectorySeparatorChar + "Assets"); EditorUtility.RevealInFinder(targetDirectory + Path.DirectorySeparatorChar + "Assets");
EditorUtility.DisplayDialog("Done!", EditorUtility.DisplayDialog("Done!", $"Done! Feel free to add it as an existing project at: {targetDirectory}", "Ok :)");
$"Done! Feel free to add it as an existing project at: {targetDirectory}", "Ok :)");
} }
private static string GetLinkCommand(string subdirectory, string targetDirectory) => static string GetLinkCommand(string subdirectory, string targetDirectory)
$"/c mklink /J \"{targetDirectory}{Path.DirectorySeparatorChar}{subdirectory}\" \"{Directory.GetCurrentDirectory()}{Path.DirectorySeparatorChar}{subdirectory}\""; {
return $"/c mklink /J \"{targetDirectory}{Path.DirectorySeparatorChar}{subdirectory}\" \"{Directory.GetCurrentDirectory()}{Path.DirectorySeparatorChar}{subdirectory}\"";
}
} }
} }

View File

@ -1,42 +0,0 @@
using System;
using UnityEditor;
namespace NegUtils.Editor
{
public class EditorUtils
{
}
public class AssetPath
{
private readonly string filter;
private string path;
public AssetPath(string filter)
{
this.filter = filter;
TryFindPath();
}
public string Path
{
get
{
if (path != null)
return path;
TryFindPath();
return path;
}
}
private void TryFindPath()
{
string[] candidates = AssetDatabase.FindAssets(filter);
if (candidates.Length == 0)
throw new Exception("Missing layout asset!");
path = AssetDatabase.GUIDToAssetPath(candidates[0]);
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: a4848b69c1024df8ad6f76cfd4415d01
timeCreated: 1698258811

View File

@ -3,16 +3,21 @@ using UnityEngine;
public class GUIDToAssetPath : EditorWindow public class GUIDToAssetPath : EditorWindow
{ {
private string guid = ""; string guid = "";
private string path = ""; string path = "";
[MenuItem("Tools/GUIDToAssetPath")]
static void CreateWindow()
{
GUIDToAssetPath window = (GUIDToAssetPath)EditorWindow.GetWindowWithRect(typeof(GUIDToAssetPath), new Rect(0, 0, 400, 120));
}
private void OnGUI() void OnGUI()
{ {
GUILayout.Label("Enter guid"); GUILayout.Label("Enter guid");
guid = GUILayout.TextField(guid); guid = GUILayout.TextField(guid);
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace(); GUILayout.FlexibleSpace();
if (GUILayout.Button("Get Asset Path", GUILayout.Width(120))) if (GUILayout.Button("Get Asset Path",GUILayout.Width(120)))
path = GetAssetPath(guid); path = GetAssetPath(guid);
GUILayout.FlexibleSpace(); GUILayout.FlexibleSpace();
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
@ -24,14 +29,7 @@ public class GUIDToAssetPath : EditorWindow
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.Label(path); GUILayout.Label(path);
} }
static string GetAssetPath(string guid)
[MenuItem("Tools/GUIDToAssetPath")]
private static void CreateWindow()
{
var window = (GUIDToAssetPath)GetWindowWithRect(typeof(GUIDToAssetPath), new Rect(0, 0, 400, 120));
}
private static string GetAssetPath(string guid)
{ {
guid = guid.Replace("-", ""); guid = guid.Replace("-", "");

View File

@ -1,6 +1,6 @@
using System.IO; using System.IO;
using UnityEditor;
using UnityEngine; using UnityEngine;
using UnityEditor;
namespace NEG.Utils.Editor namespace NEG.Utils.Editor
{ {

View File

@ -1,18 +1,18 @@
{ {
"name": "NEG.Utils.Editor", "name": "NEG.Utils.Editor",
"rootNamespace": "", "rootNamespace": "",
"references": [ "references": [
"GUID:3c4294719a93e3c4e831a9ff0c261e8a" "GUID:3c4294719a93e3c4e831a9ff0c261e8a"
], ],
"includePlatforms": [ "includePlatforms": [
"Editor" "Editor"
], ],
"excludePlatforms": [], "excludePlatforms": [],
"allowUnsafeCode": false, "allowUnsafeCode": false,
"overrideReferences": false, "overrideReferences": false,
"precompiledReferences": [], "precompiledReferences": [],
"autoReferenced": true, "autoReferenced": true,
"defineConstraints": [], "defineConstraints": [],
"versionDefines": [], "versionDefines": [],
"noEngineReferences": false "noEngineReferences": false
} }

View File

@ -1,19 +0,0 @@
using UnityEditor;
using UnityEngine;
namespace NEG.Utils.Editor
{
[CustomPropertyDrawer(typeof(ReadOnlyAttribute))]
public class ReadOnlyPropertyDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
GUI.enabled = false;
EditorGUI.PropertyField(position, property, label, true);
GUI.enabled = true;
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) =>
EditorGUI.GetPropertyHeight(property, label, true);
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 1f837bac3a7b40528454a9fb9a46d0be
timeCreated: 1699371332

View File

@ -1,6 +1,5 @@
using UnityEditor;
using UnityEngine; using UnityEngine;
using UnityEditor;
/// <summary> /// <summary>
/// Drawer for the RequireInterface attribute. /// Drawer for the RequireInterface attribute.
/// </summary> /// </summary>
@ -11,7 +10,7 @@ namespace NEG.Utils
public class RequireInterfaceDrawer : PropertyDrawer public class RequireInterfaceDrawer : PropertyDrawer
{ {
/// <summary> /// <summary>
/// Overrides GUI drawing for the attribute. /// Overrides GUI drawing for the attribute.
/// </summary> /// </summary>
/// <param name="position">Position.</param> /// <param name="position">Position.</param>
/// <param name="property">Property.</param> /// <param name="property">Property.</param>
@ -22,12 +21,11 @@ namespace NEG.Utils
if (property.propertyType == SerializedPropertyType.ObjectReference) if (property.propertyType == SerializedPropertyType.ObjectReference)
{ {
// Get attribute parameters. // Get attribute parameters.
var requiredAttribute = attribute as RequireInterfaceAttribute; var requiredAttribute = this.attribute as RequireInterfaceAttribute;
// Begin drawing property field. // Begin drawing property field.
EditorGUI.BeginProperty(position, label, property); EditorGUI.BeginProperty(position, label, property);
// Draw property field. // Draw property field.
property.objectReferenceValue = EditorGUI.ObjectField(position, label, property.objectReferenceValue, property.objectReferenceValue = EditorGUI.ObjectField(position, label, property.objectReferenceValue, requiredAttribute.requiredType, true);
requiredAttribute.requiredType, true);
// Finish drawing property field. // Finish drawing property field.
EditorGUI.EndProperty(); EditorGUI.EndProperty();
} }

View File

@ -1,4 +1,5 @@
using UnityEditor; using System.IO;
using UnityEditor;
using UnityEngine; using UnityEngine;
namespace NEG.Editor namespace NEG.Editor
@ -13,6 +14,8 @@ namespace NEG.Editor
return; return;
ScreenCapture.CaptureScreenshot(path); ScreenCapture.CaptureScreenshot(path);
} }
} }
} }

View File

@ -1,14 +1,19 @@
using System;
using UnityEditor; using UnityEditor;
namespace NEG.Utils.Serialization namespace NEG.Utils.Serialization
{ {
public static class SerializationExtentions public static class SerializationExtentions
{ {
public static SerializedProperty FindAutoProperty(this SerializedObject @this, string name) => public static SerializedProperty FindAutoProperty(this SerializedObject @this, string name)
@this.FindProperty(GetBackingFieldName(name)); {
return @this.FindProperty(GetBackingFieldName(name));
}
public static SerializedProperty FindAutoPropertyRelative(this SerializedProperty @this, string name) => public static SerializedProperty FindAutoPropertyRelative(this SerializedProperty @this, string name)
@this.FindPropertyRelative(GetBackingFieldName(name)); {
return @this.FindPropertyRelative(GetBackingFieldName(name));
}
public static string GetBackingFieldName(string name) public static string GetBackingFieldName(string name)
{ {

View File

@ -1,4 +1,5 @@
using System.IO; using System;
using System.IO;
using UnityEditor; using UnityEditor;
using UnityEditor.SceneManagement; using UnityEditor.SceneManagement;
using UnityEngine; using UnityEngine;
@ -9,34 +10,11 @@ namespace NegUtils.Editor
[InitializeOnLoad] [InitializeOnLoad]
public class ToolsWindowBase : EditorWindow public class ToolsWindowBase : EditorWindow
{ {
private const int UnitySceneExtensionLength = 6;
static ToolsWindowBase() static ToolsWindowBase()
{ {
EditorApplication.playModeStateChanged += OnPlayModeStateChanged; EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
} }
protected virtual void OnGUI()
{
if (GUILayout.Button("Select Scene"))
ShowScenesList(GUILayoutUtility.GetLastRect());
bool startFromSceneIndex0 = EditorPrefs.GetBool("StartFromSceneIndex0");
bool newVal = GUILayout.Toggle(startFromSceneIndex0, "Start from scene with index 0 on start");
if (newVal != startFromSceneIndex0) EditorPrefs.SetBool("StartFromSceneIndex0", newVal);
if (!startFromSceneIndex0)
return;
bool goToCurrentScene = EditorPrefs.GetBool("GoToCurrentSceneAfterPlay");
newVal = GUILayout.Toggle(goToCurrentScene, "Go to current scene after play");
if (newVal != goToCurrentScene) EditorPrefs.SetBool("GoToCurrentSceneAfterPlay", newVal);
bool goToFirstScene = EditorPrefs.GetBool("GoToFirstSceneAfterPlay");
newVal = GUILayout.Toggle(goToFirstScene, "Go to scene with index 1 after play");
if (newVal != goToFirstScene) EditorPrefs.SetBool("GoToFirstSceneAfterPlay", newVal);
}
[MenuItem("Tools/Show Tools Window")] [MenuItem("Tools/Show Tools Window")]
private static void ShowWindow() private static void ShowWindow()
{ {
@ -44,37 +22,52 @@ namespace NegUtils.Editor
window.Show(); window.Show();
} }
protected virtual void OnGUI()
{
if (GUILayout.Button("Select Scene"))
ShowScenesList(GUILayoutUtility.GetLastRect());
bool startFromSceneIndex0 = EditorPrefs.GetBool("StartFromSceneIndex0");
bool newVal = GUILayout.Toggle(startFromSceneIndex0, "Start from scene with index 0 on start");
if (newVal != startFromSceneIndex0)
{
EditorPrefs.SetBool("StartFromSceneIndex0", newVal);
}
if (!startFromSceneIndex0)
return;
bool goToCurrentScene = EditorPrefs.GetBool("GoToCurrentSceneAfterPlay");
newVal = GUILayout.Toggle(goToCurrentScene, "Go to current scene after play");
if (newVal != goToCurrentScene)
{
EditorPrefs.SetBool("GoToCurrentSceneAfterPlay", newVal);
}
bool goToFirstScene = EditorPrefs.GetBool("GoToFirstSceneAfterPlay");
newVal = GUILayout.Toggle(goToFirstScene, "Go to scene with index 1 after play");
if (newVal != goToFirstScene)
{
EditorPrefs.SetBool("GoToFirstSceneAfterPlay", newVal);
}
}
private static void ShowScenesList(Rect position) private static void ShowScenesList(Rect position)
{ {
var menu = new GenericMenu(); var menu = new GenericMenu();
string path = Application.dataPath + "/Scenes"; string path = Application.dataPath + "/Scenes/Production";
AddFiles(path, path, menu);
menu.DropDown(position);
}
private static void AddFiles(string path, string basePath, GenericMenu menu)
{
string[] fileInfo = Directory.GetFiles(path, "*.unity"); string[] fileInfo = Directory.GetFiles(path, "*.unity");
for (int i = 0; i < fileInfo.Length; i++)
{
string s = fileInfo[i];
menu.AddItem(
new GUIContent(s.Remove(0, basePath.Length + 1)
.Remove(s.Length - basePath.Length - UnitySceneExtensionLength - 1, UnitySceneExtensionLength)
.Replace('\\', '/')), false, () =>
{
LoadScene(s);
});
if (i == fileInfo.Length) continue; foreach (string item in fileInfo)
{
string s = item;
menu.AddItem(new GUIContent(s.Remove(0, path.Length + 1).Remove(s.Length - path.Length - 7 ,6)), false, () => {
LoadScene(s);
});
menu.AddSeparator(""); menu.AddSeparator("");
} }
menu.DropDown(position);
string[] dirInfo = Directory.GetDirectories(path);
foreach (string dir in dirInfo) AddFiles(dir, basePath, menu);
} }
private static void LoadScene(string path) private static void LoadScene(string path)
@ -85,11 +78,11 @@ namespace NegUtils.Editor
private static void OnPlayModeStateChanged(PlayModeStateChange state) private static void OnPlayModeStateChanged(PlayModeStateChange state)
{ {
switch (state) switch(state)
{ {
case PlayModeStateChange.ExitingEditMode: case PlayModeStateChange.ExitingEditMode:
{ {
if (!EditorPrefs.GetBool("StartFromSceneIndex0")) if(!EditorPrefs.GetBool("StartFromSceneIndex0"))
return; return;
EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo(); EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo();
EditorPrefs.SetString("LastOpenedScenePath", EditorSceneManager.GetSceneManagerSetup()[0].path); EditorPrefs.SetString("LastOpenedScenePath", EditorSceneManager.GetSceneManagerSetup()[0].path);
@ -98,26 +91,25 @@ namespace NegUtils.Editor
break; break;
case PlayModeStateChange.EnteredPlayMode: case PlayModeStateChange.EnteredPlayMode:
{ {
if (!EditorPrefs.GetBool("StartFromSceneIndex0")) if(!EditorPrefs.GetBool("StartFromSceneIndex0"))
return; return;
if (EditorPrefs.GetBool("GoToCurrentSceneAfterPlay")) if (EditorPrefs.GetBool("GoToCurrentSceneAfterPlay"))
{
EditorSceneManager.LoadSceneInPlayMode(EditorPrefs.GetString("LastOpenedScenePath"), EditorSceneManager.LoadSceneInPlayMode(EditorPrefs.GetString("LastOpenedScenePath"),
new LoadSceneParameters(LoadSceneMode.Single)); new LoadSceneParameters(LoadSceneMode.Single));
}
else if (EditorPrefs.GetBool("GoToFirstSceneAfterPlay")) else if (EditorPrefs.GetBool("GoToFirstSceneAfterPlay"))
SceneManager.LoadScene(1); SceneManager.LoadScene(1);
} }
break; break;
case PlayModeStateChange.EnteredEditMode: case PlayModeStateChange.EnteredEditMode:
{ {
if (!EditorPrefs.GetBool("StartFromSceneIndex0")) if(!EditorPrefs.GetBool("StartFromSceneIndex0"))
return; return;
EditorSceneManager.OpenScene(EditorPrefs.GetString("LastOpenedScenePath")); EditorSceneManager.OpenScene(EditorPrefs.GetString("LastOpenedScenePath"));
} }
break; break;
} }
} }
} }
} }

View File

@ -1,5 +1,7 @@
using System.IO; using System.IO;
using UnityEditor;
using UnityEditor.AssetImporters; using UnityEditor.AssetImporters;
using UnityEditor.Experimental.AssetImporters;
using UnityEngine; using UnityEngine;
[ScriptedImporter(1, "tsv")] [ScriptedImporter(1, "tsv")]

View File

@ -7,7 +7,8 @@ namespace NEG.Utils
{ {
public class KeyBasedFactory<T1, T2> public class KeyBasedFactory<T1, T2>
{ {
[PublicAPI] protected Dictionary<T1, Type> data; [PublicAPI]
protected Dictionary<T1, Type> data;
public KeyBasedFactory() public KeyBasedFactory()
{ {
@ -18,7 +19,7 @@ namespace NEG.Utils
{ {
ScanAssembly(typeof(T2).Assembly); ScanAssembly(typeof(T2).Assembly);
if (typeof(T2).Assembly.GetType().Assembly == typeof(T2).Assembly) if(typeof(T2).Assembly.GetType().Assembly == typeof(T2).Assembly)
return; return;
ScanAssembly(typeof(T2).Assembly.GetType().Assembly); ScanAssembly(typeof(T2).Assembly.GetType().Assembly);
@ -33,7 +34,9 @@ namespace NEG.Utils
for (int i = 0; i < methodFields.Length; i++) for (int i = 0; i < methodFields.Length; i++)
{ {
if (Attribute.GetCustomAttribute(methodFields[i], typeof(FactoryRegistration)) != null) if (Attribute.GetCustomAttribute(methodFields[i], typeof(FactoryRegistration)) != null)
{
methodFields[i].Invoke(null, Array.Empty<object>()); methodFields[i].Invoke(null, Array.Empty<object>());
}
} }
} }
} }
@ -46,5 +49,6 @@ namespace NEG.Utils
[AttributeUsage(AttributeTargets.Method)] [AttributeUsage(AttributeTargets.Method)]
public class FactoryRegistration : Attribute public class FactoryRegistration : Attribute
{ {
public FactoryRegistration() { }
} }
} }

View File

@ -1,16 +1,16 @@
{ {
"name": "NEG.Utils", "name": "NEG.Utils",
"rootNamespace": "", "rootNamespace": "",
"references": [ "references": [
"GUID:6055be8ebefd69e48b49212b09b47b2f" "GUID:6055be8ebefd69e48b49212b09b47b2f"
], ],
"includePlatforms": [], "includePlatforms": [],
"excludePlatforms": [], "excludePlatforms": [],
"allowUnsafeCode": false, "allowUnsafeCode": false,
"overrideReferences": false, "overrideReferences": false,
"precompiledReferences": [], "precompiledReferences": [],
"autoReferenced": true, "autoReferenced": true,
"defineConstraints": [], "defineConstraints": [],
"versionDefines": [], "versionDefines": [],
"noEngineReferences": false "noEngineReferences": false
} }

View File

@ -4,15 +4,15 @@ namespace NegUtils.NEG.UI
{ {
public interface IControllable public interface IControllable
{ {
public class BackUsed
{
public bool Used { get; set; }
}
event Action<object> OnOpened; event Action<object> OnOpened;
event Action OnClosed; event Action OnClosed;
event Action<BackUsed> OnBackUsed; event Action<BackUsed> OnBackUsed;
public void TryUseBack(ref BackUsed backUsed); public void TryUseBack(ref BackUsed backUsed);
public class BackUsed
{
public bool Used { get; set; }
}
} }
} }

View File

@ -1,16 +1,14 @@
{ {
"name": "NEG.UI", "name": "NEG.UI",
"rootNamespace": "", "rootNamespace": "",
"references": [ "references": ["GUID:3c4294719a93e3c4e831a9ff0c261e8a"],
"GUID:3c4294719a93e3c4e831a9ff0c261e8a" "includePlatforms": [],
], "excludePlatforms": [],
"includePlatforms": [], "allowUnsafeCode": false,
"excludePlatforms": [], "overrideReferences": false,
"allowUnsafeCode": false, "precompiledReferences": [],
"overrideReferences": false, "autoReferenced": true,
"precompiledReferences": [], "defineConstraints": [],
"autoReferenced": true, "versionDefines": [],
"defineConstraints": [], "noEngineReferences": false
"versionDefines": [],
"noEngineReferences": false
} }

View File

@ -5,14 +5,13 @@ namespace NEG.UI.Popup
{ {
public class DefaultPopupData : PopupData public class DefaultPopupData : PopupData
{ {
private readonly string content; private IDefaultPopup defaultPopup;
private readonly IDefaultPopup defaultPopup;
private readonly List<(string, Action)> options;
private readonly string title; private readonly string title;
private readonly string content;
private readonly List<(string, Action)> options;
public DefaultPopupData(IDefaultPopup popup, string title, string content, List<(string, Action)> options) : public DefaultPopupData(IDefaultPopup popup, string title, string content, List<(string, Action)> options) : base(popup)
base(popup)
{ {
defaultPopup = popup; defaultPopup = popup;
this.title = title; this.title = title;
@ -25,5 +24,12 @@ namespace NEG.UI.Popup
defaultPopup.SetContent(title, content, options); defaultPopup.SetContent(title, content, options);
base.Show(); base.Show();
} }
public void UpdatePopup(IDefaultPopup newPopup)
{
defaultPopup = newPopup;
popup = newPopup;
Show();
}
} }
} }

View File

@ -6,14 +6,11 @@ namespace NEG.UI.Popup
public interface IDefaultPopup : IPopup public interface IDefaultPopup : IPopup
{ {
/// <summary> /// <summary>
/// Sets content based on provided data. /// Sets content based on provided data.
/// </summary> /// </summary>
/// <param name="title">popup title</param> /// <param name="title">popup title</param>
/// <param name="content">popup content</param> /// <param name="content">popup content</param>
/// <param name="options"> /// <param name="options">list of tuples (name, action on click), to set buttons. Do not pass here popup closing logic, implementing class should do it</param>
/// list of tuples (name, action on click), to set buttons. Do not pass here popup closing logic,
/// implementing class should do it
/// </param>
public void SetContent(string title, string content, List<(string name, Action action)> options); public void SetContent(string title, string content, List<(string name, Action action)> options);
} }
} }

View File

@ -7,18 +7,18 @@ namespace NEG.UI.Popup
public interface IPopup public interface IPopup
{ {
/// <summary> /// <summary>
/// Event to fire when popup is closed /// Event to fire when popup is closed
/// </summary> /// </summary>
event Action<PopupData> OnPopupClosed; event Action<PopupData> OnPopupClosed;
/// <summary> /// <summary>
/// Show popup /// Show popup
/// </summary> /// </summary>
/// <param name="data">data assigned to popup, used to give info that popup is closed</param> /// <param name="data">data assigned to popup, used to give info that popup is closed</param>
public void Show(PopupData data); public void Show(PopupData data);
/// <summary> /// <summary>
/// Close popup or mark as closed if not visible /// Close popup or mark as closed if not visible
/// </summary> /// </summary>
/// <param name="silent">if true hide visually, without firing callbacks to properly close</param> /// <param name="silent">if true hide visually, without firing callbacks to properly close</param>
void Close(bool silent = false); void Close(bool silent = false);

View File

@ -1,15 +1,32 @@
using JetBrains.Annotations; using JetBrains.Annotations;
using System; using System;
using UnityEngine;
namespace NEG.UI.Popup namespace NEG.UI.Popup
{ {
[PublicAPI] [PublicAPI]
public class PopupData public class PopupData
{ {
private readonly IPopup popup; /// <summary>
/// Event that is fired on closing popup.
/// </summary>
public event Action<PopupData> PopupClosedEvent
{
add => popup.OnPopupClosed += value;
remove => popup.OnPopupClosed -= value;
}
/// <summary> /// <summary>
/// PopupData constructor. /// Is this data is still valid. If set to false, popup will not show.
/// </summary>
public bool IsValid { get; protected set; }
public IPopup Popup => popup;
protected IPopup popup;
/// <summary>
/// PopupData constructor.
/// </summary> /// </summary>
/// <param name="popup">attached to this data, can be used by different data instances</param> /// <param name="popup">attached to this data, can be used by different data instances</param>
public PopupData(IPopup popup) public PopupData(IPopup popup)
@ -19,31 +36,22 @@ namespace NEG.UI.Popup
} }
/// <summary> /// <summary>
/// Is this data is still valid. If set to false, popup will not show. /// Show popup and pass needed data.
/// </summary>
public bool IsValid { get; protected set; }
/// <summary>
/// Event that is fired on closing popup.
/// </summary>
public event Action<PopupData> PopupClosedEvent
{
add => popup.OnPopupClosed += value;
remove => popup.OnPopupClosed -= value;
}
/// <summary>
/// Show popup and pass needed data.
/// </summary> /// </summary>
public virtual void Show() => popup.Show(this); public virtual void Show() => popup.Show(this);
/// <summary> /// <summary>
/// Hide popup. Close visuals without firing events; /// Hide popup. Close visuals without firing events;
/// </summary> /// </summary>
public virtual void Hide() => popup.Close(true); public virtual void Hide()
{
if(popup is MonoBehaviour behaviour && behaviour == null)
return;
popup.Close(true);
}
/// <summary> /// <summary>
/// Invalidate popup, <see cref="UiManager" /> will automatically skip this popup /// Invalidate popup, <see cref="UiManager"/> will automatically skip this popup
/// </summary> /// </summary>
public virtual void Invalidate() public virtual void Invalidate()
{ {

View File

@ -248,10 +248,8 @@ namespace System.Collections.Generic
public PriorityQueue(int initialCapacity, IComparer<TPriority>? comparer) public PriorityQueue(int initialCapacity, IComparer<TPriority>? comparer)
{ {
if (initialCapacity < 0) if (initialCapacity < 0)
{
throw new ArgumentOutOfRangeException( throw new ArgumentOutOfRangeException(
nameof(initialCapacity), initialCapacity, SR.ArgumentOutOfRange_NeedNonNegNum); nameof(initialCapacity), initialCapacity, SR.ArgumentOutOfRange_NeedNonNegNum);
}
_nodes = new (TElement, TPriority)[initialCapacity]; _nodes = new (TElement, TPriority)[initialCapacity];
_comparer = InitializeComparer(comparer); _comparer = InitializeComparer(comparer);
@ -491,7 +489,7 @@ namespace System.Collections.Generic
{ {
int i = 0; int i = 0;
(TElement, TPriority)[] nodes = _nodes; (TElement, TPriority)[] nodes = _nodes;
foreach (var (element, priority) in items) foreach ((var element, var priority) in items)
{ {
if (nodes.Length == i) if (nodes.Length == i)
{ {
@ -510,8 +508,9 @@ namespace System.Collections.Generic
if (_size > 1) Heapify(); if (_size > 1) Heapify();
} }
else else
foreach (var (element, priority) in items) {
Enqueue(element, priority); foreach ((var element, var priority) in items) Enqueue(element, priority);
}
} }
/// <summary> /// <summary>
@ -555,8 +554,9 @@ namespace System.Collections.Generic
if (i > 1) Heapify(); if (i > 1) Heapify();
} }
else else
foreach (var element in elements) {
Enqueue(element, priority); foreach (var element in elements) Enqueue(element, priority);
}
} }
/// <summary> /// <summary>
@ -684,15 +684,11 @@ namespace System.Collections.Generic
int lastParentWithChildren = GetParentIndex(_size - 1); int lastParentWithChildren = GetParentIndex(_size - 1);
if (_comparer == null) if (_comparer == null)
{
for (int index = lastParentWithChildren; index >= 0; --index) for (int index = lastParentWithChildren; index >= 0; --index)
MoveDownDefaultComparer(nodes[index], index); MoveDownDefaultComparer(nodes[index], index);
}
else else
{
for (int index = lastParentWithChildren; index >= 0; --index) for (int index = lastParentWithChildren; index >= 0; --index)
MoveDownCustomComparer(nodes[index], index); MoveDownCustomComparer(nodes[index], index);
}
} }
/// <summary> /// <summary>
@ -719,7 +715,9 @@ namespace System.Collections.Generic
nodeIndex = parentIndex; nodeIndex = parentIndex;
} }
else else
{
break; break;
}
} }
nodes[nodeIndex] = node; nodes[nodeIndex] = node;
@ -750,7 +748,9 @@ namespace System.Collections.Generic
nodeIndex = parentIndex; nodeIndex = parentIndex;
} }
else else
{
break; break;
}
} }
nodes[nodeIndex] = node; nodes[nodeIndex] = node;
@ -894,10 +894,8 @@ namespace System.Collections.Generic
if (array.GetLowerBound(0) != 0) throw new ArgumentException(SR.Arg_NonZeroLowerBound, nameof(array)); if (array.GetLowerBound(0) != 0) throw new ArgumentException(SR.Arg_NonZeroLowerBound, nameof(array));
if (index < 0 || index > array.Length) if (index < 0 || index > array.Length)
{
throw new ArgumentOutOfRangeException(nameof(index), index, throw new ArgumentOutOfRangeException(nameof(index), index,
SR.ArgumentOutOfRange_IndexMustBeLessOrEqual); SR.ArgumentOutOfRange_IndexMustBeLessOrEqual);
}
if (array.Length - index < _queue._size) throw new ArgumentException(SR.Argument_InvalidOffLen); if (array.Length - index < _queue._size) throw new ArgumentException(SR.Argument_InvalidOffLen);

View File

@ -2,10 +2,10 @@ using JetBrains.Annotations;
using NEG.UI.Area; using NEG.UI.Area;
using NEG.UI.Popup; using NEG.UI.Popup;
using NEG.UI.Window; using NEG.UI.Window;
using NEG.Utils;
using NegUtils.NEG.UI; using NegUtils.NEG.UI;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using UnityEngine; using UnityEngine;
namespace NEG.UI namespace NEG.UI
@ -13,35 +13,10 @@ namespace NEG.UI
[PublicAPI] [PublicAPI]
public abstract class UiManager : IDisposable public abstract class UiManager : IDisposable
{ {
private IArea currentArea;
protected IDefaultPopup currentDefaultPopup;
private (PopupData data, int priority) currentShownPopup;
//TODO: localize
private string localizedYes = "Yes", localizedNo = "No", localizedOk = "Ok";
private List<IWindow> mainWindows;
private PriorityQueue<PopupData, int> popupsToShow = new();
protected UiManager(IArea startArea)
{
if (Instance != null)
{
Debug.LogError("Only one instance od UiManager is allowed");
return;
}
Instance = this;
CurrentArea = startArea;
mainWindows = new List<IWindow>();
}
public static UiManager Instance { get; protected set; } public static UiManager Instance { get; protected set; }
/// <summary> /// <summary>
/// Current area shown on screen. /// Current area shown on screen.
/// </summary> /// </summary>
public IArea CurrentArea public IArea CurrentArea
{ {
@ -57,17 +32,38 @@ namespace NEG.UI
} }
/// <summary> /// <summary>
/// Current window that is considered main (focused, lastly opened). Can be null. /// Current window that is considered main (focused, lastly opened). Can be null.
/// </summary> /// </summary>
public IWindow CurrentMainWindow => mainWindows.LastOrDefault(); public IWindow CurrentMainWindow { get; protected set; }
public PopupData CurrentPopup => currentShownPopup.data; public PopupData CurrentPopup => currentShownPopup.data;
public virtual void Dispose() => Instance = null; public IDefaultPopup CurrentDefaultPopup => currentDefaultPopup;
private IArea currentArea;
private (PopupData data, int priority) currentShownPopup;
protected IDefaultPopup currentDefaultPopup;
private PriorityQueue<PopupData, int> popupsToShow = new();
//TODO: localize
private string localizedYes = "Yes", localizedNo = "No", localizedOk = "Ok";
protected UiManager(IArea startArea)
{
if (Instance != null)
{
Debug.LogError("Only one instance od UiManager is allowed");
return;
}
Instance = this;
CurrentArea = startArea;
}
/// <summary> /// <summary>
/// Show popup if there is non other currently shown. Otherwise add current popup to ordered queue and show it later. /// Show popup if there is non other currently shown. Otherwise add current popup to ordered queue and show it later. It will be closed after pressing ok button.
/// It will be closed after pressing ok button.
/// </summary> /// </summary>
/// <param name="title">popup title</param> /// <param name="title">popup title</param>
/// <param name="content">popup content</param> /// <param name="content">popup content</param>
@ -76,18 +72,16 @@ namespace NEG.UI
/// <param name="priority">priority of popup (lower number -> show first)</param> /// <param name="priority">priority of popup (lower number -> show first)</param>
/// <param name="forceShow">force show current popup only if currently shown has lower priority</param> /// <param name="forceShow">force show current popup only if currently shown has lower priority</param>
/// <returns>data for created popup, can be used to invalidate popup (will not show)</returns> /// <returns>data for created popup, can be used to invalidate popup (will not show)</returns>
public PopupData ShowOkPopup(string title, string content, string okText = null, Action okPressed = null, public PopupData ShowOkPopup(string title, string content, string okText = null, Action okPressed = null, int priority = 0, bool forceShow = false)
int priority = 0, bool forceShow = false)
{ {
var data = new DefaultPopupData(currentDefaultPopup, title, content, var data = new DefaultPopupData(currentDefaultPopup, title, content,
new List<(string, Action)> { (okText ?? localizedOk, okPressed) }); new List<(string, Action)>() { (okText ?? localizedOk, okPressed) });
ShowPopup(data, priority, forceShow); ShowPopup(data, priority, forceShow);
return data; return data;
} }
/// <summary> /// <summary>
/// Show popup if there is non other currently shown. Otherwise add current popup to ordered queue and show it later. /// Show popup if there is non other currently shown. Otherwise add current popup to ordered queue and show it later. It will be closed after pressing yes or no button.
/// It will be closed after pressing yes or no button.
/// </summary> /// </summary>
/// <param name="title">popup title</param> /// <param name="title">popup title</param>
/// <param name="content">popup content</param> /// <param name="content">popup content</param>
@ -98,21 +92,16 @@ namespace NEG.UI
/// <param name="priority">priority of popup (lower number -> show first)</param> /// <param name="priority">priority of popup (lower number -> show first)</param>
/// <param name="forceShow">force show current popup only if currently shown has lower priority</param> /// <param name="forceShow">force show current popup only if currently shown has lower priority</param>
/// <returns>data for created popup, can be used to invalidate popup (will not show)</returns> /// <returns>data for created popup, can be used to invalidate popup (will not show)</returns>
public PopupData ShowYesNoPopup(string title, string content, string yesText = null, string noText = null, public PopupData ShowYesNoPopup(string title, string content, string yesText = null, string noText = null, Action yesPressed = null, Action noPressed = null, int priority = 0, bool forceShow = false)
Action yesPressed = null, Action noPressed = null, int priority = 0, bool forceShow = false)
{ {
var data = new DefaultPopupData(currentDefaultPopup, title, content, var data = new DefaultPopupData(currentDefaultPopup, title, content,
new List<(string, Action)> new List<(string, Action)>() { (yesText ?? localizedYes, yesPressed), (noText ?? localizedNo, noPressed) });
{
(yesText ?? localizedYes, yesPressed), (noText ?? localizedNo, noPressed)
});
ShowPopup(data, priority, forceShow); ShowPopup(data, priority, forceShow);
return data; return data;
} }
/// <summary> /// <summary>
/// Show popup if there is non other currently shown. Otherwise add current popup to ordered queue and show it later. /// Show popup if there is non other currently shown. Otherwise add current popup to ordered queue and show it later. It will be closed after pressing any button.
/// It will be closed after pressing any button.
/// </summary> /// </summary>
/// <param name="title">popup title</param> /// <param name="title">popup title</param>
/// <param name="content">popup content</param> /// <param name="content">popup content</param>
@ -120,8 +109,7 @@ namespace NEG.UI
/// <param name="priority">priority of popup (lower number -> show first)</param> /// <param name="priority">priority of popup (lower number -> show first)</param>
/// <param name="forceShow">force show current popup only if currently shown has lower priority</param> /// <param name="forceShow">force show current popup only if currently shown has lower priority</param>
/// <returns>data for created popup, can be used to invalidate popup (will not show)</returns> /// <returns>data for created popup, can be used to invalidate popup (will not show)</returns>
public PopupData ShowPopup(string title, string content, List<(string, Action)> actions, int priority = 0, public PopupData ShowPopup(string title, string content, List<(string, Action)> actions, int priority = 0, bool forceShow = false)
bool forceShow = false)
{ {
var data = new DefaultPopupData(currentDefaultPopup, title, content, actions); var data = new DefaultPopupData(currentDefaultPopup, title, content, actions);
ShowPopup(data, priority, forceShow); ShowPopup(data, priority, forceShow);
@ -129,7 +117,7 @@ namespace NEG.UI
} }
/// <summary> /// <summary>
/// Show popup if there is non other currently shown. Otherwise add current popup to ordered queue and show it later. /// Show popup if there is non other currently shown. Otherwise add current popup to ordered queue and show it later.
/// </summary> /// </summary>
/// <param name="data">popup data object</param> /// <param name="data">popup data object</param>
/// <param name="priority">priority of popup (lower number -> show first)</param> /// <param name="priority">priority of popup (lower number -> show first)</param>
@ -145,7 +133,7 @@ namespace NEG.UI
IControllable.BackUsed backUsed = new(); IControllable.BackUsed backUsed = new();
CurrentMainWindow?.TryUseBack(ref backUsed); CurrentMainWindow?.TryUseBack(ref backUsed);
if (backUsed.Used) if(backUsed.Used)
return; return;
CurrentArea.TryUseBack(ref backUsed); CurrentArea.TryUseBack(ref backUsed);
} }
@ -153,48 +141,60 @@ namespace NEG.UI
public void RefreshPopups() public void RefreshPopups()
{ {
if (currentShownPopup.data is { IsValid: true }) if(currentShownPopup.data is { IsValid: true })
return; return;
UpdatePopupsState(false); UpdatePopupsState(false);
} }
public void SetMainWindow(IWindow window) => mainWindows.Add(window); public virtual void Dispose() => Instance = null;
public void MainWindowClosed(IWindow window) => mainWindows.Remove(window); public void SetMainWindow(IWindow window) => CurrentMainWindow = window;
public void OnWindowClosed(IWindow window) => MainWindowClosed(window); public void OnWindowClosed(IWindow window)
{
if(CurrentMainWindow != window)
return;
//TODO: select new main window
}
//TODO: select new main window
protected void PopupClosed(PopupData data) protected void PopupClosed(PopupData data)
{ {
if (currentShownPopup.data != data) if (currentShownPopup.data != data)
{
//Debug.LogError("Popup was not shown"); //Debug.LogError("Popup was not shown");
return; return;
}
UpdatePopupsState(false); UpdatePopupsState(false);
} }
protected void SetDefaultPopup(IDefaultPopup popup) => currentDefaultPopup = popup; protected void SetDefaultPopup(IDefaultPopup popup)
{
if(currentShownPopup.data != null && currentShownPopup.data is DefaultPopupData defaultData && currentShownPopup.data.Popup == currentDefaultPopup)
defaultData.UpdatePopup(popup);
currentDefaultPopup = popup;
}
protected virtual void UpdatePopupsState(bool forceShow, int priority = 0, PopupData data = null) private void UpdatePopupsState(bool forceShow, int priority = 0, PopupData data = null)
{ {
if (forceShow) if (forceShow)
{ {
if (currentShownPopup.data != null && currentShownPopup.priority >= priority) if(currentShownPopup.data != null && currentShownPopup.priority >= priority)
return; return;
popupsToShow.Enqueue(currentShownPopup.data, currentShownPopup.priority); if(currentShownPopup.data != null)
popupsToShow.Enqueue(currentShownPopup.data, currentShownPopup.priority);
ShowPopup(data, priority); ShowPopup(data, priority);
return; return;
} }
while (popupsToShow.TryDequeue(out var d, out int p)) while (popupsToShow.TryDequeue(out var d, out int p))
{ {
if (d == null) if(!d.IsValid)
continue; continue;
if (!d.IsValid) if(d == currentShownPopup.data)
continue;
if (d == currentShownPopup.data)
continue; continue;
ShowPopup(d, p); ShowPopup(d, p);
return; return;
@ -214,10 +214,11 @@ namespace NEG.UI
currentShownPopup.data.PopupClosedEvent -= PopupClosed; currentShownPopup.data.PopupClosedEvent -= PopupClosed;
currentShownPopup.data.Hide(); currentShownPopup.data.Hide();
} }
currentShownPopup = (data, priority); currentShownPopup = (data, priority);
data.Show(); data.Show();
data.PopupClosedEvent += PopupClosed; data.PopupClosedEvent += PopupClosed;
} }
} }
} }

View File

@ -1,17 +0,0 @@
using NEG.UI.UnityUi.Window;
using NEG.UI.Window;
using UnityEngine;
namespace NEG.UI.Area
{
public class AutoOpenWindowWhenNoOther : MonoBehaviour
{
[SerializeField] private MonoWindow window;
private void Start()
{
if (UiManager.Instance.CurrentMainWindow == null)
window.Open();
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 473c065573984067a824ebf3b605c3ab
timeCreated: 1695048071

View File

@ -1,10 +1,12 @@
using NEG.UI.UnityUi.Window; using NEG.UI.UnityUi.Window;
using NEG.UI.Window; using NEG.UI.Window;
using System;
using KBCore.Refs;
using UnityEngine; using UnityEngine;
namespace NEG.UI.Area namespace NEG.UI.Area
{ {
[Tooltip("Automatically open attached window on start")] [Tooltip(tooltip: "Automatically open attached window on start")]
public class AutoWindowOpen : MonoBehaviour public class AutoWindowOpen : MonoBehaviour
{ {
[SerializeField] private MonoWindow window; [SerializeField] private MonoWindow window;

View File

@ -1,6 +1,9 @@
using NEG.UI.UnityUi; using KBCore.Refs;
using NEG.UI.UnityUi;
using NEG.UI.Window; using NEG.UI.Window;
using NegUtils.NEG.UI; using NegUtils.NEG.UI;
using System;
using UnityEngine;
namespace NEG.UI.Area namespace NEG.UI.Area
{ {
@ -9,7 +12,7 @@ namespace NEG.UI.Area
protected override void OnBackUsed(IControllable.BackUsed backUsed) protected override void OnBackUsed(IControllable.BackUsed backUsed)
{ {
base.OnBackUsed(backUsed); base.OnBackUsed(backUsed);
UiManager.Instance.CurrentMainWindow?.Close(); UiManager.Instance.CurrentMainWindow.Close();
backUsed.Used = true; backUsed.Used = true;
} }
} }

View File

@ -1,19 +1,40 @@
using NEG.UI.UnityUi.WindowSlot; using System.Collections.Generic;
using UnityEngine;
using NEG.UI.Popup;
using NEG.UI.UnityUi.Window;
using NEG.UI.UnityUi.WindowSlot;
using NEG.UI.Window; using NEG.UI.Window;
using NEG.UI.WindowSlot; using NEG.UI.WindowSlot;
using NegUtils.NEG.UI; using NegUtils.NEG.UI;
using System; using System;
using System.Collections.Generic;
using UnityEngine;
namespace NEG.UI.Area namespace NEG.UI.Area
{ {
public class MonoArea : MonoBehaviour, IArea public class MonoArea : MonoBehaviour, IArea
{ {
public event Action<object> OnOpened;
public event Action OnClosed;
public event Action<IControllable.BackUsed> OnBackUsed;
public IEnumerable<IWindowSlot> AvailableSlots => windowSlots;
public IWindowSlot DefaultWindowSlot => windowSlots[0];
[SerializeField] private bool setAsDefaultArea; [SerializeField] private bool setAsDefaultArea;
[SerializeField] private List<MonoWindowSlot> windowSlots; [SerializeField] private List<MonoWindowSlot> windowSlots;
public IWindowSlot DefaultWindowSlot => windowSlots[0];
public void Open()
{
gameObject.SetActive(true);
OnOpened?.Invoke(null);
}
public void Close(){
gameObject.SetActive(false);
OnClosed?.Invoke();
}
public void OpenWindow(IWindow window, object data = null) => DefaultWindowSlot.AttachWindow(window, data);
protected virtual void Awake() protected virtual void Awake()
{ {
@ -23,38 +44,16 @@ namespace NEG.UI.Area
private void Start() private void Start()
{ {
if (!setAsDefaultArea) if(!setAsDefaultArea)
Close(); Close();
} }
private void OnDestroy() private void OnDestroy()
{ {
if (UiManager.Instance == null)
return;
if (ReferenceEquals(UiManager.Instance.CurrentArea, this)) if (ReferenceEquals(UiManager.Instance.CurrentArea, this))
UiManager.Instance.CurrentArea = null; UiManager.Instance.CurrentArea = null;
} }
public event Action<object> OnOpened;
public event Action OnClosed;
public event Action<IControllable.BackUsed> OnBackUsed;
public IEnumerable<IWindowSlot> AvailableSlots => windowSlots;
public void Open()
{
gameObject.SetActive(true);
OnOpened?.Invoke(null);
}
public void Close()
{
gameObject.SetActive(false);
OnClosed?.Invoke();
}
public void OpenWindow(IWindow window, object data = null) => DefaultWindowSlot.AttachWindow(window, data);
public void TryUseBack(ref IControllable.BackUsed backUsed) => OnBackUsed?.Invoke(backUsed); public void TryUseBack(ref IControllable.BackUsed backUsed) => OnBackUsed?.Invoke(backUsed);
} }
} }

View File

@ -1,11 +1,3 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 39eb59ca1ef60934abb3f0c64169be65 guid: 39eb59ca1ef60934abb3f0c64169be65
MonoImporter: timeCreated: 1670707479
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 10
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,11 +1,12 @@
using KBCore.Refs; using KBCore.Refs;
using NEG.UI.UnityUi.Buttons.Behaviours; using NEG.UI.UnityUi.Buttons.Reaction;
using NEG.UI.UnityUi.Buttons.Settings; using NEG.UI.UnityUi.Buttons.Settings;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using TMPro; using TMPro;
using UnityEngine; using UnityEngine;
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
using UnityEngine.Serialization;
using UnityEngine.UI; using UnityEngine.UI;
namespace NEG.UI.UnityUi.Buttons namespace NEG.UI.UnityUi.Buttons
@ -15,23 +16,65 @@ namespace NEG.UI.UnityUi.Buttons
public class BaseButton : MonoBehaviour, ISelectHandler, IDeselectHandler, IPointerEnterHandler, IPointerExitHandler public class BaseButton : MonoBehaviour, ISelectHandler, IDeselectHandler, IPointerEnterHandler, IPointerExitHandler
{ {
public delegate void SelectionHandler(bool isSilent); public delegate void SelectionHandler(bool isSilent);
/// <summary>
[SerializeField] [Self(Flag.Optional)] private Button button; /// is silent
/// </summary>
[SerializeField] [Child(Flag.Optional)] public event SelectionHandler OnSelected;
private TMP_Text text; public event SelectionHandler OnDeselected;
public event Action OnButtonPressed;
[SerializeField] [Child(Flag.Optional)]
private Image icon;
[SerializeField] private ButtonSettings groupButtonSettings;
private readonly Dictionary<string, ButtonElementBehaviour> behaviours = new();
public bool Interactable { get => button.interactable; set => button.interactable = value; } public bool Interactable { get => button.interactable; set => button.interactable = value; }
public TMP_Text Text => text; public TMP_Text Text => text;
[SerializeField, Self(Flag.Optional)] private Button button;
[SerializeField, Child(Flag.Optional)] private TMP_Text text;
[SerializeField, Child(Flag.Optional)] private Image icon;
[SerializeField] private ButtonSettings groupButtonSettings;
private readonly Dictionary<string, ButtonElementBehaviour> behaviours = new Dictionary<string, ButtonElementBehaviour>();
public virtual void OnSelect(BaseEventData eventData) => OnSelected?.Invoke(eventData is SilentEventData);
public void OnDeselect(BaseEventData eventData) => OnDeselected?.Invoke(eventData is SilentEventData);
public void OnPointerEnter(PointerEventData eventData) => EventSystem.current.SetSelectedGameObject(gameObject);
public void OnPointerExit(PointerEventData eventData)
{
if(EventSystem.current.currentSelectedGameObject == gameObject)
EventSystem.current.SetSelectedGameObject(null);
}
public void SetText(string txt)
{
if(text == null)
return;
text.text = txt;
}
public void AddOrOverrideSetting(SettingData data)
{
if (behaviours.TryGetValue(data.Key, out var setting))
{
setting.ChangeData(data);
return;
}
behaviours.Add(data.Key, MonoUiManager.Instance.BehavioursFactory.CreateInstance(data.Key, this, data));
}
public void RemoveSetting(string key)
{
if (!behaviours.TryGetValue(key, out var setting))
{
Debug.LogError($"Behaviour with key {key} was not found");
return;
}
setting.Dispose();
behaviours.Remove(key);
}
protected virtual void Awake() protected virtual void Awake()
{ {
button.onClick.AddListener(OnClicked); button.onClick.AddListener(OnClicked);
@ -45,56 +88,6 @@ namespace NEG.UI.UnityUi.Buttons
private void OnValidate() => this.ValidateRefs(); private void OnValidate() => this.ValidateRefs();
public void OnDeselect(BaseEventData eventData) => OnDeselected?.Invoke(eventData is SilentEventData);
public void OnPointerEnter(PointerEventData eventData) => EventSystem.current.SetSelectedGameObject(gameObject);
public void OnPointerExit(PointerEventData eventData)
{
if (EventSystem.current.currentSelectedGameObject == gameObject)
EventSystem.current.SetSelectedGameObject(null);
}
public virtual void OnSelect(BaseEventData eventData) => OnSelected?.Invoke(eventData is SilentEventData);
/// <summary>
/// is silent
/// </summary>
public event SelectionHandler OnSelected;
public event SelectionHandler OnDeselected;
public event Action OnButtonPressed;
public void SetText(string txt)
{
if (text == null)
return;
text.text = txt;
}
public void AddOrOverrideSetting(SettingData data)
{
if (behaviours.TryGetValue(data.Key, out var setting))
{
setting.ChangeData(data);
return;
}
behaviours.Add(data.Key, MonoUiManager.Instance.BehavioursFactory.CreateInstance(data.Key, this, data));
}
public void RemoveSetting(string key)
{
if (!behaviours.TryGetValue(key, out var setting))
{
Debug.LogError($"Behaviour with key {key} was not found");
return;
}
setting.Dispose();
behaviours.Remove(key);
}
protected virtual void OnClicked() protected virtual void OnClicked()
{ {
OnDeselect(null); OnDeselect(null);

View File

@ -1,4 +1,5 @@
using KBCore.Refs; using System;
using KBCore.Refs;
using UnityEngine; using UnityEngine;
namespace NEG.UI.UnityUi.Buttons namespace NEG.UI.UnityUi.Buttons
@ -6,7 +7,7 @@ namespace NEG.UI.UnityUi.Buttons
[RequireComponent(typeof(BaseButton))] [RequireComponent(typeof(BaseButton))]
public abstract class ButtonReaction : MonoBehaviour public abstract class ButtonReaction : MonoBehaviour
{ {
[SerializeField] [Self(Flag.Optional)] protected BaseButton button; [SerializeField, Self(Flag.Optional)] protected BaseButton button;
protected virtual void Awake() => button.OnButtonPressed += OnClicked; protected virtual void Awake() => button.OnButtonPressed += OnClicked;

View File

@ -1,13 +1,16 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine; using UnityEngine;
using UnityEngine.SceneManagement; using UnityEngine.SceneManagement;
namespace NEG.UI.UnityUi.Buttons.Reactions namespace NEG.UI.UnityUi.Buttons
{ {
[RequireComponent(typeof(BaseButton))] [RequireComponent(typeof(BaseButton))]
public class ChangeScene : ButtonReaction public class ChangeSceneButton : ButtonReaction
{ {
[Header("Leave empty to use int value")] [SerializeField] [Header("Leave empty to use int value")]
private string sceneName; [SerializeField] private string sceneName;
[SerializeField] private int sceneIndex; [SerializeField] private int sceneIndex;

View File

@ -1,4 +1,6 @@
namespace NEG.UI.UnityUi.Buttons.Reactions using UnityEngine;
namespace NEG.UI.UnityUi.Buttons
{ {
public class CloseAllWindows : ButtonReaction public class CloseAllWindows : ButtonReaction
{ {

View File

@ -1,21 +1,22 @@
using NEG.UI.UnityUi.Window; using NEG.UI.UnityUi.Window;
using NEG.UI.Window; using NEG.UI.Window;
using System;
using UnityEngine; using UnityEngine;
namespace NEG.UI.UnityUi.Buttons.Reactions namespace NEG.UI.UnityUi.Buttons
{ {
[RequireComponent(typeof(BaseButton))] [RequireComponent(typeof(BaseButton))]
public class CloseWindow : ButtonReaction public class CloseWindow : ButtonReaction
{ {
[SerializeField] private MonoWindow windowToClose; [SerializeField] private MonoWindow windowToClose;
protected override void OnClicked() => windowToClose.Close();
private void OnValidate() private void OnValidate()
{ {
if (windowToClose != null) if(windowToClose != null)
return; return;
windowToClose = GetComponentInParent<MonoWindow>(); windowToClose = GetComponentInParent<MonoWindow>();
} }
protected override void OnClicked() => windowToClose.Close();
} }
} }

View File

@ -18,19 +18,19 @@ namespace NEG.UI.UnityUi.Buttons
switch (eventData.moveDir) switch (eventData.moveDir)
{ {
case MoveDirection.Left: case MoveDirection.Left:
if (TryNavigate(eventData, leftOverride)) if(TryNavigate(eventData, leftOverride))
return; return;
break; break;
case MoveDirection.Up: case MoveDirection.Up:
if (TryNavigate(eventData, upOverride)) if(TryNavigate(eventData, upOverride))
return; return;
break; break;
case MoveDirection.Right: case MoveDirection.Right:
if (TryNavigate(eventData, rightOverride)) if(TryNavigate(eventData, rightOverride))
return; return;
break; break;
case MoveDirection.Down: case MoveDirection.Down:
if (TryNavigate(eventData, downOverride)) if(TryNavigate(eventData, downOverride))
return; return;
break; break;
case MoveDirection.None: case MoveDirection.None:
@ -38,13 +38,12 @@ namespace NEG.UI.UnityUi.Buttons
default: default:
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();
} }
base.OnMove(eventData); base.OnMove(eventData);
} }
public static bool TryNavigate(BaseEventData eventData, OverridableNavigation overrideNavigation) public static bool TryNavigate(BaseEventData eventData, OverridableNavigation overrideNavigation)
{ {
if (!overrideNavigation.Override) if(!overrideNavigation.Override)
return false; return false;
if (overrideNavigation.Selectable == null || !overrideNavigation.Selectable.IsActive()) if (overrideNavigation.Selectable == null || !overrideNavigation.Selectable.IsActive())

View File

@ -1,12 +1,14 @@
using NEG.UI.UnityUi.Window; using KBCore.Refs;
using NEG.UI.UnityUi.Window;
using NEG.UI.Window; using NEG.UI.Window;
using UnityEngine; using UnityEngine;
namespace NEG.UI.UnityUi.Buttons.Reactions namespace NEG.UI.UnityUi.Buttons
{ {
public class OpenAsCurrentMainChild : ButtonReaction public class OpenAsCurrentMainChild : ButtonReaction
{ {
[SerializeField] private MonoWindow windowToOpen; [SerializeField]
private MonoWindow windowToOpen;
protected override void OnClicked() => UiManager.Instance.CurrentMainWindow.OpenAsChild(windowToOpen); protected override void OnClicked() => UiManager.Instance.CurrentMainWindow.OpenAsChild(windowToOpen);
} }

View File

@ -1,17 +1,18 @@
using NEG.UI.UnityUi.Window; using NEG.UI.UnityUi.Window;
using NEG.UI.UnityUi.WindowSlot; using NEG.UI.UnityUi.WindowSlot;
using NEG.UI.Window; using System;
using UnityEngine; using UnityEngine;
using NEG.UI.Window;
using NEG.UI.WindowSlot;
namespace NEG.UI.UnityUi.Buttons.Reactions namespace NEG.UI.UnityUi.Buttons
{ {
[RequireComponent(typeof(BaseButton))] [RequireComponent(typeof(BaseButton))]
public class OpenWindow : ButtonReaction public class OpenWindow : ButtonReaction
{ {
[SerializeField] private MonoWindow window; [SerializeField] private MonoWindow window;
[Header("Open on default area slot if empty")]
[Header("Open on default area slot if empty")] [SerializeField] [SerializeField] private MonoWindowSlot slot;
private MonoWindowSlot slot;
protected override void OnClicked() => window.Open(slot); protected override void OnClicked() => window.Open(slot);
} }

View File

@ -1,12 +1,13 @@
using NEG.UI.UnityUi.Buttons.Settings; using NEG.UI.UnityUi.Buttons.Settings;
using System; using System;
using UnityEngine.EventSystems;
namespace NEG.UI.UnityUi.Buttons.Behaviours namespace NEG.UI.UnityUi.Buttons.Reaction
{ {
public abstract class ButtonElementBehaviour : IDisposable public abstract class ButtonElementBehaviour : IDisposable
{ {
protected readonly BaseButton button;
protected SettingData baseData; protected SettingData baseData;
protected readonly BaseButton button;
public ButtonElementBehaviour(BaseButton baseButton, SettingData settingData) public ButtonElementBehaviour(BaseButton baseButton, SettingData settingData)
{ {
@ -14,8 +15,8 @@ namespace NEG.UI.UnityUi.Buttons.Behaviours
baseData = settingData; baseData = settingData;
} }
public abstract void Dispose();
public virtual void ChangeData(SettingData newData) => baseData = newData; public virtual void ChangeData(SettingData newData) => baseData = newData;
public abstract void Dispose();
} }
} }

View File

@ -1,8 +1,9 @@
using NEG.UI.UnityUi.Buttons.Settings; using NEG.UI.UnityUi.Buttons.Settings;
using NEG.Utils; using NEG.Utils;
using UnityEngine; using UnityEngine;
using UnityEngine.EventSystems;
namespace NEG.UI.UnityUi.Buttons.Behaviours namespace NEG.UI.UnityUi.Buttons.Reaction
{ {
public class ChangeTextColorBehaviour : ButtonElementBehaviour public class ChangeTextColorBehaviour : ButtonElementBehaviour
{ {
@ -37,5 +38,6 @@ namespace NEG.UI.UnityUi.Buttons.Behaviours
private void OnButtonSelected(bool _) => button.Text.color = data.SelectedColor; private void OnButtonSelected(bool _) => button.Text.color = data.SelectedColor;
private void OnButtonDeselected(bool _) => button.Text.color = data.DeselectedColor; private void OnButtonDeselected(bool _) => button.Text.color = data.DeselectedColor;
} }
} }

View File

@ -1,5 +1,8 @@
#if FMOD using NEG.UI.UnityUi.Buttons.Settings;
namespace NEG.UI.UnityUi.Buttons.Behaviours using NEG.Utils;
#if FMOD
namespace NEG.UI.UnityUi.Buttons.Reaction
{ {
public class SimpleSoundBehaviour : ButtonElementBehaviour public class SimpleSoundBehaviour : ButtonElementBehaviour
{ {

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: ccaf0d2f63be41f6956471dcd9c210d9
timeCreated: 1709465582

View File

@ -1,16 +0,0 @@
using UnityEditor;
using UnityEngine;
namespace NEG.UI.UnityUi.Buttons.Reactions
{
public class ExitGame : ButtonReaction
{
protected override void OnClicked()
{
#if UNITY_EDITOR
EditorApplication.isPlaying = false;
#endif
Application.Quit();
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 185f765a5bf7487ab85a7d95fc0ef2c7
timeCreated: 1709473462

View File

@ -1,5 +1,8 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine; using UnityEngine;
using UnityEngine.UI;
namespace NEG.UI.UnityUi.Buttons.Settings namespace NEG.UI.UnityUi.Buttons.Settings
{ {
@ -9,7 +12,10 @@ namespace NEG.UI.UnityUi.Buttons.Settings
public void Apply(BaseButton button) public void Apply(BaseButton button)
{ {
foreach (var setting in settingDatas) setting.Apply(button); foreach (var setting in settingDatas)
{
setting.Apply(button);
}
} }
[ContextMenu("Refresh")] [ContextMenu("Refresh")]
@ -17,7 +23,10 @@ namespace NEG.UI.UnityUi.Buttons.Settings
{ {
settingDatas.Clear(); settingDatas.Clear();
var components = GetComponents<SettingData>(); var components = GetComponents<SettingData>();
foreach (var data in components) settingDatas.Add(data); foreach (var data in components)
{
settingDatas.Add(data);
}
} }
} }
} }

View File

@ -1,16 +1,20 @@
using KBCore.Refs; using KBCore.Refs;
using System;
using UnityEngine; using UnityEngine;
using UnityEngine.Serialization;
namespace NEG.UI.UnityUi.Buttons.Settings namespace NEG.UI.UnityUi.Buttons.Settings
{ {
public abstract class SettingData : MonoBehaviour public abstract class SettingData : MonoBehaviour
{ {
[field: SerializeField] public string Key { get; private set; } [field: SerializeField] public string Key { get; private set; }
[SerializeField] [Self(Flag.Optional)] private BaseButton attachedButton; [SerializeField, Self(Flag.Optional)] private BaseButton attachedButton;
public virtual void Apply(BaseButton button) => button.AddOrOverrideSetting(this);
private void Awake() private void Awake()
{ {
if (attachedButton != null) if(attachedButton != null)
Apply(attachedButton); Apply(attachedButton);
} }
@ -20,7 +24,5 @@ namespace NEG.UI.UnityUi.Buttons.Settings
if (attachedButton == null && TryGetComponent(out ButtonSettings settings)) if (attachedButton == null && TryGetComponent(out ButtonSettings settings))
settings.Refresh(); settings.Refresh();
} }
public virtual void Apply(BaseButton button) => button.AddOrOverrideSetting(this);
} }
} }

View File

@ -5,11 +5,23 @@ using System.Collections.Generic;
using TMPro; using TMPro;
using UnityEngine; using UnityEngine;
namespace NEG.UI.UnityUi namespace NEG.UI.UnityUi
{ {
[PublicAPI] [PublicAPI]
public class CarouselList : MonoBehaviour public class CarouselList : MonoBehaviour
{ {
public event Action<int> OnSelectedItemChanged;
/// <summary>
/// Current option
/// </summary>
public string CurrentOption { get; private set; }
/// <summary>
/// Current selected option id
/// </summary>
public int CurrentOptionId { get; private set; }
[SerializeField] private BaseButton nextButton; [SerializeField] private BaseButton nextButton;
[SerializeField] private BaseButton prevButton; [SerializeField] private BaseButton prevButton;
[SerializeField] private TMP_Text currentOptionText; [SerializeField] private TMP_Text currentOptionText;
@ -17,31 +29,7 @@ namespace NEG.UI.UnityUi
private List<string> options; private List<string> options;
/// <summary> /// <summary>
/// Current option /// Sets new options list, automatically first will be selected.
/// </summary>
public string CurrentOption { get; private set; }
/// <summary>
/// Current selected option id
/// </summary>
public int CurrentOptionId { get; private set; }
private void Awake()
{
nextButton.OnButtonPressed += SelectNextOption;
prevButton.OnButtonPressed += SelectPrevOption;
}
private void OnDestroy()
{
nextButton.OnButtonPressed -= SelectNextOption;
prevButton.OnButtonPressed -= SelectPrevOption;
}
public event Action<int> OnSelectedItemChanged;
/// <summary>
/// Sets new options list, automatically first will be selected.
/// </summary> /// </summary>
/// <param name="options">list of options names</param> /// <param name="options">list of options names</param>
public void SetOptions(List<string> options) public void SetOptions(List<string> options)
@ -54,7 +42,7 @@ namespace NEG.UI.UnityUi
public void SelectPrevOption() => ChangeOption(false); public void SelectPrevOption() => ChangeOption(false);
/// <summary> /// <summary>
/// Selects option with provided id. /// Selects option with provided id.
/// </summary> /// </summary>
/// <param name="option">option number</param> /// <param name="option">option number</param>
public void SelectOption(int option) public void SelectOption(int option)
@ -64,7 +52,6 @@ namespace NEG.UI.UnityUi
Debug.LogError("Invalid option number"); Debug.LogError("Invalid option number");
return; return;
} }
CurrentOptionId = option; CurrentOptionId = option;
CurrentOption = options[option]; CurrentOption = options[option];
currentOptionText.text = CurrentOption; currentOptionText.text = CurrentOption;
@ -72,7 +59,7 @@ namespace NEG.UI.UnityUi
} }
/// <summary> /// <summary>
/// Select option with provided value. Use with caution, better use <see cref="SelectOption(int)" />. /// Select option with provided value. Use with caution, better use <see cref="SelectOption(int)"/>.
/// </summary> /// </summary>
/// <param name="option">option value to select</param> /// <param name="option">option value to select</param>
public void SelectOption(string option) public void SelectOption(string option)
@ -93,7 +80,18 @@ namespace NEG.UI.UnityUi
SelectOption(index); SelectOption(index);
} }
private void ChangeOption(bool next) => private void Awake()
SelectOption((CurrentOptionId + (next ? 1 : -1) + options.Count) % options.Count); {
nextButton.OnButtonPressed += SelectNextOption;
prevButton.OnButtonPressed += SelectPrevOption;
}
private void OnDestroy()
{
nextButton.OnButtonPressed -= SelectNextOption;
prevButton.OnButtonPressed -= SelectPrevOption;
}
private void ChangeOption(bool next) => SelectOption((CurrentOptionId + (next ? 1 : -1) + options.Count) % options.Count);
} }
} }

View File

@ -1,4 +1,6 @@
using NEG.UI.UnityUi.Buttons; using NEG.UI.UnityUi.Buttons;
using System.Collections.Generic;
using System.IO;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
@ -35,5 +37,6 @@ namespace NEG.UI.UnityUi.Editor
scriptProperty.objectReferenceValue = chosenTextAsset; scriptProperty.objectReferenceValue = chosenTextAsset;
so.ApplyModifiedProperties(); so.ApplyModifiedProperties();
} }
} }
} }

View File

@ -1,16 +1,16 @@
using NEG.UI.UnityUi.Buttons; using UnityEditor;
using UnityEditor;
using UnityEditor.UI; using UnityEditor.UI;
using UnityEngine.UIElements;
namespace NEG.UI.UnityUi.Editor namespace NEG.UI.UnityUi.Editor
{ {
[CustomEditor(typeof(CustomNavigationButton), true)] [CustomEditor(typeof(Buttons.CustomNavigationButton), true)]
public class CustomNavigationButtonEditor : ButtonEditor public class CustomNavigationButtonEditor : ButtonEditor
{ {
private SerializedProperty upOverrideProperty;
private SerializedProperty downOverrideProperty; private SerializedProperty downOverrideProperty;
private SerializedProperty leftOverrideProperty; private SerializedProperty leftOverrideProperty;
private SerializedProperty rightOverrideProperty; private SerializedProperty rightOverrideProperty;
private SerializedProperty upOverrideProperty;
protected override void OnEnable() protected override void OnEnable()
{ {

View File

@ -1,21 +1,21 @@
{ {
"name": "NEG.UI.UnityUi.Editor", "name": "NEG.UI.UnityUi.Editor",
"rootNamespace": "", "rootNamespace": "",
"references": [ "references": [
"GUID:e2aaf8effe1c9634d87b2edda6988a6a", "GUID:e2aaf8effe1c9634d87b2edda6988a6a",
"GUID:5928dc8d9173fd348aa77d4593ca3fd8" "GUID:5928dc8d9173fd348aa77d4593ca3fd8"
], ],
"includePlatforms": [ "includePlatforms": [
"Editor" "Editor"
], ],
"excludePlatforms": [], "excludePlatforms": [],
"allowUnsafeCode": false, "allowUnsafeCode": false,
"overrideReferences": false, "overrideReferences": false,
"precompiledReferences": [ "precompiledReferences": [
"" ""
], ],
"autoReferenced": true, "autoReferenced": true,
"defineConstraints": [], "defineConstraints": [],
"versionDefines": [], "versionDefines": [],
"noEngineReferences": false "noEngineReferences": false
} }

View File

@ -6,12 +6,13 @@ using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityEngine.UIElements; using UnityEngine.UIElements;
using ObjectField = UnityEditor.Search.ObjectField; using ObjectField = UnityEditor.Search.ObjectField;
using Toggle = UnityEngine.UIElements.Toggle; using Toggle = UnityEngine.UIElements.Toggle;
namespace NEG.UI.UnityUi.Editor namespace NEG.UI.UnityUi.Editor
{ {
[CustomPropertyDrawer(typeof(OverridableNavigation))] [CustomPropertyDrawer(typeof(OverridableNavigation))]
public class OverridableNavigationDrawer : PropertyDrawer public class OverridableNavigationDrawer: PropertyDrawer
{ {
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{ {
@ -29,10 +30,8 @@ namespace NEG.UI.UnityUi.Editor
var unitRect = new Rect(position.x + 35, position.y, 200, position.height); var unitRect = new Rect(position.x + 35, position.y, 200, position.height);
// Draw fields - pass GUIContent.none to each so they are drawn without labels // Draw fields - pass GUIContent.none to each so they are drawn without labels
EditorGUI.PropertyField(amountRect, EditorGUI.PropertyField(amountRect, property.FindAutoPropertyRelative(nameof(OverridableNavigation.Override)), GUIContent.none);
property.FindAutoPropertyRelative(nameof(OverridableNavigation.Override)), GUIContent.none); EditorGUI.PropertyField(unitRect, property.FindAutoPropertyRelative(nameof(OverridableNavigation.Selectable)), GUIContent.none);
EditorGUI.PropertyField(unitRect,
property.FindAutoPropertyRelative(nameof(OverridableNavigation.Selectable)), GUIContent.none);
// Set indent back to what it was // Set indent back to what it was
EditorGUI.indentLevel = indent; EditorGUI.indentLevel = indent;
@ -42,7 +41,7 @@ namespace NEG.UI.UnityUi.Editor
public override VisualElement CreatePropertyGUI(SerializedProperty property) public override VisualElement CreatePropertyGUI(SerializedProperty property)
{ {
var container = new VisualElement var container = new VisualElement()
{ {
style = style =
{ {
@ -54,9 +53,12 @@ namespace NEG.UI.UnityUi.Editor
}; };
string name = property.name; string name = property.name;
if (name.Length > 0) name = $"{char.ToUpper(name[0])}{name[1..]}"; if (name.Length > 0)
{
name = $"{char.ToUpper(name[0])}{name[1..]}";
}
var innerContainer = new VisualElement var innerContainer = new VisualElement()
{ {
style = style =
{ {
@ -72,7 +74,13 @@ namespace NEG.UI.UnityUi.Editor
var enabler = new Toggle(); var enabler = new Toggle();
enabler.BindProperty(property.FindPropertyRelative("<Override>k__BackingField")); enabler.BindProperty(property.FindPropertyRelative("<Override>k__BackingField"));
var field = new ObjectField { style = { flexGrow = 100 } }; var field = new ObjectField()
{
style =
{
flexGrow = 100
}
};
var selectableField = property.FindAutoPropertyRelative(nameof(OverridableNavigation.Selectable)); var selectableField = property.FindAutoPropertyRelative(nameof(OverridableNavigation.Selectable));

View File

@ -1,13 +1,16 @@
using KBCore.Refs; using KBCore.Refs;
using NEG.UI.UnityUi.Window; using NEG.UI.UnityUi.Window;
using NegUtils.NEG.UI; using NegUtils.NEG.UI;
using System;
using UnityEngine; using UnityEngine;
namespace NEG.UI.UnityUi namespace NEG.UI.UnityUi
{ {
public abstract class MonoController : MonoBehaviour, IController public abstract class MonoController : MonoBehaviour, IController
{ {
[SerializeField] [Self] protected InterfaceRef<IControllable> controllable; public IControllable Controllable => controllable.Value;
[SerializeField, Self] protected InterfaceRef<IControllable> controllable;
protected MonoWindow ControllableAsWindow => (MonoWindow)controllable.Value; protected MonoWindow ControllableAsWindow => (MonoWindow)controllable.Value;
@ -19,7 +22,6 @@ namespace NEG.UI.UnityUi
} }
private void OnValidate() => this.ValidateRefs(); private void OnValidate() => this.ValidateRefs();
public IControllable Controllable => controllable.Value;
protected virtual void OnOpened(object data) { } protected virtual void OnOpened(object data) { }

View File

@ -1,5 +1,4 @@
using NEG.UI.UnityUi.Window; using UnityEngine;
using UnityEngine;
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
using UnityEngine.InputSystem; using UnityEngine.InputSystem;
using UnityEngine.InputSystem.UI; using UnityEngine.InputSystem.UI;
@ -12,20 +11,16 @@ namespace NEG.UI.UnityUi
Direction Direction
} }
public class UiInputModule public class UiInputModule { }
{
public SelectionSource CurrentSelectionSource { get; protected set; }
}
public class DefaultInputModule : UiInputModule public class DefaultInputModule : UiInputModule
{ {
public SelectionSource CurrentSelectionSource { get; private set; }
public DefaultInputModule() public DefaultInputModule()
{ {
var defaultActions = new DefaultInputActions(); var defaultActions = new DefaultInputActions();
InputActionReference.Create(defaultActions.UI.Navigate).action.performed += InputActionReference.Create(defaultActions.UI.Navigate).action.performed += (ctx) => OnSelectionChangeStarted();
ctx => OnSelectionChangeStarted();
InputActionReference.Create(defaultActions.UI.Cancel).action.performed +=
_ => UiManager.Instance.UseBack();
defaultActions.Enable(); defaultActions.Enable();
if (Gamepad.current != null) if (Gamepad.current != null)
@ -36,14 +31,14 @@ namespace NEG.UI.UnityUi
//var keyboardAction = new InputAction(binding: "/<Keyboard>/*"); //var keyboardAction = new InputAction(binding: "/<Keyboard>/*");
//keyboardAction.performed += (context) => CurrentInputSource = EInputSource.Keyboard; //keyboardAction.performed += (context) => CurrentInputSource = EInputSource.Keyboard;
//keyboardAction.Enable(); //keyboardAction.Enable();
//var gamepadAction = new InputAction(binding: "/<Gamepad>/*"); var gamepadAction = new InputAction(binding: "/<Gamepad>/*");
//gamepadAction.performed += (context) => OnSelectionChangeStarted(); gamepadAction.performed += (context) => OnSelectionChangeStarted();
//gamepadAction.Enable(); gamepadAction.Enable();
var mouseAction = new InputAction(binding: "/<Mouse>/*"); var mouseAction = new InputAction(binding: "/<Mouse>/*");
mouseAction.performed += context => mouseAction.performed += (context) =>
{ {
if (CurrentSelectionSource == SelectionSource.Pointer) if(CurrentSelectionSource == SelectionSource.Pointer)
return; return;
SetPointerInput(); SetPointerInput();
}; };
@ -52,36 +47,15 @@ namespace NEG.UI.UnityUi
private void OnSelectionChangeStarted() private void OnSelectionChangeStarted()
{ {
if (CurrentSelectionSource == SelectionSource.Direction && if(CurrentSelectionSource == SelectionSource.Direction)
EventSystem.current.currentSelectedGameObject != null)
return; return;
SetDirectionInput(); SetDirectionInput();
} }
private void SetDirectionInput() private void SetDirectionInput()
{ {
if (EventSystem.current == null || MonoUiManager.Instance == null) return;
CurrentSelectionSource = SelectionSource.Direction; CurrentSelectionSource = SelectionSource.Direction;
Cursor.visible = false; Cursor.visible = false;
if (EventSystem.current.currentSelectedGameObject == null &&
MonoUiManager.Instance.CurrentMainWindow != null)
{
EventSystem.current.SetSelectedGameObject(((MonoWindow)MonoUiManager.Instance.CurrentMainWindow)
.DefaultSelectedItem);
return;
}
var data = new PointerEventData(EventSystem.current);
var currentSelected = EventSystem.current.currentSelectedGameObject;
if (currentSelected != null)
{
for (var current = EventSystem.current.currentSelectedGameObject.transform;
current != null;
current = current.parent)
ExecuteEvents.Execute(current.gameObject, data, ExecuteEvents.pointerExitHandler);
}
EventSystem.current.SetSelectedGameObject(currentSelected);
} }
private void SetPointerInput() private void SetPointerInput()
@ -98,18 +72,18 @@ namespace NEG.UI.UnityUi
if (EventSystem.current.currentInputModule == null) if (EventSystem.current.currentInputModule == null)
return; return;
EventSystem.current.SetSelectedGameObject(null);
var module = (InputSystemUIInputModule)EventSystem.current.currentInputModule; var module = (InputSystemUIInputModule)EventSystem.current.currentInputModule;
var result = module.GetLastRaycastResult(0); var result = module.GetLastRaycastResult(0);
if (result.gameObject == null) if(result.gameObject == null)
return; return;
var data = new PointerEventData(EventSystem.current); var data = new PointerEventData(EventSystem.current);
for (var current = result.gameObject.transform; for (var current = result.gameObject.transform;
current != null; current != null;
current = current.parent) current = current.parent)
{
ExecuteEvents.Execute(current.gameObject, data, ExecuteEvents.pointerEnterHandler); ExecuteEvents.Execute(current.gameObject, data, ExecuteEvents.pointerEnterHandler);
}
} }
} }
} }

View File

@ -1,38 +1,40 @@
using NEG.UI.Area; using NEG.UI.Area;
using NEG.UI.Popup; using NEG.UI.Popup;
using NEG.UI.UnityUi.Buttons.Behaviours; using NEG.UI.UnityUi.Buttons.Reaction;
using NEG.UI.UnityUi.Buttons.Settings; using NEG.UI.UnityUi.Buttons.Settings;
using NEG.UI.UnityUi.Popup; using NEG.UI.UnityUi.Popup;
using NEG.UI.UnityUi.Window;
using NEG.Utils; using NEG.Utils;
using System; using System;
using System.Collections.Generic;
using UnityEngine; using UnityEngine;
using UnityEngine.Assertions; using UnityEngine.Assertions;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement; using UnityEngine.SceneManagement;
using Object = UnityEngine.Object; using Object = UnityEngine.Object;
namespace NEG.UI.UnityUi namespace NEG.UI.UnityUi
{ {
/// <summary> /// <summary>
/// Implements ui using UnityUI and Unity Event System with New Input System. /// Implements ui using UnityUI and Unity Event System with New Input System.
/// <para>You have to provide prefabs within resources:</para> /// <para>You have to provide prefabs within resources:</para>
/// <para> - UI/PopupCanvas - prefab with canvas to create popups (will be created on every scene)</para> /// <para> - UI/PopupCanvas - prefab with canvas to create popups (will be created on every scene)</para>
/// <para> /// <para> - UI/DefaultPopupPrefab - prefab of default popup with 2 options (has to have <see cref="MonoDefaultPopup"/> component)</para>
/// - UI/DefaultPopupPrefab - prefab of default popup with 2 options (has to have /// NEG_UI_DISABLE_WARNING_DEFAULT_SELECTION
/// <see cref="MonoDefaultPopup" /> component)
/// </para>
/// NEG_UI_DISABLE_WARNING_DEFAULT_SELECTION
/// </summary> /// </summary>
public class MonoUiManager : UiManager, IDisposable public class MonoUiManager : UiManager, IDisposable
{ {
private readonly GameObject canvasPrefab; //TODO: use default unity selection
//TODO: window snaping to slots
public static new MonoUiManager Instance { get; private set; }
public ButtonSettings DefaultUiSettings { get; }
public KeyBasedFactory<string, ButtonElementBehaviour> BehavioursFactory { get; private set; }
//TODO: editor to auto add slots, buttons //TODO: editor to auto add slots, buttons
private readonly MonoDefaultPopup defaultPopupPrefab; private readonly MonoDefaultPopup defaultPopupPrefab;
private readonly GameObject canvasPrefab;
private readonly UiInputModule inputModule; private UiInputModule inputModule;
public MonoUiManager(IArea startArea, Type inputModuleType, ButtonSettings defaultUiSettings) : base(startArea) public MonoUiManager(IArea startArea, Type inputModuleType, ButtonSettings defaultUiSettings) : base(startArea)
{ {
@ -41,10 +43,8 @@ namespace NEG.UI.UnityUi
var popupCanvas = Resources.Load<GameObject>("UI/PopupCanvas"); var popupCanvas = Resources.Load<GameObject>("UI/PopupCanvas");
var defaultPopup = Resources.Load<GameObject>("UI/DefaultPopupPrefab"); var defaultPopup = Resources.Load<GameObject>("UI/DefaultPopupPrefab");
Assert.IsNotNull(popupCanvas, Assert.IsNotNull(popupCanvas,"No canvas prefab was provided. Please check MonoUiManager class documentation");
"No canvas prefab was provided. Please check MonoUiManager class documentation"); Assert.IsNotNull(defaultPopup,"No popup prefab was provided. Please check MonoUiManager class documentation");
Assert.IsNotNull(defaultPopup,
"No popup prefab was provided. Please check MonoUiManager class documentation");
Assert.IsNotNull(popupCanvas.GetComponent<Canvas>()); Assert.IsNotNull(popupCanvas.GetComponent<Canvas>());
Assert.IsNotNull(defaultPopup.GetComponent<MonoDefaultPopup>()); Assert.IsNotNull(defaultPopup.GetComponent<MonoDefaultPopup>());
@ -62,34 +62,12 @@ namespace NEG.UI.UnityUi
DefaultUiSettings = defaultUiSettings; DefaultUiSettings = defaultUiSettings;
} }
//TODO: use default unity selection
//TODO: window snaping to slots
public static new MonoUiManager Instance { get; private set; }
public ButtonSettings DefaultUiSettings { get; }
public KeyBasedFactory<string, ButtonElementBehaviour> BehavioursFactory { get; }
public override void Dispose() public override void Dispose()
{ {
base.Dispose(); base.Dispose();
Instance = null; Instance = null;
} }
protected override void UpdatePopupsState(bool forceShow, int priority = 0, PopupData data = null)
{
base.UpdatePopupsState(forceShow, priority, data);
if (inputModule.CurrentSelectionSource != SelectionSource.Direction)
return;
if (CurrentPopup == null && (EventSystem.current.currentSelectedGameObject == null ||
!EventSystem.current.currentSelectedGameObject.activeInHierarchy))
{
if (((MonoWindow)CurrentMainWindow).DefaultSelectedItem == null)
return;
EventSystem.current.SetSelectedGameObject(((MonoWindow)CurrentMainWindow).DefaultSelectedItem);
}
}
private void SpawnDefaultPopup() private void SpawnDefaultPopup()
{ {
var canvas = Object.Instantiate(canvasPrefab); var canvas = Object.Instantiate(canvasPrefab);

View File

@ -1,25 +1,25 @@
{ {
"name": "NEG.UI.UnityUi", "name": "NEG.UI.UnityUi",
"rootNamespace": "", "rootNamespace": "",
"references": [ "references": [
"GUID:343deaaf83e0cee4ca978e7df0b80d21", "GUID:343deaaf83e0cee4ca978e7df0b80d21",
"GUID:7361f1d9c43da6649923760766194746", "GUID:7361f1d9c43da6649923760766194746",
"GUID:6055be8ebefd69e48b49212b09b47b2f", "GUID:6055be8ebefd69e48b49212b09b47b2f",
"GUID:23eed6c2401dca1419d1ebd180e58c5a", "GUID:23eed6c2401dca1419d1ebd180e58c5a",
"GUID:33759803a11f4d538227861a78aba30b", "GUID:33759803a11f4d538227861a78aba30b",
"GUID:0c752da273b17c547ae705acf0f2adf2", "GUID:0c752da273b17c547ae705acf0f2adf2",
"GUID:3c4294719a93e3c4e831a9ff0c261e8a", "GUID:3c4294719a93e3c4e831a9ff0c261e8a",
"GUID:75469ad4d38634e559750d17036d5f7c" "GUID:75469ad4d38634e559750d17036d5f7c"
], ],
"includePlatforms": [], "includePlatforms": [],
"excludePlatforms": [], "excludePlatforms": [],
"allowUnsafeCode": false, "allowUnsafeCode": false,
"overrideReferences": false, "overrideReferences": false,
"precompiledReferences": [ "precompiledReferences": [
"" ""
], ],
"autoReferenced": true, "autoReferenced": true,
"defineConstraints": [], "defineConstraints": [],
"versionDefines": [], "versionDefines": [],
"noEngineReferences": false "noEngineReferences": false
} }

View File

@ -16,7 +16,10 @@ namespace NEG.UI.UnityUi.Popup
public void SetContent(string title, string content, List<(string, Action)> options) public void SetContent(string title, string content, List<(string, Action)> options)
{ {
foreach (Transform child in buttonsParent) Destroy(child.gameObject); foreach (Transform child in buttonsParent)
{
Destroy(child.gameObject);
}
titleText.text = title; titleText.text = title;
contentText.text = content; contentText.text = content;

View File

@ -6,9 +6,10 @@ namespace NEG.UI.UnityUi.Popup
{ {
public class MonoPopup : MonoBehaviour, IPopup public class MonoPopup : MonoBehaviour, IPopup
{ {
protected PopupData data;
public event Action<PopupData> OnPopupClosed; public event Action<PopupData> OnPopupClosed;
protected PopupData data;
public virtual void Show(PopupData data) public virtual void Show(PopupData data)
{ {
this.data = data; this.data = data;
@ -19,10 +20,11 @@ namespace NEG.UI.UnityUi.Popup
{ {
gameObject.SetActive(false); gameObject.SetActive(false);
if (silent) if(silent)
return; return;
OnPopupClosed?.Invoke(data); OnPopupClosed?.Invoke(data);
} }
} }
} }

View File

@ -7,7 +7,7 @@ namespace NEG.UI.UnityUi.Window
{ {
public class CloseWindowOnBack : MonoController public class CloseWindowOnBack : MonoController
{ {
[SerializeField] [Self(Flag.Editable)] private MonoWindow window; [SerializeField, Self(Flag.Editable)] private MonoWindow window;
protected override void OnBackUsed(IControllable.BackUsed backUsed) protected override void OnBackUsed(IControllable.BackUsed backUsed)
{ {

View File

@ -1,4 +1,6 @@
using NEG.UI.UnityUi.WindowSlot; using NEG.UI.Area;
using NEG.UI.UnityUi.Buttons;
using NEG.UI.UnityUi.WindowSlot;
using NEG.UI.Window; using NEG.UI.Window;
using NEG.UI.WindowSlot; using NEG.UI.WindowSlot;
using NegUtils.NEG.UI; using NegUtils.NEG.UI;
@ -6,41 +8,12 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
using UnityEngine.Serialization;
namespace NEG.UI.UnityUi.Window namespace NEG.UI.UnityUi.Window
{ {
[DefaultExecutionOrder(10)]
public class MonoWindow : MonoBehaviour, IWindow public class MonoWindow : MonoBehaviour, IWindow
{ {
[SerializeField] private List<MonoWindowSlot> windowSlots;
[SerializeField] private GameObject defaultSelectedItem;
public bool IsMainWindow { get; }
public bool IsOpened { get; protected set; }
private IWindowSlot DefaultWindowSlot => windowSlots[0];
public GameObject DefaultSelectedItem => defaultSelectedItem;
private void Awake() => ((IWindow)this).SetHiddenState();
private void OnDestroy()
{
if (UiManager.Instance == null)
return;
if (IsOpened)
UiManager.Instance.OnWindowClosed(this);
}
private void OnValidate()
{
#if !NEG_UI_DISABLE_WARNING_DEFAULT_SELECTION
if (defaultSelectedItem == null)
Debug.LogWarning($"Window {name} should have default selected item set");
#endif
}
public event Action<object> OnOpened; public event Action<object> OnOpened;
public event Action OnClosed; public event Action OnClosed;
public event Action<IControllable.BackUsed> OnBackUsed; public event Action<IControllable.BackUsed> OnBackUsed;
@ -48,10 +21,17 @@ namespace NEG.UI.UnityUi.Window
public IEnumerable<IWindowSlot> AvailableSlots => windowSlots; public IEnumerable<IWindowSlot> AvailableSlots => windowSlots;
public IWindowSlot Parent { get; private set; } public IWindowSlot Parent { get; private set; }
public bool IsMainWindow { get; private set; }
private IWindowSlot DefaultWindowSlot => windowSlots[0];
[SerializeField] private List<MonoWindowSlot> windowSlots;
[SerializeField] private GameObject defaultSelectedItem;
public void SetOpenedState(IWindowSlot parentSlot, object data) public void SetOpenedState(IWindowSlot parentSlot, object data)
{ {
gameObject.SetActive(true); gameObject.SetActive(true);
IsOpened = true;
Parent = parentSlot; Parent = parentSlot;
EventSystem.current.SetSelectedGameObject(defaultSelectedItem); EventSystem.current.SetSelectedGameObject(defaultSelectedItem);
if (parentSlot.OpenWindowAsMain) if (parentSlot.OpenWindowAsMain)
@ -62,7 +42,6 @@ namespace NEG.UI.UnityUi.Window
public void SetClosedState() public void SetClosedState()
{ {
gameObject.SetActive(false); gameObject.SetActive(false);
IsOpened = false;
Parent = null; Parent = null;
((ISlotsHolder)this).CloseAllWindows(); ((ISlotsHolder)this).CloseAllWindows();
UiManager.Instance.OnWindowClosed(this); UiManager.Instance.OnWindowClosed(this);
@ -73,10 +52,20 @@ namespace NEG.UI.UnityUi.Window
public void SeVisibleState() => gameObject.SetActive(true); public void SeVisibleState() => gameObject.SetActive(true);
private void Awake() => ((IWindow)this).SetHiddenState();
private void OnValidate()
{
#if !NEG_UI_DISABLE_WARNING_DEFAULT_SELECTION
if(defaultSelectedItem == null)
Debug.LogWarning($"Window {name} should have default selected item set");
#endif
}
public void OpenWindow(IWindow window, object data = null) => DefaultWindowSlot.AttachWindow(window, data); public void OpenWindow(IWindow window, object data = null) => DefaultWindowSlot.AttachWindow(window, data);
public void TryUseBack(ref IControllable.BackUsed backUsed) => OnBackUsed?.Invoke(backUsed);
public void SetDefaultSelectedItem(GameObject item) => defaultSelectedItem = item; public void SetDefaultSelectedItem(GameObject item) => defaultSelectedItem = item;
public void TryUseBack(ref IControllable.BackUsed backUsed) => OnBackUsed?.Invoke(backUsed);
} }
} }

View File

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

View File

@ -1,13 +0,0 @@
using NegUtils.NEG.UI;
namespace NEG.UI.UnityUi.Window
{
public class NoReactionOnBack : MonoController
{
protected override void OnBackUsed(IControllable.BackUsed backUsed)
{
base.OnBackUsed(backUsed);
backUsed.Used = true;
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: c4a5380c95244c76ac79f820d16ea11c
timeCreated: 1702565120

View File

@ -1,18 +1,22 @@
using NEG.UI.Window; using KBCore.Refs;
using NEG.UI.Area;
using NEG.UI.Window;
using NEG.UI.WindowSlot; using NEG.UI.WindowSlot;
using TNRD; using System;
using UnityEngine; using UnityEngine;
using TNRD;
namespace NEG.UI.UnityUi.WindowSlot namespace NEG.UI.UnityUi.WindowSlot
{ {
public abstract class MonoWindowSlot : MonoBehaviour, IWindowSlot public abstract class MonoWindowSlot : MonoBehaviour, IWindowSlot
{ {
[SerializeField] private SerializableInterface<ISlotsHolder> slotsHolder;
[field: SerializeField] public bool OpenWindowAsMain { get; private set; } [field: SerializeField] public bool OpenWindowAsMain { get; private set; }
public ISlotsHolder ParentHolder => slotsHolder.Value; public ISlotsHolder ParentHolder => slotsHolder.Value;
public abstract void AttachWindow(IWindow window, object data); public abstract void AttachWindow(IWindow window, object data);
public abstract void DetachWindow(IWindow window); public abstract void DetachWindow(IWindow window);
public abstract void CloseAllWindows(); public abstract void CloseAllWindows();
[SerializeField] private SerializableInterface<ISlotsHolder> slotsHolder;
} }
} }

View File

@ -1,12 +1,11 @@
using NEG.UI.UnityUi.WindowSlot; using NEG.UI.UnityUi.WindowSlot;
using NEG.UI.Window; using NEG.UI.Window;
using UnityEngine;
namespace NEG.UI.WindowSlot namespace NEG.UI.WindowSlot
{ {
public class SingleWindowSlot : MonoWindowSlot public class SingleWindowSlot : MonoWindowSlot
{ {
private IWindow currentWindow;
public IWindow CurrentWindow public IWindow CurrentWindow
{ {
get => currentWindow; get => currentWindow;
@ -17,19 +16,15 @@ namespace NEG.UI.WindowSlot
} }
} }
private IWindow currentWindow;
public override void AttachWindow(IWindow window, object data) public override void AttachWindow(IWindow window, object data)
{ {
CurrentWindow = window; CurrentWindow = window;
window.SetOpenedState(this, data); window.SetOpenedState(this, data);
} }
public override void DetachWindow(IWindow window) public override void DetachWindow(IWindow window) => CurrentWindow = null;
{
if (UiManager.Instance.CurrentMainWindow == window)
UiManager.Instance.MainWindowClosed(window);
CurrentWindow = null;
}
public override void CloseAllWindows() => CurrentWindow = null; public override void CloseAllWindows() => CurrentWindow = null;
} }
} }

View File

@ -1,18 +1,12 @@
using NEG.UI; using NEG.UI.UnityUi.WindowSlot;
using NEG.UI.UnityUi.Window;
using NEG.UI.UnityUi.WindowSlot;
using NEG.UI.Window; using NEG.UI.Window;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine.EventSystems; using System.Linq;
namespace NegUtils.NEG.UI.UnityUi.WindowSlot namespace NegUtils.NEG.UI.UnityUi.WindowSlot
{ {
public class SingleWindowSlotWithHistory : MonoWindowSlot public class SingleWindowSlotWithHistory : MonoWindowSlot
{ {
private readonly List<IWindow> windowsHistory = new();
private IWindow currentWindow;
public IWindow CurrentWindow public IWindow CurrentWindow
{ {
get => currentWindow; get => currentWindow;
@ -30,6 +24,10 @@ namespace NegUtils.NEG.UI.UnityUi.WindowSlot
} }
} }
private IWindow currentWindow;
private readonly List<IWindow> windowsHistory = new List<IWindow>();
public override void AttachWindow(IWindow window, object data) public override void AttachWindow(IWindow window, object data)
{ {
CurrentWindow = window; CurrentWindow = window;
@ -38,22 +36,21 @@ namespace NegUtils.NEG.UI.UnityUi.WindowSlot
public override void DetachWindow(IWindow window) public override void DetachWindow(IWindow window)
{ {
if (window == null) if(window == null)
return; return;
window.SetClosedState(); window.SetClosedState();
windowsHistory.Remove(window); windowsHistory.Remove(window);
if (window != currentWindow || windowsHistory.Count == 0) return; if (window != currentWindow || windowsHistory.Count == 0) return;
windowsHistory[^1].SeVisibleState();
currentWindow = windowsHistory[^1]; currentWindow = windowsHistory[^1];
currentWindow.SeVisibleState();
if (UiManager.Instance.CurrentMainWindow == window)
UiManager.Instance.MainWindowClosed(window);
EventSystem.current.SetSelectedGameObject(((MonoWindow)currentWindow).DefaultSelectedItem);
} }
public override void CloseAllWindows() public override void CloseAllWindows()
{ {
currentWindow = null; currentWindow = null;
foreach (var window in windowsHistory) window.SetClosedState(); foreach (var window in windowsHistory)
{
window.SetClosedState();
}
windowsHistory.Clear(); windowsHistory.Clear();
} }
} }

View File

@ -1,4 +1,5 @@
using NEG.UI.Area; using JetBrains.Annotations;
using NEG.UI.Area;
using NEG.UI.WindowSlot; using NEG.UI.WindowSlot;
using NegUtils.NEG.UI; using NegUtils.NEG.UI;
using UnityEngine; using UnityEngine;
@ -8,29 +9,29 @@ namespace NEG.UI.Window
public interface IWindow : ISlotsHolder, IControllable public interface IWindow : ISlotsHolder, IControllable
{ {
/// <summary> /// <summary>
/// Parent slot of this window. /// Parent slot of this window.
/// </summary> /// </summary>
IWindowSlot Parent { get; } IWindowSlot Parent { get; }
/// <summary> /// <summary>
/// Called internally by slot to open window. /// Called internally by slot to open window.
/// </summary> /// </summary>
/// <param name="parentSlot">slot that opens window</param> /// <param name="parentSlot">slot that opens window</param>
/// <param name="data">data</param> /// <param name="data">data</param>
void SetOpenedState(IWindowSlot parentSlot, object data); void SetOpenedState(IWindowSlot parentSlot, object data);
/// <summary> /// <summary>
/// Called internally to close window by slot. /// Called internally to close window by slot.
/// </summary> /// </summary>
void SetClosedState(); void SetClosedState();
/// <summary> /// <summary>
/// Called internally to hide window by slot. /// Called internally to hide window by slot.
/// </summary> /// </summary>
void SetHiddenState(); void SetHiddenState();
/// <summary> /// <summary>
/// Called internally to set window visible by slot. /// Called internally to set window visible by slot.
/// </summary> /// </summary>
void SeVisibleState(); void SeVisibleState();
} }
@ -38,7 +39,7 @@ namespace NEG.UI.Window
public static class WindowInterfaceExtensions public static class WindowInterfaceExtensions
{ {
/// <summary> /// <summary>
/// Opens window as slot child. If slot is null or not provided, as child of current area. /// Opens window as slot child. If slot is null or not provided, as child of current area.
/// </summary> /// </summary>
/// <param name="window">window to open</param> /// <param name="window">window to open</param>
/// <param name="slot">slot to attach window</param> /// <param name="slot">slot to attach window</param>
@ -55,7 +56,7 @@ namespace NEG.UI.Window
} }
/// <summary> /// <summary>
/// Opens window as child of selected area. /// Opens window as child of selected area.
/// </summary> /// </summary>
/// <param name="window">window to open</param> /// <param name="window">window to open</param>
/// <param name="area">area to attach window</param> /// <param name="area">area to attach window</param>
@ -63,7 +64,7 @@ namespace NEG.UI.Window
public static void Open(this IWindow window, IArea area, object data = null) => area.OpenWindow(window, data); public static void Open(this IWindow window, IArea area, object data = null) => area.OpenWindow(window, data);
/// <summary> /// <summary>
/// Open passed window as child of this window. /// Open passed window as child of this window.
/// </summary> /// </summary>
/// <param name="window">parent window</param> /// <param name="window">parent window</param>
/// <param name="windowToOpen">window to open</param> /// <param name="windowToOpen">window to open</param>
@ -72,16 +73,14 @@ namespace NEG.UI.Window
{ {
if (windowToOpen == null) if (windowToOpen == null)
{ {
Debug.LogError("Window to open cannot be null"); Debug.LogError($"Window to open cannot be null");
return; return;
} }
window.OpenWindow(windowToOpen, data); window.OpenWindow(windowToOpen, data);
} }
/// <summary> /// <summary>
/// Open window as child of provided window. If <typeparamref name="parentWindow" /> is null, as child of current main /// Open window as child of provided window. If <typeparamref name="parentWindow"/> is null, as child of current main window in <see cref="UiManager"/>. If there is no main window, open on current area.
/// window in <see cref="UiManager" />. If there is no main window, open on current area.
/// </summary> /// </summary>
/// <param name="window">window to open</param> /// <param name="window">window to open</param>
/// <param name="parentWindow">parent window</param> /// <param name="parentWindow">parent window</param>
@ -104,7 +103,7 @@ namespace NEG.UI.Window
} }
/// <summary> /// <summary>
/// Close window. /// Close window.
/// </summary> /// </summary>
/// <param name="window">window to close</param> /// <param name="window">window to close</param>
public static void Close(this IWindow window) => window.Parent.DetachWindow(window); public static void Close(this IWindow window) => window.Parent.DetachWindow(window);

View File

@ -8,7 +8,7 @@ namespace NEG.UI.WindowSlot
IEnumerable<IWindowSlot> AvailableSlots { get; } IEnumerable<IWindowSlot> AvailableSlots { get; }
/// <summary> /// <summary>
/// Open window /// Open window
/// </summary> /// </summary>
/// <param name="window"></param> /// <param name="window"></param>
/// <param name="data"></param> /// <param name="data"></param>
@ -16,7 +16,10 @@ namespace NEG.UI.WindowSlot
void CloseAllWindows() void CloseAllWindows()
{ {
foreach (var slot in AvailableSlots) slot.CloseAllWindows(); foreach (var slot in AvailableSlots)
{
slot.CloseAllWindows();
}
} }
} }
} }

View File

@ -1,4 +1,5 @@
using NEG.UI.Window; using NEG.UI.Area;
using NEG.UI.Window;
namespace NEG.UI.WindowSlot namespace NEG.UI.WindowSlot
{ {

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: d0004992787c4a7ea519cf20f089657c
timeCreated: 1709376205

View File

@ -1,8 +0,0 @@
using UnityEngine;
namespace NEG.Utils
{
public class ReadOnlyAttribute : PropertyAttribute
{
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 8d9e0fab2ff2444cab62737cf994d519
timeCreated: 1709376222

View File

@ -1,85 +0,0 @@
using System;
using UnityEngine;
namespace NEG.Utils
{
[Serializable]
public struct SerializableGuid : IComparable, IComparable<SerializableGuid>, IEquatable<SerializableGuid>
{
[SerializeField] private uint value0;
[SerializeField] private uint value1;
[SerializeField] private uint value2;
[SerializeField] private uint value3;
// public SerializableGuid(string hexRepresentation)
// {
// value0 = 0U;
// value1 = 0U;
// value2 = 0U;
// value3 = 0U;
// TryParse(hexRepresentation, out this);
// }
public static bool operator ==(SerializableGuid x, SerializableGuid y)
{
return (int)x.value0 == (int)y.value0 && (int)x.value1 == (int)y.value1 &&
(int)x.value2 == (int)y.value2 && (int)x.value3 == (int)y.value3;
}
public static bool operator !=(SerializableGuid x, SerializableGuid y) => !(x == y);
public static bool operator <(SerializableGuid x, SerializableGuid y)
{
if ((int)x.value0 != (int)y.value0)
return x.value0 < y.value0;
if ((int)x.value1 != (int)y.value1)
return x.value1 < y.value1;
return (int)x.value2 != (int)y.value2 ? x.value2 < y.value2 : x.value3 < y.value3;
}
public static bool operator > (SerializableGuid x, SerializableGuid y) => !(x < y) && !(x == y);
public override bool Equals(object obj) => obj is SerializableGuid guid && Equals(guid);
public bool Equals(SerializableGuid obj) => this == obj;
public override int GetHashCode()
{
return ((((((int)value0 * 397) ^ (int)value1) * 397) ^ (int)value2) * 397) ^
(int)value3;
}
public int CompareTo(object obj) => obj == null ? 1 : CompareTo((SerializableGuid)obj);
public int CompareTo(SerializableGuid rhs)
{
if (this < rhs)
return -1;
return this > rhs ? 1 : 0;
}
public bool Empty() => value0 == 0U && value1 == 0U && value2 == 0U && value3 == 0U;
// public static bool TryParse(string hex, out SerializableGuid result)
// {
// result = HexToSerializableGuidInternal(hex);
// return !result.Empty();
// }
public static SerializableGuid Generate()
{
byte[] array = Guid.NewGuid().ToByteArray();
var guid = new SerializableGuid
{
value0 = BitConverter.ToUInt32(new ReadOnlySpan<byte>(array, 0, 4)),
value1 = BitConverter.ToUInt32(new ReadOnlySpan<byte>(array, 4, 4)),
value2 = BitConverter.ToUInt32(new ReadOnlySpan<byte>(array, 8, 4)),
value3 = BitConverter.ToUInt32(new ReadOnlySpan<byte>(array, 12, 4))
};
return guid;
}
//public override string ToString() => SerializableGuidToHexInternal(ref this);
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: a81e7c502db5413194d0ebfc7bad054e
timeCreated: 1741038901

8
RequireInterface.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: da80835a1611a5e4d908f51f09e8d3bc
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 849242dbc213969488426f85222a43a9
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,6 +1,4 @@
using System;
using UnityEngine; using UnityEngine;
/// <summary> /// <summary>
/// Attribute that require implementation of the provided interface. /// Attribute that require implementation of the provided interface.
/// </summary> /// </summary>
@ -9,16 +7,15 @@ namespace NEG.Utils
{ {
public class RequireInterfaceAttribute : PropertyAttribute public class RequireInterfaceAttribute : PropertyAttribute
{ {
// Interface type.
public System.Type requiredType { get; private set; }
/// <summary> /// <summary>
/// Requiring implementation of the <see cref="T:RequireInterfaceAttribute" /> interface. /// Requiring implementation of the <see cref="T:RequireInterfaceAttribute"/> interface.
/// </summary> /// </summary>
/// <param name="type">Interface type.</param> /// <param name="type">Interface type.</param>
public RequireInterfaceAttribute(Type type) public RequireInterfaceAttribute(System.Type type)
{ {
requiredType = type; requiredType = type;
} }
// Interface type.
public Type requiredType { get; private set; }
} }
} }

View File

@ -5,6 +5,12 @@ namespace NEG.Utils.Timing
{ {
public class AutoTimeMachine public class AutoTimeMachine
{ {
[PublicAPI]
public double Interval { get; set; }
[PublicAPI]
public Action Action { get; }
private readonly TimeMachine machine; private readonly TimeMachine machine;
public AutoTimeMachine(Action action, double interval) public AutoTimeMachine(Action action, double interval)
@ -14,19 +20,18 @@ namespace NEG.Utils.Timing
machine = new TimeMachine(); machine = new TimeMachine();
} }
[PublicAPI] public double Interval { get; set; }
[PublicAPI] public Action Action { get; }
/// <summary> /// <summary>
/// Forwards the time by given amount, triggers assigned action relevant amount of times /// Forwards the time by given amount, triggers assigned action relevant amount of times
/// </summary> /// </summary>
/// <param name="time">Amount of time to forward by</param> /// <param name="time">Amount of time to forward by</param>
public void Forward(double time) public void Forward(double time)
{ {
machine.Accumulate(time); machine.Accumulate(time);
int rolls = machine.RetrieveAll(Interval); int rolls = machine.RetrieveAll(Interval);
for (int i = 0; i < rolls; i++) Action(); for (int i = 0; i < rolls; i++)
{
Action();
}
} }
} }
} }

View File

@ -13,34 +13,32 @@ namespace NEG.Utils.Timing
} }
/// <summary> /// <summary>
/// Adds time into the TimeMachine /// Adds time into the TimeMachine
/// </summary> /// </summary>
/// <param name="time">Amount of time to be added</param> /// <param name="time">Amount of time to be added</param>
public void Accumulate(double time) => timeInternal += time; public void Accumulate(double time) => timeInternal += time;
/// <summary> /// <summary>
/// Retrieves given amount of time from the TimeMachine /// Retrieves given amount of time from the TimeMachine
/// </summary> /// </summary>
/// <param name="maxTime"></param> /// <param name="maxTime"></param>
/// <returns>Amount of time retrieved</returns> /// <returns>Amount of time retrieved</returns>
public double Retrieve(double maxTime) public double Retrieve(double maxTime)
{ {
if (!double.IsFinite(maxTime)) if(!double.IsFinite(maxTime))
{ {
double timeRetrieved = timeInternal; double timeRetrieved = timeInternal;
timeInternal = 0; timeInternal = 0;
return timeRetrieved; return timeRetrieved;
} }
double timeLeft = timeInternal - maxTime; double timeLeft = timeInternal - maxTime;
timeInternal = Math.Max(timeLeft, 0); timeInternal = Math.Max(timeLeft, 0);
return Math.Min(maxTime + timeLeft, maxTime); return Math.Min(maxTime + timeLeft, maxTime);
} }
/// <summary> /// <summary>
/// Attempts to retrieves given amount of time from the TimeMachine <br /> /// Attempts to retrieves given amount of time from the TimeMachine <br/>
/// If there is enough <paramref name="time" /> accumulated in this machine subtracts that amount and returns true, /// If there is enough <paramref name="time"/> accumulated in this machine subtracts that amount and returns true, otherwise returns false
/// otherwise returns false
/// </summary> /// </summary>
/// <param name="time"></param> /// <param name="time"></param>
public bool TryRetrieve(double time) public bool TryRetrieve(double time)
@ -52,8 +50,7 @@ namespace NEG.Utils.Timing
} }
/// <summary> /// <summary>
/// Result is equivalent to calling <see cref="TryRetrieve(double)" /> as many times as possible, but is faster for /// Result is equivalent to calling <see cref="TryRetrieve(double)"/> as many times as possible, but is faster for larger <paramref name="limit"/> values
/// larger <paramref name="limit" /> values
/// </summary> /// </summary>
/// <param name="interval">Single unit of warp time, must be positive</param> /// <param name="interval">Single unit of warp time, must be positive</param>
/// <param name="limit">Maximum amount of warps, must be positive</param> /// <param name="limit">Maximum amount of warps, must be positive</param>

View File

@ -1,4 +1,5 @@
using UnityEngine; using System.Collections;
using UnityEngine;
namespace NEG.Utils.UiToolkits namespace NEG.Utils.UiToolkits
{ {

View File

@ -4,10 +4,12 @@ namespace NEG.Utils.UiToolkits
{ {
public class MultiSelectChipItem public class MultiSelectChipItem
{ {
public VisualElement VisualElement { get; }
public IMultiSelectChipItem ChipItem { get; }
private readonly MultiSelectChips parent; private readonly MultiSelectChips parent;
public MultiSelectChipItem(VisualElement visualElement, IMultiSelectChipItem element, public MultiSelectChipItem(VisualElement visualElement, IMultiSelectChipItem element, MultiSelectChips multiSelectChips)
MultiSelectChips multiSelectChips)
{ {
VisualElement = visualElement; VisualElement = visualElement;
ChipItem = element; ChipItem = element;
@ -16,8 +18,5 @@ namespace NEG.Utils.UiToolkits
visualElement.Q<VisualElement>("Color").style.backgroundColor = element.Color; visualElement.Q<VisualElement>("Color").style.backgroundColor = element.Color;
visualElement.Q<Button>("RemoveBtn").clicked += () => parent.TryRemoveItem(element); visualElement.Q<Button>("RemoveBtn").clicked += () => parent.TryRemoveItem(element);
} }
public VisualElement VisualElement { get; }
public IMultiSelectChipItem ChipItem { get; }
} }
} }

View File

@ -1,7 +1,8 @@
using System;
using System.Collections.Generic;
using UnityEngine; using UnityEngine;
using UnityEngine.UIElements; using UnityEngine.UIElements;
using System.Collections.Generic;
using System;
using System.IO;
#if UNITY_EDITOR #if UNITY_EDITOR
using UnityEditor; using UnityEditor;
#endif #endif
@ -10,29 +11,8 @@ namespace NEG.Utils.UiToolkits
{ {
public class MultiSelectChips : VisualElement public class MultiSelectChips : VisualElement
{ {
private readonly VisualTreeAsset itemPrefab; public event Action<IMultiSelectChipItem> OnTryingToRemoveItem;
private readonly List<MultiSelectChipItem> spawnedItems = new(); public event Action<Rect> OnTryingToAddItem;
private ICollection<IMultiSelectChipItem> itemsSource;
private Label label;
private VisualElement realItemsParent;
public MultiSelectChips()
{
#if UNITY_EDITOR
string path =
AssetDatabase.GUIDToAssetPath(AssetDatabase.FindAssets($"t:Script {nameof(MultiSelectChips)}")[0]);
path = path.Remove(path.LastIndexOf('/'));
SetVisuals(AssetDatabase.LoadAssetAtPath<VisualTreeAsset>($"{path}/Resources/MultiSelectChips.uxml"));
itemPrefab = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>($"{path}/Resources/MultiSelectChipItem.uxml");
#else
SetVisuals(Resources.Load<VisualTreeAsset>("MultiSelectChips.uxml"));
itemPrefab = Resources.Load<VisualTreeAsset>("MultiSelectChipItem.uxml");
#endif
}
public string LabelText public string LabelText
{ {
@ -41,7 +21,10 @@ namespace NEG.Utils.UiToolkits
{ {
if (!string.IsNullOrEmpty(value)) if (!string.IsNullOrEmpty(value))
{ {
if (label == null) InitLabel(); if (label == null)
{
InitLabel();
}
label.text = value; label.text = value;
} }
@ -64,8 +47,53 @@ namespace NEG.Utils.UiToolkits
} }
} }
public event Action<IMultiSelectChipItem> OnTryingToRemoveItem; private Label label;
public event Action<Rect> OnTryingToAddItem;
private VisualTreeAsset itemPrefab;
private ICollection<IMultiSelectChipItem> itemsSource;
private readonly List<MultiSelectChipItem> spawnedItems = new();
private VisualElement realItemsParent;
public new class UxmlFactory : UxmlFactory<MultiSelectChips, UxmlTraits>
{
}
public new class UxmlTraits : VisualElement.UxmlTraits
{
private readonly UxmlStringAttributeDescription label;
public UxmlTraits()
{
label = new()
{
name = "label"
};
}
public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
{
base.Init(ve, bag, cc);
((MultiSelectChips)ve).LabelText = label.GetValueFromBag(bag, cc);
}
}
public MultiSelectChips() : base()
{
#if UNITY_EDITOR
string path = AssetDatabase.GUIDToAssetPath(AssetDatabase.FindAssets($"t:Script {nameof(MultiSelectChips)}")[0]);
path = path.Remove(path.LastIndexOf('/'));
SetVisuals(AssetDatabase.LoadAssetAtPath<VisualTreeAsset>($"{path}/Resources/MultiSelectChips.uxml"));
itemPrefab = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>($"{path}/Resources/MultiSelectChipItem.uxml");
#else
SetVisuals(Resources.Load<VisualTreeAsset>("MultiSelectChips.uxml"));
itemPrefab = Resources.Load<VisualTreeAsset>("MultiSelectChipItem.uxml");
#endif
}
public void UpdateItems() public void UpdateItems()
{ {
@ -74,7 +102,7 @@ namespace NEG.Utils.UiToolkits
var itemsToDestroy = new List<MultiSelectChipItem>(spawnedItems); var itemsToDestroy = new List<MultiSelectChipItem>(spawnedItems);
itemsToDestroy.RemoveAll(x => itemsSource.Contains(x.ChipItem)); itemsToDestroy.RemoveAll((x) => itemsSource.Contains(x.ChipItem));
foreach (var item in itemsToDestroy) foreach (var item in itemsToDestroy)
{ {
@ -102,7 +130,10 @@ namespace NEG.Utils.UiToolkits
private void InitLabel() private void InitLabel()
{ {
label = new Label { pickingMode = PickingMode.Ignore }; label = new Label()
{
pickingMode = PickingMode.Ignore
};
Insert(0, label); Insert(0, label);
} }
@ -117,25 +148,5 @@ namespace NEG.Utils.UiToolkits
realItemsParent = button.parent; realItemsParent = button.parent;
} }
public new class UxmlFactory : UxmlFactory<MultiSelectChips, UxmlTraits>
{
}
public new class UxmlTraits : VisualElement.UxmlTraits
{
private readonly UxmlStringAttributeDescription label;
public UxmlTraits()
{
label = new UxmlStringAttributeDescription { name = "label" };
}
public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
{
base.Init(ve, bag, cc);
((MultiSelectChips)ve).LabelText = label.GetValueFromBag(bag, cc);
}
}
} }
} }

View File

@ -1,12 +1,7 @@
<ui:UXML xmlns:ui="UnityEngine.UIElements" <ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" xsi="http://www.w3.org/2001/XMLSchema-instance" engine="UnityEngine.UIElements" editor="UnityEditor.UIElements" noNamespaceSchemaLocation="../../../../../UIElementsSchema/UIElements.xsd" editor-extension-mode="False">
xsi="http://www.w3.org/2001/XMLSchema-instance" engine="UnityEngine.UIElements" editor="UnityEditor.UIElements" <ui:VisualElement name="VisualElement" class="unity-button" style="min-height: 21px; flex-shrink: 1; flex-direction: row; flex-wrap: wrap; align-items: center;">
noNamespaceSchemaLocation="../../../../../UIElementsSchema/UIElements.xsd" editor-extension-mode="False"> <ui:VisualElement name="Color" style="min-height: 15px; min-width: 15px; border-top-left-radius: 22px; border-bottom-left-radius: 22px; border-top-right-radius: 22px; border-bottom-right-radius: 22px; border-left-width: 0; border-right-width: 0; border-top-width: 0; border-bottom-width: 0; border-left-color: rgba(0, 0, 125, 255); border-right-color: rgba(0, 0, 125, 255); border-top-color: rgba(0, 0, 125, 255); border-bottom-color: rgba(0, 0, 125, 255); background-color: rgba(255, 0, 0, 255); max-height: 15px; justify-content: center; align-items: center;" />
<ui:VisualElement name="VisualElement" class="unity-button" <ui:Label text="Name" display-tooltip-when-elided="true" name="Name" style="padding-left: 7px; padding-right: 5px;" />
style="min-height: 21px; flex-shrink: 1; flex-direction: row; flex-wrap: wrap; align-items: center;"> <ui:Button text="X" display-tooltip-when-elided="true" name="RemoveBtn" />
<ui:VisualElement name="Color"
style="min-height: 15px; min-width: 15px; border-top-left-radius: 22px; border-bottom-left-radius: 22px; border-top-right-radius: 22px; border-bottom-right-radius: 22px; border-left-width: 0; border-right-width: 0; border-top-width: 0; border-bottom-width: 0; border-left-color: rgba(0, 0, 125, 255); border-right-color: rgba(0, 0, 125, 255); border-top-color: rgba(0, 0, 125, 255); border-bottom-color: rgba(0, 0, 125, 255); background-color: rgba(255, 0, 0, 255); max-height: 15px; justify-content: center; align-items: center;"/>
<ui:Label text="Name" display-tooltip-when-elided="true" name="Name"
style="padding-left: 7px; padding-right: 5px;"/>
<ui:Button text="X" display-tooltip-when-elided="true" name="RemoveBtn"/>
</ui:VisualElement> </ui:VisualElement>
</ui:UXML> </ui:UXML>

View File

@ -1,7 +1,5 @@
<ui:UXML xmlns:ui="UnityEngine.UIElements" <ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" xsi="http://www.w3.org/2001/XMLSchema-instance" engine="UnityEngine.UIElements" editor="UnityEditor.UIElements" noNamespaceSchemaLocation="../../../../../UIElementsSchema/UIElements.xsd" editor-extension-mode="False">
xsi="http://www.w3.org/2001/XMLSchema-instance" engine="UnityEngine.UIElements" editor="UnityEditor.UIElements"
noNamespaceSchemaLocation="../../../../../UIElementsSchema/UIElements.xsd" editor-extension-mode="False">
<ui:VisualElement style="flex-direction: row; flex-wrap: wrap;"> <ui:VisualElement style="flex-direction: row; flex-wrap: wrap;">
<ui:Button text="+" display-tooltip-when-elided="true" name="AddItem"/> <ui:Button text="+" display-tooltip-when-elided="true" name="AddItem" />
</ui:VisualElement> </ui:VisualElement>
</ui:UXML> </ui:UXML>

View File

@ -1,28 +1,28 @@
{ {
"name": "NEG_UiToolkit", "name": "NEG_UiToolkit",
"rootNamespace": "", "rootNamespace": "",
"references": [ "references": [
"Unity.Addressables", "Unity.Addressables",
"Unity.ResourceManager" "Unity.ResourceManager"
], ],
"includePlatforms": [], "includePlatforms": [],
"excludePlatforms": [], "excludePlatforms": [],
"allowUnsafeCode": false, "allowUnsafeCode": false,
"overrideReferences": false, "overrideReferences": false,
"precompiledReferences": [], "precompiledReferences": [],
"autoReferenced": true, "autoReferenced": true,
"defineConstraints": [], "defineConstraints": [],
"versionDefines": [ "versionDefines": [
{ {
"name": "com.unity.addressables", "name": "com.unity.addressables",
"expression": "", "expression": "",
"define": "ADDRESSABLES" "define": "ADDRESSABLES"
}, },
{ {
"name": "com.unity.modules.uielements", "name": "com.unity.modules.uielements",
"expression": "", "expression": "",
"define": "UI_ELEMENTS" "define": "UI_ELEMENTS"
} }
], ],
"noEngineReferences": false "noEngineReferences": false
} }

File diff suppressed because it is too large Load Diff