From bf5eeaf61e507d24aa55b4b491ba95bbbc569175 Mon Sep 17 00:00:00 2001 From: Emil Sjolander Date: Thu, 5 Jan 2017 12:48:07 -0800 Subject: [PATCH 1/5] Add api for retrieving computed padding Summary: Add API for retrieving the computed final padding of a node. Many frameworks such as React Native retrieve padding via `YGNodeStyleGetPadding` but given that we now support percentage values this is not correct anymore. Differential Revision: D4376572 fbshipit-source-id: 3ffb66e77090fc1257511bec5c933f9b0c304b9f --- tests/YGComputedPaddingTest.cpp | 30 +++++++++++++++++++++++++++ yoga/Yoga.c | 36 +++++++++++++++++++++++++++++---- yoga/Yoga.h | 6 ++++++ 3 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 tests/YGComputedPaddingTest.cpp diff --git a/tests/YGComputedPaddingTest.cpp b/tests/YGComputedPaddingTest.cpp new file mode 100644 index 00000000..79f8be0c --- /dev/null +++ b/tests/YGComputedPaddingTest.cpp @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#include +#include + +TEST(YogaTest, computed_layout_padding) { + const YGNodeRef root = YGNodeNew(); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + YGNodeStyleSetPaddingPercent(root, YGEdgeStart, 10); + + YGNodeCalculateLayout(root, 100, 100, YGDirectionLTR); + + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetPadding(root, YGEdgeLeft)); + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetPadding(root, YGEdgeRight)); + + YGNodeCalculateLayout(root, 100, 100, YGDirectionRTL); + + ASSERT_FLOAT_EQ(0, YGNodeLayoutGetPadding(root, YGEdgeLeft)); + ASSERT_FLOAT_EQ(10, YGNodeLayoutGetPadding(root, YGEdgeRight)); + + YGNodeFreeRecursive(root); +} diff --git a/yoga/Yoga.c b/yoga/Yoga.c index df176de0..bc73ecbc 100644 --- a/yoga/Yoga.c +++ b/yoga/Yoga.c @@ -47,6 +47,7 @@ typedef struct YGCachedMeasurement { typedef struct YGLayout { float position[4]; float dimensions[2]; + float padding[6]; YGDirection direction; uint32_t computedFlexBasisGeneration; @@ -563,6 +564,28 @@ YG_NODE_LAYOUT_PROPERTY_IMPL(float, Width, dimensions[YGDimensionWidth]); YG_NODE_LAYOUT_PROPERTY_IMPL(float, Height, dimensions[YGDimensionHeight]); YG_NODE_LAYOUT_PROPERTY_IMPL(YGDirection, Direction, direction); +float YGNodeLayoutGetPadding(const YGNodeRef node, const YGEdge edge) { + YG_ASSERT(edge <= YGEdgeEnd, "Cannot get layout paddings of multi-edge shorthands"); + + if (edge == YGEdgeLeft) { + if (node->layout.direction == YGDirectionRTL) { + return node->layout.padding[YGEdgeEnd]; + } else { + return node->layout.padding[YGEdgeStart]; + } + } + + if (edge == YGEdgeRight) { + if (node->layout.direction == YGDirectionRTL) { + return node->layout.padding[YGEdgeStart]; + } else { + return node->layout.padding[YGEdgeEnd]; + } + } + + return node->layout.padding[edge]; +} + uint32_t gCurrentGenerationCount = 0; bool YGLayoutNodeInternal(const YGNodeRef node, @@ -870,8 +893,8 @@ static float YGNodeLeadingPadding(const YGNodeRef node, } return fmaxf(YGValueResolve(YGComputedEdgeValue(node->style.padding, leading[axis], &YGValueZero), - widthSize), - 0.0f); + widthSize), + 0.0f); } static float YGNodeTrailingPadding(const YGNodeRef node, @@ -883,8 +906,8 @@ static float YGNodeTrailingPadding(const YGNodeRef node, } return fmaxf(YGValueResolve(YGComputedEdgeValue(node->style.padding, trailing[axis], &YGValueZero), - widthSize), - 0.0f); + widthSize), + 0.0f); } static float YGNodeLeadingBorder(const YGNodeRef node, const YGFlexDirection axis) { @@ -1657,6 +1680,11 @@ static void YGNodelayoutImpl(const YGNodeRef node, const YGDirection direction = YGNodeResolveDirection(node, parentDirection); node->layout.direction = direction; + node->layout.padding[YGEdgeStart] = YGNodeLeadingPadding(node, YGFlexDirectionResolve(YGFlexDirectionRow, direction), parentWidth); + node->layout.padding[YGEdgeEnd] = YGNodeTrailingPadding(node, YGFlexDirectionResolve(YGFlexDirectionRow, direction), parentWidth); + node->layout.padding[YGEdgeTop] = YGNodeLeadingPadding(node, YGFlexDirectionResolve(YGFlexDirectionColumn, direction), parentWidth); + node->layout.padding[YGEdgeBottom] = YGNodeTrailingPadding(node, YGFlexDirectionResolve(YGFlexDirectionColumn, direction), parentWidth); + if (node->measure) { YGNodeWithMeasureFuncSetMeasuredDimensions( node, availableWidth, availableHeight, widthMeasureMode, heightMeasureMode); diff --git a/yoga/Yoga.h b/yoga/Yoga.h index 083d3073..ba96b9cf 100644 --- a/yoga/Yoga.h +++ b/yoga/Yoga.h @@ -191,6 +191,12 @@ YG_NODE_LAYOUT_PROPERTY(float, Width); YG_NODE_LAYOUT_PROPERTY(float, Height); YG_NODE_LAYOUT_PROPERTY(YGDirection, Direction); +// Get the computed padding for this node after performing layout. If padding was set using +// pixel values then the returned value will be the same as YGNodeStyleGetPadding. However if +// padding was set using a percentage value then the returned value is the computed value used +// during layout. +WIN_EXPORT float YGNodeLayoutGetPadding(const YGNodeRef node, const YGEdge edge); + WIN_EXPORT void YGSetLogger(YGLogger logger); WIN_EXPORT void YGLog(YGLogLevel level, const char *message, ...); -- 2.50.1.windows.1 From e539ecc9aa7bf9c836fec65a90df79480ca24d9e Mon Sep 17 00:00:00 2001 From: Emil Sjolander Date: Thu, 5 Jan 2017 12:48:10 -0800 Subject: [PATCH 2/5] Expose layout paddding in java api Summary: Expose layout padding api from D4376572 Reviewed By: passy Differential Revision: D4377069 fbshipit-source-id: 2e0c04104560e1be586562b27b3457576590dc18 --- java/com/facebook/yoga/YogaNode.java | 28 +++++++++++++++++++ java/com/facebook/yoga/YogaNodeAPI.java | 1 + java/jni/YGJNI.cpp | 11 ++++++++ .../tests/com/facebook/yoga/YogaNodeTest.java | 17 +++++++++++ 4 files changed, 57 insertions(+) diff --git a/java/com/facebook/yoga/YogaNode.java b/java/com/facebook/yoga/YogaNode.java index 0a4e0bac..87b22985 100644 --- a/java/com/facebook/yoga/YogaNode.java +++ b/java/com/facebook/yoga/YogaNode.java @@ -69,6 +69,14 @@ public class YogaNode implements YogaNodeAPI { @DoNotStrip private float mLeft = YogaConstants.UNDEFINED; @DoNotStrip + private float mPaddingLeft = 0; + @DoNotStrip + private float mPaddingTop = 0; + @DoNotStrip + private float mPaddingRight = 0; + @DoNotStrip + private float mPaddingBottom = 0; + @DoNotStrip private int mLayoutDirection = 0; private native long jni_YGNodeNew(); @@ -564,6 +572,26 @@ public class YogaNode implements YogaNodeAPI { return mHeight; } + @Override + public float getLayoutPadding(YogaEdge edge) { + switch (edge) { + case LEFT: + return mPaddingLeft; + case TOP: + return mPaddingTop; + case RIGHT: + return mPaddingRight; + case BOTTOM: + return mPaddingBottom; + case START: + return getLayoutDirection() == YogaDirection.RTL ? mPaddingRight : mPaddingLeft; + case END: + return getLayoutDirection() == YogaDirection.RTL ? mPaddingLeft : mPaddingRight; + default: + throw new IllegalArgumentException("Cannot get layout paddings of multi-edge shorthands"); + } + } + @Override public YogaDirection getLayoutDirection() { return YogaDirection.values()[mLayoutDirection]; diff --git a/java/com/facebook/yoga/YogaNodeAPI.java b/java/com/facebook/yoga/YogaNodeAPI.java index b3cf0ac3..98742d34 100644 --- a/java/com/facebook/yoga/YogaNodeAPI.java +++ b/java/com/facebook/yoga/YogaNodeAPI.java @@ -81,6 +81,7 @@ public interface YogaNodeAPI { float getLayoutY(); float getLayoutWidth(); float getLayoutHeight(); + float getLayoutPadding(YogaEdge edge); YogaDirection getLayoutDirection(); YogaOverflow getOverflow(); void setOverflow(YogaOverflow overflow); diff --git a/java/jni/YGJNI.cpp b/java/jni/YGJNI.cpp index de648a4b..46aaecfb 100644 --- a/java/jni/YGJNI.cpp +++ b/java/jni/YGJNI.cpp @@ -30,10 +30,21 @@ static void YGTransferLayoutOutputsRecursive(YGNodeRef root) { static auto leftField = obj->getClass()->getField("mLeft"); static auto topField = obj->getClass()->getField("mTop"); + static auto paddingLeftField = obj->getClass()->getField("mPaddingLeft"); + static auto paddingTopField = obj->getClass()->getField("mPaddingTop"); + static auto paddingRightField = obj->getClass()->getField("mPaddingRight"); + static auto paddingBottomField = obj->getClass()->getField("mPaddingBottom"); + obj->setFieldValue(widthField, YGNodeLayoutGetWidth(root)); obj->setFieldValue(heightField, YGNodeLayoutGetHeight(root)); obj->setFieldValue(leftField, YGNodeLayoutGetLeft(root)); obj->setFieldValue(topField, YGNodeLayoutGetTop(root)); + + obj->setFieldValue(paddingLeftField, YGNodeLayoutGetPadding(root, YGEdgeLeft)); + obj->setFieldValue(paddingTopField, YGNodeLayoutGetPadding(root, YGEdgeTop)); + obj->setFieldValue(paddingRightField, YGNodeLayoutGetPadding(root, YGEdgeRight)); + obj->setFieldValue(paddingBottomField, YGNodeLayoutGetPadding(root, YGEdgeBottom)); + YGTransferLayoutDirection(root, obj); for (uint32_t i = 0; i < YGNodeGetChildCount(root); i++) { diff --git a/java/tests/com/facebook/yoga/YogaNodeTest.java b/java/tests/com/facebook/yoga/YogaNodeTest.java index 2e7a9e7a..ddec8cff 100644 --- a/java/tests/com/facebook/yoga/YogaNodeTest.java +++ b/java/tests/com/facebook/yoga/YogaNodeTest.java @@ -138,4 +138,21 @@ public class YogaNodeTest { node0.copyStyle(node1); assertEquals(100, (int) node0.getMaxHeight().value); } + + @Test + public void testLayoutPadding() { + final YogaNode node = new YogaNode(); + node.setWidth(100); + node.setHeight(100); + node.setPadding(YogaEdge.START, 1); + node.setPadding(YogaEdge.END, 2); + node.setPadding(YogaEdge.TOP, 3); + node.setPadding(YogaEdge.BOTTOM, 4); + node.calculateLayout(); + + assertEquals(1, (int) node.getLayoutPadding(YogaEdge.LEFT)); + assertEquals(2, (int) node.getLayoutPadding(YogaEdge.RIGHT)); + assertEquals(3, (int) node.getLayoutPadding(YogaEdge.TOP)); + assertEquals(4, (int) node.getLayoutPadding(YogaEdge.BOTTOM)); + } } -- 2.50.1.windows.1 From 6d48307c8d7ec993caac04f98cea11be4466ace7 Mon Sep 17 00:00:00 2001 From: Emil Sjolander Date: Thu, 5 Jan 2017 12:48:11 -0800 Subject: [PATCH 3/5] Expose layout paddding in csharp api Summary: Expose layout padding api from D4376572 Reviewed By: splhack Differential Revision: D4377070 fbshipit-source-id: 501628612675e3fdce9a8e77f929cb9c6eddc209 --- csharp/Facebook.Yoga/Native.cs | 5 ++++- csharp/Facebook.Yoga/YogaNode.cs | 5 +++++ csharp/tests/Facebook.Yoga/YogaNodeTest.cs | 17 +++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/csharp/Facebook.Yoga/Native.cs b/csharp/Facebook.Yoga/Native.cs index cb7371d3..b66cf8a6 100644 --- a/csharp/Facebook.Yoga/Native.cs +++ b/csharp/Facebook.Yoga/Native.cs @@ -315,9 +315,12 @@ namespace Facebook.Yoga [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] public static extern float YGNodeLayoutGetHeight(YGNodeHandle node); + [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] + public static extern float YGNodeLayoutGetPadding(YGNodeHandle node, YogaEdge edge); + [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] public static extern YogaDirection YGNodeLayoutGetDirection(YGNodeHandle node); #endregion } -} \ No newline at end of file +} diff --git a/csharp/Facebook.Yoga/YogaNode.cs b/csharp/Facebook.Yoga/YogaNode.cs index 94cafa50..5732748b 100644 --- a/csharp/Facebook.Yoga/YogaNode.cs +++ b/csharp/Facebook.Yoga/YogaNode.cs @@ -308,6 +308,11 @@ namespace Facebook.Yoga } } + public float GetLayoutPadding(YogaEdge edge) + { + return Native.YGNodeLayoutGetPadding(_ygNode, edge); + } + public YogaValue Width { get diff --git a/csharp/tests/Facebook.Yoga/YogaNodeTest.cs b/csharp/tests/Facebook.Yoga/YogaNodeTest.cs index ebb09083..9979f091 100644 --- a/csharp/tests/Facebook.Yoga/YogaNodeTest.cs +++ b/csharp/tests/Facebook.Yoga/YogaNodeTest.cs @@ -355,5 +355,22 @@ namespace Facebook.Yoga return MeasureOutput.Make(120, 130); }); } + + [Test] + public void TestLayoutPadding() { + YogaNode node = new YogaNode(); + node.Width = 100; + node.Height = 100; + node.SetPadding(YogaEdge.Start, 1); + node.SetPadding(YogaEdge.End, 2); + node.SetPadding(YogaEdge.Top, 3); + node.SetPadding(YogaEdge.Bottom, 4); + node.CalculateLayout(); + + Assert.AreEqual(1, node.GetLayoutPadding(YogaEdge.Left)); + Assert.AreEqual(2, node.GetLayoutPadding(YogaEdge.Right)); + Assert.AreEqual(3, node.GetLayoutPadding(YogaEdge.Top)); + Assert.AreEqual(4, node.GetLayoutPadding(YogaEdge.Bottom)); + } } } -- 2.50.1.windows.1 From 35ceb973bb9cf48cd5e0147c144dd7c79bacde5a Mon Sep 17 00:00:00 2001 From: David Hart Date: Fri, 6 Jan 2017 00:21:41 +0100 Subject: [PATCH 4/5] improved the objective-c and swift api --- YogaKit/Tests/YogaKitTests.m | 214 +++++----- YogaKit/UIView+Yoga.h | 72 +--- YogaKit/UIView+Yoga.m | 403 +----------------- YogaKit/YGLayout+Private.h | 16 + YogaKit/YGLayout.h | 115 +++++ YogaKit/YGLayout.m | 385 +++++++++++++++++ .../YogaKitSample.xcodeproj/project.pbxproj | 31 +- .../YogaKitSample/SwiftViewController.swift | 40 ++ .../YogaKitSample/ViewController.m | 18 +- .../YogaKitSample-Bridging-Header.h | 6 + enums.py | 12 +- yoga/YGEnums.h | 121 ++++++ yoga/YGValue.h | 19 + yoga/Yoga.h | 6 +- 14 files changed, 874 insertions(+), 584 deletions(-) create mode 100644 YogaKit/YGLayout+Private.h create mode 100644 YogaKit/YGLayout.h create mode 100644 YogaKit/YGLayout.m create mode 100644 YogaKit/YogaKitSample/YogaKitSample/SwiftViewController.swift create mode 100644 YogaKit/YogaKitSample/YogaKitSample/YogaKitSample-Bridging-Header.h create mode 100644 yoga/YGValue.h diff --git a/YogaKit/Tests/YogaKitTests.m b/YogaKit/Tests/YogaKitTests.m index bdade66e..79f7043c 100644 --- a/YogaKit/Tests/YogaKitTests.m +++ b/YogaKit/Tests/YogaKitTests.m @@ -23,7 +23,7 @@ XCTAssertEqual(0, YGNodeGetInstanceCount()); UIView *view = [[UIView alloc] initWithFrame:CGRectZero]; - [view yg_setFlexBasis:1]; + view.yoga.flexBasis = 1; XCTAssertEqual(1, YGNodeGetInstanceCount()); view = nil; @@ -35,11 +35,11 @@ XCTAssertEqual(0, YGNodeGetInstanceCount()); UIView *view = [[UIView alloc] initWithFrame:CGRectZero]; - [view yg_setFlexBasis:1]; + view.yoga.flexBasis = 1; for (int i=0; i<10; i++) { UIView *subview = [[UIView alloc] initWithFrame:CGRectZero]; - [subview yg_setFlexBasis:1]; + subview.yoga.flexBasis = 1; [view addSubview:subview]; } XCTAssertEqual(11, YGNodeGetInstanceCount()); @@ -53,69 +53,69 @@ - (void)testUsesYoga { UIView *view = [[UIView alloc] initWithFrame:CGRectZero]; - XCTAssertFalse([view yg_usesYoga]); + XCTAssertFalse(view.yoga.isEnabled); - [view yg_setUsesYoga:YES]; - XCTAssertTrue([view yg_usesYoga]); + view.yoga.isEnabled = YES; + XCTAssertTrue(view.yoga.isEnabled); - [view yg_setUsesYoga:NO]; - XCTAssertFalse([view yg_usesYoga]); + view.yoga.isEnabled = NO; + XCTAssertFalse(view.yoga.isEnabled); } - (void)testSizeThatFitsAsserts { UIView *view = [[UIView alloc] initWithFrame:CGRectZero]; dispatch_sync(dispatch_queue_create("com.facebook.Yoga.testing", DISPATCH_QUEUE_SERIAL), ^(void){ - XCTAssertThrows([view yg_intrinsicSize]); + XCTAssertThrows(view.yoga.intrinsicSize); }); } - (void)testSizeThatFitsSmoke { UIView *container = [[UIView alloc] initWithFrame:CGRectZero]; - [container yg_setUsesYoga:YES]; - [container yg_setFlexDirection:YGFlexDirectionRow]; - [container yg_setAlignItems:YGAlignFlexStart]; + container.yoga.isEnabled = YES; + container.yoga.flexDirection = YGFlexDirectionRow; + container.yoga.alignItems = YGAlignFlexStart; UILabel *longTextLabel = [[UILabel alloc] initWithFrame:CGRectZero]; longTextLabel.text = @"This is a very very very very very very very very long piece of text."; longTextLabel.lineBreakMode = NSLineBreakByTruncatingTail; longTextLabel.numberOfLines = 1; - [longTextLabel yg_setUsesYoga:YES]; - [longTextLabel yg_setFlexShrink:1]; + longTextLabel.yoga.isEnabled = YES; + longTextLabel.yoga.flexShrink = 1; [container addSubview:longTextLabel]; UIView *textBadgeView = [[UIView alloc] initWithFrame:CGRectZero]; - [textBadgeView yg_setUsesYoga:YES]; - [textBadgeView yg_setMargin:3.0 forEdge:YGEdgeLeft]; - [textBadgeView yg_setWidth:10]; - [textBadgeView yg_setHeight:10]; + textBadgeView.yoga.isEnabled = YES; + textBadgeView.yoga.marginLeft = 3.0; + textBadgeView.yoga.width = 10; + textBadgeView.yoga.height = 10; [container addSubview:textBadgeView]; - const CGSize containerSize = [container yg_intrinsicSize]; + const CGSize containerSize = container.yoga.intrinsicSize; XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(514,21), containerSize), @"Size is actually %@", NSStringFromCGSize(containerSize)); } - (void)testThatMarkingLeafsAsDirtyWillTriggerASizeRecalculation { UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 50)]; - [container yg_setUsesYoga:YES]; - [container yg_setFlexDirection:YGFlexDirectionRow]; - [container yg_setAlignItems:YGAlignFlexStart]; + container.yoga.isEnabled = YES; + container.yoga.flexDirection = YGFlexDirectionRow; + container.yoga.alignItems = YGAlignFlexStart; UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero]; label.text = @"This is a short text."; label.numberOfLines = 1; - [label yg_setUsesYoga:YES]; + label.yoga.isEnabled = YES; [container addSubview:label]; - [container yg_applyLayout]; + [container.yoga applyLayout]; XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(146,21), label.bounds.size), @"Size is actually %@", NSStringFromCGSize(label.bounds.size)); label.text = @"This is a slightly longer text."; - [label yg_markDirty]; + [label.yoga markDirty]; - [container yg_applyLayout]; + [container.yoga applyLayout]; XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(213,21), label.bounds.size), @"Size is actually %@", NSStringFromCGSize(label.bounds.size)); } @@ -124,17 +124,17 @@ const CGSize containerSize = CGSizeMake(320, 50); UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, containerSize.width, containerSize.height)]; - [container yg_setUsesYoga:YES]; - [container yg_setFlexDirection:YGFlexDirectionRow]; + container.yoga.isEnabled = YES; + container.yoga.flexDirection = YGFlexDirectionRow; for (int i = 0; i < 3; i++) { UIView *subview = [[UIView alloc] initWithFrame:CGRectZero]; - [subview yg_setUsesYoga:YES]; - [subview yg_setFlexGrow:1]; + subview.yoga.isEnabled = YES; + subview.yoga.flexGrow = 1; [container addSubview:subview]; } - [container yg_applyLayout]; + [container.yoga applyLayout]; XCTAssertFalse(CGRectIntersectsRect([container.subviews objectAtIndex:0].frame, [container.subviews objectAtIndex:1].frame)); XCTAssertFalse(CGRectIntersectsRect([container.subviews objectAtIndex:1].frame, [container.subviews objectAtIndex:2].frame)); @@ -153,33 +153,33 @@ const CGSize containerSize = CGSizeMake(300, 50); UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, containerSize.width, containerSize.height)]; - [container yg_setUsesYoga:YES]; - [container yg_setFlexDirection:YGFlexDirectionRow]; + container.yoga.isEnabled = YES; + container.yoga.flexDirection = YGFlexDirectionRow; UIView *subview1 = [[UIView alloc] initWithFrame:CGRectZero]; - [subview1 yg_setUsesYoga:YES]; - [subview1 yg_setFlexGrow:1]; + subview1.yoga.isEnabled = YES; + subview1.yoga.flexGrow = 1; [container addSubview:subview1]; UIView *subview2 = [[UIView alloc] initWithFrame:CGRectZero]; - [subview2 yg_setUsesYoga:YES]; - [subview2 yg_setFlexGrow:1]; + subview2.yoga.isEnabled = YES; + subview2.yoga.flexGrow = 1; [container addSubview:subview2]; UIView *subview3 = [[UIView alloc] initWithFrame:CGRectZero]; - [subview3 yg_setUsesYoga:YES]; - [subview3 yg_setFlexGrow:1]; + subview3.yoga.isEnabled = YES; + subview3.yoga.flexGrow = 1; [container addSubview:subview3]; - [container yg_applyLayout]; + [container.yoga applyLayout]; XCTAssertTrue(CGRectEqualToRect(subview1.frame, CGRectMake(0, 0, 100, 50))); XCTAssertTrue(CGRectEqualToRect(subview2.frame, CGRectMake(100, 0, 100, 50)), @"It's actually %@", NSStringFromCGRect(subview2.frame)); XCTAssertTrue(CGRectEqualToRect(subview3.frame, CGRectMake(200, 0, 100, 50))); [container exchangeSubviewAtIndex:2 withSubviewAtIndex:0]; - [subview2 yg_setIncludeInLayout:NO]; - [container yg_applyLayout]; + subview2.yoga.isIncludedInLayout = NO; + [container.yoga applyLayout]; XCTAssertTrue(CGRectEqualToRect(subview3.frame, CGRectMake(0, 0, 150, 50))); XCTAssertTrue(CGRectEqualToRect(subview1.frame, CGRectMake(150, 0, 150, 50))); @@ -193,32 +193,32 @@ const CGSize containerSize = CGSizeMake(300, 50); UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, containerSize.width, containerSize.height)]; - [container yg_setUsesYoga:YES]; - [container yg_setFlexDirection:YGFlexDirectionRow]; + container.yoga.isEnabled = YES; + container.yoga.flexDirection = YGFlexDirectionRow; UIView *subview1 = [[UIView alloc] initWithFrame:CGRectZero]; - [subview1 yg_setUsesYoga:YES]; - [subview1 yg_setFlexGrow:1]; + subview1.yoga.isEnabled = YES; + subview1.yoga.flexGrow = 1; [container addSubview:subview1]; UIView *subview2 = [[UIView alloc] initWithFrame:CGRectZero]; - [subview2 yg_setUsesYoga:YES]; - [subview2 yg_setFlexGrow:1]; + subview2.yoga.isEnabled = YES; + subview2.yoga.flexGrow = 1; [container addSubview:subview2]; UIView *subview3 = [[UIView alloc] initWithFrame:CGRectZero]; - [subview3 yg_setUsesYoga:YES]; - [subview3 yg_setFlexGrow:1]; + subview3.yoga.isEnabled = YES; + subview3.yoga.flexGrow = 1; [container addSubview:subview3]; - [container yg_applyLayout]; + [container.yoga applyLayout]; for (UIView *view in container.subviews) { XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(100, 50), subview1.bounds.size), @"Actual size is %@", NSStringFromCGSize(view.bounds.size)); } - [subview3 yg_setIncludeInLayout:NO]; - [container yg_applyLayout]; + subview3.yoga.isIncludedInLayout = NO; + [container.yoga applyLayout]; XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(150, 50), subview1.bounds.size), @"Actual size is %@", NSStringFromCGSize(subview1.bounds.size)); XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(150, 50), subview2.bounds.size), @"Actual size is %@", NSStringFromCGSize(subview2.bounds.size)); @@ -230,62 +230,62 @@ - (void)testThatNumberOfChildrenIsCorrectWhenWeIgnoreSubviews { UIView *container = [[UIView alloc] initWithFrame:CGRectZero]; - [container yg_setUsesYoga:YES]; - [container yg_setFlexDirection:YGFlexDirectionRow]; + container.yoga.isEnabled = YES; + container.yoga.flexDirection = YGFlexDirectionRow; UIView *subview1 = [[UIView alloc] initWithFrame:CGRectZero]; - [subview1 yg_setUsesYoga:YES]; - [subview1 yg_setIncludeInLayout:NO]; + subview1.yoga.isEnabled = YES; + subview1.yoga.isIncludedInLayout = NO; [container addSubview:subview1]; UIView *subview2 = [[UIView alloc] initWithFrame:CGRectZero]; - [subview2 yg_setUsesYoga:YES]; - [subview2 yg_setIncludeInLayout:NO]; + subview2.yoga.isEnabled = YES; + subview2.yoga.isIncludedInLayout = NO; [container addSubview:subview2]; UIView *subview3 = [[UIView alloc] initWithFrame:CGRectZero]; - [subview3 yg_setUsesYoga:YES]; - [subview3 yg_setIncludeInLayout:YES]; + subview3.yoga.isEnabled = YES; + subview3.yoga.isIncludedInLayout = YES; [container addSubview:subview3]; - [container yg_applyLayout]; - XCTAssertEqual(1, [container yg_numberOfChildren]); + [container.yoga applyLayout]; + XCTAssertEqual(1, container.yoga.numberOfChildren); - [subview2 yg_setIncludeInLayout:YES]; - [container yg_applyLayout]; - XCTAssertEqual(2, [container yg_numberOfChildren]); + subview2.yoga.isIncludedInLayout = YES; + [container.yoga applyLayout]; + XCTAssertEqual(2, container.yoga.numberOfChildren); } - (void)testThatViewNotIncludedInFirstLayoutPassAreIncludedInSecond { UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 50)]; - [container yg_setUsesYoga:YES]; - [container yg_setFlexDirection:YGFlexDirectionRow]; + container.yoga.isEnabled = YES; + container.yoga.flexDirection = YGFlexDirectionRow; UIView *subview1 = [[UIView alloc] initWithFrame:CGRectZero]; - [subview1 yg_setUsesYoga:YES]; - [subview1 yg_setFlexGrow:1]; + subview1.yoga.isEnabled = YES; + subview1.yoga.flexGrow = 1; [container addSubview:subview1]; UIView *subview2 = [[UIView alloc] initWithFrame:CGRectZero]; - [subview2 yg_setUsesYoga:YES]; - [subview2 yg_setFlexGrow:1]; + subview2.yoga.isEnabled = YES; + subview2.yoga.flexGrow = 1; [container addSubview:subview2]; UIView *subview3 = [[UIView alloc] initWithFrame:CGRectZero]; - [subview3 yg_setUsesYoga:YES]; - [subview3 yg_setFlexGrow:1]; - [subview3 yg_setIncludeInLayout:NO]; + subview3.yoga.isEnabled = YES; + subview3.yoga.flexGrow = 1; + subview3.yoga.isIncludedInLayout = NO; [container addSubview:subview3]; - [container yg_applyLayout]; + [container.yoga applyLayout]; XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(150, 50), subview1.bounds.size), @"Actual size is %@", NSStringFromCGSize(subview1.bounds.size)); XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(150, 50), subview2.bounds.size), @"Actual size is %@", NSStringFromCGSize(subview2.bounds.size)); XCTAssertTrue(CGSizeEqualToSize(CGSizeZero, subview3.bounds.size), @"Actual size %@", NSStringFromCGSize(subview3.bounds.size)); - [subview3 yg_setIncludeInLayout:YES]; - [container yg_applyLayout]; + subview3.yoga.isIncludedInLayout = YES; + [container.yoga applyLayout]; for (UIView *view in container.subviews) { XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(100, 50), subview1.bounds.size), @"Actual size is %@", NSStringFromCGSize(view.bounds.size)); } @@ -294,60 +294,60 @@ - (void)testyg_isLeafFlag { UIView *view = [[UIView alloc] initWithFrame:CGRectZero]; - XCTAssertTrue(view.yg_isLeaf); + XCTAssertTrue(view.yoga.isLeaf); for (int i=0; i<10; i++) { UIView *subview = [[UIView alloc] initWithFrame:CGRectZero]; [view addSubview:subview]; } - XCTAssertTrue(view.yg_isLeaf); + XCTAssertTrue(view.yoga.isLeaf); - [view yg_setUsesYoga:YES]; - [view yg_setWidth:50.0]; - XCTAssertTrue(view.yg_isLeaf); + view.yoga.isEnabled = YES; + view.yoga.width = 50.0; + XCTAssertTrue(view.yoga.isLeaf); UIView *const subview = view.subviews[0]; - [subview yg_setUsesYoga:YES]; - [subview yg_setWidth:50.0]; - XCTAssertFalse(view.yg_isLeaf); + subview.yoga.isEnabled = YES; + subview.yoga.width = 50.0; + XCTAssertFalse(view.yoga.isLeaf); } - (void)testThatWeCorrectlyAttachNestedViews { UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 50)]; - [container yg_setUsesYoga:YES]; - [container yg_setFlexDirection:YGFlexDirectionColumn]; + container.yoga.isEnabled = YES; + container.yoga.flexDirection = YGFlexDirectionColumn; UIView *subview1 = [[UIView alloc] initWithFrame:CGRectZero]; - [subview1 yg_setUsesYoga:YES]; - [subview1 yg_setWidth:100]; - [subview1 yg_setFlexGrow:1]; - [subview1 yg_setFlexDirection:YGFlexDirectionColumn]; + subview1.yoga.isEnabled = YES; + subview1.yoga.width = 100; + subview1.yoga.flexGrow = 1; + subview1.yoga.flexDirection = YGFlexDirectionColumn; [container addSubview:subview1]; UIView *subview2 = [[UIView alloc] initWithFrame:CGRectZero]; - [subview2 yg_setUsesYoga:YES]; - [subview2 yg_setWidth:150]; - [subview2 yg_setFlexGrow:1]; - [subview2 yg_setFlexDirection:YGFlexDirectionColumn]; + subview2.yoga.isEnabled = YES; + subview2.yoga.width = 150; + subview2.yoga.flexGrow = 1; + subview2.yoga.flexDirection = YGFlexDirectionColumn; [container addSubview:subview2]; for (UIView *view in @[subview1, subview2]) { UIView *someView = [[UIView alloc] initWithFrame:CGRectZero]; - [someView yg_setUsesYoga:YES]; - [someView yg_setFlexGrow:1]; + someView.yoga.isEnabled = YES; + someView.yoga.flexGrow = 1; [view addSubview:someView]; } - [container yg_applyLayout]; + [container.yoga applyLayout]; // Add the same amount of new views, reapply layout. for (UIView *view in @[subview1, subview2]) { UIView *someView = [[UIView alloc] initWithFrame:CGRectZero]; - [someView yg_setUsesYoga:YES]; - [someView yg_setFlexGrow:1]; + someView.yoga.isEnabled = YES; + someView.yoga.flexGrow = 1; [view addSubview:someView]; } - [container yg_applyLayout]; + [container.yoga applyLayout]; XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(100, 25), subview1.bounds.size), @"Actual size is %@", NSStringFromCGSize(subview1.bounds.size)); for (UIView *subview in subview1.subviews) { @@ -369,19 +369,19 @@ - (void)testThatANonLeafNodeCanBecomeALeafNode { UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 50)]; - [container yg_setUsesYoga:YES]; + container.yoga.isEnabled = YES; UIView *subview1 = [[UIView alloc] initWithFrame:CGRectZero]; - [subview1 yg_setUsesYoga:YES]; + subview1.yoga.isEnabled = YES; [container addSubview:subview1]; UIView *subview2 = [[UIView alloc] initWithFrame:CGRectZero]; - [subview2 yg_setUsesYoga:YES]; + subview2.yoga.isEnabled = YES; [subview1 addSubview:subview2]; - [container yg_applyLayout]; + [container.yoga applyLayout]; [subview2 removeFromSuperview]; - [container yg_applyLayout]; + [container.yoga applyLayout]; } @end diff --git a/YogaKit/UIView+Yoga.h b/YogaKit/UIView+Yoga.h index b565b1ae..f1ceb8c7 100644 --- a/YogaKit/UIView+Yoga.h +++ b/YogaKit/UIView+Yoga.h @@ -8,76 +8,10 @@ */ #import -#import +#import "YGLayout.h" @interface UIView (Yoga) -/** - The property that decides if we should include this view when calculating layout. Defaults to YES. - */ -@property (nonatomic, readwrite, assign, setter=yg_setIncludeInLayout:) BOOL yg_includeInLayout; +@property (nonatomic, readonly, retain) YGLayout *yoga; -/** - The property that decides during layout/sizing whether or not yg_* properties should be applied. Defaults to NO. - */ -@property (nonatomic, readwrite, assign, setter=yg_setUsesYoga:) BOOL yg_usesYoga; - -- (void)yg_setDirection:(YGDirection)direction; -- (void)yg_setFlexDirection:(YGFlexDirection)flexDirection; -- (void)yg_setJustifyContent:(YGJustify)justifyContent; -- (void)yg_setAlignContent:(YGAlign)alignContent; -- (void)yg_setAlignItems:(YGAlign)alignItems; -- (void)yg_setAlignSelf:(YGAlign)alignSelf; -- (void)yg_setPositionType:(YGPositionType)positionType; -- (void)yg_setFlexWrap:(YGWrap)flexWrap; -- (void)yg_setOverflow:(YGOverflow)overflow; - -- (void)yg_setFlexGrow:(CGFloat)flexGrow; -- (void)yg_setFlexShrink:(CGFloat)flexShrink; -- (void)yg_setFlexBasis:(CGFloat)flexBasis; - -- (void)yg_setPosition:(CGFloat)position forEdge:(YGEdge)edge; -- (void)yg_setMargin:(CGFloat)margin forEdge:(YGEdge)edge; -- (void)yg_setPadding:(CGFloat)padding forEdge:(YGEdge)edge; - -- (void)yg_setWidth:(CGFloat)width; -- (void)yg_setHeight:(CGFloat)height; -- (void)yg_setMinWidth:(CGFloat)minWidth; -- (void)yg_setMinHeight:(CGFloat)minHeight; -- (void)yg_setMaxWidth:(CGFloat)maxWidth; -- (void)yg_setMaxHeight:(CGFloat)maxHeight; - -// Yoga specific properties, not compatible with flexbox specification -- (void)yg_setAspectRatio:(CGFloat)aspectRatio; - -/** - Get the resolved direction of this node. This won't be YGDirectionInherit - */ -- (YGDirection)yg_resolvedDirection; - -/** - Perform a layout calculation and update the frames of the views in the hierarchy with the results - */ -- (void)yg_applyLayout; - -/** - Returns the size of the view if no constraints were given. This could equivalent to calling [self sizeThatFits:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)]; - */ -- (CGSize)yg_intrinsicSize; - -/** - Returns the number of children that are using Flexbox. - */ -- (NSUInteger)yg_numberOfChildren; - -/** - Return a BOOL indiciating whether or not we this node contains any subviews that are included in Yoga's layout. - */ -- (BOOL)yg_isLeaf; - -/** - Mark that a view's layout needs to be recalculated. Only works for leaf views. - */ -- (void)yg_markDirty; - -@end +@end \ No newline at end of file diff --git a/YogaKit/UIView+Yoga.m b/YogaKit/UIView+Yoga.m index cfe0afa0..175bc2f7 100644 --- a/YogaKit/UIView+Yoga.m +++ b/YogaKit/UIView+Yoga.m @@ -8,404 +8,23 @@ */ #import "UIView+Yoga.h" - +#import "YGLayout+Private.h" #import -static const void *kYGNodeBridgeAssociatedKey = &kYGNodeBridgeAssociatedKey; +static const void *kYGYogaAssociatedKey = &kYGYogaAssociatedKey; -@interface YGNodeBridge : NSObject -@property (nonatomic, assign, readonly) YGNodeRef cnode; -@end +@implementation UIView (YogaKit) -@implementation YGNodeBridge - -+ (void)initialize +- (YGLayout *)yoga { - YGSetExperimentalFeatureEnabled(YGExperimentalFeatureWebFlexBasis, true); -} - -- (instancetype)init -{ - if ([super init]) { - _cnode = YGNodeNew(); + YGLayout *yoga = objc_getAssociatedObject(self, kYGYogaAssociatedKey); + if (!yoga) { + yoga = [[YGLayout alloc] initWithView:self]; + objc_setAssociatedObject(self, kYGYogaAssociatedKey, yoga, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - - return self; + + return yoga; } -- (void)dealloc -{ - YGNodeFree(_cnode); -} -@end -@implementation UIView (Yoga) - -- (void)yg_markDirty -{ - YGNodeBridge *const bridge = objc_getAssociatedObject(self, kYGNodeBridgeAssociatedKey); - if (bridge != nil && [self yg_isLeaf]) { - YGNodeMarkDirty(bridge.cnode); - } -} - -- (BOOL)yg_usesYoga -{ - NSNumber *usesYoga = objc_getAssociatedObject(self, @selector(yg_usesYoga)); - return [usesYoga boolValue]; -} - -- (BOOL)yg_includeInLayout -{ - NSNumber *includeInLayout = objc_getAssociatedObject(self, @selector(yg_includeInLayout)); - return (includeInLayout != nil) ? [includeInLayout boolValue] : YES; -} - -- (NSUInteger)yg_numberOfChildren -{ - return YGNodeGetChildCount([self ygNode]); -} - -- (BOOL)yg_isLeaf -{ - NSAssert([NSThread isMainThread], @"This method must be called on the main thread."); - if ([self yg_usesYoga]) { - for (UIView *subview in self.subviews) { - if ([subview yg_usesYoga] && [subview yg_includeInLayout]) { - return NO; - } - } - } - - return YES; -} - -#pragma mark - Setters - -- (void)yg_setIncludeInLayout:(BOOL)includeInLayout -{ - objc_setAssociatedObject( - self, - @selector(yg_includeInLayout), - @(includeInLayout), - OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -- (void)yg_setUsesYoga:(BOOL)enabled -{ - objc_setAssociatedObject( - self, - @selector(yg_usesYoga), - @(enabled), - OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -- (void)yg_setDirection:(YGDirection)direction -{ - YGNodeStyleSetDirection([self ygNode], direction); -} - -- (void)yg_setFlexDirection:(YGFlexDirection)flexDirection -{ - YGNodeStyleSetFlexDirection([self ygNode], flexDirection); -} - -- (void)yg_setJustifyContent:(YGJustify)justifyContent -{ - YGNodeStyleSetJustifyContent([self ygNode], justifyContent); -} - -- (void)yg_setAlignContent:(YGAlign)alignContent -{ - YGNodeStyleSetAlignContent([self ygNode], alignContent); -} - -- (void)yg_setAlignItems:(YGAlign)alignItems -{ - YGNodeStyleSetAlignItems([self ygNode], alignItems); -} - -- (void)yg_setAlignSelf:(YGAlign)alignSelf -{ - YGNodeStyleSetAlignSelf([self ygNode], alignSelf); -} - -- (void)yg_setPositionType:(YGPositionType)positionType -{ - YGNodeStyleSetPositionType([self ygNode], positionType); -} - -- (void)yg_setFlexWrap:(YGWrap)flexWrap -{ - YGNodeStyleSetFlexWrap([self ygNode], flexWrap); -} - -- (void)yg_setOverflow:(YGOverflow)overflow -{ - YGNodeStyleSetOverflow([self ygNode], overflow); -} - -- (void)yg_setFlexGrow:(CGFloat)flexGrow -{ - YGNodeStyleSetFlexGrow([self ygNode], flexGrow); -} - -- (void)yg_setFlexShrink:(CGFloat)flexShrink -{ - YGNodeStyleSetFlexShrink([self ygNode], flexShrink); -} - -- (void)yg_setFlexBasis:(CGFloat)flexBasis -{ - YGNodeStyleSetFlexBasis([self ygNode], flexBasis); -} - -- (void)yg_setPosition:(CGFloat)position forEdge:(YGEdge)edge -{ - YGNodeStyleSetPosition([self ygNode], edge, position); -} - -- (void)yg_setMargin:(CGFloat)margin forEdge:(YGEdge)edge -{ - YGNodeStyleSetMargin([self ygNode], edge, margin); -} - -- (void)yg_setPadding:(CGFloat)padding forEdge:(YGEdge)edge -{ - YGNodeStyleSetPadding([self ygNode], edge, padding); -} - -- (void)yg_setWidth:(CGFloat)width -{ - YGNodeStyleSetWidth([self ygNode], width); -} - -- (void)yg_setHeight:(CGFloat)height -{ - YGNodeStyleSetHeight([self ygNode], height); -} - -- (void)yg_setMinWidth:(CGFloat)minWidth -{ - YGNodeStyleSetMinWidth([self ygNode], minWidth); -} - -- (void)yg_setMinHeight:(CGFloat)minHeight -{ - YGNodeStyleSetMinHeight([self ygNode], minHeight); -} - -- (void)yg_setMaxWidth:(CGFloat)maxWidth -{ - YGNodeStyleSetMaxWidth([self ygNode], maxWidth); -} - -- (void)yg_setMaxHeight:(CGFloat)maxHeight -{ - YGNodeStyleSetMaxHeight([self ygNode], maxHeight); -} - -- (void)yg_setAspectRatio:(CGFloat)aspectRatio -{ - YGNodeStyleSetAspectRatio([self ygNode], aspectRatio); -} - -#pragma mark - Layout and Sizing - -- (YGDirection)yg_resolvedDirection -{ - return YGNodeLayoutGetDirection([self ygNode]); -} - -- (void)yg_applyLayout -{ - [self calculateLayoutWithSize:self.bounds.size]; - YGApplyLayoutToViewHierarchy(self); -} - -- (CGSize)yg_intrinsicSize -{ - const CGSize constrainedSize = { - .width = YGUndefined, - .height = YGUndefined, - }; - return [self calculateLayoutWithSize:constrainedSize]; -} - -#pragma mark - Private - -- (YGNodeRef)ygNode -{ - YGNodeBridge *node = objc_getAssociatedObject(self, kYGNodeBridgeAssociatedKey); - if (!node) { - node = [YGNodeBridge new]; - YGNodeSetContext(node.cnode, (__bridge void *) self); - objc_setAssociatedObject(self, kYGNodeBridgeAssociatedKey, node, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - } - return node.cnode; -} - -- (CGSize)calculateLayoutWithSize:(CGSize)size -{ - NSAssert([NSThread isMainThread], @"YG Layout calculation must be done on main."); - NSAssert([self yg_usesYoga], @"YG Layout is not enabled for this view."); - - YGAttachNodesFromViewHierachy(self); - - const YGNodeRef node = [self ygNode]; - YGNodeCalculateLayout( - node, - size.width, - size.height, - YGNodeStyleGetDirection(node)); - - return (CGSize) { - .width = YGNodeLayoutGetWidth(node), - .height = YGNodeLayoutGetHeight(node), - }; -} - -static YGSize YGMeasureView( - YGNodeRef node, - float width, - YGMeasureMode widthMode, - float height, - YGMeasureMode heightMode) -{ - const CGFloat constrainedWidth = (widthMode == YGMeasureModeUndefined) ? CGFLOAT_MAX : width; - const CGFloat constrainedHeight = (heightMode == YGMeasureModeUndefined) ? CGFLOAT_MAX: height; - - UIView *view = (__bridge UIView*) YGNodeGetContext(node); - const CGSize sizeThatFits = [view sizeThatFits:(CGSize) { - .width = constrainedWidth, - .height = constrainedHeight, - }]; - - return (YGSize) { - .width = YGSanitizeMeasurement(constrainedWidth, sizeThatFits.width, widthMode), - .height = YGSanitizeMeasurement(constrainedHeight, sizeThatFits.height, heightMode), - }; -} - -static CGFloat YGSanitizeMeasurement( - CGFloat constrainedSize, - CGFloat measuredSize, - YGMeasureMode measureMode) -{ - CGFloat result; - if (measureMode == YGMeasureModeExactly) { - result = constrainedSize; - } else if (measureMode == YGMeasureModeAtMost) { - result = MIN(constrainedSize, measuredSize); - } else { - result = measuredSize; - } - - return result; -} - -static BOOL YGNodeHasExactSameChildren(const YGNodeRef node, NSArray *subviews) -{ - if (YGNodeGetChildCount(node) != subviews.count) { - return NO; - } - - for (int i=0; i *subviewsToInclude = [[NSMutableArray alloc] initWithCapacity:view.subviews.count]; - for (UIView *subview in view.subviews) { - if ([subview yg_includeInLayout]) { - [subviewsToInclude addObject:subview]; - } - } - - if (!YGNodeHasExactSameChildren(node, subviewsToInclude)) { - YGRemoveAllChildren(node); - for (int i=0; i 0) { - YGNodeRemoveChild(node, YGNodeGetChild(node, YGNodeGetChildCount(node) - 1)); - } -} - -static CGFloat YGRoundPixelValue(CGFloat value) -{ - static CGFloat scale; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^(){ - scale = [UIScreen mainScreen].scale; - }); - - return round(value * scale) / scale; -} - -static void YGApplyLayoutToViewHierarchy(UIView *view) -{ - NSCAssert([NSThread isMainThread], @"Framesetting should only be done on the main thread."); - if (![view yg_includeInLayout]) { - return; - } - - YGNodeRef node = [view ygNode]; - const CGPoint topLeft = { - YGNodeLayoutGetLeft(node), - YGNodeLayoutGetTop(node), - }; - - const CGPoint bottomRight = { - topLeft.x + YGNodeLayoutGetWidth(node), - topLeft.y + YGNodeLayoutGetHeight(node), - }; - - view.frame = (CGRect) { - .origin = { - .x = YGRoundPixelValue(topLeft.x), - .y = YGRoundPixelValue(topLeft.y), - }, - .size = { - .width = YGRoundPixelValue(bottomRight.x) - YGRoundPixelValue(topLeft.x), - .height = YGRoundPixelValue(bottomRight.y) - YGRoundPixelValue(topLeft.y), - }, - }; - - if (!view.yg_isLeaf) { - for (NSUInteger i=0; i +#import +#import + +@interface YGLayout : NSObject + +/** + The property that decides if we should include this view when calculating layout. Defaults to YES. + */ +@property (nonatomic, readwrite, assign, setter=setIncludedInLayout:) BOOL isIncludedInLayout; + +/** + The property that decides during layout/sizing whether or not styling properties should be applied. Defaults to NO. + */ +@property (nonatomic, readwrite, assign, setter=setEnabled:) BOOL isEnabled; + +@property (nonatomic, readwrite, assign) YGDirection direction; +@property (nonatomic, readwrite, assign) YGFlexDirection flexDirection; +@property (nonatomic, readwrite, assign) YGJustify justifyContent; +@property (nonatomic, readwrite, assign) YGAlign alignContent; +@property (nonatomic, readwrite, assign) YGAlign alignItems; +@property (nonatomic, readwrite, assign) YGAlign alignSelf; +@property (nonatomic, readwrite, assign) YGPositionType position; +@property (nonatomic, readwrite, assign) YGWrap flexWrap; +@property (nonatomic, readwrite, assign) YGOverflow overflow; + +@property (nonatomic, readwrite, assign) CGFloat flexGrow; +@property (nonatomic, readwrite, assign) CGFloat flexShrink; +@property (nonatomic, readwrite, assign) CGFloat flexBasis; + +@property (nonatomic, readwrite, assign) CGFloat left; +@property (nonatomic, readwrite, assign) CGFloat top; +@property (nonatomic, readwrite, assign) CGFloat right; +@property (nonatomic, readwrite, assign) CGFloat bottom; +@property (nonatomic, readwrite, assign) CGFloat start; +@property (nonatomic, readwrite, assign) CGFloat end; + +@property (nonatomic, readwrite, assign) CGFloat marginLeft; +@property (nonatomic, readwrite, assign) CGFloat marginTop; +@property (nonatomic, readwrite, assign) CGFloat marginRight; +@property (nonatomic, readwrite, assign) CGFloat marginBottom; +@property (nonatomic, readwrite, assign) CGFloat marginStart; +@property (nonatomic, readwrite, assign) CGFloat marginEnd; +@property (nonatomic, readwrite, assign) CGFloat marginHorizontal; +@property (nonatomic, readwrite, assign) CGFloat marginVertical; +@property (nonatomic, readwrite, assign) CGFloat margin; + +@property (nonatomic, readwrite, assign) CGFloat paddingLeft; +@property (nonatomic, readwrite, assign) CGFloat paddingTop; +@property (nonatomic, readwrite, assign) CGFloat paddingRight; +@property (nonatomic, readwrite, assign) CGFloat paddingBottom; +@property (nonatomic, readwrite, assign) CGFloat paddingStart; +@property (nonatomic, readwrite, assign) CGFloat paddingEnd; +@property (nonatomic, readwrite, assign) CGFloat paddingHorizontal; +@property (nonatomic, readwrite, assign) CGFloat paddingVertical; +@property (nonatomic, readwrite, assign) CGFloat padding; + +@property (nonatomic, readwrite, assign) CGFloat borderLeftWidth; +@property (nonatomic, readwrite, assign) CGFloat borderTopWidth; +@property (nonatomic, readwrite, assign) CGFloat borderRightWidth; +@property (nonatomic, readwrite, assign) CGFloat borderBottomWidth; +@property (nonatomic, readwrite, assign) CGFloat borderStartWidth; +@property (nonatomic, readwrite, assign) CGFloat borderEndWidth; +@property (nonatomic, readwrite, assign) CGFloat borderWidth; + +@property (nonatomic, readwrite, assign) CGFloat width; +@property (nonatomic, readwrite, assign) CGFloat height; +@property (nonatomic, readwrite, assign) CGFloat minWidth; +@property (nonatomic, readwrite, assign) CGFloat minHeight; +@property (nonatomic, readwrite, assign) CGFloat maxWidth; +@property (nonatomic, readwrite, assign) CGFloat maxHeight; + +// Yoga specific properties, not compatible with flexbox specification +@property (nonatomic, readwrite, assign) CGFloat aspectRatio; + +/** + Get the resolved direction of this node. This won't be YGDirectionInherit + */ + @property (nonatomic, readonly, assign) YGDirection resolvedDirection; + +/** + Perform a layout calculation and update the frames of the views in the hierarchy with the results + */ +- (void)applyLayout NS_SWIFT_NAME(applyLayout()); + +/** + Returns the size of the view if no constraints were given. This could equivalent to calling [self sizeThatFits:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)]; + */ + @property (nonatomic, readonly, assign) CGSize intrinsicSize; + +/** + Returns the number of children that are using Flexbox. + */ + @property (nonatomic, readonly, assign) NSUInteger numberOfChildren; + +/** + Return a BOOL indiciating whether or not we this node contains any subviews that are included in Yoga's layout. + */ + @property (nonatomic, readonly, assign) BOOL isLeaf; + +/** + Mark that a view's layout needs to be recalculated. Only works for leaf views. + */ +- (void)markDirty; + +@end diff --git a/YogaKit/YGLayout.m b/YogaKit/YGLayout.m new file mode 100644 index 00000000..dda188f5 --- /dev/null +++ b/YogaKit/YGLayout.m @@ -0,0 +1,385 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "YGLayout+Private.h" +#import "UIView+Yoga.h" +#import +#import + +extern YGValue YGValueMake(CGFloat value, YGUnit unit) { + return (YGValue) { .value = value, .unit = unit }; +} + +#define YG_STYLE_PROPERTY_IMPL(type, lowercased_name, capitalized_name) \ +- (type)lowercased_name \ +{ \ + return YGNodeStyleGet##capitalized_name(self.node); \ +} \ + \ +- (void)set##capitalized_name:(type)lowercased_name \ +{ \ + YGNodeStyleSet##capitalized_name(self.node, lowercased_name); \ +} + +#define YG_STYLE_EDGE_PROPERTY_IMPL(lowercased_name, capitalized_name, property, edge) \ +- (CGFloat)lowercased_name { \ + return YGNodeStyleGet##property(self.node, edge); \ +} \ + \ +- (void)set##capitalized_name:(CGFloat)lowercased_name { \ + YGNodeStyleSet##property(self.node, edge, lowercased_name); \ +} + +#define YG_STYLE_VALUE_PROPERTY_IMPL(lowercased_name, capitalized_name) \ +- (CGFloat)lowercased_name \ +{ \ + YGValue value = YGNodeStyleGet##capitalized_name(self.node); \ + if (value.unit == YGUnitPixel) { \ + return value.value; \ + } else { \ + return YGUndefined; \ + } \ +} \ + \ +- (void)set##capitalized_name:(CGFloat)lowercased_name \ +{ \ + YGNodeStyleSet##capitalized_name(self.node, lowercased_name); \ +} + +#define YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(lowercased_name, capitalized_name, edge, edge_suffix) \ +- (CGFloat)lowercased_name##edge_suffix \ +{ \ + YGValue value = YGNodeStyleGet##capitalized_name(self.node, edge); \ + if (value.unit == YGUnitPixel) { \ + return value.value; \ + } else { \ + return YGUndefined; \ + } \ +} \ + \ +- (void)set##capitalized_name##edge_suffix:(CGFloat)lowercased_name \ +{ \ + YGNodeStyleSet##capitalized_name(self.node, edge, lowercased_name); \ +} + +#define YG_STYLE_ALL_EDGE_PROPERTY_UNIT_IMPL(lowercased_name, capitalized_name) \ +YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(lowercased_name, capitalized_name, YGEdgeLeft, Left) \ +YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(lowercased_name, capitalized_name, YGEdgeTop, Top) \ +YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(lowercased_name, capitalized_name, YGEdgeRight, Right) \ +YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(lowercased_name, capitalized_name, YGEdgeBottom, Bottom) \ +YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(lowercased_name, capitalized_name, YGEdgeStart, Start) \ +YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(lowercased_name, capitalized_name, YGEdgeEnd, End) \ +YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(lowercased_name, capitalized_name, YGEdgeHorizontal, Horizontal) \ +YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(lowercased_name, capitalized_name, YGEdgeVertical, Vertical) \ +YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(lowercased_name, capitalized_name, YGEdgeAll, ) + +@interface YGLayout () + +@property (nonatomic, weak, readonly) UIView* view; +@property (nonatomic, assign, readonly) YGNodeRef node; + +@end + +@implementation YGLayout + +@synthesize isEnabled=_isEnabled; +@synthesize isIncludedInLayout=_isIncludedInLayout; + ++ (void)initialize +{ + YGSetExperimentalFeatureEnabled(YGExperimentalFeatureWebFlexBasis, true); +} + +- (instancetype)initWithView:(UIView*)view +{ + if ([super init]) { + _view = view; + _node = YGNodeNew(); + YGNodeSetContext(_node, (__bridge void *) view); + _isEnabled = NO; + _isIncludedInLayout = YES; + } + + return self; +} + +- (void)dealloc +{ + YGNodeFree(self.node); +} + +- (void)markDirty +{ + if (self.isLeaf) { + YGNodeMarkDirty(self.node); + } +} + +- (NSUInteger)numberOfChildren +{ + return YGNodeGetChildCount(self.node); +} + +- (BOOL)isLeaf +{ + NSAssert([NSThread isMainThread], @"This method must be called on the main thread."); + if (self.isEnabled) { + for (UIView *subview in self.view.subviews) { + YGLayout *yoga = subview.yoga; + if (yoga.isEnabled && yoga.isIncludedInLayout) { + return NO; + } + } + } + + return YES; +} + +#pragma mark - Style + +- (YGPositionType)position +{ + return YGNodeStyleGetPositionType(self.node); +} + +- (void)setPosition:(YGPositionType)position +{ + YGNodeStyleSetPositionType(self.node, position); +} + +YG_STYLE_PROPERTY_IMPL(YGDirection, direction, Direction) +YG_STYLE_PROPERTY_IMPL(YGFlexDirection, flexDirection, FlexDirection) +YG_STYLE_PROPERTY_IMPL(YGJustify, justifyContent, JustifyContent) +YG_STYLE_PROPERTY_IMPL(YGAlign, alignContent, AlignContent) +YG_STYLE_PROPERTY_IMPL(YGAlign, alignItems, AlignItems) +YG_STYLE_PROPERTY_IMPL(YGAlign, alignSelf, AlignSelf) +YG_STYLE_PROPERTY_IMPL(YGWrap, flexWrap, FlexWrap) +YG_STYLE_PROPERTY_IMPL(YGOverflow, overflow, Overflow) + +YG_STYLE_PROPERTY_IMPL(CGFloat, flexGrow, FlexGrow) +YG_STYLE_PROPERTY_IMPL(CGFloat, flexShrink, FlexShrink) +YG_STYLE_VALUE_PROPERTY_IMPL(flexBasis, FlexBasis) + +YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(position, Position, YGEdgeLeft, Left) +YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(position, Position, YGEdgeTop, Top) +YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(position, Position, YGEdgeRight, Right) +YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(position, Position, YGEdgeBottom, Bottom) +YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(position, Position, YGEdgeStart, Start) +YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(position, Position, YGEdgeEnd, End) +YG_STYLE_ALL_EDGE_PROPERTY_UNIT_IMPL(margin, Margin) +YG_STYLE_ALL_EDGE_PROPERTY_UNIT_IMPL(padding, Padding) + +YG_STYLE_EDGE_PROPERTY_IMPL(borderLeftWidth, BorderLeftWidth, Border, YGEdgeLeft) +YG_STYLE_EDGE_PROPERTY_IMPL(borderTopWidth, BorderTopWidth, Border, YGEdgeTop) +YG_STYLE_EDGE_PROPERTY_IMPL(borderRightWidth, BorderRightWidth, Border, YGEdgeRight) +YG_STYLE_EDGE_PROPERTY_IMPL(borderBottomWidth, BorderBottomWidth, Border, YGEdgeBottom) +YG_STYLE_EDGE_PROPERTY_IMPL(borderStartWidth, BorderStartWidth, Border, YGEdgeStart) +YG_STYLE_EDGE_PROPERTY_IMPL(borderEndWidth, BorderEndWidth, Border, YGEdgeEnd) +YG_STYLE_EDGE_PROPERTY_IMPL(borderWidth, BorderWidth, Border, YGEdgeAll) + +YG_STYLE_VALUE_PROPERTY_IMPL(width, Width) +YG_STYLE_VALUE_PROPERTY_IMPL(height, Height) +YG_STYLE_VALUE_PROPERTY_IMPL(minWidth, MinWidth) +YG_STYLE_VALUE_PROPERTY_IMPL(minHeight, MinHeight) +YG_STYLE_VALUE_PROPERTY_IMPL(maxWidth, MaxWidth) +YG_STYLE_VALUE_PROPERTY_IMPL(maxHeight, MaxHeight) +YG_STYLE_PROPERTY_IMPL(CGFloat, aspectRatio, AspectRatio) + +#pragma mark - Layout and Sizing + +- (YGDirection)resolvedDirection +{ + return YGNodeLayoutGetDirection(self.node); +} + +- (void)applyLayout +{ + [self calculateLayoutWithSize:self.view.bounds.size]; + YGApplyLayoutToViewHierarchy(self.view); +} + +- (CGSize)intrinsicSize +{ + const CGSize constrainedSize = { + .width = YGUndefined, + .height = YGUndefined, + }; + return [self calculateLayoutWithSize:constrainedSize]; +} + +#pragma mark - Private + +- (CGSize)calculateLayoutWithSize:(CGSize)size +{ + NSAssert([NSThread isMainThread], @"Yoga calculation must be done on main."); + NSAssert(self.isEnabled, @"Yoga is not enabled for this view."); + + YGAttachNodesFromViewHierachy(self.view); + + const YGNodeRef node = self.node; + YGNodeCalculateLayout( + node, + size.width, + size.height, + YGNodeStyleGetDirection(node)); + + return (CGSize) { + .width = YGNodeLayoutGetWidth(node), + .height = YGNodeLayoutGetHeight(node), + }; +} + +static YGSize YGMeasureView( + YGNodeRef node, + float width, + YGMeasureMode widthMode, + float height, + YGMeasureMode heightMode) +{ + const CGFloat constrainedWidth = (widthMode == YGMeasureModeUndefined) ? CGFLOAT_MAX : width; + const CGFloat constrainedHeight = (heightMode == YGMeasureModeUndefined) ? CGFLOAT_MAX: height; + + UIView *view = (__bridge UIView*) YGNodeGetContext(node); + const CGSize sizeThatFits = [view sizeThatFits:(CGSize) { + .width = constrainedWidth, + .height = constrainedHeight, + }]; + + return (YGSize) { + .width = YGSanitizeMeasurement(constrainedWidth, sizeThatFits.width, widthMode), + .height = YGSanitizeMeasurement(constrainedHeight, sizeThatFits.height, heightMode), + }; +} + +static CGFloat YGSanitizeMeasurement( + CGFloat constrainedSize, + CGFloat measuredSize, + YGMeasureMode measureMode) +{ + CGFloat result; + if (measureMode == YGMeasureModeExactly) { + result = constrainedSize; + } else if (measureMode == YGMeasureModeAtMost) { + result = MIN(constrainedSize, measuredSize); + } else { + result = measuredSize; + } + + return result; +} + +static BOOL YGNodeHasExactSameChildren(const YGNodeRef node, NSArray *subviews) +{ + if (YGNodeGetChildCount(node) != subviews.count) { + return NO; + } + + for (int i=0; i *subviewsToInclude = [[NSMutableArray alloc] initWithCapacity:view.subviews.count]; + for (UIView *subview in view.subviews) { + if (subview.yoga.isIncludedInLayout) { + [subviewsToInclude addObject:subview]; + } + } + + if (!YGNodeHasExactSameChildren(node, subviewsToInclude)) { + YGRemoveAllChildren(node); + for (int i=0; i 0) { + YGNodeRemoveChild(node, YGNodeGetChild(node, YGNodeGetChildCount(node) - 1)); + } +} + +static CGFloat YGRoundPixelValue(CGFloat value) +{ + static CGFloat scale; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^(){ + scale = [UIScreen mainScreen].scale; + }); + + return round(value * scale) / scale; +} + +static void YGApplyLayoutToViewHierarchy(UIView *view) +{ + NSCAssert([NSThread isMainThread], @"Framesetting should only be done on the main thread."); + + const YGLayout *yoga = view.yoga; + + if (!yoga.isIncludedInLayout) { + return; + } + + YGNodeRef node = yoga.node; + const CGPoint topLeft = { + YGNodeLayoutGetLeft(node), + YGNodeLayoutGetTop(node), + }; + + const CGPoint bottomRight = { + topLeft.x + YGNodeLayoutGetWidth(node), + topLeft.y + YGNodeLayoutGetHeight(node), + }; + + view.frame = (CGRect) { + .origin = { + .x = YGRoundPixelValue(topLeft.x), + .y = YGRoundPixelValue(topLeft.y), + }, + .size = { + .width = YGRoundPixelValue(bottomRight.x) - YGRoundPixelValue(topLeft.x), + .height = YGRoundPixelValue(bottomRight.y) - YGRoundPixelValue(topLeft.y), + }, + }; + + if (!yoga.isLeaf) { + for (NSUInteger i=0; i diff --git a/enums.py b/enums.py index 3b433a72..6945f566 100644 --- a/enums.py +++ b/enums.py @@ -118,7 +118,7 @@ def to_java_upper(symbol): root = os.path.dirname(os.path.abspath(__file__)) -# write out C headers +# write out C & Objective-C headers with open(root + '/yoga/YGEnums.h', 'w') as f: f.write(LICENSE) f.write('#pragma once\n\n') @@ -126,6 +126,7 @@ with open(root + '/yoga/YGEnums.h', 'w') as f: f.write('YG_EXTERN_C_BEGIN\n\n') for name, values in ENUMS.items(): f.write('#define YG%sCount %s\n' % (name, len(values))) + f.write('#ifndef NS_ENUM\n') f.write('typedef enum YG%s {\n' % name) for value in values: if isinstance(value, tuple): @@ -133,6 +134,15 @@ with open(root + '/yoga/YGEnums.h', 'w') as f: else: f.write(' YG%s%s,\n' % (name, value)) f.write('} YG%s;\n' % name) + f.write('#else\n') + f.write('typedef NS_ENUM(NSInteger, YG%s) {\n' % name) + for value in values: + if isinstance(value, tuple): + f.write(' YG%s%s = %d,\n' % (name, value[0], value[1])) + else: + f.write(' YG%s%s,\n' % (name, value)) + f.write('};\n') + f.write('#endif\n') f.write('\n') f.write('YG_EXTERN_C_END\n') diff --git a/yoga/YGEnums.h b/yoga/YGEnums.h index be60e02c..1b2b44ef 100644 --- a/yoga/YGEnums.h +++ b/yoga/YGEnums.h @@ -14,28 +14,54 @@ YG_EXTERN_C_BEGIN #define YGFlexDirectionCount 4 +#ifndef NS_ENUM typedef enum YGFlexDirection { YGFlexDirectionColumn, YGFlexDirectionColumnReverse, YGFlexDirectionRow, YGFlexDirectionRowReverse, } YGFlexDirection; +#else +typedef NS_ENUM(NSInteger, YGFlexDirection) { + YGFlexDirectionColumn, + YGFlexDirectionColumnReverse, + YGFlexDirectionRow, + YGFlexDirectionRowReverse, +}; +#endif #define YGMeasureModeCount 3 +#ifndef NS_ENUM typedef enum YGMeasureMode { YGMeasureModeUndefined, YGMeasureModeExactly, YGMeasureModeAtMost, } YGMeasureMode; +#else +typedef NS_ENUM(NSInteger, YGMeasureMode) { + YGMeasureModeUndefined, + YGMeasureModeExactly, + YGMeasureModeAtMost, +}; +#endif #define YGPrintOptionsCount 3 +#ifndef NS_ENUM typedef enum YGPrintOptions { YGPrintOptionsLayout = 1, YGPrintOptionsStyle = 2, YGPrintOptionsChildren = 4, } YGPrintOptions; +#else +typedef NS_ENUM(NSInteger, YGPrintOptions) { + YGPrintOptionsLayout = 1, + YGPrintOptionsStyle = 2, + YGPrintOptionsChildren = 4, +}; +#endif #define YGEdgeCount 9 +#ifndef NS_ENUM typedef enum YGEdge { YGEdgeLeft, YGEdgeTop, @@ -47,20 +73,48 @@ typedef enum YGEdge { YGEdgeVertical, YGEdgeAll, } YGEdge; +#else +typedef NS_ENUM(NSInteger, YGEdge) { + YGEdgeLeft, + YGEdgeTop, + YGEdgeRight, + YGEdgeBottom, + YGEdgeStart, + YGEdgeEnd, + YGEdgeHorizontal, + YGEdgeVertical, + YGEdgeAll, +}; +#endif #define YGPositionTypeCount 2 +#ifndef NS_ENUM typedef enum YGPositionType { YGPositionTypeRelative, YGPositionTypeAbsolute, } YGPositionType; +#else +typedef NS_ENUM(NSInteger, YGPositionType) { + YGPositionTypeRelative, + YGPositionTypeAbsolute, +}; +#endif #define YGDimensionCount 2 +#ifndef NS_ENUM typedef enum YGDimension { YGDimensionWidth, YGDimensionHeight, } YGDimension; +#else +typedef NS_ENUM(NSInteger, YGDimension) { + YGDimensionWidth, + YGDimensionHeight, +}; +#endif #define YGJustifyCount 5 +#ifndef NS_ENUM typedef enum YGJustify { YGJustifyFlexStart, YGJustifyCenter, @@ -68,15 +122,33 @@ typedef enum YGJustify { YGJustifySpaceBetween, YGJustifySpaceAround, } YGJustify; +#else +typedef NS_ENUM(NSInteger, YGJustify) { + YGJustifyFlexStart, + YGJustifyCenter, + YGJustifyFlexEnd, + YGJustifySpaceBetween, + YGJustifySpaceAround, +}; +#endif #define YGDirectionCount 3 +#ifndef NS_ENUM typedef enum YGDirection { YGDirectionInherit, YGDirectionLTR, YGDirectionRTL, } YGDirection; +#else +typedef NS_ENUM(NSInteger, YGDirection) { + YGDirectionInherit, + YGDirectionLTR, + YGDirectionRTL, +}; +#endif #define YGLogLevelCount 5 +#ifndef NS_ENUM typedef enum YGLogLevel { YGLogLevelError, YGLogLevelWarn, @@ -84,27 +156,59 @@ typedef enum YGLogLevel { YGLogLevelDebug, YGLogLevelVerbose, } YGLogLevel; +#else +typedef NS_ENUM(NSInteger, YGLogLevel) { + YGLogLevelError, + YGLogLevelWarn, + YGLogLevelInfo, + YGLogLevelDebug, + YGLogLevelVerbose, +}; +#endif #define YGWrapCount 2 +#ifndef NS_ENUM typedef enum YGWrap { YGWrapNoWrap, YGWrapWrap, } YGWrap; +#else +typedef NS_ENUM(NSInteger, YGWrap) { + YGWrapNoWrap, + YGWrapWrap, +}; +#endif #define YGOverflowCount 3 +#ifndef NS_ENUM typedef enum YGOverflow { YGOverflowVisible, YGOverflowHidden, YGOverflowScroll, } YGOverflow; +#else +typedef NS_ENUM(NSInteger, YGOverflow) { + YGOverflowVisible, + YGOverflowHidden, + YGOverflowScroll, +}; +#endif #define YGExperimentalFeatureCount 2 +#ifndef NS_ENUM typedef enum YGExperimentalFeature { YGExperimentalFeatureRounding, YGExperimentalFeatureWebFlexBasis, } YGExperimentalFeature; +#else +typedef NS_ENUM(NSInteger, YGExperimentalFeature) { + YGExperimentalFeatureRounding, + YGExperimentalFeatureWebFlexBasis, +}; +#endif #define YGAlignCount 5 +#ifndef NS_ENUM typedef enum YGAlign { YGAlignAuto, YGAlignFlexStart, @@ -112,12 +216,29 @@ typedef enum YGAlign { YGAlignFlexEnd, YGAlignStretch, } YGAlign; +#else +typedef NS_ENUM(NSInteger, YGAlign) { + YGAlignAuto, + YGAlignFlexStart, + YGAlignCenter, + YGAlignFlexEnd, + YGAlignStretch, +}; +#endif #define YGUnitCount 3 +#ifndef NS_ENUM typedef enum YGUnit { YGUnitUndefined, YGUnitPixel, YGUnitPercent, } YGUnit; +#else +typedef NS_ENUM(NSInteger, YGUnit) { + YGUnitUndefined, + YGUnitPixel, + YGUnitPercent, +}; +#endif YG_EXTERN_C_END diff --git a/yoga/YGValue.h b/yoga/YGValue.h new file mode 100644 index 00000000..de5aafbf --- /dev/null +++ b/yoga/YGValue.h @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#pragma once + +YG_EXTERN_C_BEGIN + +typedef struct YGValue { + float value; + YGUnit unit; +} YGValue; + +YG_EXTERN_C_END \ No newline at end of file diff --git a/yoga/Yoga.h b/yoga/Yoga.h index 083d3073..05d8b883 100644 --- a/yoga/Yoga.h +++ b/yoga/Yoga.h @@ -30,6 +30,7 @@ static const unsigned long __nan[2] = {0xffffffff, 0x7fffffff}; #include "YGEnums.h" #include "YGMacros.h" +#include "YGValue.h" YG_EXTERN_C_BEGIN @@ -38,11 +39,6 @@ typedef struct YGSize { float height; } YGSize; -typedef struct YGValue { - float value; - YGUnit unit; -} YGValue; - typedef struct YGNode *YGNodeRef; typedef YGSize (*YGMeasureFunc)(YGNodeRef node, float width, -- 2.50.1.windows.1 From ec28cacb9fc8559063737372c3accf50444839e1 Mon Sep 17 00:00:00 2001 From: David Hart Date: Fri, 6 Jan 2017 15:07:52 +0100 Subject: [PATCH 5/5] review improvements --- YogaKit/YGLayout.h | 1 - YogaKit/YGLayout.m | 7 +- enums.py | 16 +--- yoga/YGEnums.h | 177 +++++++-------------------------------------- yoga/YGMacros.h | 16 ++++ yoga/YGValue.h | 19 ----- yoga/Yoga.h | 6 +- 7 files changed, 53 insertions(+), 189 deletions(-) delete mode 100644 yoga/YGValue.h diff --git a/YogaKit/YGLayout.h b/YogaKit/YGLayout.h index 70363507..6b8ded62 100644 --- a/YogaKit/YGLayout.h +++ b/YogaKit/YGLayout.h @@ -9,7 +9,6 @@ #import #import -#import @interface YGLayout : NSObject diff --git a/YogaKit/YGLayout.m b/YogaKit/YGLayout.m index dda188f5..1817eb38 100644 --- a/YogaKit/YGLayout.m +++ b/YogaKit/YGLayout.m @@ -10,11 +10,6 @@ #import "YGLayout+Private.h" #import "UIView+Yoga.h" #import -#import - -extern YGValue YGValueMake(CGFloat value, YGUnit unit) { - return (YGValue) { .value = value, .unit = unit }; -} #define YG_STYLE_PROPERTY_IMPL(type, lowercased_name, capitalized_name) \ - (type)lowercased_name \ @@ -81,7 +76,7 @@ YG_STYLE_EDGE_PROPERTY_UNIT_IMPL(lowercased_name, capitalized_name, YGEdgeAll, ) @interface YGLayout () -@property (nonatomic, weak, readonly) UIView* view; +@property (nonatomic, weak, readonly) UIView *view; @property (nonatomic, assign, readonly) YGNodeRef node; @end diff --git a/enums.py b/enums.py index 6945f566..860d886e 100644 --- a/enums.py +++ b/enums.py @@ -125,24 +125,14 @@ with open(root + '/yoga/YGEnums.h', 'w') as f: f.write('#include "YGMacros.h"\n\n') f.write('YG_EXTERN_C_BEGIN\n\n') for name, values in ENUMS.items(): - f.write('#define YG%sCount %s\n' % (name, len(values))) - f.write('#ifndef NS_ENUM\n') - f.write('typedef enum YG%s {\n' % name) + f.write('#define YG%sCount %s\n' % (name, len(values))) + f.write('typedef YG_ENUM_BEGIN(YG%s) {\n' % name) for value in values: if isinstance(value, tuple): f.write(' YG%s%s = %d,\n' % (name, value[0], value[1])) else: f.write(' YG%s%s,\n' % (name, value)) - f.write('} YG%s;\n' % name) - f.write('#else\n') - f.write('typedef NS_ENUM(NSInteger, YG%s) {\n' % name) - for value in values: - if isinstance(value, tuple): - f.write(' YG%s%s = %d,\n' % (name, value[0], value[1])) - else: - f.write(' YG%s%s,\n' % (name, value)) - f.write('};\n') - f.write('#endif\n') + f.write('} YG_ENUM_END(YG%s);\n' % name) f.write('\n') f.write('YG_EXTERN_C_END\n') diff --git a/yoga/YGEnums.h b/yoga/YGEnums.h index 1b2b44ef..099f6d30 100644 --- a/yoga/YGEnums.h +++ b/yoga/YGEnums.h @@ -14,55 +14,29 @@ YG_EXTERN_C_BEGIN #define YGFlexDirectionCount 4 -#ifndef NS_ENUM -typedef enum YGFlexDirection { +typedef YG_ENUM_BEGIN(YGFlexDirection) { YGFlexDirectionColumn, YGFlexDirectionColumnReverse, YGFlexDirectionRow, YGFlexDirectionRowReverse, -} YGFlexDirection; -#else -typedef NS_ENUM(NSInteger, YGFlexDirection) { - YGFlexDirectionColumn, - YGFlexDirectionColumnReverse, - YGFlexDirectionRow, - YGFlexDirectionRowReverse, -}; -#endif +} YG_ENUM_END(YGFlexDirection); #define YGMeasureModeCount 3 -#ifndef NS_ENUM -typedef enum YGMeasureMode { +typedef YG_ENUM_BEGIN(YGMeasureMode) { YGMeasureModeUndefined, YGMeasureModeExactly, YGMeasureModeAtMost, -} YGMeasureMode; -#else -typedef NS_ENUM(NSInteger, YGMeasureMode) { - YGMeasureModeUndefined, - YGMeasureModeExactly, - YGMeasureModeAtMost, -}; -#endif +} YG_ENUM_END(YGMeasureMode); #define YGPrintOptionsCount 3 -#ifndef NS_ENUM -typedef enum YGPrintOptions { +typedef YG_ENUM_BEGIN(YGPrintOptions) { YGPrintOptionsLayout = 1, YGPrintOptionsStyle = 2, YGPrintOptionsChildren = 4, -} YGPrintOptions; -#else -typedef NS_ENUM(NSInteger, YGPrintOptions) { - YGPrintOptionsLayout = 1, - YGPrintOptionsStyle = 2, - YGPrintOptionsChildren = 4, -}; -#endif +} YG_ENUM_END(YGPrintOptions); #define YGEdgeCount 9 -#ifndef NS_ENUM -typedef enum YGEdge { +typedef YG_ENUM_BEGIN(YGEdge) { YGEdgeLeft, YGEdgeTop, YGEdgeRight, @@ -72,173 +46,78 @@ typedef enum YGEdge { YGEdgeHorizontal, YGEdgeVertical, YGEdgeAll, -} YGEdge; -#else -typedef NS_ENUM(NSInteger, YGEdge) { - YGEdgeLeft, - YGEdgeTop, - YGEdgeRight, - YGEdgeBottom, - YGEdgeStart, - YGEdgeEnd, - YGEdgeHorizontal, - YGEdgeVertical, - YGEdgeAll, -}; -#endif +} YG_ENUM_END(YGEdge); #define YGPositionTypeCount 2 -#ifndef NS_ENUM -typedef enum YGPositionType { +typedef YG_ENUM_BEGIN(YGPositionType) { YGPositionTypeRelative, YGPositionTypeAbsolute, -} YGPositionType; -#else -typedef NS_ENUM(NSInteger, YGPositionType) { - YGPositionTypeRelative, - YGPositionTypeAbsolute, -}; -#endif +} YG_ENUM_END(YGPositionType); #define YGDimensionCount 2 -#ifndef NS_ENUM -typedef enum YGDimension { +typedef YG_ENUM_BEGIN(YGDimension) { YGDimensionWidth, YGDimensionHeight, -} YGDimension; -#else -typedef NS_ENUM(NSInteger, YGDimension) { - YGDimensionWidth, - YGDimensionHeight, -}; -#endif +} YG_ENUM_END(YGDimension); #define YGJustifyCount 5 -#ifndef NS_ENUM -typedef enum YGJustify { +typedef YG_ENUM_BEGIN(YGJustify) { YGJustifyFlexStart, YGJustifyCenter, YGJustifyFlexEnd, YGJustifySpaceBetween, YGJustifySpaceAround, -} YGJustify; -#else -typedef NS_ENUM(NSInteger, YGJustify) { - YGJustifyFlexStart, - YGJustifyCenter, - YGJustifyFlexEnd, - YGJustifySpaceBetween, - YGJustifySpaceAround, -}; -#endif +} YG_ENUM_END(YGJustify); #define YGDirectionCount 3 -#ifndef NS_ENUM -typedef enum YGDirection { +typedef YG_ENUM_BEGIN(YGDirection) { YGDirectionInherit, YGDirectionLTR, YGDirectionRTL, -} YGDirection; -#else -typedef NS_ENUM(NSInteger, YGDirection) { - YGDirectionInherit, - YGDirectionLTR, - YGDirectionRTL, -}; -#endif +} YG_ENUM_END(YGDirection); #define YGLogLevelCount 5 -#ifndef NS_ENUM -typedef enum YGLogLevel { +typedef YG_ENUM_BEGIN(YGLogLevel) { YGLogLevelError, YGLogLevelWarn, YGLogLevelInfo, YGLogLevelDebug, YGLogLevelVerbose, -} YGLogLevel; -#else -typedef NS_ENUM(NSInteger, YGLogLevel) { - YGLogLevelError, - YGLogLevelWarn, - YGLogLevelInfo, - YGLogLevelDebug, - YGLogLevelVerbose, -}; -#endif +} YG_ENUM_END(YGLogLevel); #define YGWrapCount 2 -#ifndef NS_ENUM -typedef enum YGWrap { +typedef YG_ENUM_BEGIN(YGWrap) { YGWrapNoWrap, YGWrapWrap, -} YGWrap; -#else -typedef NS_ENUM(NSInteger, YGWrap) { - YGWrapNoWrap, - YGWrapWrap, -}; -#endif +} YG_ENUM_END(YGWrap); #define YGOverflowCount 3 -#ifndef NS_ENUM -typedef enum YGOverflow { +typedef YG_ENUM_BEGIN(YGOverflow) { YGOverflowVisible, YGOverflowHidden, YGOverflowScroll, -} YGOverflow; -#else -typedef NS_ENUM(NSInteger, YGOverflow) { - YGOverflowVisible, - YGOverflowHidden, - YGOverflowScroll, -}; -#endif +} YG_ENUM_END(YGOverflow); #define YGExperimentalFeatureCount 2 -#ifndef NS_ENUM -typedef enum YGExperimentalFeature { +typedef YG_ENUM_BEGIN(YGExperimentalFeature) { YGExperimentalFeatureRounding, YGExperimentalFeatureWebFlexBasis, -} YGExperimentalFeature; -#else -typedef NS_ENUM(NSInteger, YGExperimentalFeature) { - YGExperimentalFeatureRounding, - YGExperimentalFeatureWebFlexBasis, -}; -#endif +} YG_ENUM_END(YGExperimentalFeature); #define YGAlignCount 5 -#ifndef NS_ENUM -typedef enum YGAlign { +typedef YG_ENUM_BEGIN(YGAlign) { YGAlignAuto, YGAlignFlexStart, YGAlignCenter, YGAlignFlexEnd, YGAlignStretch, -} YGAlign; -#else -typedef NS_ENUM(NSInteger, YGAlign) { - YGAlignAuto, - YGAlignFlexStart, - YGAlignCenter, - YGAlignFlexEnd, - YGAlignStretch, -}; -#endif +} YG_ENUM_END(YGAlign); #define YGUnitCount 3 -#ifndef NS_ENUM -typedef enum YGUnit { +typedef YG_ENUM_BEGIN(YGUnit) { YGUnitUndefined, YGUnitPixel, YGUnitPercent, -} YGUnit; -#else -typedef NS_ENUM(NSInteger, YGUnit) { - YGUnitUndefined, - YGUnitPixel, - YGUnitPercent, -}; -#endif +} YG_ENUM_END(YGUnit); YG_EXTERN_C_END diff --git a/yoga/YGMacros.h b/yoga/YGMacros.h index def8371a..0d09a693 100644 --- a/yoga/YGMacros.h +++ b/yoga/YGMacros.h @@ -40,3 +40,19 @@ YG_ABORT(); \ } #endif + +#ifndef YG_ENUM_BEGIN +#ifndef NS_ENUM +#define YG_ENUM_BEGIN(name) enum name +#else +#define YG_ENUM_BEGIN(name) NS_ENUM(NSInteger, name) +#endif +#endif + +#ifndef YG_ENUM_END +#ifndef NS_ENUM +#define YG_ENUM_END(name) name +#else +#define YG_ENUM_END(name) +#endif +#endif \ No newline at end of file diff --git a/yoga/YGValue.h b/yoga/YGValue.h deleted file mode 100644 index de5aafbf..00000000 --- a/yoga/YGValue.h +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -#pragma once - -YG_EXTERN_C_BEGIN - -typedef struct YGValue { - float value; - YGUnit unit; -} YGValue; - -YG_EXTERN_C_END \ No newline at end of file diff --git a/yoga/Yoga.h b/yoga/Yoga.h index 05d8b883..083d3073 100644 --- a/yoga/Yoga.h +++ b/yoga/Yoga.h @@ -30,7 +30,6 @@ static const unsigned long __nan[2] = {0xffffffff, 0x7fffffff}; #include "YGEnums.h" #include "YGMacros.h" -#include "YGValue.h" YG_EXTERN_C_BEGIN @@ -39,6 +38,11 @@ typedef struct YGSize { float height; } YGSize; +typedef struct YGValue { + float value; + YGUnit unit; +} YGValue; + typedef struct YGNode *YGNodeRef; typedef YGSize (*YGMeasureFunc)(YGNodeRef node, float width, -- 2.50.1.windows.1