Skip to content

Commit 613655e

Browse files
committed
add utilities for batch rendering
1 parent a20bdb3 commit 613655e

File tree

6 files changed

+220
-6
lines changed

6 files changed

+220
-6
lines changed

Runtime/Core/ReactContext.cs

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
using System.Collections.Generic;
33
using System.Text.RegularExpressions;
44
using ExCSS;
5+
using Newtonsoft.Json;
6+
using Newtonsoft.Json.Linq;
57
using ReactUnity.Helpers;
68
using ReactUnity.Helpers.Visitors;
79
using ReactUnity.Html;
@@ -58,6 +60,11 @@ public class Options
5860
public CursorAPI CursorAPI { get; }
5961
public List<Action> Disposables { get; } = new List<Action>();
6062

63+
protected Dictionary<int, IReactComponent> Refs = new Dictionary<int, IReactComponent>();
64+
internal Callback CommandsCallback;
65+
internal Callback FireEventByRefCallback;
66+
internal Callback GetObjectCallback;
67+
6168
public ReactContext(Options options)
6269
{
6370
this.options = options;
@@ -159,6 +166,7 @@ private string GetResourceUrl(string fullUrl)
159166

160167
public void Start()
161168
{
169+
SetRef(0, Host);
162170
var renderCount = 0;
163171

164172
var scriptJob = Source.GetScript((code) => {
@@ -198,5 +206,190 @@ public void Dispose()
198206
}
199207

200208
protected virtual IDispatcher CreateDispatcher() => Application.isPlaying ? RuntimeDispatcher.Create(this) as IDispatcher : new EditorDispatcher(this);
209+
210+
internal void SetRef(int refId, IReactComponent cmp)
211+
{
212+
Refs[refId] = cmp;
213+
//Refs.Add(refId, cmp);
214+
}
215+
216+
public IReactComponent GetRef(int refId)
217+
{
218+
Refs.TryGetValue(refId, out var cmp);
219+
return cmp;
220+
}
221+
222+
public void BindCommands(object commandsObject, object callbacksObject, object getObjectCallback)
223+
{
224+
CommandsCallback = new Callback(commandsObject);
225+
FireEventByRefCallback = new Callback(callbacksObject);
226+
GetObjectCallback = new Callback(getObjectCallback);
227+
}
228+
229+
230+
IEnumerator<KeyValuePair<string, object>> PropsEnumerator(JToken props)
231+
{
232+
foreach (JProperty child in props)
233+
{
234+
var val = child.Value;
235+
object value = null;
236+
237+
if (child.Name == "style") value = PropsEnumerator(val);
238+
else
239+
{
240+
switch (val.Type)
241+
{
242+
case JTokenType.Integer:
243+
value = val.Value<int>();
244+
break;
245+
case JTokenType.Float:
246+
value = val.Value<float>();
247+
break;
248+
case JTokenType.Boolean:
249+
value = val.Value<bool>();
250+
break;
251+
case JTokenType.TimeSpan:
252+
case JTokenType.Guid:
253+
case JTokenType.Date:
254+
case JTokenType.Uri:
255+
case JTokenType.String:
256+
value = val.ToString();
257+
break;
258+
case JTokenType.Null:
259+
case JTokenType.Undefined:
260+
case JTokenType.Raw:
261+
case JTokenType.Bytes:
262+
case JTokenType.None:
263+
case JTokenType.Object:
264+
case JTokenType.Array:
265+
case JTokenType.Constructor:
266+
case JTokenType.Property:
267+
case JTokenType.Comment:
268+
default:
269+
break;
270+
}
271+
}
272+
273+
yield return new KeyValuePair<string, object>(child.Name, value);
274+
}
275+
}
276+
277+
IEnumerator<KeyValuePair<string, object>> EventsEnumerator(JToken events)
278+
{
279+
foreach (JProperty child in events)
280+
{
281+
var ind = child.Value.Value<int>();
282+
var callback = ind > 0 ? Callback.From(ind, this) : null;
283+
yield return new KeyValuePair<string, object>(child.Name, callback);
284+
}
285+
}
286+
287+
IEnumerator<KeyValuePair<string, object>> ObjectsEnumerator(JToken objs)
288+
{
289+
foreach (JProperty child in objs)
290+
{
291+
var ind = child.Value.Value<int>();
292+
var callback = ind > 0 ? GetObjectCallback.Call(ind) : null;
293+
yield return new KeyValuePair<string, object>(child.Name, callback);
294+
}
295+
}
296+
297+
public void FlushCommands()
298+
{
299+
if (CommandsCallback == null) return;
300+
301+
var cmds = CommandsCallback.Call().ToString();
302+
var jo = JArray.Parse(cmds);
303+
304+
for (int i = 0; i < jo.Count; i++)
305+
{
306+
var cmd = jo[i];
307+
308+
var key = cmd[0].ToString();
309+
var val = cmd[1];
310+
311+
if (key == "c")
312+
{
313+
var refId = val["r"].Value<int>();
314+
var type = val["t"].ToString();
315+
var props = val["p"];
316+
317+
var el = ReactUnityBridge.Instance.createElement(type, null, Host, PropsEnumerator(props));
318+
if (refId > 0) SetRef(refId, el);
319+
320+
var objs = val["o"];
321+
ReactUnityBridge.Instance.applyUpdate(el, PropsEnumerator(objs), type);
322+
323+
var events = val["e"];
324+
ReactUnityBridge.Instance.applyUpdate(el, EventsEnumerator(events), type);
325+
}
326+
else if (key == "t")
327+
{
328+
var refId = val["r"].Value<int>();
329+
var children = val["c"].ToString();
330+
var el = ReactUnityBridge.Instance.createText(children, Host);
331+
if (refId > 0) SetRef(refId, el);
332+
}
333+
else if (key == "a")
334+
{
335+
var parentRef = val["p"].Value<int>();
336+
var childRef = val["c"].Value<int>();
337+
var parent = GetRef(parentRef);
338+
var child = GetRef(childRef);
339+
ReactUnityBridge.Instance.appendChild(parent, child);
340+
}
341+
else if (key == "r")
342+
{
343+
var parentRef = val["p"].Value<int>();
344+
var childRef = val["c"].Value<int>();
345+
var parent = GetRef(parentRef);
346+
var child = GetRef(childRef);
347+
ReactUnityBridge.Instance.removeChild(parent, child);
348+
}
349+
else if (key == "i")
350+
{
351+
var parentRef = val["p"].Value<int>();
352+
var childRef = val["c"].Value<int>();
353+
var insertRef = val["i"].Value<int>();
354+
var parent = GetRef(parentRef);
355+
var child = GetRef(childRef);
356+
var insert = GetRef(insertRef);
357+
ReactUnityBridge.Instance.insertBefore(parent, child, insert);
358+
}
359+
else if (key == "u")
360+
{
361+
var refId = val["r"].Value<int>();
362+
var el = GetRef(refId);
363+
var type = val["t"].ToString();
364+
365+
var props = val["p"];
366+
ReactUnityBridge.Instance.applyUpdate(el, PropsEnumerator(props), type);
367+
368+
var objs = val["o"];
369+
ReactUnityBridge.Instance.applyUpdate(el, PropsEnumerator(objs), type);
370+
371+
var events = val["e"];
372+
ReactUnityBridge.Instance.applyUpdate(el, EventsEnumerator(events), type);
373+
}
374+
else if (key == "x")
375+
{
376+
var elRef = val["r"].Value<int>();
377+
var el = GetRef(elRef);
378+
var text = val["t"]?.ToString();
379+
ReactUnityBridge.Instance.setText(el, text);
380+
}
381+
else if (key == "h")
382+
{
383+
var elRef = val["r"].Value<int>();
384+
var el = GetRef(elRef);
385+
var hidden = val["h"].Value<bool>();
386+
el?.ClassList.ToggleWithoutNotify("react-unity__renderer__hidden", hidden);
387+
}
388+
else if (key == "o")
389+
{
390+
ReactUnityBridge.Instance.clearContainer(Host);
391+
}
392+
}
393+
}
201394
}
202395
}

Runtime/Core/ReactUnityBridge.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ private ReactUnityBridge() { }
2222
[Preserve]
2323
public ITextComponent createText(string text, IReactComponent host)
2424
{
25-
return host.Context.CreateText(text);
25+
var el = host.Context.CreateText(text);
26+
return el;
2627
}
2728

2829
[Preserve]
@@ -124,8 +125,12 @@ public void applyUpdate(object instance, object payload, string type)
124125
{
125126
var cmp = instance as IReactComponent;
126127
if (cmp == null) return;
128+
if (payload == null) return;
127129

128-
var updatePayload = cmp.Context.Script.Engine.TraverseScriptObject(payload);
130+
var updatePayload =
131+
typeof(IEnumerator<KeyValuePair<string, object>>).IsAssignableFrom(payload.GetType()) ?
132+
((IEnumerator<KeyValuePair<string, object>>) payload) :
133+
cmp.Context.Script.Engine.TraverseScriptObject(payload);
129134

130135
if (updatePayload == null) return;
131136

@@ -151,7 +156,10 @@ public void applyUpdate(object instance, object payload, string type)
151156
{
152157
if (!(value is string))
153158
{
154-
var stylePayload = cmp.Context.Script.Engine.TraverseScriptObject(value);
159+
var stylePayload =
160+
typeof(IEnumerator<KeyValuePair<string, object>>).IsAssignableFrom(value.GetType()) ?
161+
((IEnumerator<KeyValuePair<string, object>>) value) :
162+
cmp.Context.Script.Engine.TraverseScriptObject(value);
155163
var st = cmp.Style;
156164

157165
while (stylePayload.MoveNext())

Runtime/Helpers/Callback.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ namespace ReactUnity.Helpers
2121
public class Callback
2222
{
2323
public object callback;
24+
private ReactContext context;
2425
#if REACT_JINT
2526
public Engine Engine;
2627
#endif
@@ -33,6 +34,7 @@ public static Callback From(object value, ReactContext context = null, object th
3334
return context.Script.CreateEventCallback(s, thisVal);
3435
}
3536
if (value is Callback cb) return cb;
37+
if (value is int cbi) return new Callback(cbi, context);
3638
#if REACT_JINT
3739
if (value is Func<JsValue, JsValue[], JsValue> jv) return new Callback(jv, context.Script.Engine.NativeEngine as Engine);
3840
if (value is ObjectInstance v) return new Callback(v);
@@ -59,6 +61,12 @@ public Callback(object callback)
5961
this.callback = callback;
6062
}
6163

64+
public Callback(int index, ReactContext context)
65+
{
66+
this.context = context;
67+
this.callback = index;
68+
}
69+
6270
public object Call()
6371
{
6472
return Call(new object[0]);
@@ -93,6 +101,10 @@ public object Call(params object[] args)
93101
if (args.Length > argCount) args = args.Take(argCount).ToArray();
94102
return d.DynamicInvoke(args);
95103
}
104+
else if (callback is int i)
105+
{
106+
return context.FireEventByRefCallback.Call(i, args);
107+
}
96108
#if REACT_CLEARSCRIPT
97109
else if (callback is Microsoft.ClearScript.ScriptObject so)
98110
{

Runtime/ReactUnity.asmdef

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
"RU.System.Buffers.dll",
2323
"RU.System.Numerics.Vectors.dll",
2424
"RU.System.Runtime.CompilerServices.Unsafe.dll",
25-
"RU.System.Memory.dll"
25+
"RU.System.Memory.dll",
26+
"Newtonsoft.Json.dll"
2627
],
2728
"autoReferenced": true,
2829
"defineConstraints": [],

Runtime/Scripting/ClearScriptEngine.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
#if REACT_CLEARSCRIPT
66
using System;
77
using System.Collections.Generic;
8-
using System.Globalization;
98
using System.Reflection;
109
using Microsoft.ClearScript;
1110
using Microsoft.ClearScript.V8;

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"type": "library",
1616
"samples": [],
1717
"dependencies": {
18-
"com.unity.editorcoroutines": "1.0.0"
18+
"com.unity.editorcoroutines": "1.0.0",
19+
"com.unity.nuget.newtonsoft-json": "3.0.2"
1920
}
2021
}

0 commit comments

Comments
 (0)