Skip to content

Commit 7adb510

Browse files
committed
added prefab component
1 parent 839fb70 commit 7adb510

22 files changed

+425
-22
lines changed

Runtime/Core/BaseReactComponent.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ public void ApplyLayoutStyles()
291291
ApplyLayoutStylesSelf();
292292
}
293293

294+
public abstract void Relayout();
294295

295296
private void OnStylesUpdated(NodeStyle obj, bool hasLayout)
296297
{

Runtime/Core/ComponentInterfaces.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public interface IReactComponent
3333
void ApplyLayoutStyles();
3434
void ScheduleLayout();
3535
void ResolveStyle(bool recursive = false);
36+
void Relayout();
3637

3738
void Update();
3839
void Accept(ReactComponentVisitor visitor);

Runtime/Core/ReactUnityBridge.cs

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
using Jint.Native;
2-
using Jint.Native.Function;
31
using ReactUnity.Helpers.TypescriptUtils;
42
using ReactUnity.Helpers;
53
using System;
@@ -83,19 +81,10 @@ public void setData(object element, string property, object value)
8381
c.SetData(property, value);
8482
}
8583

86-
public void setEventListener(IReactComponent element, string eventType, JsValue value)
87-
{
88-
var hasValue = value != null && !value.IsNull() && !value.IsUndefined() && !value.IsBoolean();
89-
var callback = value.As<FunctionInstance>();
90-
if (hasValue && callback == null) throw new Exception("The callback for an event must be a function.");
91-
92-
element.SetEventListener(eventType, new Callback(callback));
93-
}
94-
9584
public void setEventListener(object element, string eventType, object value)
9685
{
97-
if (element is IReactComponent c && value != null)
98-
c.SetEventListener(eventType, new Callback(value));
86+
if (element is IReactComponent c)
87+
c.SetEventListener(eventType, Callback.From(value));
9988
}
10089

10190
#endregion
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
using Facebook.Yoga;
2+
using ReactUnity.Helpers;
3+
using System.Collections.Generic;
4+
using UnityEngine;
5+
6+
namespace ReactUnity.UGUI
7+
{
8+
public class PrefabComponent : UGUIComponent
9+
{
10+
GameObject currentTarget;
11+
public GameObject Instance { get; private set; }
12+
private RectTransform InstanceTransform;
13+
private Transform InstanceParent;
14+
private bool InstanceWasPrefab;
15+
IPrefabTarget TargetHandler;
16+
17+
Callback onMount;
18+
Callback onUnmount;
19+
20+
public PrefabComponent(UGUIContext context, string tag = "prefab") : base(context, tag)
21+
{
22+
Layout.SetMeasureFunction(Measure);
23+
}
24+
25+
private YogaSize Measure(YogaNode node, float width, YogaMeasureMode widthMode, float height, YogaMeasureMode heightMode)
26+
{
27+
if (!InstanceTransform) return new YogaSize { width = 0, height = 0 };
28+
29+
return new YogaSize
30+
{
31+
width = InstanceTransform.rect.width,
32+
height = InstanceTransform.rect.height,
33+
};
34+
}
35+
36+
void SetTarget(GameObject target)
37+
{
38+
if (currentTarget == target) return;
39+
40+
if (currentTarget)
41+
{
42+
TargetHandler?.Unmount(this);
43+
onUnmount?.Call(currentTarget, this);
44+
currentTarget = null;
45+
TargetHandler = null;
46+
47+
if (Instance)
48+
{
49+
if (InstanceWasPrefab) GameObject.Destroy(Instance);
50+
else Instance.transform.SetParent(InstanceParent, false);
51+
}
52+
Instance = null;
53+
InstanceTransform = null;
54+
InstanceParent = null;
55+
}
56+
57+
currentTarget = target;
58+
59+
if (currentTarget)
60+
{
61+
ResolveInstance();
62+
63+
Instance.transform.SetParent(Container, false);
64+
TargetHandler?.Mount(this);
65+
onMount?.Call(currentTarget, this);
66+
}
67+
68+
Relayout();
69+
}
70+
71+
void ResolveInstance()
72+
{
73+
if (!currentTarget) return;
74+
75+
var isPrefab = currentTarget.scene.rootCount == 0;
76+
77+
if (isPrefab)
78+
{
79+
Instance = null;
80+
#if UNITY_EDITOR
81+
Instance = UnityEditor.PrefabUtility.InstantiatePrefab(currentTarget, Container) as GameObject;
82+
#endif
83+
if (!Instance) Instance = GameObject.Instantiate(currentTarget);
84+
InstanceParent = null;
85+
InstanceWasPrefab = true;
86+
}
87+
else
88+
{
89+
Instance = currentTarget;
90+
InstanceParent = currentTarget.transform.parent;
91+
InstanceWasPrefab = false;
92+
}
93+
InstanceTransform = Instance.transform as RectTransform;
94+
95+
TargetHandler = currentTarget.GetComponent<IPrefabTarget>();
96+
}
97+
98+
GameObject FindTarget(object value)
99+
{
100+
if (value == null) return null;
101+
if (value is GameObject g && g) return g;
102+
if (value is Component c && c) return c.gameObject;
103+
return null;
104+
}
105+
106+
public override void SetProperty(string propertyName, object value)
107+
{
108+
switch (propertyName)
109+
{
110+
case "target":
111+
SetTarget(FindTarget(value));
112+
break;
113+
default:
114+
var handled = TargetHandler != null ? TargetHandler.SetProperty(propertyName, value) : false;
115+
if (!handled) base.SetProperty(propertyName, value);
116+
break;
117+
}
118+
}
119+
120+
public override void SetEventListener(string eventName, Callback callback)
121+
{
122+
switch (eventName)
123+
{
124+
case "onMount":
125+
onMount = callback;
126+
return;
127+
case "onUnmount":
128+
onUnmount = callback;
129+
return;
130+
default:
131+
var handled = TargetHandler != null ? TargetHandler.SetEventListener(eventName, callback) : false;
132+
if (!handled) base.SetEventListener(eventName, callback);
133+
return;
134+
}
135+
}
136+
}
137+
}

