From e69e1b552646030a65cfc013fc1d7cd7ab55f09b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Tue, 12 Dec 2023 13:38:29 +0100 Subject: [PATCH] Show Buttons in entity editor - ScriptEngine: Added HasCustomAttribute- extension method - ScriptEngine: Added Application class --- GlitchyEditor/src/EditorLayer.bf | 6 ++ GlitchyEngine/src/Scripting/ScriptEngine.bf | 64 ++++++++++++++++++++- GlitchyEngine/src/Scripting/ScriptGlue.bf | 24 +++++++- GlitchyEngine/src/World/Scene.bf | 8 --- ScriptCore/Core/Application.cs | 34 +++++++++-- ScriptCore/Core/EngineObject.cs | 2 - ScriptCore/Editor/EntityEditor.cs | 50 ++++++++++++---- ScriptCore/Editor/HideInEditorAttribute.cs | 11 ++++ ScriptCore/Editor/ShowButtonAttribute.cs | 42 ++++++++++++++ ScriptCore/Editor/ShowInEditorAttribute.cs | 13 +++-- ScriptCore/Extensions/FieldInfoExtension.cs | 18 ++++++ ScriptCore/ScriptGlue.cs | 23 +++++--- 12 files changed, 253 insertions(+), 42 deletions(-) create mode 100644 ScriptCore/Editor/HideInEditorAttribute.cs create mode 100644 ScriptCore/Editor/ShowButtonAttribute.cs create mode 100644 ScriptCore/Extensions/FieldInfoExtension.cs diff --git a/GlitchyEditor/src/EditorLayer.bf b/GlitchyEditor/src/EditorLayer.bf index e180351..0994e11 100644 --- a/GlitchyEditor/src/EditorLayer.bf +++ b/GlitchyEditor/src/EditorLayer.bf @@ -110,6 +110,8 @@ namespace GlitchyEditor { Application.Get().Window.IsVSync = true; + ScriptEngine.ApplicationInfo.IsEditor = true; + _contentManager = contentManager; InitGraphics(); @@ -871,6 +873,8 @@ namespace GlitchyEditor using (Scene runtimeScene = new Scene()) { + ScriptEngine.ApplicationInfo.IsInPlayMode = true; + _editorScene.CopyTo(runtimeScene, true); SetActiveScene(runtimeScene, startRuntime: true, startSimulation: true); @@ -951,6 +955,8 @@ namespace GlitchyEditor /// Stops the simulation or game and returns to edit mode. private void OnSceneStop() { + ScriptEngine.ApplicationInfo.IsInEditMode = true; + SetActiveScene(_editorScene, startRuntime: true, startSimulation: false); // TODO: _editorScene.DeserializeScripts(scriptData); diff --git a/GlitchyEngine/src/Scripting/ScriptEngine.bf b/GlitchyEngine/src/Scripting/ScriptEngine.bf index 11f3c19..822869d 100644 --- a/GlitchyEngine/src/Scripting/ScriptEngine.bf +++ b/GlitchyEngine/src/Scripting/ScriptEngine.bf @@ -112,7 +112,69 @@ static class ScriptEngine internal static MonoClass* s_ShowInEditorAttribute; internal static MonoClass* s_RunInEditModeAttribute; } - + + public class ApplicationData + { + private bool _isEditor; + private bool _isPlayer; + + private bool _isInEditMode; + private bool _isInPlayMode; + + public bool IsEditor + { + get => _isEditor; + set + { + _isEditor = value; + + if (_isEditor) + _isPlayer = false; + } + } + + public bool IsPlayer + { + get => _isPlayer; + set + { + _isPlayer = value; + + if (_isPlayer) + _isEditor = false; + } + } + + public bool IsInEditMode + { + get => _isInEditMode; + set + { + _isInEditMode = value; + + if (_isInEditMode) + _isInPlayMode = false; + } + } + + public bool IsInPlayMode + { + get => _isInPlayMode; + set + { + _isInPlayMode = value; + + if (_isInPlayMode) + _isInEditMode = false; + } + } + } + + private static ApplicationData _applicationData = new ApplicationData() ~ delete _; + + /// Contains information about the application context + public static ApplicationData ApplicationInfo => _applicationData; + public static void Init() { InitMono(); diff --git a/GlitchyEngine/src/Scripting/ScriptGlue.bf b/GlitchyEngine/src/Scripting/ScriptGlue.bf index 9a44790..a4e4ee7 100644 --- a/GlitchyEngine/src/Scripting/ScriptGlue.bf +++ b/GlitchyEngine/src/Scripting/ScriptGlue.bf @@ -475,9 +475,31 @@ static class ScriptGlue id = UUID.Create(); } -#region Editor Stuff +#region Application + + [RegisterCall("ScriptGlue::Application_IsEditor")] + static bool Application_IsEditor() + { + return ScriptEngine.ApplicationInfo.IsEditor; + } + [RegisterCall("ScriptGlue::Application_IsPlayer")] + static bool Application_IsPlayer() + { + return ScriptEngine.ApplicationInfo.IsPlayer; + } + [RegisterCall("ScriptGlue::Application_IsInEditMode")] + static bool Application_IsInEditMode() + { + return ScriptEngine.ApplicationInfo.IsInEditMode; + } + + [RegisterCall("ScriptGlue::Application_IsInPlayMode")] + static bool Application_IsInPlayMode() + { + return ScriptEngine.ApplicationInfo.IsInPlayMode; + } #endregion diff --git a/GlitchyEngine/src/World/Scene.bf b/GlitchyEngine/src/World/Scene.bf index 3a97832..cf3047f 100644 --- a/GlitchyEngine/src/World/Scene.bf +++ b/GlitchyEngine/src/World/Scene.bf @@ -202,14 +202,6 @@ namespace GlitchyEngine.World } } - public void Start(bool simulation, bool runtime) - { - if (simulation) - StartSimulation(); - - - } - public void StartRuntime() { ScriptEngine.StartRuntime(this); diff --git a/ScriptCore/Core/Application.cs b/ScriptCore/Core/Application.cs index 44fc5b2..36d98e2 100644 --- a/ScriptCore/Core/Application.cs +++ b/ScriptCore/Core/Application.cs @@ -1,15 +1,37 @@ namespace GlitchyEngine.Core; -public class Application +/// +/// Contains information about the application runtime and methods to control it. +/// +public static class Application { /// - /// Returns true when the script is running in edit mode. + /// Returns when the application is running in the editor (any mode e.g. edit mode or play mode). /// - /// - public bool IsEditor = false; + /// + /// + /// + public static bool IsEditor => ScriptGlue.Application_IsEditor(); + /// - /// Returns true when the script is running in play mode or in the player. + /// Returns when the application is running in the dedicated player. /// /// - public bool IsPlaying = false; + public static bool IsPlayer => ScriptGlue.Application_IsPlayer(); + + /// + /// Returns when the application is running in the editor and the editor is in edit mode.
+ /// If is (i.e. the application is not running in the editor), will always be . + ///
+ /// + /// + public static bool IsInEditMode => ScriptGlue.Application_IsInEditMode(); + + /// + /// Returns when the application is running in the editor ( = ) and the editor is in play mode. Or when the application in running in the dedicated player ( = )
+ ///
+ /// + /// + /// + public static bool IsInPlayMode => ScriptGlue.Application_IsInPlayMode(); } diff --git a/ScriptCore/Core/EngineObject.cs b/ScriptCore/Core/EngineObject.cs index 5587146..c81653d 100644 --- a/ScriptCore/Core/EngineObject.cs +++ b/ScriptCore/Core/EngineObject.cs @@ -1,5 +1,3 @@ -using System; - namespace GlitchyEngine.Core; public abstract class EngineObject diff --git a/ScriptCore/Editor/EntityEditor.cs b/ScriptCore/Editor/EntityEditor.cs index 679fabf..ef03bb1 100644 --- a/ScriptCore/Editor/EntityEditor.cs +++ b/ScriptCore/Editor/EntityEditor.cs @@ -24,11 +24,16 @@ internal class EntityEditor public static bool ShowFieldInEditor(FieldInfo fieldInfo) { if (fieldInfo.IsPublic) + { + // Public fields are visible by default (No HideInEditorAttribute) + if (fieldInfo.HasCustomAttribute()) + return false; + return true; - - var showInEditorAttribute = fieldInfo.GetCustomAttribute(); - - if (showInEditorAttribute != null) + } + + // Private, protected and internal fields are hidden by default (No ShowInEditorAttribute) + if (fieldInfo.HasCustomAttribute()) return true; return false; @@ -357,13 +362,9 @@ internal class EntityEditor public static void ShowEditor(Type type, object reference) { - T GetValue(FieldInfo fieldInfo) - { - return default; - } - int i = 0; + // Iterate all fields foreach (FieldInfo field in type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) { i++; @@ -371,8 +372,6 @@ internal class EntityEditor if (ShowFieldInEditor(field)) { - //Log.Info(field.Name); - object value = field.GetValue(reference); IEnumerable attributes = field.GetCustomAttributes(); @@ -388,6 +387,35 @@ internal class EntityEditor ImGui.PopID(); } + + // Iterate all methods + foreach (MethodInfo method in type.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) + { + var showButton = method.GetCustomAttribute(); + + if (showButton == null) + continue; + + if (Application.IsInEditMode && !showButton.Visibility.HasFlag(ButtonVisibility.InEditMode)) + continue; + + if (Application.IsInPlayMode && !showButton.Visibility.HasFlag(ButtonVisibility.InPlayMode)) + continue; + + i++; + ImGui.PushID(i); + + if (method.GetParameters().Length > 0) + { + Log.Error($"Method {method.Name} cannot be executed from editor, because it expects arguments."); + } + else if (ImGui.Button(showButton.ButtonText)) + { + method.Invoke(reference, null); + } + + ImGui.PopID(); + } } struct ListPayload diff --git a/ScriptCore/Editor/HideInEditorAttribute.cs b/ScriptCore/Editor/HideInEditorAttribute.cs new file mode 100644 index 0000000..3ac6533 --- /dev/null +++ b/ScriptCore/Editor/HideInEditorAttribute.cs @@ -0,0 +1,11 @@ +using System; + +namespace GlitchyEngine.Editor; + +/// +/// Specifies, that the field should not be visible in the editor. +/// +[AttributeUsage(AttributeTargets.Field)] +public sealed class HideInEditorAttribute : Attribute +{ +} diff --git a/ScriptCore/Editor/ShowButtonAttribute.cs b/ScriptCore/Editor/ShowButtonAttribute.cs new file mode 100644 index 0000000..53d4bc8 --- /dev/null +++ b/ScriptCore/Editor/ShowButtonAttribute.cs @@ -0,0 +1,42 @@ + +using System; + +namespace GlitchyEngine.Editor; + +public enum ButtonVisibility +{ + /// + /// The button will be visible in edit mode. + /// + InEditMode = 1, + /// + /// The button will be visible in play mode. + /// + InPlayMode = 2, + /// + /// The button will always be visible. + /// + Always = InEditMode | InPlayMode +} + +/// +/// Specifies that a button will be shown in the editor that, when clicked, executes the method this attribute is attached to. +/// +[AttributeUsage(AttributeTargets.Method)] +public sealed class ShowButtonAttribute : Attribute +{ + /// + /// The text on the button. + /// + public string ButtonText { get; private set; } + + /// + /// Determines whether the button is visible in edit and/or play mode. + /// + public ButtonVisibility Visibility { get; set; } = ButtonVisibility.Always; + + public ShowButtonAttribute(string buttonText) + { + ButtonText = buttonText; + } +} diff --git a/ScriptCore/Editor/ShowInEditorAttribute.cs b/ScriptCore/Editor/ShowInEditorAttribute.cs index 6cce197..faa0be5 100644 --- a/ScriptCore/Editor/ShowInEditorAttribute.cs +++ b/ScriptCore/Editor/ShowInEditorAttribute.cs @@ -7,11 +7,16 @@ namespace GlitchyEngine.Editor; * - this attribute also means that the Field is being serialized * - Editor-Namespace wont be available for distribution builds of the game */ +/// +/// Specifies that the field should be visible in the editor, regardless of it's code visibility (private, protected, etc...) +/// If this attribute is specified, the field will also be serialized. +/// [AttributeUsage(AttributeTargets.Field)] public sealed class ShowInEditorAttribute : Attribute { + /// + /// The label with which the field will be shown in the editor. + /// If not specified, the fields name will be used. + /// public string DisplayName { get; set; } = null; - - public ShowInEditorAttribute() - {} -} \ No newline at end of file +} diff --git a/ScriptCore/Extensions/FieldInfoExtension.cs b/ScriptCore/Extensions/FieldInfoExtension.cs new file mode 100644 index 0000000..2a87d80 --- /dev/null +++ b/ScriptCore/Extensions/FieldInfoExtension.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; + +namespace GlitchyEngine.Extensions; + +public static class FieldInfoExtension +{ + /// + /// Returns if the field has the specified attribute. + /// + /// The type of the attribte. + public static bool HasCustomAttribute(this FieldInfo fiedInfo) where T : Attribute + { + return fiedInfo.GetCustomAttribute() != null; + } +} diff --git a/ScriptCore/ScriptGlue.cs b/ScriptCore/ScriptGlue.cs index 7b0c4a4..10ae215 100644 --- a/ScriptCore/ScriptGlue.cs +++ b/ScriptCore/ScriptGlue.cs @@ -120,14 +120,19 @@ internal static class ScriptGlue [MethodImpl(MethodImplOptions.InternalCall)] internal static extern void UUID_CreateNew(out UUID uuid); -#region Application - -// TODO: Implement IsEditor and IsPlaying - //[MethodImpl(MethodImplOptions.InternalCall)] - //internal static extern bool Application_IsEditor(); - - //[MethodImpl(MethodImplOptions.InternalCall)] - //internal static extern bool Application_IsPlaying(); + #region Application -#endregion + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern bool Application_IsEditor(); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern bool Application_IsPlayer(); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern bool Application_IsInEditMode(); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern bool Application_IsInPlayMode(); + + #endregion }