From 573d6d508eb60d584c75b543b94be0e19d70ec82 Mon Sep 17 00:00:00 2001 From: Gokhan Kurt Date: Mon, 4 Dec 2023 11:31:05 +0300 Subject: [PATCH] expose GlobalRecord instead of SerializableDictionary in renderer components --- Editor/Renderer/ReactInspector.cs | 4 ++-- Editor/Renderer/ReactProperty.cs | 4 ++-- Editor/Renderer/ReactWindow.cs | 4 ++-- Editor/UIToolkit/ReactUnityEditorElement.cs | 2 +- Runtime/Core/ReactContext.cs | 4 ++-- Runtime/Core/ReactRendererBase.cs | 11 ++++++++-- .../UIToolkit/General/ReactUnityElement.cs | 4 ++-- Runtime/Helpers/GlobalRecord.cs | 21 ++++++------------- Tests/Editor/Utils/EditorTestBase.cs | 4 ++-- Tests/Editor/Utils/TestReactWindow.cs | 4 ++-- Tests/Runtime/Base/InteropTests.cs | 15 +++++++++++++ Tests/Runtime/Utils/TestBase.cs | 4 ++-- Tests/Runtime/Utils/UIToolkitTestBase.cs | 4 ++-- 13 files changed, 49 insertions(+), 36 deletions(-) diff --git a/Editor/Renderer/ReactInspector.cs b/Editor/Renderer/ReactInspector.cs index 783a5f89..df1df3f0 100644 --- a/Editor/Renderer/ReactInspector.cs +++ b/Editor/Renderer/ReactInspector.cs @@ -31,9 +31,9 @@ public override VisualElement CreateInspectorGUI() protected abstract ScriptSource GetScript(); - protected virtual SerializableDictionary GetGlobals() + protected virtual GlobalRecord GetGlobals() { - return new SerializableDictionary() + return new GlobalRecord() { { "Inspector", this }, }; diff --git a/Editor/Renderer/ReactProperty.cs b/Editor/Renderer/ReactProperty.cs index 8ae1d6c3..76537410 100644 --- a/Editor/Renderer/ReactProperty.cs +++ b/Editor/Renderer/ReactProperty.cs @@ -32,9 +32,9 @@ public override VisualElement CreatePropertyGUI(SerializedProperty property) protected abstract ScriptSource GetScript(); - protected virtual SerializableDictionary GetGlobals(SerializedProperty property) + protected virtual GlobalRecord GetGlobals(SerializedProperty property) { - return new SerializableDictionary() + return new GlobalRecord() { { "Property", property }, { "Drawer", this }, diff --git a/Editor/Renderer/ReactWindow.cs b/Editor/Renderer/ReactWindow.cs index 0147b002..e9763eae 100644 --- a/Editor/Renderer/ReactWindow.cs +++ b/Editor/Renderer/ReactWindow.cs @@ -98,9 +98,9 @@ public virtual void Run(VisualElement root = null) protected abstract ScriptSource GetScript(); - protected virtual SerializableDictionary GetGlobals() + protected virtual GlobalRecord GetGlobals() { - return new SerializableDictionary() + return new GlobalRecord() { { "Window", this }, }; diff --git a/Editor/UIToolkit/ReactUnityEditorElement.cs b/Editor/UIToolkit/ReactUnityEditorElement.cs index 6213822e..decd6b28 100644 --- a/Editor/UIToolkit/ReactUnityEditorElement.cs +++ b/Editor/UIToolkit/ReactUnityEditorElement.cs @@ -13,7 +13,7 @@ public class ReactUnityEditorElement : ReactUnityElement public ReactInspector Inspector { get; internal set; } public ReactProperty Property { get; internal set; } - public ReactUnityEditorElement(ScriptSource script, SerializableDictionary globals, ITimer timer, IMediaProvider mediaProvider, JavascriptEngineType engineType = JavascriptEngineType.Auto, bool debug = false, bool awaitDebugger = false, bool autorun = false) + public ReactUnityEditorElement(ScriptSource script, GlobalRecord globals, ITimer timer, IMediaProvider mediaProvider, JavascriptEngineType engineType = JavascriptEngineType.Auto, bool debug = false, bool awaitDebugger = false, bool autorun = false) : base(script, globals, timer, mediaProvider, engineType, debug, awaitDebugger, autorun) { } diff --git a/Runtime/Core/ReactContext.cs b/Runtime/Core/ReactContext.cs index 364a6d6f..7cd6e96c 100644 --- a/Runtime/Core/ReactContext.cs +++ b/Runtime/Core/ReactContext.cs @@ -34,7 +34,7 @@ public enum UnknownPropertyHandling public class Options { - public SerializableDictionary Globals; + public GlobalRecord Globals; public ScriptSource Source; public ITimer Timer; public IMediaProvider MediaProvider; @@ -83,7 +83,7 @@ public ReactContext(Options options) Source = options.Source; Timer = options.Timer; Dispatcher = CreateDispatcher(); - Globals = GlobalRecord.BindSerializableDictionary(options.Globals, Dispatcher, false); + Globals = options.Globals; OnRestart = options.OnRestart ?? (() => { }); CalculatesLayout = options.CalculatesLayout; Location = new Location(this); diff --git a/Runtime/Core/ReactRendererBase.cs b/Runtime/Core/ReactRendererBase.cs index dff86af7..17ac020e 100644 --- a/Runtime/Core/ReactRendererBase.cs +++ b/Runtime/Core/ReactRendererBase.cs @@ -45,13 +45,19 @@ public class ReactAdvancedOptions public ReactContext Context { get; private set; } public ITimer Timer { get; set; } - public SerializableDictionary Globals = new SerializableDictionary(); + [SerializeField] + [FormerlySerializedAs("Globals")] + protected SerializableDictionary globals = new SerializableDictionary(); + public ReactAdvancedOptions AdvancedOptions = new ReactAdvancedOptions(); + public GlobalRecord Globals { get; } = new GlobalRecord(); + private Coroutine MediaProviderCoroutine; void OnEnable() { + Globals.BindSerializableDictionary(globals, true); if (AdvancedOptions.AutoRender) Render(); } @@ -67,7 +73,8 @@ private void OnDestroy() private void OnValidate() { - Context?.Globals.UpdateStringObjectDictionary(Globals, true); + Globals.BindSerializableDictionary(globals, true); + Context?.Dispatcher?.OnceUpdate(() => Globals.Change()); } protected virtual void Clean() diff --git a/Runtime/Frameworks/UIToolkit/General/ReactUnityElement.cs b/Runtime/Frameworks/UIToolkit/General/ReactUnityElement.cs index 353057a4..426b2d81 100644 --- a/Runtime/Frameworks/UIToolkit/General/ReactUnityElement.cs +++ b/Runtime/Frameworks/UIToolkit/General/ReactUnityElement.cs @@ -14,14 +14,14 @@ public class ReactUnityElement : VisualElement public IMediaProvider MediaProvider { get; private set; } public ScriptSource Script { get; } - public SerializableDictionary Globals { get; } + public GlobalRecord Globals { get; } public JavascriptEngineType EngineType { get; } public bool Debug = false; public bool AwaitDebugger = false; - public ReactUnityElement(ScriptSource script, SerializableDictionary globals, ITimer timer, IMediaProvider mediaProvider, JavascriptEngineType engineType = JavascriptEngineType.Auto, bool debug = false, bool awaitDebugger = false, bool autorun = true) + public ReactUnityElement(ScriptSource script, GlobalRecord globals, ITimer timer, IMediaProvider mediaProvider, JavascriptEngineType engineType = JavascriptEngineType.Auto, bool debug = false, bool awaitDebugger = false, bool autorun = true) { Script = script; Globals = globals; diff --git a/Runtime/Helpers/GlobalRecord.cs b/Runtime/Helpers/GlobalRecord.cs index b26ce138..13dd1d8f 100644 --- a/Runtime/Helpers/GlobalRecord.cs +++ b/Runtime/Helpers/GlobalRecord.cs @@ -1,28 +1,21 @@ +using System; using ReactUnity.Reactive; -using ReactUnity.Scheduling; namespace ReactUnity.Helpers { public class GlobalRecord : ReactiveObjectRecord { - private System.Action removeStringDictionaryListener; - private IDispatcher dispatcher; + private Action removeStringDictionaryListener; public GlobalRecord() { } - public static GlobalRecord BindSerializableDictionary(SerializableDictionary dict, IDispatcher dispatcher, bool isSerializing) - { - var res = new GlobalRecord(); - res.dispatcher = dispatcher; - res.BindSerializableDictionary(dict, isSerializing); - return res; - } - public void BindSerializableDictionary(SerializableDictionary dict, bool isSerializing) { removeStringDictionaryListener?.Invoke(); removeStringDictionaryListener = null; + if (dict == null) return; + UpdateStringObjectDictionary(dict, isSerializing); removeStringDictionaryListener = dict.AddListener((key, value, dc) => { @@ -38,15 +31,13 @@ public void BindSerializableDictionary(SerializableDictionary dict, bool isSeria public void UpdateStringObjectDictionary(ReactiveRecord dict, bool isSerializing) { - ClearWithoutNotify(); foreach (var entry in dict) { - SetWithoutNotify(entry.Key, entry.Value); + if (entry.Value == null) RemoveWithoutNotify(entry.Key); + else SetWithoutNotify(entry.Key, entry.Value); } if (!isSerializing) Change(null, default); - else if (dispatcher != null) - dispatcher.OnceUpdate(() => Change(null, default)); } } diff --git a/Tests/Editor/Utils/EditorTestBase.cs b/Tests/Editor/Utils/EditorTestBase.cs index 093a4601..78e57e81 100644 --- a/Tests/Editor/Utils/EditorTestBase.cs +++ b/Tests/Editor/Utils/EditorTestBase.cs @@ -17,7 +17,7 @@ using ReactUnity.Editor; using ReactUnity.Editor.Renderer; using ReactUnity.Editor.UIToolkit; -using ReactUnity.Helpers; +using ReactUnity.Reactive; using ReactUnity.Scripting; using ReactUnity.Styling; using ReactUnity.Styling.Rules; @@ -43,7 +43,7 @@ public abstract class EditorTestBase protected EditorContext EditorContext => Context as EditorContext; protected IMediaProvider MediaProvider => Context?.MediaProvider; protected HostComponent Host => Context?.Host as HostComponent; - protected GlobalRecord Globals => Context?.Globals; + protected ReactiveObjectRecord Globals => Context?.Globals; internal ReactUnityBridge Bridge => ReactUnityBridge.Instance; public readonly JavascriptEngineType EngineType; diff --git a/Tests/Editor/Utils/TestReactWindow.cs b/Tests/Editor/Utils/TestReactWindow.cs index debbcf82..87bf15fd 100644 --- a/Tests/Editor/Utils/TestReactWindow.cs +++ b/Tests/Editor/Utils/TestReactWindow.cs @@ -9,7 +9,7 @@ namespace ReactUnity.Tests.Editor public class TestReactWindow : ReactWindow { public Func ScriptCallback; - public SerializableDictionary Globals = new SerializableDictionary(); + public GlobalRecord Globals = new GlobalRecord(); public override bool AutoRun => false; public override JavascriptEngineType EngineType { get; set; } @@ -31,7 +31,7 @@ protected override ScriptSource GetScript() return ScriptCallback?.Invoke(); } - protected override SerializableDictionary GetGlobals() + protected override GlobalRecord GetGlobals() { Globals["Window"] = this; return Globals; diff --git a/Tests/Runtime/Base/InteropTests.cs b/Tests/Runtime/Base/InteropTests.cs index 6f3ae83c..4297de5c 100644 --- a/Tests/Runtime/Base/InteropTests.cs +++ b/Tests/Runtime/Base/InteropTests.cs @@ -133,6 +133,21 @@ public IEnumerator GlobalsKeysCanBeAccessedNaturally() Render(); } + [UGUITest(Script = @" + function App() { }; + + Globals.Set('a', 5); + Assert.AreEqual(5, Globals.a); + ", AutoRender = false)] + public IEnumerator GlobalsGetChangedOnCsharpSide() + { + yield return null; + Render(); + Assert.AreEqual(Globals, Context.Globals); + Assert.AreEqual(5, Globals["a"]); + Assert.AreEqual(5, Context.Globals["a"]); + } + [UGUITest(Script = @" function App() { }; diff --git a/Tests/Runtime/Utils/TestBase.cs b/Tests/Runtime/Utils/TestBase.cs index 4f0cafab..233d606c 100644 --- a/Tests/Runtime/Utils/TestBase.cs +++ b/Tests/Runtime/Utils/TestBase.cs @@ -14,7 +14,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; -using ReactUnity.Helpers; +using ReactUnity.Reactive; using ReactUnity.Scripting; using ReactUnity.Styling; using ReactUnity.Styling.Rules; @@ -51,7 +51,7 @@ public abstract class TestBase : InputTestFixture protected UGUIContext UGUIContext => Context as UGUIContext; protected IMediaProvider MediaProvider => Context?.MediaProvider; protected HostComponent Host => Context?.Host as HostComponent; - protected SerializableDictionary Globals => Component?.Globals; + protected ReactiveObjectRecord Globals => Component?.Globals; internal ReactUnityBridge Bridge => ReactUnityBridge.Instance; public readonly JavascriptEngineType EngineType; diff --git a/Tests/Runtime/Utils/UIToolkitTestBase.cs b/Tests/Runtime/Utils/UIToolkitTestBase.cs index 49da9694..cd2cbf38 100644 --- a/Tests/Runtime/Utils/UIToolkitTestBase.cs +++ b/Tests/Runtime/Utils/UIToolkitTestBase.cs @@ -14,7 +14,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; -using ReactUnity.Helpers; +using ReactUnity.Reactive; using ReactUnity.Scripting; using ReactUnity.Styling.Rules; using ReactUnity.UIToolkit; @@ -53,7 +53,7 @@ public abstract class UIToolkitTestBase : InputTestFixture protected UIToolkitContext UGUIContext => Context as UIToolkitContext; protected IMediaProvider MediaProvider => Context?.MediaProvider; protected HostComponent Host => Context?.Host as HostComponent; - protected SerializableDictionary Globals => Component?.Globals; + protected ReactiveObjectRecord Globals => Component?.Globals; internal ReactUnityBridge Bridge => ReactUnityBridge.Instance; public readonly JavascriptEngineType EngineType;