Skip to content

Commit e11eff6

Browse files
committed
Add support for measure function on nodes with ILayoutElement components
1 parent c7cdad2 commit e11eff6

File tree

5 files changed

+180
-1
lines changed

5 files changed

+180
-1
lines changed

Plugins/src~/flex-ui.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ EXPORT void FlexUi_NodeRemoveAllChildren(YGNodeRef node) {
9595
YGNodeRemoveAllChildren(node);
9696
}
9797

98+
EXPORT int FlexUi_NodeGetChildCount(YGNodeConstRef node) {
99+
return YGNodeGetChildCount(node);
100+
}
101+
98102
EXPORT const char *FlexUi_NodeSetConfig(YGNodeRef node, YGConfigRef config) {
99103
try {
100104
YGNodeSetConfig(node, config);
@@ -105,6 +109,26 @@ EXPORT const char *FlexUi_NodeSetConfig(YGNodeRef node, YGConfigRef config) {
105109
}
106110
}
107111

112+
EXPORT void FlexUi_NodeSetContext(YGNodeRef node, void* context) {
113+
YGNodeSetContext(node, context);
114+
}
115+
116+
EXPORT void *FlexUi_NodeGetContext(YGNodeRef node) {
117+
return YGNodeGetContext(node);
118+
}
119+
120+
EXPORT void FlexUi_NodeSetMeasureFunc(YGNodeRef node, YGMeasureFunc measureFunc) {
121+
YGNodeSetMeasureFunc(node, measureFunc);
122+
}
123+
124+
EXPORT bool FlexUi_NodeHasMeasureFunc(YGNodeRef node) {
125+
return YGNodeHasMeasureFunc(node);
126+
}
127+
128+
EXPORT void FlexUi_NodeSetDirty(YGNodeRef node) {
129+
YGNodeMarkDirty(node);
130+
}
131+
108132
EXPORT float FlexUi_NodeLayoutGetLeft(YGNodeConstRef node) {
109133
return YGNodeLayoutGetLeft(node);
110134
}

Runtime/FlexLayout.cs

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
using System;
12
using System.Collections.Generic;
3+
using System.Runtime.InteropServices;
24
using System.Threading.Tasks;
5+
using AOT;
36
using Gilzoide.FlexUi.Yoga;
47
using UnityEngine;
58
using UnityEngine.EventSystems;
9+
using UnityEngine.UI;
610

711
namespace Gilzoide.FlexUi
812
{
@@ -442,6 +446,7 @@ protected YGNode LayoutNode
442446
if (_layoutNode.IsNull)
443447
{
444448
_layoutNode.Instantiate();
449+
UpdateNodeMeasure();
445450
UpdateNodeStyle();
446451
RefreshRootLayout();
447452
}
@@ -490,7 +495,17 @@ protected virtual void OnTransformChildrenChanged()
490495
protected override void OnRectTransformDimensionsChange()
491496
{
492497
base.OnRectTransformDimensionsChange();
493-
if (IsActive() && IsRootLayoutNode)
498+
if (!IsActive())
499+
{
500+
return;
501+
}
502+
503+
if (LayoutNode.HasMeasureFunc())
504+
{
505+
LayoutNode.SetDirty();
506+
RefreshRootLayout();
507+
}
508+
else if (IsRootLayoutNode && !_isRefreshScheduled)
494509
{
495510
RefreshLayout();
496511
}
@@ -603,6 +618,20 @@ protected void UpdateNodeStyle()
603618
layoutNode.StyleSetGap(Gutter.Row, _gapRow);
604619
}
605620

621+
protected void UpdateNodeMeasure()
622+
{
623+
if (_layoutNode.GetChildCount() == 0 && TryGetComponent(out ILayoutElement _))
624+
{
625+
_layoutNode.SetContext(RectTransform);
626+
_layoutNode.SetMeasureFunc(RectTransformMeasureFuncPtr);
627+
}
628+
else
629+
{
630+
_layoutNode.SetContext(default);
631+
_layoutNode.SetMeasureFunc(IntPtr.Zero);
632+
}
633+
}
634+
606635
protected void RefreshParent()
607636
{
608637
Transform parent = RectTransform.parent;
@@ -725,5 +754,40 @@ protected override void Reset()
725754
}
726755
}
727756
#endif
757+
758+
[MonoPInvokeCallback(typeof(Yoga.Yoga.YGMeasureFunc))]
759+
private static Vector2 RectTransformMeasureFunc(IntPtr nodePtr, float width, MeasureMode widthMode, float height, MeasureMode heightMode)
760+
{
761+
var node = new YGNode(nodePtr);
762+
RectTransform rectTransform = node.GetContext<RectTransform>();
763+
switch (widthMode)
764+
{
765+
case MeasureMode.Undefined:
766+
width = LayoutUtility.GetPreferredWidth(rectTransform);
767+
break;
768+
769+
case MeasureMode.AtMost:
770+
width = Mathf.Min(width, LayoutUtility.GetPreferredWidth(rectTransform));
771+
break;
772+
773+
default:
774+
break;
775+
}
776+
switch (heightMode)
777+
{
778+
case MeasureMode.Undefined:
779+
height = LayoutUtility.GetPreferredHeight(rectTransform);
780+
break;
781+
782+
case MeasureMode.AtMost:
783+
height = Mathf.Min(height, LayoutUtility.GetPreferredHeight(rectTransform));
784+
break;
785+
786+
default:
787+
break;
788+
}
789+
return new Vector2(width, height);
790+
}
791+
private static readonly IntPtr RectTransformMeasureFuncPtr = Marshal.GetFunctionPointerForDelegate<Yoga.Yoga.YGMeasureFunc>(RectTransformMeasureFunc);
728792
}
729793
}