Runtime/Frameworks/UGUI/Components/PrefabComponent.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Runtime/Frameworks/UGUI/Components/UGUIComponent.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,12 @@ protected override void ApplyStylesSelf()
129129
UpdateBackgroundGraphic(false, true);
130130
}
131131

132+
public override void Relayout()
133+
{
134+
Layout.MarkDirty();
135+
Context.ScheduleLayout();
136+
}
137+
132138
#endregion
133139

134140

Runtime/Frameworks/UGUI/General.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using ReactUnity.Helpers;
2+
using System;
3+
using UnityEngine.Events;
4+
using UnityEngine.EventSystems;
5+
6+
namespace ReactUnity.UGUI
7+
{
8+
public class PrefabTarget : UIBehaviour, IPrefabTarget
9+
{
10+
public PrefabComponent MountedTo;
11+
public PrefabEvent OnMount;
12+
public PrefabEvent OnUnmount;
13+
public SetPropertyEvent OnSetProperty;
14+
public SetEventListenerEvent OnSetEventListener;
15+
16+
protected override void OnRectTransformDimensionsChange()
17+
{
18+
MountedTo?.Relayout();
19+
}
20+
21+
public virtual void Mount(PrefabComponent cmp)
22+
{
23+
MountedTo = cmp;
24+
OnMount?.Invoke(cmp, this);
25+
}
26+
27+
public virtual void Unmount(PrefabComponent cmp)
28+
{
29+
OnUnmount?.Invoke(cmp, this);
30+
MountedTo = null;
31+
}
32+
33+
public virtual bool SetEventListener(string eventName, Callback callback)
34+
{
35+
OnSetEventListener?.Invoke(eventName, callback);
36+
return OnSetEventListener != null;
37+
}
38+
39+
public virtual bool SetProperty(string propertyName, object value)
40+
{
41+
OnSetProperty?.Invoke(propertyName, value);
42+
return OnSetProperty != null;
43+
}
44+
45+
[Serializable]
46+
public class PrefabEvent : UnityEvent<PrefabComponent, PrefabTarget> { }
47+
}
48+
49+
public interface IPrefabTarget
50+
{
51+
/* Return true to notify that this property is handled */
52+
bool SetProperty(string propertyName, object value);
53+
54+
/* Return true to notify that this event is handled */
55+
bool SetEventListener(string eventName, Callback callback);
56+
57+
/* Callback called when the target is mounted on the component */
58+
void Mount(PrefabComponent cmp);
59+
60+
/* Callback called when the target is unmounted off the component */
61+
void Unmount(PrefabComponent cmp);
62+
}
63+
}

Runtime/Frameworks/UGUI/General/PrefabTarget.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Runtime/Frameworks/UGUI/Layout/TextMeasurer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public class TextMeasurer : MonoBehaviour, ILayoutSelfController
1414
public YogaNode Layout;
1515
public UGUIContext Context;
1616

17-
private void Start()
17+
void Start()
1818
{
1919
if (Layout == null) DestroyImmediate(this);
2020
}

0 commit comments

Comments
 (0)