From 70a4221b9e360b32268ab31749171f42a8249df6 Mon Sep 17 00:00:00 2001 From: Kazuki Sakamoto Date: Sat, 7 Jan 2017 09:06:06 -0800 Subject: [PATCH 1/7] C# Baseline function Summary: same as Measure function, based on #317 Closes https://github.com/facebook/yoga/pull/321 Reviewed By: emilsjolander Differential Revision: D4385336 Pulled By: splhack fbshipit-source-id: b583ec79861d2e9abba31a72503e2f706bfda8e8 --- csharp/Facebook.Yoga/BaselineFunction.cs | 13 +++++ .../Facebook.Yoga.Shared.projitems | 2 + csharp/Facebook.Yoga/Native.cs | 5 +- csharp/Facebook.Yoga/YogaBaselineFunc.cs | 17 +++++++ csharp/Facebook.Yoga/YogaNode.cs | 28 +++++++++++ csharp/tests/Facebook.Yoga/YogaNodeTest.cs | 48 +++++++++++++++++++ 6 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 csharp/Facebook.Yoga/BaselineFunction.cs create mode 100644 csharp/Facebook.Yoga/YogaBaselineFunc.cs diff --git a/csharp/Facebook.Yoga/BaselineFunction.cs b/csharp/Facebook.Yoga/BaselineFunction.cs new file mode 100644 index 00000000..cf055b19 --- /dev/null +++ b/csharp/Facebook.Yoga/BaselineFunction.cs @@ -0,0 +1,13 @@ +/** + * 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. + */ + +namespace Facebook.Yoga +{ + public delegate float BaselineFunction(YogaNode node, float width, float height); +} diff --git a/csharp/Facebook.Yoga/Facebook.Yoga.Shared.projitems b/csharp/Facebook.Yoga/Facebook.Yoga.Shared.projitems index 5889d249..b48fb339 100644 --- a/csharp/Facebook.Yoga/Facebook.Yoga.Shared.projitems +++ b/csharp/Facebook.Yoga/Facebook.Yoga.Shared.projitems @@ -9,12 +9,14 @@ Facebook.Yoga.Shared + + diff --git a/csharp/Facebook.Yoga/Native.cs b/csharp/Facebook.Yoga/Native.cs index b66cf8a6..248fc17f 100644 --- a/csharp/Facebook.Yoga/Native.cs +++ b/csharp/Facebook.Yoga/Native.cs @@ -104,8 +104,9 @@ namespace Facebook.Yoga [MarshalAs(UnmanagedType.FunctionPtr)] YogaMeasureFunc measureFunc); [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.FunctionPtr)] - public static extern YogaMeasureFunc YGNodeGetMeasureFunc(YGNodeHandle node); + public static extern void YGNodeSetBaselineFunc( + YGNodeHandle node, + [MarshalAs(UnmanagedType.FunctionPtr)] YogaBaselineFunc baselineFunc); [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] public static extern void YGNodeSetHasNewLayout(YGNodeHandle node, [MarshalAs(UnmanagedType.I1)] bool hasNewLayout); diff --git a/csharp/Facebook.Yoga/YogaBaselineFunc.cs b/csharp/Facebook.Yoga/YogaBaselineFunc.cs new file mode 100644 index 00000000..78575434 --- /dev/null +++ b/csharp/Facebook.Yoga/YogaBaselineFunc.cs @@ -0,0 +1,17 @@ +/** + * 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. + */ + +using System; +using System.Runtime.InteropServices; + +namespace Facebook.Yoga +{ + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate float YogaBaselineFunc(IntPtr node, float width, float height); +} diff --git a/csharp/Facebook.Yoga/YogaNode.cs b/csharp/Facebook.Yoga/YogaNode.cs index 5732748b..ffc2c6f6 100644 --- a/csharp/Facebook.Yoga/YogaNode.cs +++ b/csharp/Facebook.Yoga/YogaNode.cs @@ -21,6 +21,8 @@ namespace Facebook.Yoga private List _children; private MeasureFunction _measureFunction; private YogaMeasureFunc _ygMeasureFunc; + private BaselineFunction _baselineFunction; + private YogaBaselineFunc _ygBaselineFunc; private object _data; public YogaNode() @@ -37,6 +39,7 @@ namespace Facebook.Yoga public void Reset() { _measureFunction = null; + _baselineFunction = null; _data = null; Native.YGNodeReset(_ygNode); @@ -84,6 +87,14 @@ namespace Facebook.Yoga } } + public bool IsBaselineDefined + { + get + { + return _baselineFunction != null; + } + } + public void CopyStyle(YogaNode srcNode) { Native.YGNodeCopyStyle(_ygNode, srcNode._ygNode); @@ -585,6 +596,13 @@ namespace Facebook.Yoga Native.YGNodeSetMeasureFunc(_ygNode, _ygMeasureFunc); } + public void SetBaselineFunction(BaselineFunction baselineFunction) + { + _baselineFunction = baselineFunction; + _ygBaselineFunc = baselineFunction != null ? BaselineInternal : (YogaBaselineFunc)null; + Native.YGNodeSetBaselineFunc(_ygNode, _ygBaselineFunc); + } + public void CalculateLayout() { Native.YGNodeCalculateLayout( @@ -609,6 +627,16 @@ namespace Facebook.Yoga return _measureFunction(this, width, widthMode, height, heightMode); } + private float BaselineInternal(IntPtr node, float width, float height) + { + if (_baselineFunction == null) + { + throw new InvalidOperationException("Baseline function is not defined."); + } + + return _baselineFunction(this, width, height); + } + public string Print(YogaPrintOptions options = YogaPrintOptions.Layout|YogaPrintOptions.Style|YogaPrintOptions.Children) { diff --git a/csharp/tests/Facebook.Yoga/YogaNodeTest.cs b/csharp/tests/Facebook.Yoga/YogaNodeTest.cs index 9979f091..a0fc5240 100644 --- a/csharp/tests/Facebook.Yoga/YogaNodeTest.cs +++ b/csharp/tests/Facebook.Yoga/YogaNodeTest.cs @@ -197,6 +197,54 @@ namespace Facebook.Yoga }); } + [Test] + public void TestBaselineFunc() + { + YogaNode node = new YogaNode(); + node.Height = 200; + node.FlexDirection = YogaFlexDirection.Row; + node.AlignItems = YogaAlign.Baseline; + + YogaNode child0 = new YogaNode(); + child0.Width = 100; + child0.Height = 110; + child0.SetBaselineFunction((_, width, height) => { + Assert.AreEqual(100, width); + Assert.AreEqual(110, height); + return 65; + }); + node.Insert(0, child0); + + YogaNode child1 = new YogaNode(); + child1.Width = 100; + child1.Height = 110; + child1.SetBaselineFunction((_, width, height) => { + Assert.AreEqual(100, width); + Assert.AreEqual(110, height); + return 80; + }); + node.Insert(1, child1); + + YogaNode child2 = new YogaNode(); + child2.Width = 100; + child2.Height = 110; + child2.SetBaselineFunction((_, width, height) => { + Assert.AreEqual(100, width); + Assert.AreEqual(110, height); + return 88; + }); + node.Insert(2, child2); + + node.CalculateLayout(); + + Assert.AreEqual(0, child0.LayoutX); + Assert.AreEqual(23, child0.LayoutY); + Assert.AreEqual(100, child1.LayoutX); + Assert.AreEqual(8, child1.LayoutY); + Assert.AreEqual(200, child2.LayoutX); + Assert.AreEqual(0, child2.LayoutY); + } + [Test] public void TestPrint() { From e2a7938b267cd0f93b0d3cc18bc017b793f76af0 Mon Sep 17 00:00:00 2001 From: Ben Holcomb Date: Sat, 7 Jan 2017 10:23:02 -0800 Subject: [PATCH 2/7] Revert D4386906: [YogaKit] Improved the objective-c and swift api Summary: This reverts commit 05ac0e571ef3a8ff0be31469e449a7b23f102218 Differential Revision: D4386906 fbshipit-source-id: adac0d8e71ce9f3442f3bfd14b5157f88367c998 --- YogaKit/Tests/YogaKitTests.m | 214 +++++----- YogaKit/UIView+Yoga.h | 70 ++- YogaKit/UIView+Yoga.m | 403 +++++++++++++++++- YogaKit/YGLayout+Private.h | 16 - YogaKit/YGLayout.h | 114 ----- YogaKit/YGLayout.m | 380 ----------------- .../YogaKitSample.xcodeproj/project.pbxproj | 31 +- .../YogaKitSample/SwiftViewController.swift | 40 -- .../YogaKitSample/ViewController.m | 18 +- .../YogaKitSample-Bridging-Header.h | 10 - enums.py | 8 +- yoga/YGEnums.h | 56 +-- yoga/YGMacros.h | 16 - 13 files changed, 609 insertions(+), 767 deletions(-) delete mode 100644 YogaKit/YGLayout+Private.h delete mode 100644 YogaKit/YGLayout.h delete mode 100644 YogaKit/YGLayout.m delete mode 100644 YogaKit/YogaKitSample/YogaKitSample/SwiftViewController.swift delete mode 100644 YogaKit/YogaKitSample/YogaKitSample/YogaKitSample-Bridging-Header.h diff --git a/YogaKit/Tests/YogaKitTests.m b/YogaKit/Tests/YogaKitTests.m index 79f7043c..bdade66e 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.yoga.flexBasis = 1; + [view yg_setFlexBasis:1]; XCTAssertEqual(1, YGNodeGetInstanceCount()); view = nil; @@ -35,11 +35,11 @@ XCTAssertEqual(0, YGNodeGetInstanceCount()); UIView *view = [[UIView alloc] initWithFrame:CGRectZero]; - view.yoga.flexBasis = 1; + [view yg_setFlexBasis:1]; for (int i=0; i<10; i++) { UIView *subview = [[UIView alloc] initWithFrame:CGRectZero]; - subview.yoga.flexBasis = 1; + [subview yg_setFlexBasis:1]; [view addSubview:subview]; } XCTAssertEqual(11, YGNodeGetInstanceCount()); @@ -53,69 +53,69 @@ - (void)testUsesYoga { UIView *view = [[UIView alloc] initWithFrame:CGRectZero]; - XCTAssertFalse(view.yoga.isEnabled); + XCTAssertFalse([view yg_usesYoga]); - view.yoga.isEnabled = YES; - XCTAssertTrue(view.yoga.isEnabled); + [view yg_setUsesYoga:YES]; + XCTAssertTrue([view yg_usesYoga]); - view.yoga.isEnabled = NO; - XCTAssertFalse(view.yoga.isEnabled); + [view yg_setUsesYoga:NO]; + XCTAssertFalse([view yg_usesYoga]); } - (void)testSizeThatFitsAsserts { UIView *view = [[UIView alloc] initWithFrame:CGRectZero]; dispatch_sync(dispatch_queue_create("com.facebook.Yoga.testing", DISPATCH_QUEUE_SERIAL), ^(void){ - XCTAssertThrows(view.yoga.intrinsicSize); + XCTAssertThrows([view yg_intrinsicSize]); }); } - (void)testSizeThatFitsSmoke { UIView *container = [[UIView alloc] initWithFrame:CGRectZero]; - container.yoga.isEnabled = YES; - container.yoga.flexDirection = YGFlexDirectionRow; - container.yoga.alignItems = YGAlignFlexStart; + [container yg_setUsesYoga:YES]; + [container yg_setFlexDirection:YGFlexDirectionRow]; + [container yg_setAlignItems: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.yoga.isEnabled = YES; - longTextLabel.yoga.flexShrink = 1; + [longTextLabel yg_setUsesYoga:YES]; + [longTextLabel yg_setFlexShrink:1]; [container addSubview:longTextLabel]; UIView *textBadgeView = [[UIView alloc] initWithFrame:CGRectZero]; - textBadgeView.yoga.isEnabled = YES; - textBadgeView.yoga.marginLeft = 3.0; - textBadgeView.yoga.width = 10; - textBadgeView.yoga.height = 10; + [textBadgeView yg_setUsesYoga:YES]; + [textBadgeView yg_setMargin:3.0 forEdge:YGEdgeLeft]; + [textBadgeView yg_setWidth:10]; + [textBadgeView yg_setHeight:10]; [container addSubview:textBadgeView]; - const CGSize containerSize = container.yoga.intrinsicSize; + const CGSize containerSize = [container yg_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.yoga.isEnabled = YES; - container.yoga.flexDirection = YGFlexDirectionRow; - container.yoga.alignItems = YGAlignFlexStart; + [container yg_setUsesYoga:YES]; + [container yg_setFlexDirection:YGFlexDirectionRow]; + [container yg_setAlignItems:YGAlignFlexStart]; UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero]; label.text = @"This is a short text."; label.numberOfLines = 1; - label.yoga.isEnabled = YES; + [label yg_setUsesYoga:YES]; [container addSubview:label]; - [container.yoga applyLayout]; + [container yg_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.yoga markDirty]; + [label yg_markDirty]; - [container.yoga applyLayout]; + [container yg_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.yoga.isEnabled = YES; - container.yoga.flexDirection = YGFlexDirectionRow; + [container yg_setUsesYoga:YES]; + [container yg_setFlexDirection:YGFlexDirectionRow]; for (int i = 0; i < 3; i++) { UIView *subview = [[UIView alloc] initWithFrame:CGRectZero]; - subview.yoga.isEnabled = YES; - subview.yoga.flexGrow = 1; + [subview yg_setUsesYoga:YES]; + [subview yg_setFlexGrow:1]; [container addSubview:subview]; } - [container.yoga applyLayout]; + [container yg_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.yoga.isEnabled = YES; - container.yoga.flexDirection = YGFlexDirectionRow; + [container yg_setUsesYoga:YES]; + [container yg_setFlexDirection:YGFlexDirectionRow]; UIView *subview1 = [[UIView alloc] initWithFrame:CGRectZero]; - subview1.yoga.isEnabled = YES; - subview1.yoga.flexGrow = 1; + [subview1 yg_setUsesYoga:YES]; + [subview1 yg_setFlexGrow:1]; [container addSubview:subview1]; UIView *subview2 = [[UIView alloc] initWithFrame:CGRectZero]; - subview2.yoga.isEnabled = YES; - subview2.yoga.flexGrow = 1; + [subview2 yg_setUsesYoga:YES]; + [subview2 yg_setFlexGrow:1]; [container addSubview:subview2]; UIView *subview3 = [[UIView alloc] initWithFrame:CGRectZero]; - subview3.yoga.isEnabled = YES; - subview3.yoga.flexGrow = 1; + [subview3 yg_setUsesYoga:YES]; + [subview3 yg_setFlexGrow:1]; [container addSubview:subview3]; - [container.yoga applyLayout]; + [container yg_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.yoga.isIncludedInLayout = NO; - [container.yoga applyLayout]; + [subview2 yg_setIncludeInLayout:NO]; + [container yg_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.yoga.isEnabled = YES; - container.yoga.flexDirection = YGFlexDirectionRow; + [container yg_setUsesYoga:YES]; + [container yg_setFlexDirection:YGFlexDirectionRow]; UIView *subview1 = [[UIView alloc] initWithFrame:CGRectZero]; - subview1.yoga.isEnabled = YES; - subview1.yoga.flexGrow = 1; + [subview1 yg_setUsesYoga:YES]; + [subview1 yg_setFlexGrow:1]; [container addSubview:subview1]; UIView *subview2 = [[UIView alloc] initWithFrame:CGRectZero]; - subview2.yoga.isEnabled = YES; - subview2.yoga.flexGrow = 1; + [subview2 yg_setUsesYoga:YES]; + [subview2 yg_setFlexGrow:1]; [container addSubview:subview2]; UIView *subview3 = [[UIView alloc] initWithFrame:CGRectZero]; - subview3.yoga.isEnabled = YES; - subview3.yoga.flexGrow = 1; + [subview3 yg_setUsesYoga:YES]; + [subview3 yg_setFlexGrow:1]; [container addSubview:subview3]; - [container.yoga applyLayout]; + [container yg_applyLayout]; for (UIView *view in container.subviews) { XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(100, 50), subview1.bounds.size), @"Actual size is %@", NSStringFromCGSize(view.bounds.size)); } - subview3.yoga.isIncludedInLayout = NO; - [container.yoga applyLayout]; + [subview3 yg_setIncludeInLayout:NO]; + [container yg_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.yoga.isEnabled = YES; - container.yoga.flexDirection = YGFlexDirectionRow; + [container yg_setUsesYoga:YES]; + [container yg_setFlexDirection:YGFlexDirectionRow]; UIView *subview1 = [[UIView alloc] initWithFrame:CGRectZero]; - subview1.yoga.isEnabled = YES; - subview1.yoga.isIncludedInLayout = NO; + [subview1 yg_setUsesYoga:YES]; + [subview1 yg_setIncludeInLayout:NO]; [container addSubview:subview1]; UIView *subview2 = [[UIView alloc] initWithFrame:CGRectZero]; - subview2.yoga.isEnabled = YES; - subview2.yoga.isIncludedInLayout = NO; + [subview2 yg_setUsesYoga:YES]; + [subview2 yg_setIncludeInLayout:NO]; [container addSubview:subview2]; UIView *subview3 = [[UIView alloc] initWithFrame:CGRectZero]; - subview3.yoga.isEnabled = YES; - subview3.yoga.isIncludedInLayout = YES; + [subview3 yg_setUsesYoga:YES]; + [subview3 yg_setIncludeInLayout:YES]; [container addSubview:subview3]; - [container.yoga applyLayout]; - XCTAssertEqual(1, container.yoga.numberOfChildren); + [container yg_applyLayout]; + XCTAssertEqual(1, [container yg_numberOfChildren]); - subview2.yoga.isIncludedInLayout = YES; - [container.yoga applyLayout]; - XCTAssertEqual(2, container.yoga.numberOfChildren); + [subview2 yg_setIncludeInLayout:YES]; + [container yg_applyLayout]; + XCTAssertEqual(2, [container yg_numberOfChildren]); } - (void)testThatViewNotIncludedInFirstLayoutPassAreIncludedInSecond { UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 50)]; - container.yoga.isEnabled = YES; - container.yoga.flexDirection = YGFlexDirectionRow; + [container yg_setUsesYoga:YES]; + [container yg_setFlexDirection:YGFlexDirectionRow]; UIView *subview1 = [[UIView alloc] initWithFrame:CGRectZero]; - subview1.yoga.isEnabled = YES; - subview1.yoga.flexGrow = 1; + [subview1 yg_setUsesYoga:YES]; + [subview1 yg_setFlexGrow:1]; [container addSubview:subview1]; UIView *subview2 = [[UIView alloc] initWithFrame:CGRectZero]; - subview2.yoga.isEnabled = YES; - subview2.yoga.flexGrow = 1; + [subview2 yg_setUsesYoga:YES]; + [subview2 yg_setFlexGrow:1]; [container addSubview:subview2]; UIView *subview3 = [[UIView alloc] initWithFrame:CGRectZero]; - subview3.yoga.isEnabled = YES; - subview3.yoga.flexGrow = 1; - subview3.yoga.isIncludedInLayout = NO; + [subview3 yg_setUsesYoga:YES]; + [subview3 yg_setFlexGrow:1]; + [subview3 yg_setIncludeInLayout:NO]; [container addSubview:subview3]; - [container.yoga applyLayout]; + [container yg_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.yoga.isIncludedInLayout = YES; - [container.yoga applyLayout]; + [subview3 yg_setIncludeInLayout:YES]; + [container yg_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.yoga.isLeaf); + XCTAssertTrue(view.yg_isLeaf); for (int i=0; i<10; i++) { UIView *subview = [[UIView alloc] initWithFrame:CGRectZero]; [view addSubview:subview]; } - XCTAssertTrue(view.yoga.isLeaf); + XCTAssertTrue(view.yg_isLeaf); - view.yoga.isEnabled = YES; - view.yoga.width = 50.0; - XCTAssertTrue(view.yoga.isLeaf); + [view yg_setUsesYoga:YES]; + [view yg_setWidth:50.0]; + XCTAssertTrue(view.yg_isLeaf); UIView *const subview = view.subviews[0]; - subview.yoga.isEnabled = YES; - subview.yoga.width = 50.0; - XCTAssertFalse(view.yoga.isLeaf); + [subview yg_setUsesYoga:YES]; + [subview yg_setWidth:50.0]; + XCTAssertFalse(view.yg_isLeaf); } - (void)testThatWeCorrectlyAttachNestedViews { UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 50)]; - container.yoga.isEnabled = YES; - container.yoga.flexDirection = YGFlexDirectionColumn; + [container yg_setUsesYoga:YES]; + [container yg_setFlexDirection:YGFlexDirectionColumn]; UIView *subview1 = [[UIView alloc] initWithFrame:CGRectZero]; - subview1.yoga.isEnabled = YES; - subview1.yoga.width = 100; - subview1.yoga.flexGrow = 1; - subview1.yoga.flexDirection = YGFlexDirectionColumn; + [subview1 yg_setUsesYoga:YES]; + [subview1 yg_setWidth:100]; + [subview1 yg_setFlexGrow:1]; + [subview1 yg_setFlexDirection:YGFlexDirectionColumn]; [container addSubview:subview1]; UIView *subview2 = [[UIView alloc] initWithFrame:CGRectZero]; - subview2.yoga.isEnabled = YES; - subview2.yoga.width = 150; - subview2.yoga.flexGrow = 1; - subview2.yoga.flexDirection = YGFlexDirectionColumn; + [subview2 yg_setUsesYoga:YES]; + [subview2 yg_setWidth:150]; + [subview2 yg_setFlexGrow:1]; + [subview2 yg_setFlexDirection:YGFlexDirectionColumn]; [container addSubview:subview2]; for (UIView *view in @[subview1, subview2]) { UIView *someView = [[UIView alloc] initWithFrame:CGRectZero]; - someView.yoga.isEnabled = YES; - someView.yoga.flexGrow = 1; + [someView yg_setUsesYoga:YES]; + [someView yg_setFlexGrow:1]; [view addSubview:someView]; } - [container.yoga applyLayout]; + [container yg_applyLayout]; // Add the same amount of new views, reapply layout. for (UIView *view in @[subview1, subview2]) { UIView *someView = [[UIView alloc] initWithFrame:CGRectZero]; - someView.yoga.isEnabled = YES; - someView.yoga.flexGrow = 1; + [someView yg_setUsesYoga:YES]; + [someView yg_setFlexGrow:1]; [view addSubview:someView]; } - [container.yoga applyLayout]; + [container yg_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.yoga.isEnabled = YES; + [container yg_setUsesYoga:YES]; UIView *subview1 = [[UIView alloc] initWithFrame:CGRectZero]; - subview1.yoga.isEnabled = YES; + [subview1 yg_setUsesYoga:YES]; [container addSubview:subview1]; UIView *subview2 = [[UIView alloc] initWithFrame:CGRectZero]; - subview2.yoga.isEnabled = YES; + [subview2 yg_setUsesYoga:YES]; [subview1 addSubview:subview2]; - [container.yoga applyLayout]; + [container yg_applyLayout]; [subview2 removeFromSuperview]; - [container.yoga applyLayout]; + [container yg_applyLayout]; } @end diff --git a/YogaKit/UIView+Yoga.h b/YogaKit/UIView+Yoga.h index 70e91917..b565b1ae 100644 --- a/YogaKit/UIView+Yoga.h +++ b/YogaKit/UIView+Yoga.h @@ -8,10 +8,76 @@ */ #import -#import "YGLayout.h" +#import @interface UIView (Yoga) -@property (nonatomic, readonly, strong) YGLayout *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; + +/** + 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 diff --git a/YogaKit/UIView+Yoga.m b/YogaKit/UIView+Yoga.m index 66b7eed0..cfe0afa0 100644 --- a/YogaKit/UIView+Yoga.m +++ b/YogaKit/UIView+Yoga.m @@ -8,23 +8,404 @@ */ #import "UIView+Yoga.h" -#import "YGLayout+Private.h" + #import -static const void *kYGYogaAssociatedKey = &kYGYogaAssociatedKey; +static const void *kYGNodeBridgeAssociatedKey = &kYGNodeBridgeAssociatedKey; -@implementation UIView (YogaKit) +@interface YGNodeBridge : NSObject +@property (nonatomic, assign, readonly) YGNodeRef cnode; +@end -- (YGLayout *)yoga +@implementation YGNodeBridge + ++ (void)initialize { - YGLayout *yoga = objc_getAssociatedObject(self, kYGYogaAssociatedKey); - if (!yoga) { - yoga = [[YGLayout alloc] initWithView:self]; - objc_setAssociatedObject(self, kYGYogaAssociatedKey, yoga, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - } - - return yoga; + YGSetExperimentalFeatureEnabled(YGExperimentalFeatureWebFlexBasis, true); } +- (instancetype)init +{ + if ([super init]) { + _cnode = YGNodeNew(); + } + + return self; +} + +- (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 - -@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 deleted file mode 100644 index d6a2baf0..00000000 --- a/YogaKit/YGLayout.m +++ /dev/null @@ -1,380 +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. - */ - -#import "YGLayout+Private.h" -#import "UIView+Yoga.h" -#import - -#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 (self = [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 *const 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 2f388ccc..53b2ab53 100644 --- a/enums.py +++ b/enums.py @@ -119,21 +119,21 @@ def to_java_upper(symbol): root = os.path.dirname(os.path.abspath(__file__)) -# write out C & Objective-C headers +# write out C headers with open(root + '/yoga/YGEnums.h', 'w') as f: f.write(LICENSE) f.write('#pragma once\n\n') 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('typedef YG_ENUM_BEGIN(YG%s) {\n' % name) + f.write('#define YG%sCount %s\n' % (name, len(values))) + f.write('typedef enum 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_ENUM_END(YG%s);\n' % name) + f.write('} 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 0a10bbc2..24606bcd 100644 --- a/yoga/YGEnums.h +++ b/yoga/YGEnums.h @@ -14,29 +14,29 @@ YG_EXTERN_C_BEGIN #define YGFlexDirectionCount 4 -typedef YG_ENUM_BEGIN(YGFlexDirection) { +typedef enum YGFlexDirection { YGFlexDirectionColumn, YGFlexDirectionColumnReverse, YGFlexDirectionRow, YGFlexDirectionRowReverse, -} YG_ENUM_END(YGFlexDirection); +} YGFlexDirection; #define YGMeasureModeCount 3 -typedef YG_ENUM_BEGIN(YGMeasureMode) { +typedef enum YGMeasureMode { YGMeasureModeUndefined, YGMeasureModeExactly, YGMeasureModeAtMost, -} YG_ENUM_END(YGMeasureMode); +} YGMeasureMode; #define YGPrintOptionsCount 3 -typedef YG_ENUM_BEGIN(YGPrintOptions) { +typedef enum YGPrintOptions { YGPrintOptionsLayout = 1, YGPrintOptionsStyle = 2, YGPrintOptionsChildren = 4, -} YG_ENUM_END(YGPrintOptions); +} YGPrintOptions; #define YGEdgeCount 9 -typedef YG_ENUM_BEGIN(YGEdge) { +typedef enum YGEdge { YGEdgeLeft, YGEdgeTop, YGEdgeRight, @@ -46,79 +46,79 @@ typedef YG_ENUM_BEGIN(YGEdge) { YGEdgeHorizontal, YGEdgeVertical, YGEdgeAll, -} YG_ENUM_END(YGEdge); +} YGEdge; #define YGPositionTypeCount 2 -typedef YG_ENUM_BEGIN(YGPositionType) { +typedef enum YGPositionType { YGPositionTypeRelative, YGPositionTypeAbsolute, -} YG_ENUM_END(YGPositionType); +} YGPositionType; #define YGDimensionCount 2 -typedef YG_ENUM_BEGIN(YGDimension) { +typedef enum YGDimension { YGDimensionWidth, YGDimensionHeight, -} YG_ENUM_END(YGDimension); +} YGDimension; #define YGJustifyCount 5 -typedef YG_ENUM_BEGIN(YGJustify) { +typedef enum YGJustify { YGJustifyFlexStart, YGJustifyCenter, YGJustifyFlexEnd, YGJustifySpaceBetween, YGJustifySpaceAround, -} YG_ENUM_END(YGJustify); +} YGJustify; #define YGDirectionCount 3 -typedef YG_ENUM_BEGIN(YGDirection) { +typedef enum YGDirection { YGDirectionInherit, YGDirectionLTR, YGDirectionRTL, -} YG_ENUM_END(YGDirection); +} YGDirection; #define YGLogLevelCount 5 -typedef YG_ENUM_BEGIN(YGLogLevel) { +typedef enum YGLogLevel { YGLogLevelError, YGLogLevelWarn, YGLogLevelInfo, YGLogLevelDebug, YGLogLevelVerbose, -} YG_ENUM_END(YGLogLevel); +} YGLogLevel; #define YGWrapCount 2 -typedef YG_ENUM_BEGIN(YGWrap) { +typedef enum YGWrap { YGWrapNoWrap, YGWrapWrap, -} YG_ENUM_END(YGWrap); +} YGWrap; #define YGOverflowCount 3 -typedef YG_ENUM_BEGIN(YGOverflow) { +typedef enum YGOverflow { YGOverflowVisible, YGOverflowHidden, YGOverflowScroll, -} YG_ENUM_END(YGOverflow); +} YGOverflow; #define YGExperimentalFeatureCount 2 -typedef YG_ENUM_BEGIN(YGExperimentalFeature) { +typedef enum YGExperimentalFeature { YGExperimentalFeatureRounding, YGExperimentalFeatureWebFlexBasis, -} YG_ENUM_END(YGExperimentalFeature); +} YGExperimentalFeature; #define YGAlignCount 6 -typedef YG_ENUM_BEGIN(YGAlign) { +typedef enum YGAlign { YGAlignAuto, YGAlignFlexStart, YGAlignCenter, YGAlignFlexEnd, YGAlignStretch, YGAlignBaseline, -} YG_ENUM_END(YGAlign); +} YGAlign; #define YGUnitCount 3 -typedef YG_ENUM_BEGIN(YGUnit) { +typedef enum YGUnit { YGUnitUndefined, YGUnitPixel, YGUnitPercent, -} YG_ENUM_END(YGUnit); +} YGUnit; YG_EXTERN_C_END diff --git a/yoga/YGMacros.h b/yoga/YGMacros.h index d6724272..def8371a 100644 --- a/yoga/YGMacros.h +++ b/yoga/YGMacros.h @@ -40,19 +40,3 @@ 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 From 969b3709dbc41738e54b9f929e7bf8c5588303ec Mon Sep 17 00:00:00 2001 From: Chris Hamons Date: Sun, 8 Jan 2017 07:56:46 -0800 Subject: [PATCH 3/7] Add basic Xamarin.Mac support Summary: - Does not contain "magic" found in YogaKit yet, but enough to get started - Simple test project showing use - YGInteropSetLogger and YGNodeInit(IntPtr) were missing from native lib built by buck Closes https://github.com/facebook/yoga/pull/278 Reviewed By: emilsjolander Differential Revision: D4388480 Pulled By: splhack fbshipit-source-id: a7387bb5b5554b6fce80d08c23e4fa18a4611cce --- csharp/Mac/.gitignore | 2 + csharp/Mac/ApiDefinition.cs | 23 + csharp/Mac/CustomBuildAction.targets | 11 + .../Mac/Facebook.Yoga.Mac.Test/AppDelegate.cs | 30 + .../Facebook.Yoga.Mac.Test/Entitlements.plist | 6 + .../Facebook.Yoga.Mac.Test.csproj | 84 +++ csharp/Mac/Facebook.Yoga.Mac.Test/Info.plist | 30 + csharp/Mac/Facebook.Yoga.Mac.Test/Main.cs | 21 + .../Facebook.Yoga.Mac.Test/Main.storyboard | 681 ++++++++++++++++++ .../Facebook.Yoga.Mac.Test/ViewController.cs | 137 ++++ .../ViewController.designer.cs | 18 + csharp/Mac/Facebook.Yoga.Mac.csproj | 51 ++ csharp/Mac/Facebook.Yoga.Mac.sln | 25 + 13 files changed, 1119 insertions(+) create mode 100644 csharp/Mac/.gitignore create mode 100644 csharp/Mac/ApiDefinition.cs create mode 100644 csharp/Mac/CustomBuildAction.targets create mode 100644 csharp/Mac/Facebook.Yoga.Mac.Test/AppDelegate.cs create mode 100644 csharp/Mac/Facebook.Yoga.Mac.Test/Entitlements.plist create mode 100644 csharp/Mac/Facebook.Yoga.Mac.Test/Facebook.Yoga.Mac.Test.csproj create mode 100644 csharp/Mac/Facebook.Yoga.Mac.Test/Info.plist create mode 100644 csharp/Mac/Facebook.Yoga.Mac.Test/Main.cs create mode 100644 csharp/Mac/Facebook.Yoga.Mac.Test/Main.storyboard create mode 100644 csharp/Mac/Facebook.Yoga.Mac.Test/ViewController.cs create mode 100644 csharp/Mac/Facebook.Yoga.Mac.Test/ViewController.designer.cs create mode 100644 csharp/Mac/Facebook.Yoga.Mac.csproj create mode 100644 csharp/Mac/Facebook.Yoga.Mac.sln diff --git a/csharp/Mac/.gitignore b/csharp/Mac/.gitignore new file mode 100644 index 00000000..1cf1debb --- /dev/null +++ b/csharp/Mac/.gitignore @@ -0,0 +1,2 @@ +libyoga.dylib +*.userprefs diff --git a/csharp/Mac/ApiDefinition.cs b/csharp/Mac/ApiDefinition.cs new file mode 100644 index 00000000..9fec5cd6 --- /dev/null +++ b/csharp/Mac/ApiDefinition.cs @@ -0,0 +1,23 @@ +/** + * 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. + */ + +using System; + +using AppKit; +using Foundation; +using ObjCRuntime; +using CoreGraphics; + +namespace Facebook.Yoga.Mac +{ + // Xamarin.Mac binding projects allow you to include native libraries inside C# DLLs for easy consumption + // later. However, the binding project build files currently assume you are binding some objective-c API + // and that you need an ApiDefinition.cs for that. yoga is all C APIs, so just include this "blank" file so + // the dylib gets packaged +} diff --git a/csharp/Mac/CustomBuildAction.targets b/csharp/Mac/CustomBuildAction.targets new file mode 100644 index 00000000..e20e70b8 --- /dev/null +++ b/csharp/Mac/CustomBuildAction.targets @@ -0,0 +1,11 @@ + + + + CopyInNativeLib;$(CompileDependsOn) + + + + + + + diff --git a/csharp/Mac/Facebook.Yoga.Mac.Test/AppDelegate.cs b/csharp/Mac/Facebook.Yoga.Mac.Test/AppDelegate.cs new file mode 100644 index 00000000..f6a46196 --- /dev/null +++ b/csharp/Mac/Facebook.Yoga.Mac.Test/AppDelegate.cs @@ -0,0 +1,30 @@ +/** + * Copyright 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the license found in the + * LICENSE-examples file in the root directory of this source tree. + */ + +using AppKit; +using Foundation; + +namespace Facebook.Yoga.Mac.Test +{ + [Register("AppDelegate")] + public class AppDelegate : NSApplicationDelegate + { + public AppDelegate() + { + } + + public override void DidFinishLaunching(NSNotification notification) + { + } + + public override void WillTerminate(NSNotification notification) + { + // Insert code here to tear down your application + } + } +} diff --git a/csharp/Mac/Facebook.Yoga.Mac.Test/Entitlements.plist b/csharp/Mac/Facebook.Yoga.Mac.Test/Entitlements.plist new file mode 100644 index 00000000..9ae59937 --- /dev/null +++ b/csharp/Mac/Facebook.Yoga.Mac.Test/Entitlements.plist @@ -0,0 +1,6 @@ + + + + + + diff --git a/csharp/Mac/Facebook.Yoga.Mac.Test/Facebook.Yoga.Mac.Test.csproj b/csharp/Mac/Facebook.Yoga.Mac.Test/Facebook.Yoga.Mac.Test.csproj new file mode 100644 index 00000000..70535e37 --- /dev/null +++ b/csharp/Mac/Facebook.Yoga.Mac.Test/Facebook.Yoga.Mac.Test.csproj @@ -0,0 +1,84 @@ + + + + Debug + AnyCPU + {64E0AB97-A904-4607-A535-EEA5A966C09E} + {A3F8F2AB-B479-4A4A-A458-A89E7DC349F1};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Exe + Facebook.Yoga.Mac.Test + Facebook.Yoga.Mac.Test + v2.0 + Xamarin.Mac + Resources + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + false + Mac Developer + false + false + false + true + true + true + HttpClientHandler + Default + None + x86_64 + + + pdbonly + true + bin\Release + + prompt + 4 + false + true + false + true + true + true + SdkOnly + HttpClientHandler + Default + + + + + + + + + + + + + + + + + + + + ViewController.cs + + + + + + + + {19A1C7D7-C9CC-476A-B604-DF6A3DE1BA71} + Facebook.Yoga.Mac + + + + diff --git a/csharp/Mac/Facebook.Yoga.Mac.Test/Info.plist b/csharp/Mac/Facebook.Yoga.Mac.Test/Info.plist new file mode 100644 index 00000000..7e955671 --- /dev/null +++ b/csharp/Mac/Facebook.Yoga.Mac.Test/Info.plist @@ -0,0 +1,30 @@ + + + + + CFBundleName + Facebook.Yoga.Mac.Test + CFBundleIdentifier + com.companyname.facebook-yoga-mac-test + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSMinimumSystemVersion + 10.11 + CFBundleDevelopmentRegion + en + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + CFBundleSignature + ???? + NSHumanReadableCopyright + ${AuthorCopyright} + NSPrincipalClass + NSApplication + NSMainStoryboardFile + Main + + diff --git a/csharp/Mac/Facebook.Yoga.Mac.Test/Main.cs b/csharp/Mac/Facebook.Yoga.Mac.Test/Main.cs new file mode 100644 index 00000000..97adb4db --- /dev/null +++ b/csharp/Mac/Facebook.Yoga.Mac.Test/Main.cs @@ -0,0 +1,21 @@ +/** + * Copyright 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the license found in the + * LICENSE-examples file in the root directory of this source tree. + */ + +using AppKit; + +namespace Facebook.Yoga.Mac.Test +{ + static class MainClass + { + static void Main(string[] args) + { + NSApplication.Init(); + NSApplication.Main(args); + } + } +} diff --git a/csharp/Mac/Facebook.Yoga.Mac.Test/Main.storyboard b/csharp/Mac/Facebook.Yoga.Mac.Test/Main.storyboard new file mode 100644 index 00000000..1d8f728d --- /dev/null +++ b/csharp/Mac/Facebook.Yoga.Mac.Test/Main.storyboard @@ -0,0 +1,681 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/csharp/Mac/Facebook.Yoga.Mac.Test/ViewController.cs b/csharp/Mac/Facebook.Yoga.Mac.Test/ViewController.cs new file mode 100644 index 00000000..e802aa4e --- /dev/null +++ b/csharp/Mac/Facebook.Yoga.Mac.Test/ViewController.cs @@ -0,0 +1,137 @@ +/** + * Copyright 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the license found in the + * LICENSE-examples file in the root directory of this source tree. + */ + +//#define DEBUG_LAYOUT +using System; + +using AppKit; +using Foundation; +using CoreGraphics; + +namespace Facebook.Yoga.Mac.Test +{ + public static class NSViewYogaExtensions + { + public static void ApplyYogaLayout (this NSView view, YogaNode n, bool root = true) + { +#if DEBUG_LAYOUT + Console.WriteLine ($"ApplyYogaLayout {view.ToolTip}, {n.LayoutX}, {n.LayoutY}, {n.LayoutWidth}, {n.LayoutHeight}"); +#endif + + // A bit of gross special casing + // This really should mostly go away if/when the UIView+Yoga.m magic gets ported to AppKit + if (root) + view.Frame = new CGRect (n.LayoutX, n.LayoutY, n.LayoutWidth, n.LayoutHeight); +#if DEBUG_LAYOUT + Console.WriteLine ($"Setting {view.ToolTip} frame to {view.Frame}"); +#endif + + // This assumes your YogaNode and NSView children were inserted in same order + for (int i = 0; i < n.Count; ++i) { + YogaNode childNode = n[i]; + // Cocoa coord space is from bottom left not top left + view.Subviews[i].Frame = new CGRect (childNode.LayoutX, n.LayoutHeight - childNode.LayoutY - childNode.LayoutHeight, childNode.LayoutWidth, childNode.LayoutHeight); +#if DEBUG_LAYOUT + Console.WriteLine ($"Setting {view.Subviews[i].ToolTip} frame to {view.Subviews[i].Frame}"); +#endif + if (childNode.Count > 0){ +#if DEBUG_LAYOUT + Console.WriteLine ($"Calling ApplyYogaLayout recursively on {view.Subviews[i].ToolTip}"); +#endif + ApplyYogaLayout (view.Subviews[i], childNode, false); + } + } + } + } + + public partial class ViewController : NSViewController + { + public ViewController(IntPtr handle) : base(handle) + { + } + + public override void ViewDidLoad() + { + base.ViewDidLoad (); + + NSImage image = NSImage.ImageNamed (NSImageName.TrashFull); + + NSView root = CreateViewHierarchy (image); + var rootNode = CalculateLayout (View.Frame, image.Size); + + root.ApplyYogaLayout (rootNode); + + View.AddSubview (root); + } + + static NSView CreateViewHierarchy (NSImage image) + { + var root = new NSView () { + WantsLayer = true, + ToolTip = "Root" + }; + root.Layer.BackgroundColor = NSColor.Red.CGColor; + + NSView child1 = new NSView () { + WantsLayer = true, + ToolTip = "Child 1" + }; + child1.Layer.BackgroundColor = NSColor.Blue.CGColor; + + NSView child2 = new NSView () { + WantsLayer = true, + ToolTip = "Child 2" + }; + child2.Layer.BackgroundColor = NSColor.Green.CGColor; + + NSView child3 = new NSView () { + WantsLayer = true, + ToolTip = "Child 3" + }; + child3.Layer.BackgroundColor = NSColor.Yellow.CGColor; + + root.AddSubview (child1); + root.AddSubview (child2); + child2.AddSubview (child3); + + return root; + } + + static YogaNode CalculateLayout (CGRect rootFrame, CGSize imageSize) + { + var rootNode = new YogaNode () { + Width = (float)rootFrame.Width, + Height = (float)rootFrame.Height, + AlignItems = YogaAlign.Center, + JustifyContent = YogaJustify.Center + }; + + var child1Node = new YogaNode () { + Width = 100, + Height = 100 + }; + + var child2Node = new YogaNode () { + Width = 200, + Height = 100 + }; + + var child3Node = new YogaNode () { + Width = 100, + Height = 100 + }; + + rootNode.Insert (0, child1Node); + rootNode.Insert (1, child2Node); + child2Node.Insert (0, child3Node); + rootNode.CalculateLayout (); + + return rootNode; + } + } +} diff --git a/csharp/Mac/Facebook.Yoga.Mac.Test/ViewController.designer.cs b/csharp/Mac/Facebook.Yoga.Mac.Test/ViewController.designer.cs new file mode 100644 index 00000000..1cf1e327 --- /dev/null +++ b/csharp/Mac/Facebook.Yoga.Mac.Test/ViewController.designer.cs @@ -0,0 +1,18 @@ +// WARNING +// +// This file has been generated automatically by Xamarin Studio to store outlets and +// actions made in the UI designer. If it is removed, they will be lost. +// Manual changes to this file may not be handled correctly. +// +using Foundation; + +namespace Facebook.Yoga.Mac.Test +{ + [Register("ViewController")] + partial class ViewController + { + void ReleaseDesignerOutlets() + { + } + } +} diff --git a/csharp/Mac/Facebook.Yoga.Mac.csproj b/csharp/Mac/Facebook.Yoga.Mac.csproj new file mode 100644 index 00000000..63dcfe16 --- /dev/null +++ b/csharp/Mac/Facebook.Yoga.Mac.csproj @@ -0,0 +1,51 @@ + + + + Debug + AnyCPU + {19A1C7D7-C9CC-476A-B604-DF6A3DE1BA71} + {810C163F-4746-4721-8B8E-88A3673A62EA};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Library + Facebook.Yoga.Mac + Facebook.Yoga.Mac + Resources + + + true + full + false + bin\Debug + __UNIFIED__;DEBUG;MONOMAC + prompt + 4 + false + + + true + bin\Release + __UNIFIED__;MONOMAC + prompt + 4 + false + + + + + + + + + + + + + + + Dynamic + False + + + + + + diff --git a/csharp/Mac/Facebook.Yoga.Mac.sln b/csharp/Mac/Facebook.Yoga.Mac.sln new file mode 100644 index 00000000..c99766c6 --- /dev/null +++ b/csharp/Mac/Facebook.Yoga.Mac.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Facebook.Yoga.Mac.Test", "Facebook.Yoga.Mac.Test\Facebook.Yoga.Mac.Test.csproj", "{64E0AB97-A904-4607-A535-EEA5A966C09E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Facebook.Yoga.Mac", "Facebook.Yoga.Mac.csproj", "{19A1C7D7-C9CC-476A-B604-DF6A3DE1BA71}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Facebook.Yoga.Shared", "..\Facebook.Yoga\Facebook.Yoga.Shared.shproj", "{91C42D32-291D-4B72-90B4-551663D60B8B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {64E0AB97-A904-4607-A535-EEA5A966C09E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {64E0AB97-A904-4607-A535-EEA5A966C09E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {64E0AB97-A904-4607-A535-EEA5A966C09E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {64E0AB97-A904-4607-A535-EEA5A966C09E}.Release|Any CPU.Build.0 = Release|Any CPU + {19A1C7D7-C9CC-476A-B604-DF6A3DE1BA71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {19A1C7D7-C9CC-476A-B604-DF6A3DE1BA71}.Debug|Any CPU.Build.0 = Debug|Any CPU + {19A1C7D7-C9CC-476A-B604-DF6A3DE1BA71}.Release|Any CPU.ActiveCfg = Release|Any CPU + {19A1C7D7-C9CC-476A-B604-DF6A3DE1BA71}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal From 8ed71b2777d31088b006dbe5f13d7eacb4e47808 Mon Sep 17 00:00:00 2001 From: Kazuki Sakamoto Date: Sun, 8 Jan 2017 07:58:31 -0800 Subject: [PATCH 4/7] Add spacing properties Summary: Align C# implementation with YogaKit #322 Closes https://github.com/facebook/yoga/pull/327 Reviewed By: emilsjolander Differential Revision: D4390687 Pulled By: splhack fbshipit-source-id: 28c87a45898fcd958a422d5e254ead0ec00d3562 --- csharp/Facebook.Yoga/Border.cs | 3 +- .../Facebook.Yoga.Shared.projitems | 1 + csharp/Facebook.Yoga/Spacing.cs | 1 + csharp/Facebook.Yoga/YogaNode.Create.cs | 33 +- csharp/Facebook.Yoga/YogaNode.Spacing.cs | 503 ++++++++++++++++++ csharp/Facebook.Yoga/YogaNode.cs | 9 + .../Facebook.Yoga/YGAbsolutePositionTest.cs | 60 +-- .../tests/Facebook.Yoga/YGAlignItemsTest.cs | 36 +- csharp/tests/Facebook.Yoga/YGBorderTest.cs | 38 +- csharp/tests/Facebook.Yoga/YGMarginTest.cs | 16 +- csharp/tests/Facebook.Yoga/YGPaddingTest.cs | 46 +- .../tests/Facebook.Yoga/YGPercentageTest.cs | 76 +-- csharp/tests/Facebook.Yoga/YGRoundingTest.cs | 8 +- .../tests/Facebook.Yoga/YogaNodeCreateTest.cs | 143 ----- .../Facebook.Yoga/YogaNodeSpacingTest.cs | 114 ++++ csharp/tests/Facebook.Yoga/YogaNodeTest.cs | 16 +- gentest/gentest-cs.js | 20 +- 17 files changed, 805 insertions(+), 318 deletions(-) create mode 100644 csharp/Facebook.Yoga/YogaNode.Spacing.cs delete mode 100644 csharp/tests/Facebook.Yoga/YogaNodeCreateTest.cs create mode 100644 csharp/tests/Facebook.Yoga/YogaNodeSpacingTest.cs diff --git a/csharp/Facebook.Yoga/Border.cs b/csharp/Facebook.Yoga/Border.cs index 27c15cf4..6f046664 100644 --- a/csharp/Facebook.Yoga/Border.cs +++ b/csharp/Facebook.Yoga/Border.cs @@ -9,6 +9,7 @@ namespace Facebook.Yoga { + [System.Obsolete] public class Border { public float? Top; @@ -28,4 +29,4 @@ namespace Facebook.Yoga Right = right; } } -} \ No newline at end of file +} diff --git a/csharp/Facebook.Yoga/Facebook.Yoga.Shared.projitems b/csharp/Facebook.Yoga/Facebook.Yoga.Shared.projitems index b48fb339..66fff377 100644 --- a/csharp/Facebook.Yoga/Facebook.Yoga.Shared.projitems +++ b/csharp/Facebook.Yoga/Facebook.Yoga.Shared.projitems @@ -29,6 +29,7 @@ + diff --git a/csharp/Facebook.Yoga/Spacing.cs b/csharp/Facebook.Yoga/Spacing.cs index 7f5dffbd..0c02800f 100644 --- a/csharp/Facebook.Yoga/Spacing.cs +++ b/csharp/Facebook.Yoga/Spacing.cs @@ -9,6 +9,7 @@ namespace Facebook.Yoga { + [System.Obsolete] public class Spacing { public YogaValue? Top; diff --git a/csharp/Facebook.Yoga/YogaNode.Create.cs b/csharp/Facebook.Yoga/YogaNode.Create.cs index ee0726b5..b44eb798 100644 --- a/csharp/Facebook.Yoga/YogaNode.Create.cs +++ b/csharp/Facebook.Yoga/YogaNode.Create.cs @@ -13,6 +13,7 @@ namespace Facebook.Yoga { public partial class YogaNode { + [Obsolete("use Object Initializer")] public static YogaNode Create( YogaDirection? styleDirection = null, YogaFlexDirection? flexDirection = null, @@ -109,22 +110,22 @@ namespace Facebook.Yoga { if (position.Top.HasValue) { - node.SetPosition(YogaEdge.Top, position.Top.Value); + node.Top = position.Top.Value; } if (position.Bottom.HasValue) { - node.SetPosition(YogaEdge.Bottom, position.Bottom.Value); + node.Bottom = position.Bottom.Value; } if (position.Left.HasValue) { - node.SetPosition(YogaEdge.Left, position.Left.Value); + node.Left = position.Left.Value; } if (position.Right.HasValue) { - node.SetPosition(YogaEdge.Right, position.Right.Value); + node.Right = position.Right.Value; } } @@ -132,22 +133,22 @@ namespace Facebook.Yoga { if (margin.Top.HasValue) { - node.SetMargin(YogaEdge.Top, margin.Top.Value); + node.MarginTop = margin.Top.Value; } if (margin.Bottom.HasValue) { - node.SetMargin(YogaEdge.Bottom, margin.Bottom.Value); + node.MarginBottom = margin.Bottom.Value; } if (margin.Left.HasValue) { - node.SetMargin(YogaEdge.Left, margin.Left.Value); + node.MarginLeft = margin.Left.Value; } if (margin.Right.HasValue) { - node.SetMargin(YogaEdge.Right, margin.Right.Value); + node.MarginRight = margin.Right.Value; } } @@ -155,22 +156,22 @@ namespace Facebook.Yoga { if (padding.Top.HasValue) { - node.SetPadding(YogaEdge.Top, padding.Top.Value); + node.PaddingTop = padding.Top.Value; } if (padding.Bottom.HasValue) { - node.SetPadding(YogaEdge.Bottom, padding.Bottom.Value); + node.PaddingBottom = padding.Bottom.Value; } if (padding.Left.HasValue) { - node.SetPadding(YogaEdge.Left, padding.Left.Value); + node.PaddingLeft = padding.Left.Value; } if (padding.Right.HasValue) { - node.SetPadding(YogaEdge.Right, padding.Right.Value); + node.PaddingRight = padding.Right.Value; } } @@ -178,22 +179,22 @@ namespace Facebook.Yoga { if (border.Top.HasValue) { - node.SetBorder(YogaEdge.Top, border.Top.Value); + node.BorderTopWidth = border.Top.Value; } if (border.Bottom.HasValue) { - node.SetBorder(YogaEdge.Bottom, border.Bottom.Value); + node.BorderBottomWidth = border.Bottom.Value; } if (border.Left.HasValue) { - node.SetBorder(YogaEdge.Left, border.Left.Value); + node.BorderLeftWidth = border.Left.Value; } if (border.Right.HasValue) { - node.SetBorder(YogaEdge.Right, border.Right.Value); + node.BorderRightWidth = border.Right.Value; } } diff --git a/csharp/Facebook.Yoga/YogaNode.Spacing.cs b/csharp/Facebook.Yoga/YogaNode.Spacing.cs new file mode 100644 index 00000000..ff93988d --- /dev/null +++ b/csharp/Facebook.Yoga/YogaNode.Spacing.cs @@ -0,0 +1,503 @@ +/** + * 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. + */ + +using System; + +namespace Facebook.Yoga +{ + public partial class YogaNode + { + public YogaValue Left + { + get + { + return Native.YGNodeStyleGetPosition(_ygNode, YogaEdge.Left); + } + + set + { + SetStylePosition(YogaEdge.Left, value); + } + } + + public YogaValue Top + { + get + { + return Native.YGNodeStyleGetPosition(_ygNode, YogaEdge.Top); + } + + set + { + SetStylePosition(YogaEdge.Top, value); + } + } + + public YogaValue Right + { + get + { + return Native.YGNodeStyleGetPosition(_ygNode, YogaEdge.Right); + } + + set + { + SetStylePosition(YogaEdge.Right, value); + } + } + + public YogaValue Bottom + { + get + { + return Native.YGNodeStyleGetPosition(_ygNode, YogaEdge.Bottom); + } + + set + { + SetStylePosition(YogaEdge.Bottom, value); + } + } + + public YogaValue Start + { + get + { + return Native.YGNodeStyleGetPosition(_ygNode, YogaEdge.Start); + } + + set + { + SetStylePosition(YogaEdge.Start, value); + } + } + + public YogaValue End + { + get + { + return Native.YGNodeStyleGetPosition(_ygNode, YogaEdge.End); + } + + set + { + SetStylePosition(YogaEdge.End, value); + } + } + + private void SetStylePosition(YogaEdge edge, YogaValue value) + { + if (value.Unit == YogaUnit.Percent) + { + Native.YGNodeStyleSetPositionPercent(_ygNode, edge, value.Value); + } + else + { + Native.YGNodeStyleSetPosition(_ygNode, edge, value.Value); + } + } + + public YogaValue MarginLeft + { + get + { + return Native.YGNodeStyleGetMargin(_ygNode, YogaEdge.Left); + } + + set + { + SetStyleMargin(YogaEdge.Left, value); + } + } + + public YogaValue MarginTop + { + get + { + return Native.YGNodeStyleGetMargin(_ygNode, YogaEdge.Top); + } + + set + { + SetStyleMargin(YogaEdge.Top, value); + } + } + + public YogaValue MarginRight + { + get + { + return Native.YGNodeStyleGetMargin(_ygNode, YogaEdge.Right); + } + + set + { + SetStyleMargin(YogaEdge.Right, value); + } + } + + public YogaValue MarginBottom + { + get + { + return Native.YGNodeStyleGetMargin(_ygNode, YogaEdge.Bottom); + } + + set + { + SetStyleMargin(YogaEdge.Bottom, value); + } + } + + public YogaValue MarginStart + { + get + { + return Native.YGNodeStyleGetMargin(_ygNode, YogaEdge.Start); + } + + set + { + SetStyleMargin(YogaEdge.Start, value); + } + } + + public YogaValue MarginEnd + { + get + { + return Native.YGNodeStyleGetMargin(_ygNode, YogaEdge.End); + } + + set + { + SetStyleMargin(YogaEdge.End, value); + } + } + + public YogaValue MarginHorizontal + { + get + { + return Native.YGNodeStyleGetMargin(_ygNode, YogaEdge.Horizontal); + } + + set + { + SetStyleMargin(YogaEdge.Horizontal, value); + } + } + + public YogaValue MarginVertical + { + get + { + return Native.YGNodeStyleGetMargin(_ygNode, YogaEdge.Vertical); + } + + set + { + SetStyleMargin(YogaEdge.Vertical, value); + } + } + + public YogaValue Margin + { + get + { + return Native.YGNodeStyleGetMargin(_ygNode, YogaEdge.All); + } + + set + { + SetStyleMargin(YogaEdge.All, value); + } + } + + private void SetStyleMargin(YogaEdge edge, YogaValue value) + { + if (value.Unit == YogaUnit.Percent) + { + Native.YGNodeStyleSetMarginPercent(_ygNode, edge, value.Value); + } + else + { + Native.YGNodeStyleSetMargin(_ygNode, edge, value.Value); + } + } + + public YogaValue PaddingLeft + { + get + { + return Native.YGNodeStyleGetPadding(_ygNode, YogaEdge.Left); + } + + set + { + SetStylePadding(YogaEdge.Left, value); + } + } + + public YogaValue PaddingTop + { + get + { + return Native.YGNodeStyleGetPadding(_ygNode, YogaEdge.Top); + } + + set + { + SetStylePadding(YogaEdge.Top, value); + } + } + + public YogaValue PaddingRight + { + get + { + return Native.YGNodeStyleGetPadding(_ygNode, YogaEdge.Right); + } + + set + { + SetStylePadding(YogaEdge.Right, value); + } + } + + public YogaValue PaddingBottom + { + get + { + return Native.YGNodeStyleGetPadding(_ygNode, YogaEdge.Bottom); + } + + set + { + SetStylePadding(YogaEdge.Bottom, value); + } + } + + public YogaValue PaddingStart + { + get + { + return Native.YGNodeStyleGetPadding(_ygNode, YogaEdge.Start); + } + + set + { + SetStylePadding(YogaEdge.Start, value); + } + } + + public YogaValue PaddingEnd + { + get + { + return Native.YGNodeStyleGetPadding(_ygNode, YogaEdge.End); + } + + set + { + SetStylePadding(YogaEdge.End, value); + } + } + + public YogaValue PaddingHorizontal + { + get + { + return Native.YGNodeStyleGetPadding(_ygNode, YogaEdge.Horizontal); + } + + set + { + SetStylePadding(YogaEdge.Horizontal, value); + } + } + + public YogaValue PaddingVertical + { + get + { + return Native.YGNodeStyleGetPadding(_ygNode, YogaEdge.Vertical); + } + + set + { + SetStylePadding(YogaEdge.Vertical, value); + } + } + + public YogaValue Padding + { + get + { + return Native.YGNodeStyleGetPadding(_ygNode, YogaEdge.All); + } + + set + { + SetStylePadding(YogaEdge.All, value); + } + } + + private void SetStylePadding(YogaEdge edge, YogaValue value) + { + if (value.Unit == YogaUnit.Percent) + { + Native.YGNodeStyleSetPaddingPercent(_ygNode, edge, value.Value); + } + else + { + Native.YGNodeStyleSetPadding(_ygNode, edge, value.Value); + } + } + + public float BorderLeftWidth + { + get + { + return Native.YGNodeStyleGetBorder(_ygNode, YogaEdge.Left); + } + + set + { + Native.YGNodeStyleSetBorder(_ygNode, YogaEdge.Left, value); + } + } + + public float BorderTopWidth + { + get + { + return Native.YGNodeStyleGetBorder(_ygNode, YogaEdge.Top); + } + + set + { + Native.YGNodeStyleSetBorder(_ygNode, YogaEdge.Top, value); + } + } + + public float BorderRightWidth + { + get + { + return Native.YGNodeStyleGetBorder(_ygNode, YogaEdge.Right); + } + + set + { + Native.YGNodeStyleSetBorder(_ygNode, YogaEdge.Right, value); + } + } + + public float BorderBottomWidth + { + get + { + return Native.YGNodeStyleGetBorder(_ygNode, YogaEdge.Bottom); + } + + set + { + Native.YGNodeStyleSetBorder(_ygNode, YogaEdge.Bottom, value); + } + } + + public float BorderStartWidth + { + get + { + return Native.YGNodeStyleGetBorder(_ygNode, YogaEdge.Start); + } + + set + { + Native.YGNodeStyleSetBorder(_ygNode, YogaEdge.Start, value); + } + } + + public float BorderEndWidth + { + get + { + return Native.YGNodeStyleGetBorder(_ygNode, YogaEdge.End); + } + + set + { + Native.YGNodeStyleSetBorder(_ygNode, YogaEdge.End, value); + } + } + + public float BorderWidth + { + get + { + return Native.YGNodeStyleGetBorder(_ygNode, YogaEdge.All); + } + + set + { + Native.YGNodeStyleSetBorder(_ygNode, YogaEdge.All, value); + } + } + + public float LayoutPaddingLeft + { + get + { + return Native.YGNodeLayoutGetPadding(_ygNode, YogaEdge.Left); + } + } + + public float LayoutPaddingTop + { + get + { + return Native.YGNodeLayoutGetPadding(_ygNode, YogaEdge.Top); + } + } + + public float LayoutPaddingRight + { + get + { + return Native.YGNodeLayoutGetPadding(_ygNode, YogaEdge.Right); + } + } + + public float LayoutPaddingBottom + { + get + { + return Native.YGNodeLayoutGetPadding(_ygNode, YogaEdge.Bottom); + } + } + + public float LayoutPaddingStart + { + get + { + return Native.YGNodeLayoutGetPadding(_ygNode, YogaEdge.Start); + } + } + + public float LayoutPaddingEnd + { + get + { + return Native.YGNodeLayoutGetPadding(_ygNode, YogaEdge.End); + } + } + } +} diff --git a/csharp/Facebook.Yoga/YogaNode.cs b/csharp/Facebook.Yoga/YogaNode.cs index ffc2c6f6..c7408a95 100644 --- a/csharp/Facebook.Yoga/YogaNode.cs +++ b/csharp/Facebook.Yoga/YogaNode.cs @@ -258,11 +258,13 @@ namespace Facebook.Yoga } } + [Obsolete("use Margin properties")] public YogaValue GetMargin(YogaEdge edge) { return Native.YGNodeStyleGetMargin(_ygNode, edge); } + [Obsolete("use Margin properties")] public void SetMargin(YogaEdge edge, YogaValue value) { if (value.Unit == YogaUnit.Percent) @@ -275,11 +277,13 @@ namespace Facebook.Yoga } } + [Obsolete("use Padding properties")] public YogaValue GetPadding(YogaEdge edge) { return Native.YGNodeStyleGetPadding(_ygNode, edge); } + [Obsolete("use Padding properties")] public void SetPadding(YogaEdge edge, YogaValue value) { if (value.Unit == YogaUnit.Percent) @@ -292,21 +296,25 @@ namespace Facebook.Yoga } } + [Obsolete("use BorderWidth properties")] public float GetBorder(YogaEdge edge) { return Native.YGNodeStyleGetBorder(_ygNode, edge); } + [Obsolete("use BorderWidth properties")] public void SetBorder(YogaEdge edge, float border) { Native.YGNodeStyleSetBorder(_ygNode, edge, border); } + [Obsolete("use Position properties")] public YogaValue GetPosition(YogaEdge edge) { return Native.YGNodeStyleGetPosition(_ygNode, edge); } + [Obsolete("use Position properties")] public void SetPosition(YogaEdge edge, YogaValue value) { if (value.Unit == YogaUnit.Percent) @@ -319,6 +327,7 @@ namespace Facebook.Yoga } } + [Obsolete("use LayoutPadding properties")] public float GetLayoutPadding(YogaEdge edge) { return Native.YGNodeLayoutGetPadding(_ygNode, edge); diff --git a/csharp/tests/Facebook.Yoga/YGAbsolutePositionTest.cs b/csharp/tests/Facebook.Yoga/YGAbsolutePositionTest.cs index 49f6605e..c9e25345 100644 --- a/csharp/tests/Facebook.Yoga/YGAbsolutePositionTest.cs +++ b/csharp/tests/Facebook.Yoga/YGAbsolutePositionTest.cs @@ -26,8 +26,8 @@ namespace Facebook.Yoga YogaNode root_child0 = new YogaNode(); root_child0.PositionType = YogaPositionType.Absolute; - root_child0.SetPosition(YogaEdge.Start, 10); - root_child0.SetPosition(YogaEdge.Top, 10); + root_child0.Start = 10; + root_child0.Top = 10; root_child0.Width = 10; root_child0.Height = 10; root.Insert(0, root_child0); @@ -67,8 +67,8 @@ namespace Facebook.Yoga YogaNode root_child0 = new YogaNode(); root_child0.PositionType = YogaPositionType.Absolute; - root_child0.SetPosition(YogaEdge.End, 10); - root_child0.SetPosition(YogaEdge.Bottom, 10); + root_child0.End = 10; + root_child0.Bottom = 10; root_child0.Width = 10; root_child0.Height = 10; root.Insert(0, root_child0); @@ -108,10 +108,10 @@ namespace Facebook.Yoga YogaNode root_child0 = new YogaNode(); root_child0.PositionType = YogaPositionType.Absolute; - root_child0.SetPosition(YogaEdge.Start, 10); - root_child0.SetPosition(YogaEdge.Top, 10); - root_child0.SetPosition(YogaEdge.End, 10); - root_child0.SetPosition(YogaEdge.Bottom, 10); + root_child0.Start = 10; + root_child0.Top = 10; + root_child0.End = 10; + root_child0.Bottom = 10; root.Insert(0, root_child0); root.StyleDirection = YogaDirection.LTR; root.CalculateLayout(); @@ -149,10 +149,10 @@ namespace Facebook.Yoga YogaNode root_child0 = new YogaNode(); root_child0.PositionType = YogaPositionType.Absolute; - root_child0.SetPosition(YogaEdge.Start, 10); - root_child0.SetPosition(YogaEdge.Top, 10); - root_child0.SetPosition(YogaEdge.End, 10); - root_child0.SetPosition(YogaEdge.Bottom, 10); + root_child0.Start = 10; + root_child0.Top = 10; + root_child0.End = 10; + root_child0.Bottom = 10; root_child0.Width = 10; root_child0.Height = 10; root.Insert(0, root_child0); @@ -194,8 +194,8 @@ namespace Facebook.Yoga YogaNode root_child0 = new YogaNode(); root_child0.PositionType = YogaPositionType.Absolute; - root_child0.SetPosition(YogaEdge.Start, 0); - root_child0.SetPosition(YogaEdge.Top, 0); + root_child0.Start = 0; + root_child0.Top = 0; root.Insert(0, root_child0); YogaNode root_child0_child0 = new YogaNode(); @@ -243,33 +243,33 @@ namespace Facebook.Yoga public void Test_absolute_layout_within_border() { YogaNode root = new YogaNode(); - root.SetMargin(YogaEdge.Left, 10); - root.SetMargin(YogaEdge.Top, 10); - root.SetMargin(YogaEdge.Right, 10); - root.SetMargin(YogaEdge.Bottom, 10); - root.SetPadding(YogaEdge.Left, 10); - root.SetPadding(YogaEdge.Top, 10); - root.SetPadding(YogaEdge.Right, 10); - root.SetPadding(YogaEdge.Bottom, 10); - root.SetBorder(YogaEdge.Left, 10); - root.SetBorder(YogaEdge.Top, 10); - root.SetBorder(YogaEdge.Right, 10); - root.SetBorder(YogaEdge.Bottom, 10); + root.MarginLeft = 10; + root.MarginTop = 10; + root.MarginRight = 10; + root.MarginBottom = 10; + root.PaddingLeft = 10; + root.PaddingTop = 10; + root.PaddingRight = 10; + root.PaddingBottom = 10; + root.BorderLeftWidth = 10; + root.BorderTopWidth = 10; + root.BorderRightWidth = 10; + root.BorderBottomWidth = 10; root.Width = 100; root.Height = 100; YogaNode root_child0 = new YogaNode(); root_child0.PositionType = YogaPositionType.Absolute; - root_child0.SetPosition(YogaEdge.Left, 0); - root_child0.SetPosition(YogaEdge.Top, 0); + root_child0.Left = 0; + root_child0.Top = 0; root_child0.Width = 50; root_child0.Height = 50; root.Insert(0, root_child0); YogaNode root_child1 = new YogaNode(); root_child1.PositionType = YogaPositionType.Absolute; - root_child1.SetPosition(YogaEdge.Right, 0); - root_child1.SetPosition(YogaEdge.Bottom, 0); + root_child1.Right = 0; + root_child1.Bottom = 0; root_child1.Width = 50; root_child1.Height = 50; root.Insert(1, root_child1); diff --git a/csharp/tests/Facebook.Yoga/YGAlignItemsTest.cs b/csharp/tests/Facebook.Yoga/YGAlignItemsTest.cs index 1ef9047d..01ff2c21 100644 --- a/csharp/tests/Facebook.Yoga/YGAlignItemsTest.cs +++ b/csharp/tests/Facebook.Yoga/YGAlignItemsTest.cs @@ -660,7 +660,7 @@ namespace Facebook.Yoga root.Height = 100; YogaNode root_child0 = new YogaNode(); - root_child0.SetPosition(YogaEdge.Top, 10); + root_child0.Top = 10; root_child0.Width = 50; root_child0.Height = 50; root.Insert(0, root_child0); @@ -736,7 +736,7 @@ namespace Facebook.Yoga root.Insert(0, root_child0); YogaNode root_child1 = new YogaNode(); - root_child1.SetPosition(YogaEdge.Top, 5); + root_child1.Top = 5; root_child1.Width = 50; root_child1.Height = 20; root.Insert(1, root_child1); @@ -941,10 +941,10 @@ namespace Facebook.Yoga root.Height = 100; YogaNode root_child0 = new YogaNode(); - root_child0.SetMargin(YogaEdge.Left, 5); - root_child0.SetMargin(YogaEdge.Top, 5); - root_child0.SetMargin(YogaEdge.Right, 5); - root_child0.SetMargin(YogaEdge.Bottom, 5); + root_child0.MarginLeft = 5; + root_child0.MarginTop = 5; + root_child0.MarginRight = 5; + root_child0.MarginBottom = 5; root_child0.Width = 50; root_child0.Height = 50; root.Insert(0, root_child0); @@ -955,10 +955,10 @@ namespace Facebook.Yoga root.Insert(1, root_child1); YogaNode root_child1_child0 = new YogaNode(); - root_child1_child0.SetMargin(YogaEdge.Left, 1); - root_child1_child0.SetMargin(YogaEdge.Top, 1); - root_child1_child0.SetMargin(YogaEdge.Right, 1); - root_child1_child0.SetMargin(YogaEdge.Bottom, 1); + root_child1_child0.MarginLeft = 1; + root_child1_child0.MarginTop = 1; + root_child1_child0.MarginRight = 1; + root_child1_child0.MarginBottom = 1; root_child1_child0.Width = 50; root_child1_child0.Height = 10; root_child1.Insert(0, root_child1_child0); @@ -1015,10 +1015,10 @@ namespace Facebook.Yoga YogaNode root = new YogaNode(); root.FlexDirection = YogaFlexDirection.Row; root.AlignItems = YogaAlign.Baseline; - root.SetPadding(YogaEdge.Left, 5); - root.SetPadding(YogaEdge.Top, 5); - root.SetPadding(YogaEdge.Right, 5); - root.SetPadding(YogaEdge.Bottom, 5); + root.PaddingLeft = 5; + root.PaddingTop = 5; + root.PaddingRight = 5; + root.PaddingBottom = 5; root.Width = 100; root.Height = 100; @@ -1028,10 +1028,10 @@ namespace Facebook.Yoga root.Insert(0, root_child0); YogaNode root_child1 = new YogaNode(); - root_child1.SetPadding(YogaEdge.Left, 5); - root_child1.SetPadding(YogaEdge.Top, 5); - root_child1.SetPadding(YogaEdge.Right, 5); - root_child1.SetPadding(YogaEdge.Bottom, 5); + root_child1.PaddingLeft = 5; + root_child1.PaddingTop = 5; + root_child1.PaddingRight = 5; + root_child1.PaddingBottom = 5; root_child1.Width = 50; root_child1.Height = 20; root.Insert(1, root_child1); diff --git a/csharp/tests/Facebook.Yoga/YGBorderTest.cs b/csharp/tests/Facebook.Yoga/YGBorderTest.cs index 86029fca..d8efce38 100644 --- a/csharp/tests/Facebook.Yoga/YGBorderTest.cs +++ b/csharp/tests/Facebook.Yoga/YGBorderTest.cs @@ -21,10 +21,10 @@ namespace Facebook.Yoga public void Test_border_no_size() { YogaNode root = new YogaNode(); - root.SetBorder(YogaEdge.Left, 10); - root.SetBorder(YogaEdge.Top, 10); - root.SetBorder(YogaEdge.Right, 10); - root.SetBorder(YogaEdge.Bottom, 10); + root.BorderLeftWidth = 10; + root.BorderTopWidth = 10; + root.BorderRightWidth = 10; + root.BorderBottomWidth = 10; root.StyleDirection = YogaDirection.LTR; root.CalculateLayout(); @@ -46,10 +46,10 @@ namespace Facebook.Yoga public void Test_border_container_match_child() { YogaNode root = new YogaNode(); - root.SetBorder(YogaEdge.Left, 10); - root.SetBorder(YogaEdge.Top, 10); - root.SetBorder(YogaEdge.Right, 10); - root.SetBorder(YogaEdge.Bottom, 10); + root.BorderLeftWidth = 10; + root.BorderTopWidth = 10; + root.BorderRightWidth = 10; + root.BorderBottomWidth = 10; YogaNode root_child0 = new YogaNode(); root_child0.Width = 10; @@ -86,10 +86,10 @@ namespace Facebook.Yoga public void Test_border_flex_child() { YogaNode root = new YogaNode(); - root.SetBorder(YogaEdge.Left, 10); - root.SetBorder(YogaEdge.Top, 10); - root.SetBorder(YogaEdge.Right, 10); - root.SetBorder(YogaEdge.Bottom, 10); + root.BorderLeftWidth = 10; + root.BorderTopWidth = 10; + root.BorderRightWidth = 10; + root.BorderBottomWidth = 10; root.Width = 100; root.Height = 100; @@ -128,10 +128,10 @@ namespace Facebook.Yoga public void Test_border_stretch_child() { YogaNode root = new YogaNode(); - root.SetBorder(YogaEdge.Left, 10); - root.SetBorder(YogaEdge.Top, 10); - root.SetBorder(YogaEdge.Right, 10); - root.SetBorder(YogaEdge.Bottom, 10); + root.BorderLeftWidth = 10; + root.BorderTopWidth = 10; + root.BorderRightWidth = 10; + root.BorderBottomWidth = 10; root.Width = 100; root.Height = 100; @@ -171,9 +171,9 @@ namespace Facebook.Yoga YogaNode root = new YogaNode(); root.JustifyContent = YogaJustify.Center; root.AlignItems = YogaAlign.Center; - root.SetBorder(YogaEdge.Start, 10); - root.SetBorder(YogaEdge.End, 20); - root.SetBorder(YogaEdge.Bottom, 20); + root.BorderStartWidth = 10; + root.BorderEndWidth = 20; + root.BorderBottomWidth = 20; root.Width = 100; root.Height = 100; diff --git a/csharp/tests/Facebook.Yoga/YGMarginTest.cs b/csharp/tests/Facebook.Yoga/YGMarginTest.cs index cc017ac4..dcf04ac2 100644 --- a/csharp/tests/Facebook.Yoga/YGMarginTest.cs +++ b/csharp/tests/Facebook.Yoga/YGMarginTest.cs @@ -26,7 +26,7 @@ namespace Facebook.Yoga root.Height = 100; YogaNode root_child0 = new YogaNode(); - root_child0.SetMargin(YogaEdge.Start, 10); + root_child0.MarginStart = 10; root_child0.Width = 10; root.Insert(0, root_child0); root.StyleDirection = YogaDirection.LTR; @@ -64,7 +64,7 @@ namespace Facebook.Yoga root.Height = 100; YogaNode root_child0 = new YogaNode(); - root_child0.SetMargin(YogaEdge.Top, 10); + root_child0.MarginTop = 10; root_child0.Height = 10; root.Insert(0, root_child0); root.StyleDirection = YogaDirection.LTR; @@ -104,7 +104,7 @@ namespace Facebook.Yoga root.Height = 100; YogaNode root_child0 = new YogaNode(); - root_child0.SetMargin(YogaEdge.End, 10); + root_child0.MarginEnd = 10; root_child0.Width = 10; root.Insert(0, root_child0); root.StyleDirection = YogaDirection.LTR; @@ -143,7 +143,7 @@ namespace Facebook.Yoga root.Height = 100; YogaNode root_child0 = new YogaNode(); - root_child0.SetMargin(YogaEdge.Bottom, 10); + root_child0.MarginBottom = 10; root_child0.Height = 10; root.Insert(0, root_child0); root.StyleDirection = YogaDirection.LTR; @@ -183,7 +183,7 @@ namespace Facebook.Yoga YogaNode root_child0 = new YogaNode(); root_child0.FlexGrow = 1; - root_child0.SetMargin(YogaEdge.Start, 10); + root_child0.MarginStart = 10; root.Insert(0, root_child0); root.StyleDirection = YogaDirection.LTR; root.CalculateLayout(); @@ -221,7 +221,7 @@ namespace Facebook.Yoga YogaNode root_child0 = new YogaNode(); root_child0.FlexGrow = 1; - root_child0.SetMargin(YogaEdge.Top, 10); + root_child0.MarginTop = 10; root.Insert(0, root_child0); root.StyleDirection = YogaDirection.LTR; root.CalculateLayout(); @@ -260,7 +260,7 @@ namespace Facebook.Yoga YogaNode root_child0 = new YogaNode(); root_child0.FlexGrow = 1; - root_child0.SetMargin(YogaEdge.Top, 10); + root_child0.MarginTop = 10; root.Insert(0, root_child0); root.StyleDirection = YogaDirection.LTR; root.CalculateLayout(); @@ -298,7 +298,7 @@ namespace Facebook.Yoga YogaNode root_child0 = new YogaNode(); root_child0.FlexGrow = 1; - root_child0.SetMargin(YogaEdge.Start, 10); + root_child0.MarginStart = 10; root.Insert(0, root_child0); root.StyleDirection = YogaDirection.LTR; root.CalculateLayout(); diff --git a/csharp/tests/Facebook.Yoga/YGPaddingTest.cs b/csharp/tests/Facebook.Yoga/YGPaddingTest.cs index b3f17014..bdf93edf 100644 --- a/csharp/tests/Facebook.Yoga/YGPaddingTest.cs +++ b/csharp/tests/Facebook.Yoga/YGPaddingTest.cs @@ -21,10 +21,10 @@ namespace Facebook.Yoga public void Test_padding_no_size() { YogaNode root = new YogaNode(); - root.SetPadding(YogaEdge.Left, 10); - root.SetPadding(YogaEdge.Top, 10); - root.SetPadding(YogaEdge.Right, 10); - root.SetPadding(YogaEdge.Bottom, 10); + root.PaddingLeft = 10; + root.PaddingTop = 10; + root.PaddingRight = 10; + root.PaddingBottom = 10; root.StyleDirection = YogaDirection.LTR; root.CalculateLayout(); @@ -46,10 +46,10 @@ namespace Facebook.Yoga public void Test_padding_container_match_child() { YogaNode root = new YogaNode(); - root.SetPadding(YogaEdge.Left, 10); - root.SetPadding(YogaEdge.Top, 10); - root.SetPadding(YogaEdge.Right, 10); - root.SetPadding(YogaEdge.Bottom, 10); + root.PaddingLeft = 10; + root.PaddingTop = 10; + root.PaddingRight = 10; + root.PaddingBottom = 10; YogaNode root_child0 = new YogaNode(); root_child0.Width = 10; @@ -86,10 +86,10 @@ namespace Facebook.Yoga public void Test_padding_flex_child() { YogaNode root = new YogaNode(); - root.SetPadding(YogaEdge.Left, 10); - root.SetPadding(YogaEdge.Top, 10); - root.SetPadding(YogaEdge.Right, 10); - root.SetPadding(YogaEdge.Bottom, 10); + root.PaddingLeft = 10; + root.PaddingTop = 10; + root.PaddingRight = 10; + root.PaddingBottom = 10; root.Width = 100; root.Height = 100; @@ -128,10 +128,10 @@ namespace Facebook.Yoga public void Test_padding_stretch_child() { YogaNode root = new YogaNode(); - root.SetPadding(YogaEdge.Left, 10); - root.SetPadding(YogaEdge.Top, 10); - root.SetPadding(YogaEdge.Right, 10); - root.SetPadding(YogaEdge.Bottom, 10); + root.PaddingLeft = 10; + root.PaddingTop = 10; + root.PaddingRight = 10; + root.PaddingBottom = 10; root.Width = 100; root.Height = 100; @@ -171,9 +171,9 @@ namespace Facebook.Yoga YogaNode root = new YogaNode(); root.JustifyContent = YogaJustify.Center; root.AlignItems = YogaAlign.Center; - root.SetPadding(YogaEdge.Start, 10); - root.SetPadding(YogaEdge.End, 20); - root.SetPadding(YogaEdge.Bottom, 20); + root.PaddingStart = 10; + root.PaddingEnd = 20; + root.PaddingBottom = 20; root.Width = 100; root.Height = 100; @@ -218,10 +218,10 @@ namespace Facebook.Yoga root.Height = 200; YogaNode root_child0 = new YogaNode(); - root_child0.SetPadding(YogaEdge.Left, 20); - root_child0.SetPadding(YogaEdge.Top, 20); - root_child0.SetPadding(YogaEdge.Right, 20); - root_child0.SetPadding(YogaEdge.Bottom, 20); + root_child0.PaddingLeft = 20; + root_child0.PaddingTop = 20; + root_child0.PaddingRight = 20; + root_child0.PaddingBottom = 20; root_child0.Width = 100; root_child0.Height = 100; root.Insert(0, root_child0); diff --git a/csharp/tests/Facebook.Yoga/YGPercentageTest.cs b/csharp/tests/Facebook.Yoga/YGPercentageTest.cs index 7c9bc423..7371cf43 100644 --- a/csharp/tests/Facebook.Yoga/YGPercentageTest.cs +++ b/csharp/tests/Facebook.Yoga/YGPercentageTest.cs @@ -71,8 +71,8 @@ namespace Facebook.Yoga root.Height = 400; YogaNode root_child0 = new YogaNode(); - root_child0.SetPosition(YogaEdge.Left, 10.Percent()); - root_child0.SetPosition(YogaEdge.Top, 20.Percent()); + root_child0.Left = 10.Percent(); + root_child0.Top = 20.Percent(); root_child0.Width = 45.Percent(); root_child0.Height = 55.Percent(); root.Insert(0, root_child0); @@ -116,8 +116,8 @@ namespace Facebook.Yoga root.Height = 500; YogaNode root_child0 = new YogaNode(); - root_child0.SetPosition(YogaEdge.Right, 20.Percent()); - root_child0.SetPosition(YogaEdge.Bottom, 10.Percent()); + root_child0.Right = 20.Percent(); + root_child0.Bottom = 10.Percent(); root_child0.Width = 55.Percent(); root_child0.Height = 15.Percent(); root.Insert(0, root_child0); @@ -691,38 +691,38 @@ namespace Facebook.Yoga YogaNode root_child0 = new YogaNode(); root_child0.FlexGrow = 1; root_child0.FlexBasis = 10.Percent(); - root_child0.SetMargin(YogaEdge.Left, 5); - root_child0.SetMargin(YogaEdge.Top, 5); - root_child0.SetMargin(YogaEdge.Right, 5); - root_child0.SetMargin(YogaEdge.Bottom, 5); - root_child0.SetPadding(YogaEdge.Left, 3); - root_child0.SetPadding(YogaEdge.Top, 3); - root_child0.SetPadding(YogaEdge.Right, 3); - root_child0.SetPadding(YogaEdge.Bottom, 3); + root_child0.MarginLeft = 5; + root_child0.MarginTop = 5; + root_child0.MarginRight = 5; + root_child0.MarginBottom = 5; + root_child0.PaddingLeft = 3; + root_child0.PaddingTop = 3; + root_child0.PaddingRight = 3; + root_child0.PaddingBottom = 3; root_child0.MinWidth = 60.Percent(); root.Insert(0, root_child0); YogaNode root_child0_child0 = new YogaNode(); - root_child0_child0.SetMargin(YogaEdge.Left, 5); - root_child0_child0.SetMargin(YogaEdge.Top, 5); - root_child0_child0.SetMargin(YogaEdge.Right, 5); - root_child0_child0.SetMargin(YogaEdge.Bottom, 5); - root_child0_child0.SetPadding(YogaEdge.Left, 3.Percent()); - root_child0_child0.SetPadding(YogaEdge.Top, 3.Percent()); - root_child0_child0.SetPadding(YogaEdge.Right, 3.Percent()); - root_child0_child0.SetPadding(YogaEdge.Bottom, 3.Percent()); + root_child0_child0.MarginLeft = 5; + root_child0_child0.MarginTop = 5; + root_child0_child0.MarginRight = 5; + root_child0_child0.MarginBottom = 5; + root_child0_child0.PaddingLeft = 3.Percent(); + root_child0_child0.PaddingTop = 3.Percent(); + root_child0_child0.PaddingRight = 3.Percent(); + root_child0_child0.PaddingBottom = 3.Percent(); root_child0_child0.Width = 50.Percent(); root_child0.Insert(0, root_child0_child0); YogaNode root_child0_child0_child0 = new YogaNode(); - root_child0_child0_child0.SetMargin(YogaEdge.Left, 5.Percent()); - root_child0_child0_child0.SetMargin(YogaEdge.Top, 5.Percent()); - root_child0_child0_child0.SetMargin(YogaEdge.Right, 5.Percent()); - root_child0_child0_child0.SetMargin(YogaEdge.Bottom, 5.Percent()); - root_child0_child0_child0.SetPadding(YogaEdge.Left, 3); - root_child0_child0_child0.SetPadding(YogaEdge.Top, 3); - root_child0_child0_child0.SetPadding(YogaEdge.Right, 3); - root_child0_child0_child0.SetPadding(YogaEdge.Bottom, 3); + root_child0_child0_child0.MarginLeft = 5.Percent(); + root_child0_child0_child0.MarginTop = 5.Percent(); + root_child0_child0_child0.MarginRight = 5.Percent(); + root_child0_child0_child0.MarginBottom = 5.Percent(); + root_child0_child0_child0.PaddingLeft = 3; + root_child0_child0_child0.PaddingTop = 3; + root_child0_child0_child0.PaddingRight = 3; + root_child0_child0_child0.PaddingBottom = 3; root_child0_child0_child0.Width = 45.Percent(); root_child0_child0.Insert(0, root_child0_child0_child0); @@ -801,10 +801,10 @@ namespace Facebook.Yoga YogaNode root_child0 = new YogaNode(); root_child0.FlexGrow = 1; - root_child0.SetMargin(YogaEdge.Left, 10.Percent()); - root_child0.SetMargin(YogaEdge.Top, 10.Percent()); - root_child0.SetMargin(YogaEdge.Right, 10.Percent()); - root_child0.SetMargin(YogaEdge.Bottom, 10.Percent()); + root_child0.MarginLeft = 10.Percent(); + root_child0.MarginTop = 10.Percent(); + root_child0.MarginRight = 10.Percent(); + root_child0.MarginBottom = 10.Percent(); root.Insert(0, root_child0); YogaNode root_child0_child0 = new YogaNode(); @@ -861,10 +861,10 @@ namespace Facebook.Yoga YogaNode root_child0 = new YogaNode(); root_child0.FlexGrow = 1; - root_child0.SetPadding(YogaEdge.Left, 10.Percent()); - root_child0.SetPadding(YogaEdge.Top, 10.Percent()); - root_child0.SetPadding(YogaEdge.Right, 10.Percent()); - root_child0.SetPadding(YogaEdge.Bottom, 10.Percent()); + root_child0.PaddingLeft = 10.Percent(); + root_child0.PaddingTop = 10.Percent(); + root_child0.PaddingRight = 10.Percent(); + root_child0.PaddingBottom = 10.Percent(); root.Insert(0, root_child0); YogaNode root_child0_child0 = new YogaNode(); @@ -921,8 +921,8 @@ namespace Facebook.Yoga YogaNode root_child0 = new YogaNode(); root_child0.PositionType = YogaPositionType.Absolute; - root_child0.SetPosition(YogaEdge.Left, 30.Percent()); - root_child0.SetPosition(YogaEdge.Top, 10.Percent()); + root_child0.Left = 30.Percent(); + root_child0.Top = 10.Percent(); root_child0.Width = 10; root_child0.Height = 10; root.Insert(0, root_child0); diff --git a/csharp/tests/Facebook.Yoga/YGRoundingTest.cs b/csharp/tests/Facebook.Yoga/YGRoundingTest.cs index 2ed2c39d..dc68555a 100644 --- a/csharp/tests/Facebook.Yoga/YGRoundingTest.cs +++ b/csharp/tests/Facebook.Yoga/YGRoundingTest.cs @@ -420,14 +420,14 @@ namespace Facebook.Yoga YogaNode root_child0_child0 = new YogaNode(); root_child0_child0.FlexGrow = 1; root_child0_child0.FlexBasis = 0.3f; - root_child0_child0.SetPosition(YogaEdge.Bottom, 13.3f); + root_child0_child0.Bottom = 13.3f; root_child0_child0.Height = 9.9f; root_child0.Insert(0, root_child0_child0); YogaNode root_child0_child1 = new YogaNode(); root_child0_child1.FlexGrow = 4; root_child0_child1.FlexBasis = 0.3f; - root_child0_child1.SetPosition(YogaEdge.Top, 13.3f); + root_child0_child1.Top = 13.3f; root_child0_child1.Height = 1.1f; root_child0.Insert(1, root_child0_child1); @@ -661,7 +661,7 @@ namespace Facebook.Yoga YogaNode.SetExperimentalFeatureEnabled(YogaExperimentalFeature.Rounding, true); YogaNode root = new YogaNode(); - root.SetPosition(YogaEdge.Top, 0.3f); + root.Top = 0.3f; root.Width = 100; root.Height = 113.4f; @@ -735,7 +735,7 @@ namespace Facebook.Yoga YogaNode.SetExperimentalFeatureEnabled(YogaExperimentalFeature.Rounding, true); YogaNode root = new YogaNode(); - root.SetPosition(YogaEdge.Top, 0.7f); + root.Top = 0.7f; root.Width = 100; root.Height = 113.4f; diff --git a/csharp/tests/Facebook.Yoga/YogaNodeCreateTest.cs b/csharp/tests/Facebook.Yoga/YogaNodeCreateTest.cs deleted file mode 100644 index 74573a36..00000000 --- a/csharp/tests/Facebook.Yoga/YogaNodeCreateTest.cs +++ /dev/null @@ -1,143 +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. - */ - -using NUnit.Framework; -using System; - -/** - * Tests for {@link YogaNode}. - */ -namespace Facebook.Yoga -{ - [TestFixture] - public class YogaNodeCreateTest - { - [Test] - public void TestSimple() - { - YogaNode nodeDefault = new YogaNode(); - YogaNode nodeCreated = YogaNode.Create(flexDirection: YogaFlexDirection.Row); - Assert.AreEqual(YogaFlexDirection.Row, nodeCreated.FlexDirection); - Assert.IsFalse(nodeDefault.IsDirty); - nodeDefault.CopyStyle(nodeCreated); - Assert.IsTrue(nodeDefault.IsDirty); - } - - [Test] - public void TestSame() - { - YogaNode nodeDefault = new YogaNode(); - YogaNode nodeCreated = YogaNode.Create(); - Assert.IsFalse(nodeDefault.IsDirty); - nodeDefault.CopyStyle(nodeCreated); - Assert.IsFalse(nodeDefault.IsDirty); - } - - [Test] - public void TestMultiple() - { - YogaNode node = YogaNode.Create( - positionType: YogaPositionType.Absolute, - wrap: YogaWrap.Wrap, - position: new Spacing(top:6, right:4), - margin: new Spacing(bottom:5, left:3)); - - Assert.AreEqual(YogaFlexDirection.Column, node.FlexDirection); - Assert.AreEqual(YogaPositionType.Absolute, node.PositionType); - Assert.AreEqual(YogaWrap.Wrap, node.Wrap); - Assert.AreEqual(6.Px(), node.GetPosition(YogaEdge.Top)); - Assert.IsTrue(YogaConstants.IsUndefined(node.GetPosition(YogaEdge.Bottom))); - Assert.AreEqual(4.Px(), node.GetPosition(YogaEdge.Right)); - Assert.IsTrue(YogaConstants.IsUndefined(node.GetPosition(YogaEdge.Left))); - Assert.AreEqual(0.Px(), node.GetMargin(YogaEdge.Top)); - Assert.AreEqual(5.Px(), node.GetMargin(YogaEdge.Bottom)); - Assert.AreEqual(3.Px(), node.GetMargin(YogaEdge.Left)); - Assert.AreEqual(0.Px(), node.GetMargin(YogaEdge.Right)); - } - - [Test] - public void TestFull() - { - YogaNode node = YogaNode.Create( - styleDirection: YogaDirection.RTL, - flexDirection: YogaFlexDirection.RowReverse, - - justifyContent: YogaJustify.SpaceAround, - alignContent: YogaAlign.Center, - alignItems: YogaAlign.FlexEnd, - alignSelf: YogaAlign.Stretch, - - positionType: YogaPositionType.Absolute, - wrap: YogaWrap.Wrap, - overflow: YogaOverflow.Scroll, - - flex: 1, - flexGrow: 2, - flexShrink: 3, - flexBasis: 4, - - position: new Spacing(top: 5, bottom: 6, left: 7, right: 8), - margin: new Spacing(top: 9, bottom: 10, left: 11, right: 12), - padding: new Spacing(top: 13, bottom: 14, left: 15, right: 16), - border: new Border(top: 17, bottom: 18, left: 19, right: 20), - - width: 21, - height: 22, - minWidth: 23, - minHeight: 24, - maxWidth: 25, - maxHeight: 26); - - Assert.AreEqual(YogaDirection.RTL, node.StyleDirection); - Assert.AreEqual(YogaFlexDirection.RowReverse, node.FlexDirection); - - Assert.AreEqual(YogaJustify.SpaceAround, node.JustifyContent); - Assert.AreEqual(YogaAlign.Center, node.AlignContent); - Assert.AreEqual(YogaAlign.FlexEnd, node.AlignItems); - Assert.AreEqual(YogaAlign.Stretch, node.AlignSelf); - - Assert.AreEqual(YogaPositionType.Absolute, node.PositionType); - Assert.AreEqual(YogaWrap.Wrap, node.Wrap); - Assert.AreEqual(YogaOverflow.Scroll, node.Overflow); - - Assert.AreEqual(2, node.FlexGrow); - Assert.AreEqual(3, node.FlexShrink); - Assert.AreEqual(4.Px(), node.FlexBasis); - node.FlexGrow = YogaConstants.Undefined; - Assert.AreEqual(1, node.FlexGrow); - - Assert.AreEqual(5.Px(), node.GetPosition(YogaEdge.Top)); - Assert.AreEqual(6.Px(), node.GetPosition(YogaEdge.Bottom)); - Assert.AreEqual(7.Px(), node.GetPosition(YogaEdge.Left)); - Assert.AreEqual(8.Px(), node.GetPosition(YogaEdge.Right)); - - Assert.AreEqual(9.Px(), node.GetMargin(YogaEdge.Top)); - Assert.AreEqual(10.Px(), node.GetMargin(YogaEdge.Bottom)); - Assert.AreEqual(11.Px(), node.GetMargin(YogaEdge.Left)); - Assert.AreEqual(12.Px(), node.GetMargin(YogaEdge.Right)); - - Assert.AreEqual(13.Px(), node.GetPadding(YogaEdge.Top)); - Assert.AreEqual(14.Px(), node.GetPadding(YogaEdge.Bottom)); - Assert.AreEqual(15.Px(), node.GetPadding(YogaEdge.Left)); - Assert.AreEqual(16.Px(), node.GetPadding(YogaEdge.Right)); - - Assert.AreEqual(17, node.GetBorder(YogaEdge.Top)); - Assert.AreEqual(18, node.GetBorder(YogaEdge.Bottom)); - Assert.AreEqual(19, node.GetBorder(YogaEdge.Left)); - Assert.AreEqual(20, node.GetBorder(YogaEdge.Right)); - - Assert.AreEqual(21.Px(), node.Width); - Assert.AreEqual(22.Px(), node.Height); - Assert.AreEqual(23.Px(), node.MinWidth); - Assert.AreEqual(24.Px(), node.MinHeight); - Assert.AreEqual(25.Px(), node.MaxWidth); - Assert.AreEqual(26.Px(), node.MaxHeight); - } - } -} diff --git a/csharp/tests/Facebook.Yoga/YogaNodeSpacingTest.cs b/csharp/tests/Facebook.Yoga/YogaNodeSpacingTest.cs new file mode 100644 index 00000000..d29b19e2 --- /dev/null +++ b/csharp/tests/Facebook.Yoga/YogaNodeSpacingTest.cs @@ -0,0 +1,114 @@ +/** + * 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. + */ + +using NUnit.Framework; +using System; + +/** + * Tests for {@link YogaNode}. + */ +namespace Facebook.Yoga +{ + [TestFixture] + public class YogaNodeSpacingTest + { + [Test] + public void TestObjectInitializer() + { + YogaNode node = new YogaNode + { + Top = 1, + Bottom = 2, + Left = 3, + Right = 4, + + MarginTop = 5, + MarginBottom = 6, + MarginLeft = 7, + MarginRight = 8, + + PaddingTop = 9, + PaddingBottom = 10, + PaddingLeft = 11, + PaddingRight = 12, + + BorderTopWidth = 13, + BorderBottomWidth = 14, + BorderLeftWidth = 15, + BorderRightWidth = 16, + }; + + Assert.AreEqual(1.Px(), node.Top); + Assert.AreEqual(2.Px(), node.Bottom); + Assert.AreEqual(3.Px(), node.Left); + Assert.AreEqual(4.Px(), node.Right); + + Assert.AreEqual(5.Px(), node.MarginTop); + Assert.AreEqual(6.Px(), node.MarginBottom); + Assert.AreEqual(7.Px(), node.MarginLeft); + Assert.AreEqual(8.Px(), node.MarginRight); + + Assert.AreEqual(9.Px(), node.PaddingTop); + Assert.AreEqual(10.Px(), node.PaddingBottom); + Assert.AreEqual(11.Px(), node.PaddingLeft); + Assert.AreEqual(12.Px(), node.PaddingRight); + + Assert.AreEqual(13, node.BorderTopWidth); + Assert.AreEqual(14, node.BorderBottomWidth); + Assert.AreEqual(15, node.BorderLeftWidth); + Assert.AreEqual(16, node.BorderRightWidth); + } + + [Test] + public void TestWriteRead() + { + YogaNode node = new YogaNode(); + + node.Top = 1; + node.Bottom = 2; + node.Left = 3; + node.Right = 4; + + node.MarginTop = 5; + node.MarginBottom = 6; + node.MarginLeft = 7; + node.MarginRight = 8; + + node.PaddingTop = 9; + node.PaddingBottom = 10; + node.PaddingLeft = 11; + node.PaddingRight = 12; + + node.BorderTopWidth = 13; + node.BorderBottomWidth = 14; + node.BorderLeftWidth = 15; + node.BorderRightWidth = 16; + + Assert.AreEqual(1.Px(), node.Top); + Assert.AreEqual(2.Px(), node.Bottom); + Assert.AreEqual(3.Px(), node.Left); + Assert.AreEqual(4.Px(), node.Right); + + Assert.AreEqual(5.Px(), node.MarginTop); + Assert.AreEqual(6.Px(), node.MarginBottom); + Assert.AreEqual(7.Px(), node.MarginLeft); + Assert.AreEqual(8.Px(), node.MarginRight); + + Assert.AreEqual(9.Px(), node.PaddingTop); + Assert.AreEqual(10.Px(), node.PaddingBottom); + Assert.AreEqual(11.Px(), node.PaddingLeft); + Assert.AreEqual(12.Px(), node.PaddingRight); + + Assert.AreEqual(13, node.BorderTopWidth); + Assert.AreEqual(14, node.BorderBottomWidth); + Assert.AreEqual(15, node.BorderLeftWidth); + Assert.AreEqual(16, node.BorderRightWidth); + } + } +} diff --git a/csharp/tests/Facebook.Yoga/YogaNodeTest.cs b/csharp/tests/Facebook.Yoga/YogaNodeTest.cs index a0fc5240..fb8ba9a6 100644 --- a/csharp/tests/Facebook.Yoga/YogaNodeTest.cs +++ b/csharp/tests/Facebook.Yoga/YogaNodeTest.cs @@ -409,16 +409,16 @@ namespace Facebook.Yoga 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.PaddingStart = 1; + node.PaddingEnd = 2; + node.PaddingTop = 3; + node.PaddingBottom = 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)); + Assert.AreEqual(1, node.LayoutPaddingLeft); + Assert.AreEqual(2, node.LayoutPaddingRight); + Assert.AreEqual(3, node.LayoutPaddingTop); + Assert.AreEqual(4, node.LayoutPaddingBottom); } } } diff --git a/gentest/gentest-cs.js b/gentest/gentest-cs.js index 22ffa846..08b08588 100644 --- a/gentest/gentest-cs.js +++ b/gentest/gentest-cs.js @@ -102,12 +102,12 @@ CSEmitter.prototype = Object.create(Emitter.prototype, { YGDirectionLTR:{value:'YogaDirection.LTR'}, YGDirectionRTL:{value:'YogaDirection.RTL'}, - YGEdgeBottom:{value:'YogaEdge.Bottom'}, - YGEdgeEnd:{value:'YogaEdge.End'}, - YGEdgeLeft:{value:'YogaEdge.Left'}, - YGEdgeRight:{value:'YogaEdge.Right'}, - YGEdgeStart:{value:'YogaEdge.Start'}, - YGEdgeTop:{value:'YogaEdge.Top'}, + YGEdgeBottom:{value:'Bottom'}, + YGEdgeEnd:{value:'End'}, + YGEdgeLeft:{value:'Left'}, + YGEdgeRight:{value:'Right'}, + YGEdgeStart:{value:'Start'}, + YGEdgeTop:{value:'Top'}, YGFlexDirectionColumn:{value:'YogaFlexDirection.Column'}, YGFlexDirectionColumnReverse:{value:'YogaFlexDirection.ColumnReverse'}, @@ -169,7 +169,7 @@ CSEmitter.prototype = Object.create(Emitter.prototype, { }}, YGNodeStyleSetBorder:{value:function(nodeName, edge, value) { - this.push(nodeName + '.SetBorder(' + edge + ', ' + toValueCs(value) + ');'); + this.push(nodeName + '.Border' + edge + 'Width = ' + toValueCs(value) + ';'); }}, YGNodeStyleSetDirection:{value:function(nodeName, value) { @@ -205,7 +205,7 @@ CSEmitter.prototype = Object.create(Emitter.prototype, { }}, YGNodeStyleSetMargin:{value:function(nodeName, edge, value) { - this.push(nodeName + '.SetMargin(' + edge + ', ' + toCsUnitValue(value) + ');'); + this.push(nodeName + '.Margin' + edge + ' = ' + toCsUnitValue(value) + ';'); }}, YGNodeStyleSetMaxHeight:{value:function(nodeName, value) { @@ -229,11 +229,11 @@ CSEmitter.prototype = Object.create(Emitter.prototype, { }}, YGNodeStyleSetPadding:{value:function(nodeName, edge, value) { - this.push(nodeName + '.SetPadding(' + edge + ', ' + toCsUnitValue(value) + ');'); + this.push(nodeName + '.Padding' + edge + ' = ' + toCsUnitValue(value) + ';'); }}, YGNodeStyleSetPosition:{value:function(nodeName, edge, value) { - this.push(nodeName + '.SetPosition(' + edge + ', ' + toCsUnitValue(value) + ');'); + this.push(nodeName + '.' + edge + ' = ' + toCsUnitValue(value) + ';'); }}, YGNodeStyleSetPositionType:{value:function(nodeName, value) { From 9982295e10c5bf5241696340421896d3ee4f5923 Mon Sep 17 00:00:00 2001 From: David Hart Date: Sun, 8 Jan 2017 11:42:43 -0800 Subject: [PATCH 5/7] Improved the objective-c and swift api Summary: Compared to what was planned, I added the `overflow` value which seemed missing. I had to modify the implementation a bit for all values which are backed by a `YGValue`, but we should probably enable the pixel dimensions in Objective-C and Swift somehow later. Closes https://github.com/facebook/yoga/pull/322 Reviewed By: cosmin1123 Differential Revision: D4391434 Pulled By: emilsjolander fbshipit-source-id: e33f6f7b2bbaad29553100b7a5bb424496372110 --- YogaKit/Tests/YogaKitTests.m | 214 +++++----- YogaKit/UIView+Yoga.h | 70 +-- YogaKit/UIView+Yoga.m | 399 +----------------- YogaKit/YGLayout+Private.h | 16 + YogaKit/YGLayout.h | 114 +++++ YogaKit/YGLayout.m | 380 +++++++++++++++++ .../YogaKitSample.xcodeproj/project.pbxproj | 31 +- .../YogaKitSample/SwiftViewController.swift | 40 ++ .../YogaKitSample/ViewController.m | 18 +- .../YogaKitSample-Bridging-Header.h | 10 + enums.py | 8 +- yoga/YGEnums.h | 56 +-- yoga/YGMacros.h | 16 + 13 files changed, 765 insertions(+), 607 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 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..70e91917 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; - -/** - 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; +@property (nonatomic, readonly, strong) YGLayout *yoga; @end diff --git a/YogaKit/UIView+Yoga.m b/YogaKit/UIView+Yoga.m index cfe0afa0..66b7eed0 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 + +@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..d6a2baf0 --- /dev/null +++ b/YogaKit/YGLayout.m @@ -0,0 +1,380 @@ +/** + * 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 + +#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 (self = [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 *const 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 53b2ab53..2f388ccc 100644 --- a/enums.py +++ b/enums.py @@ -119,21 +119,21 @@ 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') 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('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('} 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 24606bcd..0a10bbc2 100644 --- a/yoga/YGEnums.h +++ b/yoga/YGEnums.h @@ -14,29 +14,29 @@ YG_EXTERN_C_BEGIN #define YGFlexDirectionCount 4 -typedef enum YGFlexDirection { +typedef YG_ENUM_BEGIN(YGFlexDirection) { YGFlexDirectionColumn, YGFlexDirectionColumnReverse, YGFlexDirectionRow, YGFlexDirectionRowReverse, -} YGFlexDirection; +} YG_ENUM_END(YGFlexDirection); #define YGMeasureModeCount 3 -typedef enum YGMeasureMode { +typedef YG_ENUM_BEGIN(YGMeasureMode) { YGMeasureModeUndefined, YGMeasureModeExactly, YGMeasureModeAtMost, -} YGMeasureMode; +} YG_ENUM_END(YGMeasureMode); #define YGPrintOptionsCount 3 -typedef enum YGPrintOptions { +typedef YG_ENUM_BEGIN(YGPrintOptions) { YGPrintOptionsLayout = 1, YGPrintOptionsStyle = 2, YGPrintOptionsChildren = 4, -} YGPrintOptions; +} YG_ENUM_END(YGPrintOptions); #define YGEdgeCount 9 -typedef enum YGEdge { +typedef YG_ENUM_BEGIN(YGEdge) { YGEdgeLeft, YGEdgeTop, YGEdgeRight, @@ -46,79 +46,79 @@ typedef enum YGEdge { YGEdgeHorizontal, YGEdgeVertical, YGEdgeAll, -} YGEdge; +} YG_ENUM_END(YGEdge); #define YGPositionTypeCount 2 -typedef enum YGPositionType { +typedef YG_ENUM_BEGIN(YGPositionType) { YGPositionTypeRelative, YGPositionTypeAbsolute, -} YGPositionType; +} YG_ENUM_END(YGPositionType); #define YGDimensionCount 2 -typedef enum YGDimension { +typedef YG_ENUM_BEGIN(YGDimension) { YGDimensionWidth, YGDimensionHeight, -} YGDimension; +} YG_ENUM_END(YGDimension); #define YGJustifyCount 5 -typedef enum YGJustify { +typedef YG_ENUM_BEGIN(YGJustify) { YGJustifyFlexStart, YGJustifyCenter, YGJustifyFlexEnd, YGJustifySpaceBetween, YGJustifySpaceAround, -} YGJustify; +} YG_ENUM_END(YGJustify); #define YGDirectionCount 3 -typedef enum YGDirection { +typedef YG_ENUM_BEGIN(YGDirection) { YGDirectionInherit, YGDirectionLTR, YGDirectionRTL, -} YGDirection; +} YG_ENUM_END(YGDirection); #define YGLogLevelCount 5 -typedef enum YGLogLevel { +typedef YG_ENUM_BEGIN(YGLogLevel) { YGLogLevelError, YGLogLevelWarn, YGLogLevelInfo, YGLogLevelDebug, YGLogLevelVerbose, -} YGLogLevel; +} YG_ENUM_END(YGLogLevel); #define YGWrapCount 2 -typedef enum YGWrap { +typedef YG_ENUM_BEGIN(YGWrap) { YGWrapNoWrap, YGWrapWrap, -} YGWrap; +} YG_ENUM_END(YGWrap); #define YGOverflowCount 3 -typedef enum YGOverflow { +typedef YG_ENUM_BEGIN(YGOverflow) { YGOverflowVisible, YGOverflowHidden, YGOverflowScroll, -} YGOverflow; +} YG_ENUM_END(YGOverflow); #define YGExperimentalFeatureCount 2 -typedef enum YGExperimentalFeature { +typedef YG_ENUM_BEGIN(YGExperimentalFeature) { YGExperimentalFeatureRounding, YGExperimentalFeatureWebFlexBasis, -} YGExperimentalFeature; +} YG_ENUM_END(YGExperimentalFeature); #define YGAlignCount 6 -typedef enum YGAlign { +typedef YG_ENUM_BEGIN(YGAlign) { YGAlignAuto, YGAlignFlexStart, YGAlignCenter, YGAlignFlexEnd, YGAlignStretch, YGAlignBaseline, -} YGAlign; +} YG_ENUM_END(YGAlign); #define YGUnitCount 3 -typedef enum YGUnit { +typedef YG_ENUM_BEGIN(YGUnit) { YGUnitUndefined, YGUnitPixel, YGUnitPercent, -} YGUnit; +} YG_ENUM_END(YGUnit); YG_EXTERN_C_END diff --git a/yoga/YGMacros.h b/yoga/YGMacros.h index def8371a..d6724272 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 From 0acb6201594739d069d8973ee8391dea9bfaf9c7 Mon Sep 17 00:00:00 2001 From: Emil Sjolander Date: Mon, 9 Jan 2017 08:34:56 -0800 Subject: [PATCH 6/7] Use int instead of NSInteger for ABI compatibility Summary: @public Cannot use NSInteger as NSInteger has a different size than int (which is the default type of a enum). Therefor when linking the Yoga C library into obj-c the header is a missmatch for the Yoga ABI. Reviewed By: cwdick Differential Revision: D4392272 fbshipit-source-id: 22b92ac8f3eb7114e81dbd9b0bec9044c3d43da5 --- yoga/YGMacros.h | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/yoga/YGMacros.h b/yoga/YGMacros.h index d6724272..39e21b78 100644 --- a/yoga/YGMacros.h +++ b/yoga/YGMacros.h @@ -41,18 +41,12 @@ } #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 +#ifdef NS_ENUM +// Cannot use NSInteger as NSInteger has a different size than int (which is the default type of a enum). +// Therefor when linking the Yoga C library into obj-c the header is a missmatch for the Yoga ABI. +#define YG_ENUM_BEGIN(name) NS_ENUM(int, name) #define YG_ENUM_END(name) -#endif +#else +#define YG_ENUM_BEGIN(name) enum name +#define YG_ENUM_END(name) name #endif From 78e64e3d5ba8744a6cb1cf7ac5691ad445855295 Mon Sep 17 00:00:00 2001 From: Kazuki Sakamoto Date: Mon, 9 Jan 2017 11:09:47 -0800 Subject: [PATCH 7/7] Add Copy Constructor for C# YogaNode Summary: Add Copy Constructor for convenience. YogaNode defaultStyle = new YogaNode { Width = 100, Height = 100, }; YogaNode node0 = new YogaNode(defaultStyle) { FlexDirection = YogaFlexDirection.Row, JustifyContent = YogaJustify.FlexEnd, }; YogaNode node1 = new YogaNode(defaultStyle) { AlignItems = YogaAlign.Center, }; Reviewed By: emilsjolander Differential Revision: D4393234 fbshipit-source-id: b4b73071aab4dc1b3ae422969de659380bd0e3ad --- csharp/Facebook.Yoga/YogaNode.cs | 6 +++++ csharp/tests/Facebook.Yoga/YogaNodeTest.cs | 30 ++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/csharp/Facebook.Yoga/YogaNode.cs b/csharp/Facebook.Yoga/YogaNode.cs index c7408a95..112d2b3f 100644 --- a/csharp/Facebook.Yoga/YogaNode.cs +++ b/csharp/Facebook.Yoga/YogaNode.cs @@ -36,6 +36,12 @@ namespace Facebook.Yoga } } + public YogaNode(YogaNode srcNode) + : this() + { + CopyStyle(srcNode); + } + public void Reset() { _measureFunction = null; diff --git a/csharp/tests/Facebook.Yoga/YogaNodeTest.cs b/csharp/tests/Facebook.Yoga/YogaNodeTest.cs index fb8ba9a6..69aa0bf3 100644 --- a/csharp/tests/Facebook.Yoga/YogaNodeTest.cs +++ b/csharp/tests/Facebook.Yoga/YogaNodeTest.cs @@ -276,6 +276,36 @@ namespace Facebook.Yoga Assert.AreEqual(100.Px(), node0.MaxHeight); } + [Test] + public void TestCopyConstructor() + { + YogaNode node0 = new YogaNode(); + node0.MaxWidth = 80; + + YogaNode node1 = new YogaNode(node0); + Assert.AreEqual(80.Px(), node1.MaxWidth); + + YogaNode node2 = new YogaNode(node1) + { + MaxHeight = 90, + }; + Assert.AreEqual(80.Px(), node2.MaxWidth); + Assert.AreEqual(90.Px(), node2.MaxHeight); + + YogaNode node3 = new YogaNode(node0) + { + MaxWidth = 100, + }; + Assert.AreEqual(100.Px(), node3.MaxWidth); + + YogaNode node4 = new YogaNode(node2) + { + MaxWidth = 100, + }; + Assert.AreEqual(100.Px(), node4.MaxWidth); + Assert.AreEqual(90.Px(), node4.MaxHeight); + } + private void ForceGC() { GC.Collect(GC.MaxGeneration);