Runtime/Yoga/Enums.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,4 +133,11 @@ public enum Wrap
133133
Wrap,
134134
WrapReverse,
135135
}
136+
137+
public enum MeasureMode
138+
{
139+
Undefined,
140+
Exactly,
141+
AtMost,
142+
}
136143
}

Runtime/Yoga/YGNode.cs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Runtime.InteropServices;
23
using UnityEngine;
34

45
namespace Gilzoide.FlexUi.Yoga
@@ -9,6 +10,11 @@ public struct YGNode : IDisposable, IEquatable<YGNode>
910

1011
public bool IsNull => _nodePtr == IntPtr.Zero;
1112

13+
internal YGNode(IntPtr nodePtr)
14+
{
15+
_nodePtr = nodePtr;
16+
}
17+
1218
public void Instantiate()
1319
{
1420
_nodePtr = Yoga.YGNodeNew();
@@ -253,6 +259,11 @@ public void StyleSetPadding(Edge edge, YGValue value)
253259

254260
public void Free()
255261
{
262+
GCHandle ctx = GetContext();
263+
if (ctx.IsAllocated)
264+
{
265+
ctx.Free();
266+
}
256267
Yoga.YGNodeFree(_nodePtr);
257268
}
258269

@@ -263,6 +274,10 @@ public void CalculateLayout(float availableWidth, float availableHeight, Directi
263274

264275
public bool InsertChild(YGNode child, int index)
265276
{
277+
// Nodes with children cannot have measure functions
278+
SetContext(default);
279+
SetMeasureFunc(IntPtr.Zero);
280+
266281
string error = Yoga.YGNodeInsertChild(_nodePtr, child._nodePtr, index);
267282
if (error != null)
268283
{
@@ -283,6 +298,11 @@ public void RemoveAllChildren()
283298
Yoga.YGNodeRemoveAllChildren(_nodePtr);
284299
}
285300

301+
public int GetChildCount()
302+
{
303+
return Yoga.YGNodeGetChildCount(_nodePtr);
304+
}
305+
286306
public void SetConfig(YGConfig config)
287307
{
288308
string error = Yoga.YGNodeSetConfig(_nodePtr, config._configPtr);
@@ -292,6 +312,56 @@ public void SetConfig(YGConfig config)
292312
}
293313
}
294314

315+
public void SetContext<T>(T value)
316+
{
317+
SetContext(GCHandle.Alloc(value));
318+
}
319+
320+
public void SetContext(GCHandle value)
321+
{
322+
GCHandle ctx = GetContext();
323+
if (ctx.IsAllocated)
324+
{
325+
ctx.Free();
326+
}
327+
Yoga.YGNodeSetContext(_nodePtr, GCHandle.ToIntPtr(value));
328+
}
329+
330+
public T GetContext<T>()
331+
{
332+
GCHandle handle = GetContext();
333+
return handle.IsAllocated ? (T) handle.Target : default;
334+
}
335+
336+
public GCHandle GetContext()
337+
{
338+
IntPtr ptr = Yoga.YGNodeGetContext(_nodePtr);
339+
return ptr != IntPtr.Zero ? GCHandle.FromIntPtr(ptr) : default;
340+
}
341+
342+
public void SetMeasureFunc(Yoga.YGMeasureFunc measureFunc)
343+
{
344+
Yoga.YGNodeSetMeasureFunc(_nodePtr, measureFunc);
345+
}
346+
347+
public void SetMeasureFunc(IntPtr measureFunc)
348+
{
349+
Yoga.YGNodeSetMeasureFunc(_nodePtr, measureFunc);
350+
}
351+
352+
public bool HasMeasureFunc()
353+
{
354+
return Yoga.YGNodeHasMeasureFunc(_nodePtr);
355+
}
356+
357+
public void SetDirty()
358+
{
359+
if (HasMeasureFunc())
360+
{
361+
Yoga.YGNodeSetDirty(_nodePtr);
362+
}
363+
}
364+
295365
#endregion
296366

297367
#region Layout

Runtime/Yoga/Yoga.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Runtime.InteropServices;
3+
using UnityEngine;
34

45
namespace Gilzoide.FlexUi.Yoga
56
{
@@ -33,9 +34,22 @@ public class Yoga
3334
[DllImport(YogaDll, EntryPoint = "FlexUi_NodeInsertChild")] public static extern string YGNodeInsertChild(IntPtr node, IntPtr child, int index);
3435
[DllImport(YogaDll, EntryPoint = "FlexUi_NodeRemoveChild")] public static extern void YGNodeRemoveChild(IntPtr node, IntPtr child);
3536
[DllImport(YogaDll, EntryPoint = "FlexUi_NodeRemoveAllChildren")] public static extern void YGNodeRemoveAllChildren(IntPtr node);
37+
[DllImport(YogaDll, EntryPoint = "FlexUi_NodeGetChildCount")] public static extern int YGNodeGetChildCount(IntPtr node);
3638

3739
[DllImport(YogaDll, EntryPoint = "FlexUi_NodeSetConfig")] public static extern string YGNodeSetConfig(IntPtr node, IntPtr config);
3840

41+
[DllImport(YogaDll, EntryPoint = "FlexUi_NodeSetContext")] public static extern void YGNodeSetContext(IntPtr node, IntPtr context);
42+
[DllImport(YogaDll, EntryPoint = "FlexUi_NodeGetContext")] public static extern IntPtr YGNodeGetContext(IntPtr node);
43+
[DllImport(YogaDll, EntryPoint = "FlexUi_NodeSetMeasureFunc")] public static extern void YGNodeSetMeasureFunc(IntPtr node, IntPtr measureFunc);
44+
[DllImport(YogaDll, EntryPoint = "FlexUi_NodeHasMeasureFunc")] public static extern bool YGNodeHasMeasureFunc(IntPtr node);
45+
[DllImport(YogaDll, EntryPoint = "FlexUi_NodeSetDirty")] public static extern void YGNodeSetDirty(IntPtr node);
46+
47+
public delegate Vector2 YGMeasureFunc(IntPtr nodePtr, float width, MeasureMode widthMode, float height, MeasureMode heightMode);
48+
public static void YGNodeSetMeasureFunc(IntPtr node, YGMeasureFunc measureFunc)
49+
{
50+
YGNodeSetMeasureFunc(node, Marshal.GetFunctionPointerForDelegate(measureFunc));
51+
}
52+
3953
#endregion
4054

4155
#region YGNodeLayout

0 commit comments

Comments
 (0)