You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@weex.apache.org by ji...@apache.org on 2017/01/24 08:18:30 UTC
[15/50] [abbrv] incubator-weex git commit: + [ios] iOS init.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/185fe55c/ios/sdk/WeexSDK/Sources/Events/WXComponent+Events.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Events/WXComponent+Events.m b/ios/sdk/WeexSDK/Sources/Events/WXComponent+Events.m
new file mode 100644
index 0000000..105eccc
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Events/WXComponent+Events.m
@@ -0,0 +1,621 @@
+/**
+ * Created by Weex.
+ * Copyright (c) 2016, Alibaba, Inc. All rights reserved.
+ *
+ * This source code is licensed under the Apache Licence 2.0.
+ * For the full copyright and license information,please view the LICENSE file in the root directory of this source tree.
+ */
+
+#import "WXComponent+Events.h"
+#import "WXComponent.h"
+#import "WXComponent_internal.h"
+#import "WXSDKInstance.h"
+#import "WXAssert.h"
+#import "WXUtility.h"
+#import "WXSDKManager.h"
+#import <objc/runtime.h>
+#import <UIKit/UIGestureRecognizerSubclass.h>
+
+#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
+
+@interface UITouch (WXTouchGestureRecognizer)
+
+@property (nonatomic, strong) NSNumber *wx_identifier;
+
+@end
+
+@implementation UITouch (WXTouchGestureRecognizer)
+
+- (NSNumber *)wx_identifier
+{
+ return objc_getAssociatedObject(self, _cmd);
+}
+
+- (void)setWx_identifier:(NSNumber *)wx_identifier
+{
+ objc_setAssociatedObject(self, @selector(wx_identifier), wx_identifier, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+}
+
+@end
+
+@interface UIGestureRecognizer (WXGesture)
+
+@property (nonatomic, strong) NSNumber *wx_identifier;
+
+@end
+
+@implementation UIGestureRecognizer (WXGesture)
+
+- (NSNumber *)wx_identifier
+{
+ NSNumber *identifier = objc_getAssociatedObject(self, _cmd);
+ if (!identifier) {
+ static NSUInteger _gestureIdentifier;
+ identifier = @(_gestureIdentifier++);
+ self.wx_identifier = identifier;
+ }
+
+ return identifier;
+}
+
+- (void)setWx_identifier:(NSNumber *)wx_identifier
+{
+ objc_setAssociatedObject(self, @selector(wx_identifier), wx_identifier, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+}
+
+@end
+
+@interface WXTouchGestureRecognizer : UIGestureRecognizer
+
+@property (nonatomic, assign) BOOL listenTouchStart;
+@property (nonatomic, assign) BOOL listenTouchMove;
+@property (nonatomic, assign) BOOL listenTouchEnd;
+@property (nonatomic, assign) BOOL listenTouchCancel;
+
+- (instancetype)initWithComponent:(WXComponent *)component NS_DESIGNATED_INITIALIZER;
+
+@end
+
+@implementation WXComponent (Events)
+
+#pragma mark Public
+
+- (void)fireEvent:(NSString *)eventName params:(NSDictionary *)params
+{
+ NSMutableDictionary *dict = [NSMutableDictionary dictionary];
+ NSTimeInterval timeSp = [[NSDate date] timeIntervalSince1970] * 1000;
+ [dict setObject:@(timeSp) forKey:@"timestamp"];
+ if (params) {
+ [dict addEntriesFromDictionary:params];
+ }
+
+ [[WXSDKManager bridgeMgr] fireEvent:self.weexInstance.instanceId ref:self.ref type:eventName params:dict];
+}
+
+- (void)addEvent:(NSString *)addEventName
+{
+ WXAssertMainThread();
+}
+
+- (void)removeEvent:(NSString *)removeEventName
+{
+ WXAssertMainThread();
+}
+
+#pragma mark Add & Remove Event
+
+
+#define WX_ADD_EVENT(eventName, addSelector) \
+if ([addEventName isEqualToString:@#eventName]) {\
+ [self addSelector];\
+}
+
+#define WX_REMOVE_EVENT(eventName, removeSelector) \
+if ([removeEventName isEqualToString:@#eventName]) {\
+ [self removeSelector];\
+}
+
+- (void)_initEvents:(NSArray *)events
+{
+ for (NSString *addEventName in events) {
+ [self _addEventOnMainThread:addEventName];
+ }
+}
+
+- (void)_addEventOnMainThread:(NSString *)addEventName
+{
+ WX_ADD_EVENT(appear, addAppearEvent)
+ WX_ADD_EVENT(disappear, addDisappearEvent)
+
+ WX_ADD_EVENT(click, addClickEvent)
+ WX_ADD_EVENT(swipe, addSwipeEvent)
+ WX_ADD_EVENT(longpress, addLongPressEvent)
+
+ WX_ADD_EVENT(panstart, addPanStartEvent)
+ WX_ADD_EVENT(panmove, addPanMoveEvent)
+ WX_ADD_EVENT(panend, addPanEndEvent)
+
+ WX_ADD_EVENT(touchstart, addTouchStartEvent)
+ WX_ADD_EVENT(touchmove, addTouchMoveEvent)
+ WX_ADD_EVENT(touchend, addTouchEndEvent)
+ WX_ADD_EVENT(touchcancel, addTouchCancelEvent)
+
+ [self addEvent:addEventName];
+}
+
+- (void)_removeEventOnMainThread:(NSString *)removeEventName
+{
+ WX_REMOVE_EVENT(appear, removeAppearEvent)
+ WX_REMOVE_EVENT(disappear, removeDisappearEvent)
+
+ WX_REMOVE_EVENT(click, removeClickEvent)
+ WX_REMOVE_EVENT(swipe, removeSwipeEvent)
+ WX_REMOVE_EVENT(longpress, removeLongPressEvent)
+
+ WX_REMOVE_EVENT(panstart, removePanStartEvent)
+ WX_REMOVE_EVENT(panmove, removePanMoveEvent)
+ WX_REMOVE_EVENT(panend, removePanEndEvent)
+
+ WX_REMOVE_EVENT(touchstart, removeTouchStartEvent)
+ WX_REMOVE_EVENT(touchmove, removeTouchMoveEvent)
+ WX_REMOVE_EVENT(touchend, removeTouchEndEvent)
+ WX_REMOVE_EVENT(touchcancel, removeTouchCancelEvent)
+
+ [self removeEvent:removeEventName];
+}
+
+- (void)_removeAllEvents
+{
+ [self removeClickEvent];
+ [self removeLongPressEvent];
+ [self removePanStartEvent];
+ [self removePanMoveEvent];
+ [self removePanEndEvent];
+ [self removeTouchStartEvent];
+ [self removeTouchMoveEvent];
+ [self removeTouchEndEvent];
+ [self removeTouchCancelEvent];
+ [self removeSwipeEvent];
+}
+
+#pragma mark - Appear & Disappear
+
+- (void)addAppearEvent
+{
+ _appearEvent = YES;
+ [self.ancestorScroller addScrollToListener:self];
+}
+
+- (void)addDisappearEvent
+{
+ _disappearEvent = YES;
+ [self.ancestorScroller addScrollToListener:self];
+}
+
+- (void)removeAppearEvent
+{
+ _appearEvent = NO;
+ [self.ancestorScroller removeScrollToListener:self];
+}
+
+- (void)removeDisappearEvent
+{
+ _disappearEvent = NO;
+ [self.ancestorScroller removeScrollToListener:self];
+}
+
+#pragma mark - Click Event
+
+- (void)addClickEvent
+{
+ if (!_tapGesture) {
+ _tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onClick:)];
+ _tapGesture.delegate = self;
+ [self.view addGestureRecognizer:_tapGesture];
+ }
+}
+
+- (void)removeClickEvent
+{
+ if (_tapGesture) {
+ [self.view removeGestureRecognizer:_tapGesture];
+ _tapGesture.delegate = nil;
+ _tapGesture = nil;
+ }
+}
+
+- (void)onClick:(__unused UITapGestureRecognizer *)recognizer
+{
+ [self fireEvent:@"click" params:nil];
+}
+
+#pragma mark - Swipe event
+
+- (void)addSwipeEvent
+{
+ if (_swipeGestures) {
+ return;
+ }
+
+ _swipeGestures = [NSMutableArray arrayWithCapacity:4];
+
+ // It's a little weird because the UISwipeGestureRecognizer.direction property is an options-style bit mask, but each recognizer can only handle one direction
+ SEL selector = @selector(onSwipe:);
+ UISwipeGestureRecognizer *upSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self
+ action:selector];
+ upSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionUp;
+ upSwipeRecognizer.delegate = self;
+ [_swipeGestures addObject:upSwipeRecognizer];
+ [self.view addGestureRecognizer:upSwipeRecognizer];
+
+ UISwipeGestureRecognizer *downSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self
+ action:selector];
+ downSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionDown;
+ downSwipeRecognizer.delegate = self;
+ [_swipeGestures addObject:downSwipeRecognizer];
+ [self.view addGestureRecognizer:downSwipeRecognizer];
+
+ UISwipeGestureRecognizer *rightSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self
+ action:selector];
+ rightSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
+ rightSwipeRecognizer.delegate = self;
+ [_swipeGestures addObject:rightSwipeRecognizer];
+ [self.view addGestureRecognizer:rightSwipeRecognizer];
+
+ UISwipeGestureRecognizer *leftSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self
+ action:selector];
+ leftSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
+ leftSwipeRecognizer.delegate = self;
+ [_swipeGestures addObject:leftSwipeRecognizer];
+ [self.view addGestureRecognizer:leftSwipeRecognizer];
+}
+
+- (void)removeSwipeEvent
+{
+ if (_swipeGestures == nil) {
+ return;
+ }
+
+ for (UISwipeGestureRecognizer *recognizer in _swipeGestures) {
+ recognizer.delegate = nil;
+ [self.view removeGestureRecognizer:recognizer];
+ }
+
+ _swipeGestures = nil;
+}
+
+- (void)onSwipe:(UISwipeGestureRecognizer *)gesture
+{
+ UISwipeGestureRecognizerDirection direction = gesture.direction;
+
+ NSString *directionString;
+ switch(direction) {
+ case UISwipeGestureRecognizerDirectionLeft:
+ directionString = @"left";
+ break;
+ case UISwipeGestureRecognizerDirectionRight:
+ directionString = @"right";
+ break;
+ case UISwipeGestureRecognizerDirectionUp:
+ directionString = @"up";
+ break;
+ case UISwipeGestureRecognizerDirectionDown:
+ directionString = @"down";
+ break;
+ default:
+ directionString = @"unknown";
+ }
+
+ CGPoint screenLocation = [gesture locationInView:self.view.window];
+ CGPoint pageLoacation = [gesture locationInView:self.weexInstance.rootView];
+ NSDictionary *resultTouch = [self touchResultWithScreenLocation:screenLocation pageLocation:pageLoacation identifier:gesture.wx_identifier];
+ [self fireEvent:@"swipe" params:@{@"direction":directionString, @"changedTouches":resultTouch ? @[resultTouch] : @[]}];
+}
+
+#pragma mark - Long Press
+
+- (void)addLongPressEvent
+{
+ if (!_longPressGesture) {
+ _longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(onLongPress:)];
+ _longPressGesture.delegate = self;
+ [self.view addGestureRecognizer:_longPressGesture];
+ }
+}
+
+- (void)removeLongPressEvent
+{
+ if (_longPressGesture) {
+ [self.view removeGestureRecognizer:_longPressGesture];
+ _longPressGesture.delegate = nil;
+ _longPressGesture = nil;
+ }
+}
+
+- (void)onLongPress:(UILongPressGestureRecognizer *)gesture
+{
+ if (gesture.state == UIGestureRecognizerStateBegan) {
+ CGPoint screenLocation = [gesture locationInView:self.view.window];
+ CGPoint pageLoacation = [gesture locationInView:self.weexInstance.rootView];
+ NSDictionary *resultTouch = [self touchResultWithScreenLocation:screenLocation pageLocation:pageLoacation identifier:gesture.wx_identifier];
+ [self fireEvent:@"longpress" params:@{@"changedTouches":resultTouch ? @[resultTouch] : @[]}];
+ } else if (gesture.state == UIGestureRecognizerStateEnded) {
+ gesture.wx_identifier = nil;
+ }
+}
+
+#pragma makr - Pan
+
+- (void)addPanGesture
+{
+ if (!_panGesture) {
+ _panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(onPan:)];
+ _panGesture.delegate = self;
+ [self.view addGestureRecognizer:_panGesture];
+ }
+}
+
+- (void)addPanStartEvent
+{
+ _listenPanStart = YES;
+ [self addPanGesture];
+}
+
+- (void)addPanMoveEvent
+{
+ _listenPanMove = YES;
+ [self addPanGesture];
+}
+
+- (void)addPanEndEvent
+{
+ _listenPanEnd = YES;
+ [self addPanGesture];
+}
+
+- (void)onPan:(UIPanGestureRecognizer *)gesture
+{
+ CGPoint screenLocation = [gesture locationInView:self.view.window];
+ CGPoint pageLoacation = [gesture locationInView:self.weexInstance.rootView];
+ NSString *eventName;
+ NSDictionary *resultTouch = [self touchResultWithScreenLocation:screenLocation pageLocation:pageLoacation identifier:gesture.wx_identifier];
+
+ if (gesture.state == UIGestureRecognizerStateBegan) {
+ eventName = @"panstart";
+ } else if (gesture.state == UIGestureRecognizerStateEnded) {
+ eventName = @"panend";
+ gesture.wx_identifier = nil;
+ } else if (gesture.state == UIGestureRecognizerStateChanged) {
+ eventName = @"panmove";
+ }
+
+ if (eventName) {
+ [self fireEvent:eventName params:@{@"changedTouches":resultTouch ? @[resultTouch] : @[]}];
+ }
+}
+
+- (void)removePanStartEvent
+{
+ _listenPanStart = NO;
+ [self checkRemovePanGesture];
+}
+
+- (void)removePanMoveEvent
+{
+ _listenPanMove = NO;
+ [self checkRemovePanGesture];
+}
+
+- (void)removePanEndEvent
+{
+ _listenPanEnd = NO;
+ [self checkRemovePanGesture];
+}
+
+- (void)checkRemovePanGesture
+{
+ if (_panGesture && !_listenPanStart && !_listenPanMove && !_listenPanEnd) {
+ [self.view removeGestureRecognizer:_panGesture];
+ _panGesture.delegate = nil;
+ _panGesture = nil;
+ }
+}
+
+#pragma mark - Touch Event
+
+- (WXTouchGestureRecognizer *)touchGesture
+{
+ if (!_touchGesture) {
+ _touchGesture = [[WXTouchGestureRecognizer alloc] initWithComponent:self];
+ _touchGesture.delegate = self;
+ [self.view addGestureRecognizer:_touchGesture];
+ }
+
+ return _touchGesture;
+}
+
+- (void)addTouchStartEvent
+{
+ self.touchGesture.listenTouchStart = YES;
+}
+
+- (void)addTouchMoveEvent
+{
+ self.touchGesture.listenTouchMove = YES;
+}
+
+- (void)addTouchEndEvent
+{
+ self.touchGesture.listenTouchEnd = YES;
+}
+
+- (void)addTouchCancelEvent
+{
+ self.touchGesture.listenTouchCancel = YES;
+}
+
+- (void)removeTouchStartEvent
+{
+ _touchGesture.listenTouchStart = NO;
+ [self checkRemoveTouchGesture];
+}
+
+- (void)removeTouchMoveEvent
+{
+ _touchGesture.listenTouchMove = NO;
+ [self checkRemoveTouchGesture];
+}
+
+- (void)removeTouchEndEvent
+{
+ _touchGesture.listenTouchEnd = NO;
+ [self checkRemoveTouchGesture];
+}
+
+- (void)removeTouchCancelEvent
+{
+ _touchGesture.listenTouchCancel = NO;
+ [self checkRemoveTouchGesture];
+}
+
+- (void)checkRemoveTouchGesture
+{
+ if (_touchGesture && !_touchGesture.listenTouchStart && !_touchGesture.listenTouchMove && !_touchGesture.listenTouchEnd && !_touchGesture.listenTouchCancel) {
+ [self.view removeGestureRecognizer:_touchGesture];
+ _touchGesture.delegate = nil;
+ _touchGesture = nil;
+ }
+}
+
+#pragma mark - UIGestureRecognizerDelegate
+
+- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
+{
+ return YES;
+}
+
+- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
+{
+ return YES;
+}
+
+- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
+{
+ // trigger touches
+ if ([gestureRecognizer isKindOfClass:[WXTouchGestureRecognizer class]]) {
+ return YES;
+ }
+
+//#ifdef DEBUG
+// if ([gestureRecognizer isKindOfClass:[WXDebugLongPressGestureRecognizer class]]
+// || [otherGestureRecognizer isKindOfClass:[WXDebugLongPressGestureRecognizer class]]) {
+// return YES;
+// }
+//#endif
+
+ return NO;
+}
+
+#pragma mark - Utils
+
+- (NSDictionary *)touchResultWithScreenLocation:(CGPoint)screenLocation pageLocation:(CGPoint)pageLocation identifier:(NSNumber *)identifier
+{
+ NSMutableDictionary *resultTouch = [[NSMutableDictionary alloc] initWithCapacity:5];
+ resultTouch[@"screenX"] = @(screenLocation.x/WXScreenResizeRadio());
+ resultTouch[@"screenY"] = @(screenLocation.y/WXScreenResizeRadio());
+ resultTouch[@"pageX"] = @(pageLocation.x/WXScreenResizeRadio());
+ resultTouch[@"pageY"] = @(pageLocation.y/WXScreenResizeRadio());
+ resultTouch[@"identifier"] = identifier;
+
+ return resultTouch;
+}
+
+@end
+
+@implementation WXTouchGestureRecognizer
+{
+ __weak WXComponent *_component;
+ NSUInteger _touchIdentifier;
+}
+
+- (instancetype)initWithTarget:(id)target action:(SEL)action
+{
+ return [self initWithComponent:nil];;
+}
+
+- (instancetype)initWithComponent:(WXComponent *)component
+{
+ if (self = [super initWithTarget:self action:@selector(touchResponse:)]) {
+ _component = component;
+
+ _listenTouchStart = NO;
+ _listenTouchEnd = NO;
+ _listenTouchMove = NO;
+ _listenTouchCancel = NO;
+
+ self.cancelsTouchesInView = NO;
+ }
+
+ return self;
+}
+
+- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
+{
+ [super touchesBegan:touches withEvent:event];
+
+ if (_listenTouchStart) {
+ [self fireTouchEvent:@"touchstart" withTouches:touches];
+ }
+}
+
+- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
+{
+ [super touchesMoved:touches withEvent:event];
+
+ if (_listenTouchMove) {
+ [self fireTouchEvent:@"touchmove" withTouches:touches];
+ }
+}
+
+- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
+{
+
+ [super touchesEnded:touches withEvent:event];
+
+ if (_listenTouchEnd) {
+ [self fireTouchEvent:@"touchend" withTouches:touches];
+ }
+}
+
+- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
+{
+ [super touchesCancelled:touches withEvent:event];
+
+ if (_listenTouchCancel) {
+ [self fireTouchEvent:@"touchcancel" withTouches:touches];
+ }
+}
+
+- (void)fireTouchEvent:(NSString *)eventName withTouches:(NSSet<UITouch *> *)touches
+{
+ NSMutableArray *resultTouches = [NSMutableArray new];
+
+ for (UITouch *touch in touches) {
+ CGPoint screenLocation = [touch locationInView:touch.window];
+ CGPoint pageLocation = [touch locationInView:_component.weexInstance.rootView];
+ if (!touch.wx_identifier) {
+ touch.wx_identifier = @(_touchIdentifier++);
+ }
+ NSDictionary *resultTouch = [_component touchResultWithScreenLocation:screenLocation pageLocation:pageLocation identifier:touch.wx_identifier];
+ [resultTouches addObject:resultTouch];
+ }
+
+ [_component fireEvent:eventName params:@{@"changedTouches":resultTouches ?: @[]}];
+}
+
+- (void)touchResponse:(UIGestureRecognizer *)gesture
+{
+
+}
+
+@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/185fe55c/ios/sdk/WeexSDK/Sources/Handler/WXNavigationDefaultImpl.h
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Handler/WXNavigationDefaultImpl.h b/ios/sdk/WeexSDK/Sources/Handler/WXNavigationDefaultImpl.h
new file mode 100644
index 0000000..8fb1370
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Handler/WXNavigationDefaultImpl.h
@@ -0,0 +1,14 @@
+/**
+ * Created by Weex.
+ * Copyright (c) 2016, Alibaba, Inc. All rights reserved.
+ *
+ * This source code is licensed under the Apache Licence 2.0.
+ * For the full copyright and license information,please view the LICENSE file in the root directory of this source tree.
+ */
+
+#import <Foundation/Foundation.h>
+#import "WXNavigationProtocol.h"
+
+@interface WXNavigationDefaultImpl : NSObject <WXNavigationProtocol>
+
+@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/185fe55c/ios/sdk/WeexSDK/Sources/Handler/WXNavigationDefaultImpl.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Handler/WXNavigationDefaultImpl.m b/ios/sdk/WeexSDK/Sources/Handler/WXNavigationDefaultImpl.m
new file mode 100644
index 0000000..47d647d
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Handler/WXNavigationDefaultImpl.m
@@ -0,0 +1,353 @@
+/**
+ * Created by Weex.
+ * Copyright (c) 2016, Alibaba, Inc. All rights reserved.
+ *
+ * This source code is licensed under the Apache Licence 2.0.
+ * For the full copyright and license information,please view the LICENSE file in the root directory of this source tree.
+ */
+
+#import "WXNavigationDefaultImpl.h"
+#import "WXBaseViewController.h"
+#import "WXSDKManager.h"
+#import "WXComponent.h"
+#import "WXImgLoaderProtocol.h"
+#import "WXHandlerFactory.h"
+#import "WXConvert.h"
+
+@interface WXBarButton :UIButton
+
+@property (nonatomic, strong) NSString *instanceId;
+@property (nonatomic, strong) NSString *nodeRef;
+@property (nonatomic) WXNavigationItemPosition position;
+
+@end
+
+@implementation WXBarButton
+
+@end
+
+@implementation WXNavigationDefaultImpl
+
+#pragma mark -
+#pragma mark WXNavigationProtocol
+
+- (id<WXImgLoaderProtocol>)imageLoader
+{
+ static id<WXImgLoaderProtocol> imageLoader;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ imageLoader = [WXHandlerFactory handlerForProtocol:@protocol(WXImgLoaderProtocol)];
+ });
+ return imageLoader;
+}
+
+- (id)navigationControllerOfContainer:(UIViewController *)container{
+ return container.navigationController;
+}
+
+- (void)pushViewControllerWithParam:(NSDictionary *)param completion:(WXNavigationResultBlock)block
+ withContainer:(UIViewController *)container
+{
+ if (0 == [param count] || !param[@"url"] || !container) {
+ [self callback:block code:MSG_PARAM_ERR data:nil];
+ return;
+ }
+
+ BOOL animated = YES;
+ NSString *obj = [[param objectForKey:@"animated"] uppercaseString];
+ if (obj && [obj isEqualToString:@"false"]) {
+ animated = NO;
+ }
+
+ WXBaseViewController *vc = [[WXBaseViewController alloc]initWithSourceURL:[NSURL URLWithString:param[@"url"]]];
+ vc.hidesBottomBarWhenPushed = YES;
+ [container.navigationController pushViewController:vc animated:animated];
+ [self callback:block code:MSG_SUCCESS data:nil];
+}
+
+- (void)popViewControllerWithParam:(NSDictionary *)param completion:(WXNavigationResultBlock)block
+ withContainer:(UIViewController *)container
+{
+ BOOL animated = YES;
+ NSString *obj = [[param objectForKey:@"animated"] uppercaseString];
+ if (obj && [obj isEqualToString:@"false"]) {
+ animated = NO;
+ }
+
+ [container.navigationController popViewControllerAnimated:animated];
+ [self callback:block code:MSG_SUCCESS data:nil];
+}
+
+- (void)popToRootViewControllerWithParam:(NSDictionary *)param
+ completion:(WXNavigationResultBlock)block
+ withContainer:(UIViewController *)container
+{
+ BOOL animated = YES;
+ NSString *obj = [[param objectForKey:@"animated"] uppercaseString];
+ if (obj && [obj isEqualToString:@"false"]) {
+ animated = NO;
+ }
+
+ [container.navigationController popToRootViewControllerAnimated:animated];
+ [self callback:block code:MSG_SUCCESS data:nil];
+}
+
+- (void)setNavigationBarHidden:(BOOL)hidden animated:(BOOL)animated
+ withContainer:(UIViewController *)container
+{
+ //If weex launchs in the WXBaseViewController, the navigationBar is hidden according to our design.
+ if (![container isKindOfClass:[WXBaseViewController class]]) {
+ return;
+ }
+
+ container.navigationController.navigationBarHidden = hidden;
+}
+
+- (void)setNavigationBackgroundColor:(UIColor *)backgroundColor
+ withContainer:(UIViewController *)container
+{
+ if (backgroundColor) {
+ container.navigationController.navigationBar.barTintColor = backgroundColor;
+ }
+}
+
+- (void)setNavigationItemWithParam:(NSDictionary *)param
+ position:(WXNavigationItemPosition)position
+ completion:(WXNavigationResultBlock)block
+ withContainer:(UIViewController *)container
+{
+ switch (position) {
+ case WXNavigationItemPositionLeft:
+ [self setNaviBarLeftItem:param completion:block withContainer:container];
+ break;
+ case WXNavigationItemPositionRight:
+ [self setNaviBarRightItem:param completion:block withContainer:container];
+ break;
+ case WXNavigationItemPositionMore:
+ break;
+ case WXNavigationItemPositionCenter:
+ [self setNaviBarTitle:param completion:block withContainer:container];
+ break;
+ default:
+ break;
+ }
+}
+
+- (void)clearNavigationItemWithParam:(NSDictionary *)param
+ position:(WXNavigationItemPosition)position
+ completion:(WXNavigationResultBlock)block
+ withContainer:(UIViewController *)container
+{
+ switch (position) {
+ case WXNavigationItemPositionLeft:
+ [self clearNaviBarLeftItem:param completion:block withContainer:container];
+ break;
+ case WXNavigationItemPositionRight:
+ [self clearNaviBarRightItem:param completion:block withContainer:container];
+ break;
+ case WXNavigationItemPositionMore:
+ break;
+ case WXNavigationItemPositionCenter:
+ [self clearNaviBarTitle:param completion:block withContainer:container];
+ break;
+ default:
+ break;
+ }
+}
+
+- (void)setNaviBarLeftItem:(NSDictionary *)param completion:(WXNavigationResultBlock)block
+ withContainer:(UIViewController *)container
+{
+ if (0 == [param count]) {
+ [self callback:block code:MSG_PARAM_ERR data:nil];
+ return;
+ }
+
+ UIView *view = [self barButton:param position:WXNavigationItemPositionLeft withContainer:container];
+
+ if (!view) {
+ [self callback:block code:MSG_FAILED data:nil];
+ return;
+ }
+
+ container.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:view];
+
+ [self callback:block code:MSG_SUCCESS data:nil];
+}
+
+- (void)clearNaviBarLeftItem:(NSDictionary *) param completion:(WXNavigationResultBlock)block
+ withContainer:(UIViewController *)container
+{
+ container.navigationItem.leftBarButtonItem = nil;
+
+ [self callback:block code:MSG_SUCCESS data:nil];
+}
+
+- (void)setNaviBarRightItem:(NSDictionary *)param completion:(WXNavigationResultBlock)block
+ withContainer:(UIViewController *)container
+{
+ if (0 == [param count]) {
+ [self callback:block code:MSG_PARAM_ERR data:nil];
+ return;
+ }
+
+ UIView *view = [self barButton:param position:WXNavigationItemPositionRight withContainer:container];
+
+ if (!view) {
+ [self callback:block code:MSG_FAILED data:nil];
+ return;
+ }
+
+ container.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:view];
+
+ [self callback:block code:MSG_SUCCESS data:nil];
+}
+
+- (void)clearNaviBarRightItem:(NSDictionary *) param completion:(WXNavigationResultBlock)block
+ withContainer:(UIViewController *)container
+{
+ container.navigationItem.rightBarButtonItem = nil;
+
+ [self callback:block code:MSG_SUCCESS data:nil];
+}
+
+- (void)setNaviBarTitle:(NSDictionary *)param completion:(WXNavigationResultBlock)block
+ withContainer:(UIViewController *)container
+{
+ if (0 == [param count]) {
+ [self callback:block code:MSG_PARAM_ERR data:nil];
+ return;
+ }
+
+ container.navigationItem.title = param[@"title"];
+
+ [self callback:block code:MSG_SUCCESS data:nil];;
+}
+
+- (void)clearNaviBarTitle:(NSDictionary *)param completion:(WXNavigationResultBlock)block
+ withContainer:(UIViewController *)container
+{
+ container.navigationItem.title = @"";
+
+ [self callback:block code:MSG_SUCCESS data:nil];
+}
+
+- (UIView *)barButton:(NSDictionary *)param position:(WXNavigationItemPosition)position
+ withContainer:(UIViewController *)container
+{
+ if (param[@"title"]) {
+ NSString *title = param[@"title"];
+
+ NSDictionary *attribute = @{NSFontAttributeName: [UIFont systemFontOfSize:18]};
+ CGSize size = [title boundingRectWithSize:CGSizeMake(70.0f, 18.0f) options: NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:attribute context:nil].size;
+
+ WXBarButton *button = [WXBarButton buttonWithType:UIButtonTypeRoundedRect];
+ button.frame = CGRectMake(0, 0, size.width, size.height);
+ button.titleLabel.font = [UIFont systemFontOfSize:16];
+ [button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
+ [button setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted];
+ [button setTitle:title forState:UIControlStateNormal];
+ [button setTitle:title forState:UIControlStateHighlighted];
+ [button addTarget:self action:@selector(onClickBarButton:) forControlEvents:UIControlEventTouchUpInside];
+
+ button.instanceId = param[@"instanceId"];
+ button.nodeRef = param[@"nodeRef"];
+ button.position = position;
+
+ NSString *color = param[@"titleColor"];
+ if (color) {
+ [button setTitleColor:[WXConvert UIColor:color] forState:UIControlStateNormal];
+ [button setTitleColor:[WXConvert UIColor:color] forState:UIControlStateHighlighted];
+ }
+
+ return button;
+ }
+ else if (param[@"icon"]) {
+ NSString *icon = param[@"icon"];
+
+ if (icon) {
+ WXBarButton *button = [WXBarButton buttonWithType:UIButtonTypeRoundedRect];
+ button.frame = CGRectMake(0, 0, 25, 25);
+ button.instanceId = param[@"instanceId"];
+ button.nodeRef = param[@"nodeRef"];
+ button.position = position;
+ [button addTarget:self action:@selector(onClickBarButton:) forControlEvents:UIControlEventTouchUpInside];
+
+ [[self imageLoader] downloadImageWithURL:icon imageFrame:CGRectMake(0, 0, 25, 25) userInfo:nil completed:^(UIImage *image, NSError *error, BOOL finished) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [button setBackgroundImage:image forState:UIControlStateNormal];
+ [button setBackgroundImage:image forState:UIControlStateHighlighted];
+ });
+ }];
+
+ return button;
+ }
+ }
+
+ return nil;
+}
+
+- (UIView *)titleView:(NSDictionary *)param
+{
+ UIView *titleView = nil;
+
+ if (param[@"title"]) {
+ UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 200.0f, 20.0f)];
+ label.backgroundColor = [UIColor clearColor];
+ label.textAlignment = NSTextAlignmentCenter;
+ label.lineBreakMode = NSLineBreakByTruncatingTail;
+ label.textColor = [UIColor whiteColor];
+ label.font = [UIFont systemFontOfSize:18.0f];
+ label.text = param[@"title"];
+
+ UIColor *titleColor = param[@"titleColor"];
+ if (titleColor) {
+ label.textColor = [WXConvert UIColor:titleColor];
+ }
+ else {
+ label.textColor = [UIColor blueColor];
+ }
+ titleView = label;
+ }
+
+ return titleView;
+}
+
+- (void)onClickBarButton:(id)sender
+{
+ WXBarButton *button = (WXBarButton *)sender;
+ if (button.instanceId) {
+ if (button.nodeRef)
+ {
+ [[WXSDKManager bridgeMgr] fireEvent:button.instanceId ref:button.nodeRef type:@"click" params:nil];
+ }
+ else
+ {
+ NSString *eventType;
+ switch (button.position) {
+ case WXNavigationItemPositionLeft:
+ eventType = @"clickleftitem";
+ break;
+ case WXNavigationItemPositionRight:
+ eventType = @"clickrightitem";
+ break;
+ case WXNavigationItemPositionMore:
+ eventType = @"clickmoreitem";
+ break;
+ default:
+ break;
+ }
+
+ [[WXSDKManager bridgeMgr] fireEvent:button.instanceId ref:WX_SDK_ROOT_REF type:eventType params:nil];
+ }
+ }
+}
+
+- (void)callback:(WXNavigationResultBlock)block code:(NSString *)code data:(NSDictionary *)reposonData
+{
+ if (block) {
+ block(code, reposonData);
+ }
+}
+
+@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/185fe55c/ios/sdk/WeexSDK/Sources/Handler/WXNetworkDefaultImpl.h
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Handler/WXNetworkDefaultImpl.h b/ios/sdk/WeexSDK/Sources/Handler/WXNetworkDefaultImpl.h
new file mode 100644
index 0000000..3578dd5
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Handler/WXNetworkDefaultImpl.h
@@ -0,0 +1,14 @@
+/**
+ * Created by Weex.
+ * Copyright (c) 2016, Alibaba, Inc. All rights reserved.
+ *
+ * This source code is licensed under the Apache Licence 2.0.
+ * For the full copyright and license information,please view the LICENSE file in the root directory of this source tree.
+ */
+
+#import <Foundation/Foundation.h>
+#import "WXNetworkProtocol.h"
+
+@interface WXNetworkDefaultImpl : NSObject <WXNetworkProtocol, NSURLSessionDelegate>
+
+@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/185fe55c/ios/sdk/WeexSDK/Sources/Handler/WXNetworkDefaultImpl.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Handler/WXNetworkDefaultImpl.m b/ios/sdk/WeexSDK/Sources/Handler/WXNetworkDefaultImpl.m
new file mode 100644
index 0000000..72a0ed3
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Handler/WXNetworkDefaultImpl.m
@@ -0,0 +1,105 @@
+/**
+ * Created by Weex.
+ * Copyright (c) 2016, Alibaba, Inc. All rights reserved.
+ *
+ * This source code is licensed under the Apache Licence 2.0.
+ * For the full copyright and license information,please view the LICENSE file in the root directory of this source tree.
+ */
+
+#import "WXNetworkDefaultImpl.h"
+
+@interface WXNetworkCallbackInfo : NSObject
+
+@property (nonatomic, copy) void(^sendDataCallback)(int64_t, int64_t);
+@property (nonatomic, copy) void(^responseCallback)(NSURLResponse *);
+@property (nonatomic, copy) void(^receiveDataCallback)(NSData *);
+@property (nonatomic, strong) NSMutableData *data;
+@property (nonatomic, copy) void(^compeletionCallback)(NSData *, NSError *);
+
+@end
+
+@implementation WXNetworkCallbackInfo
+
+@end
+
+@implementation WXNetworkDefaultImpl
+{
+ NSMutableDictionary *_callbacks;
+ NSURLSession *_session;
+}
+
+- (id)sendRequest:(NSURLRequest *)request withSendingData:(void (^)(int64_t, int64_t))sendDataCallback
+ withResponse:(void (^)(NSURLResponse *))responseCallback
+ withReceiveData:(void (^)(NSData *))receiveDataCallback
+ withCompeletion:(void (^)(NSData *, NSError *))compeletionCallback
+{
+ WXNetworkCallbackInfo *info = [WXNetworkCallbackInfo new];
+ info.sendDataCallback = sendDataCallback;
+ info.responseCallback = responseCallback;
+ info.receiveDataCallback = receiveDataCallback;
+ info.compeletionCallback = compeletionCallback;
+
+ if (!_session) {
+ _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
+ delegate:self
+ delegateQueue:[NSOperationQueue mainQueue]];
+ }
+
+ NSURLSessionDataTask *task = [_session dataTaskWithRequest:request];
+ if (!_callbacks) {
+ _callbacks = [NSMutableDictionary dictionary];
+ }
+ [_callbacks setObject:info forKey:task];
+ [task resume];
+
+ return task;
+}
+
+- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
+ didSendBodyData:(int64_t)bytesSent
+ totalBytesSent:(int64_t)totalBytesSent
+ totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
+{
+ WXNetworkCallbackInfo *info = [_callbacks objectForKey:task];
+ if (info.sendDataCallback) {
+ info.sendDataCallback(totalBytesSent, totalBytesExpectedToSend);
+ }
+}
+
+- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)task
+ didReceiveResponse:(NSURLResponse *)response
+ completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
+{
+ WXNetworkCallbackInfo *info = [_callbacks objectForKey:task];
+ if (info.responseCallback) {
+ info.responseCallback(response);
+ }
+ completionHandler(NSURLSessionResponseAllow);
+}
+
+- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)task didReceiveData:(NSData *)data
+{
+ WXNetworkCallbackInfo *info = [_callbacks objectForKey:task];
+ if (info.receiveDataCallback) {
+ info.receiveDataCallback(data);
+ }
+
+ NSMutableData *mutableData = info.data;
+ if (!mutableData) {
+ mutableData = [NSMutableData new];
+ info.data = mutableData;
+ }
+
+ [mutableData appendData:data];
+}
+
+- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
+{
+ WXNetworkCallbackInfo *info = [_callbacks objectForKey:task];
+ if (info.compeletionCallback) {
+ info.compeletionCallback(info.data, error);
+ }
+ [_callbacks removeObjectForKey:task];
+}
+
+@end
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/185fe55c/ios/sdk/WeexSDK/Sources/Layout/Layout.c
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Layout/Layout.c b/ios/sdk/WeexSDK/Sources/Layout/Layout.c
new file mode 100644
index 0000000..44d4d60
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Layout/Layout.c
@@ -0,0 +1,1324 @@
+
+/**
+ * Copyright (c) 2014, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ */
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// in concatenated header, don't include Layout.h it's already at the top
+#ifndef CSS_LAYOUT_IMPLEMENTATION
+#include "Layout.h"
+#endif
+
+#ifdef _MSC_VER
+#include <float.h>
+#define isnan _isnan
+
+/* define fmaxf if < VC12 */
+#if _MSC_VER < 1800
+__forceinline const float fmaxf(const float a, const float b) {
+ return (a > b) ? a : b;
+}
+#endif
+#endif
+
+bool isUndefined(float value) {
+ return isnan(value);
+}
+
+static bool eq(float a, float b) {
+ if (isUndefined(a)) {
+ return isUndefined(b);
+ }
+ return fabs(a - b) < 0.0001;
+}
+
+void init_css_node(css_node_t *node) {
+ node->style.align_items = CSS_ALIGN_STRETCH;
+ node->style.align_content = CSS_ALIGN_FLEX_START;
+
+ node->style.direction = CSS_DIRECTION_INHERIT;
+ node->style.flex_direction = CSS_FLEX_DIRECTION_COLUMN;
+
+ // Some of the fields default to undefined and not 0
+ node->style.dimensions[CSS_WIDTH] = CSS_UNDEFINED;
+ node->style.dimensions[CSS_HEIGHT] = CSS_UNDEFINED;
+
+ node->style.minDimensions[CSS_WIDTH] = CSS_UNDEFINED;
+ node->style.minDimensions[CSS_HEIGHT] = CSS_UNDEFINED;
+
+ node->style.maxDimensions[CSS_WIDTH] = CSS_UNDEFINED;
+ node->style.maxDimensions[CSS_HEIGHT] = CSS_UNDEFINED;
+
+ node->style.position[CSS_LEFT] = CSS_UNDEFINED;
+ node->style.position[CSS_TOP] = CSS_UNDEFINED;
+ node->style.position[CSS_RIGHT] = CSS_UNDEFINED;
+ node->style.position[CSS_BOTTOM] = CSS_UNDEFINED;
+
+ node->style.margin[CSS_START] = CSS_UNDEFINED;
+ node->style.margin[CSS_END] = CSS_UNDEFINED;
+ node->style.padding[CSS_START] = CSS_UNDEFINED;
+ node->style.padding[CSS_END] = CSS_UNDEFINED;
+ node->style.border[CSS_START] = CSS_UNDEFINED;
+ node->style.border[CSS_END] = CSS_UNDEFINED;
+
+ node->layout.dimensions[CSS_WIDTH] = CSS_UNDEFINED;
+ node->layout.dimensions[CSS_HEIGHT] = CSS_UNDEFINED;
+
+ // Such that the comparison is always going to be false
+ node->layout.last_requested_dimensions[CSS_WIDTH] = -1;
+ node->layout.last_requested_dimensions[CSS_HEIGHT] = -1;
+ node->layout.last_parent_max_width = -1;
+ node->layout.last_parent_max_height = -1;
+ node->layout.last_direction = (css_direction_t)-1;
+ node->layout.should_update = true;
+}
+
+css_node_t *new_css_node() {
+ css_node_t *node = (css_node_t *)calloc(1, sizeof(*node));
+ init_css_node(node);
+ return node;
+}
+
+void free_css_node(css_node_t *node) {
+ free(node);
+}
+
+static void indent(int n) {
+ for (int i = 0; i < n; ++i) {
+ printf(" ");
+ }
+}
+
+static void print_number_0(const char *str, float number) {
+ if (!eq(number, 0)) {
+ printf("%s: %g, ", str, number);
+ }
+}
+
+static void print_number_nan(const char *str, float number) {
+ if (!isnan(number)) {
+ printf("%s: %g, ", str, number);
+ }
+}
+
+static bool four_equal(float four[4]) {
+ return
+ eq(four[0], four[1]) &&
+ eq(four[0], four[2]) &&
+ eq(four[0], four[3]);
+}
+
+
+static void print_css_node_rec(
+ css_node_t *node,
+ css_print_options_t options,
+ int level
+) {
+ indent(level);
+ printf("{");
+
+ if (node->print) {
+ node->print(node->context);
+ }
+
+ if (options & CSS_PRINT_LAYOUT) {
+ printf("layout: {");
+ printf("width: %g, ", node->layout.dimensions[CSS_WIDTH]);
+ printf("height: %g, ", node->layout.dimensions[CSS_HEIGHT]);
+ printf("top: %g, ", node->layout.position[CSS_TOP]);
+ printf("left: %g", node->layout.position[CSS_LEFT]);
+ printf("}, ");
+ }
+
+ if (options & CSS_PRINT_STYLE) {
+ if (node->style.flex_direction == CSS_FLEX_DIRECTION_COLUMN) {
+ printf("flexDirection: 'column', ");
+ } else if (node->style.flex_direction == CSS_FLEX_DIRECTION_COLUMN_REVERSE) {
+ printf("flexDirection: 'columnReverse', ");
+ } else if (node->style.flex_direction == CSS_FLEX_DIRECTION_ROW) {
+ printf("flexDirection: 'row', ");
+ } else if (node->style.flex_direction == CSS_FLEX_DIRECTION_ROW_REVERSE) {
+ printf("flexDirection: 'rowReverse', ");
+ }
+
+ if (node->style.justify_content == CSS_JUSTIFY_CENTER) {
+ printf("justifyContent: 'center', ");
+ } else if (node->style.justify_content == CSS_JUSTIFY_FLEX_END) {
+ printf("justifyContent: 'flex-end', ");
+ } else if (node->style.justify_content == CSS_JUSTIFY_SPACE_AROUND) {
+ printf("justifyContent: 'space-around', ");
+ } else if (node->style.justify_content == CSS_JUSTIFY_SPACE_BETWEEN) {
+ printf("justifyContent: 'space-between', ");
+ }
+
+ if (node->style.align_items == CSS_ALIGN_CENTER) {
+ printf("alignItems: 'center', ");
+ } else if (node->style.align_items == CSS_ALIGN_FLEX_END) {
+ printf("alignItems: 'flex-end', ");
+ } else if (node->style.align_items == CSS_ALIGN_STRETCH) {
+ printf("alignItems: 'stretch', ");
+ }
+
+ if (node->style.align_content == CSS_ALIGN_CENTER) {
+ printf("alignContent: 'center', ");
+ } else if (node->style.align_content == CSS_ALIGN_FLEX_END) {
+ printf("alignContent: 'flex-end', ");
+ } else if (node->style.align_content == CSS_ALIGN_STRETCH) {
+ printf("alignContent: 'stretch', ");
+ }
+
+ if (node->style.align_self == CSS_ALIGN_FLEX_START) {
+ printf("alignSelf: 'flex-start', ");
+ } else if (node->style.align_self == CSS_ALIGN_CENTER) {
+ printf("alignSelf: 'center', ");
+ } else if (node->style.align_self == CSS_ALIGN_FLEX_END) {
+ printf("alignSelf: 'flex-end', ");
+ } else if (node->style.align_self == CSS_ALIGN_STRETCH) {
+ printf("alignSelf: 'stretch', ");
+ }
+
+ print_number_nan("flex", node->style.flex);
+
+ if (four_equal(node->style.margin)) {
+ print_number_0("margin", node->style.margin[CSS_LEFT]);
+ } else {
+ print_number_0("marginLeft", node->style.margin[CSS_LEFT]);
+ print_number_0("marginRight", node->style.margin[CSS_RIGHT]);
+ print_number_0("marginTop", node->style.margin[CSS_TOP]);
+ print_number_0("marginBottom", node->style.margin[CSS_BOTTOM]);
+ print_number_0("marginStart", node->style.margin[CSS_START]);
+ print_number_0("marginEnd", node->style.margin[CSS_END]);
+ }
+
+ if (four_equal(node->style.padding)) {
+ print_number_0("padding", node->style.margin[CSS_LEFT]);
+ } else {
+ print_number_0("paddingLeft", node->style.padding[CSS_LEFT]);
+ print_number_0("paddingRight", node->style.padding[CSS_RIGHT]);
+ print_number_0("paddingTop", node->style.padding[CSS_TOP]);
+ print_number_0("paddingBottom", node->style.padding[CSS_BOTTOM]);
+ print_number_0("paddingStart", node->style.padding[CSS_START]);
+ print_number_0("paddingEnd", node->style.padding[CSS_END]);
+ }
+
+ if (four_equal(node->style.border)) {
+ print_number_0("borderWidth", node->style.border[CSS_LEFT]);
+ } else {
+ print_number_0("borderLeftWidth", node->style.border[CSS_LEFT]);
+ print_number_0("borderRightWidth", node->style.border[CSS_RIGHT]);
+ print_number_0("borderTopWidth", node->style.border[CSS_TOP]);
+ print_number_0("borderBottomWidth", node->style.border[CSS_BOTTOM]);
+ print_number_0("borderStartWidth", node->style.border[CSS_START]);
+ print_number_0("borderEndWidth", node->style.border[CSS_END]);
+ }
+
+ print_number_nan("width", node->style.dimensions[CSS_WIDTH]);
+ print_number_nan("height", node->style.dimensions[CSS_HEIGHT]);
+
+ if (node->style.position_type == CSS_POSITION_ABSOLUTE) {
+ printf("position: 'absolute', ");
+ }
+
+ print_number_nan("left", node->style.position[CSS_LEFT]);
+ print_number_nan("right", node->style.position[CSS_RIGHT]);
+ print_number_nan("top", node->style.position[CSS_TOP]);
+ print_number_nan("bottom", node->style.position[CSS_BOTTOM]);
+ }
+
+ if (options & CSS_PRINT_CHILDREN && node->children_count > 0) {
+ printf("children: [\n");
+ for (int i = 0; i < node->children_count; ++i) {
+ print_css_node_rec(node->get_child(node->context, i), options, level + 1);
+ }
+ indent(level);
+ printf("]},\n");
+ } else {
+ printf("},\n");
+ }
+}
+
+void print_css_node(css_node_t *node, css_print_options_t options) {
+ print_css_node_rec(node, options, 0);
+}
+
+
+static css_position_t leading[4] = {
+ /* CSS_FLEX_DIRECTION_COLUMN = */ CSS_TOP,
+ /* CSS_FLEX_DIRECTION_COLUMN_REVERSE = */ CSS_BOTTOM,
+ /* CSS_FLEX_DIRECTION_ROW = */ CSS_LEFT,
+ /* CSS_FLEX_DIRECTION_ROW_REVERSE = */ CSS_RIGHT
+};
+static css_position_t trailing[4] = {
+ /* CSS_FLEX_DIRECTION_COLUMN = */ CSS_BOTTOM,
+ /* CSS_FLEX_DIRECTION_COLUMN_REVERSE = */ CSS_TOP,
+ /* CSS_FLEX_DIRECTION_ROW = */ CSS_RIGHT,
+ /* CSS_FLEX_DIRECTION_ROW_REVERSE = */ CSS_LEFT
+};
+static css_position_t pos[4] = {
+ /* CSS_FLEX_DIRECTION_COLUMN = */ CSS_TOP,
+ /* CSS_FLEX_DIRECTION_COLUMN_REVERSE = */ CSS_BOTTOM,
+ /* CSS_FLEX_DIRECTION_ROW = */ CSS_LEFT,
+ /* CSS_FLEX_DIRECTION_ROW_REVERSE = */ CSS_RIGHT
+};
+static css_dimension_t dim[4] = {
+ /* CSS_FLEX_DIRECTION_COLUMN = */ CSS_HEIGHT,
+ /* CSS_FLEX_DIRECTION_COLUMN_REVERSE = */ CSS_HEIGHT,
+ /* CSS_FLEX_DIRECTION_ROW = */ CSS_WIDTH,
+ /* CSS_FLEX_DIRECTION_ROW_REVERSE = */ CSS_WIDTH
+};
+
+static bool isRowDirection(css_flex_direction_t flex_direction) {
+ return flex_direction == CSS_FLEX_DIRECTION_ROW ||
+ flex_direction == CSS_FLEX_DIRECTION_ROW_REVERSE;
+}
+
+static bool isColumnDirection(css_flex_direction_t flex_direction) {
+ return flex_direction == CSS_FLEX_DIRECTION_COLUMN ||
+ flex_direction == CSS_FLEX_DIRECTION_COLUMN_REVERSE;
+}
+
+static float getLeadingMargin(css_node_t *node, css_flex_direction_t axis) {
+ if (isRowDirection(axis) && !isUndefined(node->style.margin[CSS_START])) {
+ return node->style.margin[CSS_START];
+ }
+
+ return node->style.margin[leading[axis]];
+}
+
+static float getTrailingMargin(css_node_t *node, css_flex_direction_t axis) {
+ if (isRowDirection(axis) && !isUndefined(node->style.margin[CSS_END])) {
+ return node->style.margin[CSS_END];
+ }
+
+ return node->style.margin[trailing[axis]];
+}
+
+static float getLeadingPadding(css_node_t *node, css_flex_direction_t axis) {
+ if (isRowDirection(axis) &&
+ !isUndefined(node->style.padding[CSS_START]) &&
+ node->style.padding[CSS_START] >= 0) {
+ return node->style.padding[CSS_START];
+ }
+
+ if (node->style.padding[leading[axis]] >= 0) {
+ return node->style.padding[leading[axis]];
+ }
+
+ return 0;
+}
+
+static float getTrailingPadding(css_node_t *node, css_flex_direction_t axis) {
+ if (isRowDirection(axis) &&
+ !isUndefined(node->style.padding[CSS_END]) &&
+ node->style.padding[CSS_END] >= 0) {
+ return node->style.padding[CSS_END];
+ }
+
+ if (node->style.padding[trailing[axis]] >= 0) {
+ return node->style.padding[trailing[axis]];
+ }
+
+ return 0;
+}
+
+static float getLeadingBorder(css_node_t *node, css_flex_direction_t axis) {
+ if (isRowDirection(axis) &&
+ !isUndefined(node->style.border[CSS_START]) &&
+ node->style.border[CSS_START] >= 0) {
+ return node->style.border[CSS_START];
+ }
+
+ if (node->style.border[leading[axis]] >= 0) {
+ return node->style.border[leading[axis]];
+ }
+
+ return 0;
+}
+
+static float getTrailingBorder(css_node_t *node, css_flex_direction_t axis) {
+ if (isRowDirection(axis) &&
+ !isUndefined(node->style.border[CSS_END]) &&
+ node->style.border[CSS_END] >= 0) {
+ return node->style.border[CSS_END];
+ }
+
+ if (node->style.border[trailing[axis]] >= 0) {
+ return node->style.border[trailing[axis]];
+ }
+
+ return 0;
+}
+
+static float getLeadingPaddingAndBorder(css_node_t *node, css_flex_direction_t axis) {
+ return getLeadingPadding(node, axis) + getLeadingBorder(node, axis);
+}
+
+static float getTrailingPaddingAndBorder(css_node_t *node, css_flex_direction_t axis) {
+ return getTrailingPadding(node, axis) + getTrailingBorder(node, axis);
+}
+
+static float getBorderAxis(css_node_t *node, css_flex_direction_t axis) {
+ return getLeadingBorder(node, axis) + getTrailingBorder(node, axis);
+}
+
+static float getMarginAxis(css_node_t *node, css_flex_direction_t axis) {
+ return getLeadingMargin(node, axis) + getTrailingMargin(node, axis);
+}
+
+static float getPaddingAndBorderAxis(css_node_t *node, css_flex_direction_t axis) {
+ return getLeadingPaddingAndBorder(node, axis) + getTrailingPaddingAndBorder(node, axis);
+}
+
+static css_align_t getAlignItem(css_node_t *node, css_node_t *child) {
+ if (child->style.align_self != CSS_ALIGN_AUTO) {
+ return child->style.align_self;
+ }
+ return node->style.align_items;
+}
+
+static css_direction_t resolveDirection(css_node_t *node, css_direction_t parentDirection) {
+ css_direction_t direction = node->style.direction;
+
+ if (direction == CSS_DIRECTION_INHERIT) {
+ direction = parentDirection > CSS_DIRECTION_INHERIT ? parentDirection : CSS_DIRECTION_LTR;
+ }
+
+ return direction;
+}
+
+static css_flex_direction_t getFlexDirection(css_node_t *node) {
+ return node->style.flex_direction;
+}
+
+static css_flex_direction_t resolveAxis(css_flex_direction_t flex_direction, css_direction_t direction) {
+ if (direction == CSS_DIRECTION_RTL) {
+ if (flex_direction == CSS_FLEX_DIRECTION_ROW) {
+ return CSS_FLEX_DIRECTION_ROW_REVERSE;
+ } else if (flex_direction == CSS_FLEX_DIRECTION_ROW_REVERSE) {
+ return CSS_FLEX_DIRECTION_ROW;
+ }
+ }
+
+ return flex_direction;
+}
+
+static css_flex_direction_t getCrossFlexDirection(css_flex_direction_t flex_direction, css_direction_t direction) {
+ if (isColumnDirection(flex_direction)) {
+ return resolveAxis(CSS_FLEX_DIRECTION_ROW, direction);
+ } else {
+ return CSS_FLEX_DIRECTION_COLUMN;
+ }
+}
+
+static float getFlex(css_node_t *node) {
+ return node->style.flex;
+}
+
+static bool isFlex(css_node_t *node) {
+ return (
+ node->style.position_type == CSS_POSITION_RELATIVE &&
+ getFlex(node) > 0
+ );
+}
+
+static bool isFlexWrap(css_node_t *node) {
+ return node->style.flex_wrap == CSS_WRAP;
+}
+
+static float getDimWithMargin(css_node_t *node, css_flex_direction_t axis) {
+ return node->layout.dimensions[dim[axis]] +
+ getLeadingMargin(node, axis) +
+ getTrailingMargin(node, axis);
+}
+
+static bool isStyleDimDefined(css_node_t *node, css_flex_direction_t axis) {
+ float value = node->style.dimensions[dim[axis]];
+ return !isUndefined(value) && value >= 0.0;
+}
+
+static bool isLayoutDimDefined(css_node_t *node, css_flex_direction_t axis) {
+ float value = node->layout.dimensions[dim[axis]];
+ return !isUndefined(value) && value >= 0.0;
+}
+
+static bool isPosDefined(css_node_t *node, css_position_t position) {
+ return !isUndefined(node->style.position[position]);
+}
+
+static bool isMeasureDefined(css_node_t *node) {
+ return node->measure;
+}
+
+static float getPosition(css_node_t *node, css_position_t position) {
+ float result = node->style.position[position];
+ if (!isUndefined(result)) {
+ return result;
+ }
+ return 0;
+}
+
+static float boundAxis(css_node_t *node, css_flex_direction_t axis, float value) {
+ float min = CSS_UNDEFINED;
+ float max = CSS_UNDEFINED;
+
+ if (isColumnDirection(axis)) {
+ min = node->style.minDimensions[CSS_HEIGHT];
+ max = node->style.maxDimensions[CSS_HEIGHT];
+ } else if (isRowDirection(axis)) {
+ min = node->style.minDimensions[CSS_WIDTH];
+ max = node->style.maxDimensions[CSS_WIDTH];
+ }
+
+ float boundValue = value;
+
+ if (!isUndefined(max) && max >= 0.0 && boundValue > max) {
+ boundValue = max;
+ }
+ if (!isUndefined(min) && min >= 0.0 && boundValue < min) {
+ boundValue = min;
+ }
+
+ return boundValue;
+}
+
+// When the user specifically sets a value for width or height
+static void setDimensionFromStyle(css_node_t *node, css_flex_direction_t axis) {
+ // The parent already computed us a width or height. We just skip it
+ if (isLayoutDimDefined(node, axis)) {
+ return;
+ }
+ // We only run if there's a width or height defined
+ if (!isStyleDimDefined(node, axis)) {
+ return;
+ }
+
+ // The dimensions can never be smaller than the padding and border
+ node->layout.dimensions[dim[axis]] = fmaxf(
+ boundAxis(node, axis, node->style.dimensions[dim[axis]]),
+ getPaddingAndBorderAxis(node, axis)
+ );
+}
+
+static void setTrailingPosition(css_node_t *node, css_node_t *child, css_flex_direction_t axis) {
+ child->layout.position[trailing[axis]] = node->layout.dimensions[dim[axis]] -
+ child->layout.dimensions[dim[axis]] - child->layout.position[pos[axis]];
+ }
+
+// If both left and right are defined, then use left. Otherwise return
+// +left or -right depending on which is defined.
+static float getRelativePosition(css_node_t *node, css_flex_direction_t axis) {
+ float lead = node->style.position[leading[axis]];
+ if (!isUndefined(lead)) {
+ return lead;
+ }
+ return -getPosition(node, trailing[axis]);
+}
+
+static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, float parentMaxHeight, css_direction_t parentDirection) {
+ /** START_GENERATED **/
+ css_direction_t direction = resolveDirection(node, parentDirection);
+ css_flex_direction_t mainAxis = resolveAxis(getFlexDirection(node), direction);
+ css_flex_direction_t crossAxis = getCrossFlexDirection(mainAxis, direction);
+ css_flex_direction_t resolvedRowAxis = resolveAxis(CSS_FLEX_DIRECTION_ROW, direction);
+
+ // Handle width and height style attributes
+ setDimensionFromStyle(node, mainAxis);
+ setDimensionFromStyle(node, crossAxis);
+
+ // Set the resolved resolution in the node's layout
+ node->layout.direction = direction;
+
+ // The position is set by the parent, but we need to complete it with a
+ // delta composed of the margin and left/top/right/bottom
+ node->layout.position[leading[mainAxis]] += getLeadingMargin(node, mainAxis) +
+ getRelativePosition(node, mainAxis);
+ node->layout.position[trailing[mainAxis]] += getTrailingMargin(node, mainAxis) +
+ getRelativePosition(node, mainAxis);
+ node->layout.position[leading[crossAxis]] += getLeadingMargin(node, crossAxis) +
+ getRelativePosition(node, crossAxis);
+ node->layout.position[trailing[crossAxis]] += getTrailingMargin(node, crossAxis) +
+ getRelativePosition(node, crossAxis);
+
+ // Inline immutable values from the target node to avoid excessive method
+ // invocations during the layout calculation.
+ int childCount = node->children_count;
+ float paddingAndBorderAxisResolvedRow = getPaddingAndBorderAxis(node, resolvedRowAxis);
+ float paddingAndBorderAxisColumn = getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN);
+
+ if (isMeasureDefined(node)) {
+ bool isResolvedRowDimDefined = isLayoutDimDefined(node, resolvedRowAxis);
+
+ float width = CSS_UNDEFINED;
+ css_measure_mode_t widthMode = CSS_MEASURE_MODE_UNDEFINED;
+ if (isStyleDimDefined(node, resolvedRowAxis)) {
+ width = node->style.dimensions[CSS_WIDTH];
+ widthMode = CSS_MEASURE_MODE_EXACTLY;
+ } else if (isResolvedRowDimDefined) {
+ width = node->layout.dimensions[dim[resolvedRowAxis]];
+ widthMode = CSS_MEASURE_MODE_EXACTLY;
+ } else {
+ width = parentMaxWidth -
+ getMarginAxis(node, resolvedRowAxis);
+ widthMode = CSS_MEASURE_MODE_AT_MOST;
+ }
+ width -= paddingAndBorderAxisResolvedRow;
+ if (isUndefined(width)) {
+ widthMode = CSS_MEASURE_MODE_UNDEFINED;
+ }
+
+ float height = CSS_UNDEFINED;
+ css_measure_mode_t heightMode = CSS_MEASURE_MODE_UNDEFINED;
+ if (isStyleDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) {
+ height = node->style.dimensions[CSS_HEIGHT];
+ heightMode = CSS_MEASURE_MODE_EXACTLY;
+ } else if (isLayoutDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) {
+ height = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]];
+ heightMode = CSS_MEASURE_MODE_EXACTLY;
+ } else {
+ height = parentMaxHeight -
+ getMarginAxis(node, resolvedRowAxis);
+ heightMode = CSS_MEASURE_MODE_AT_MOST;
+ }
+ height -= getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN);
+ if (isUndefined(height)) {
+ heightMode = CSS_MEASURE_MODE_UNDEFINED;
+ }
+
+ // We only need to give a dimension for the text if we haven't got any
+ // for it computed yet. It can either be from the style attribute or because
+ // the element is flexible.
+ bool isRowUndefined = !isStyleDimDefined(node, resolvedRowAxis) && !isResolvedRowDimDefined;
+ bool isColumnUndefined = !isStyleDimDefined(node, CSS_FLEX_DIRECTION_COLUMN) &&
+ isUndefined(node->layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]);
+
+ // Let's not measure the text if we already know both dimensions
+ if (isRowUndefined || isColumnUndefined) {
+ css_dim_t measureDim = node->measure(
+ node->context,
+
+ width,
+ widthMode,
+ height,
+ heightMode
+ );
+ if (isRowUndefined) {
+ node->layout.dimensions[CSS_WIDTH] = measureDim.dimensions[CSS_WIDTH] +
+ paddingAndBorderAxisResolvedRow;
+ }
+ if (isColumnUndefined) {
+ node->layout.dimensions[CSS_HEIGHT] = measureDim.dimensions[CSS_HEIGHT] +
+ paddingAndBorderAxisColumn;
+ }
+ }
+ if (childCount == 0) {
+ return;
+ }
+ }
+
+ bool isNodeFlexWrap = isFlexWrap(node);
+
+ css_justify_t justifyContent = node->style.justify_content;
+
+ float leadingPaddingAndBorderMain = getLeadingPaddingAndBorder(node, mainAxis);
+ float leadingPaddingAndBorderCross = getLeadingPaddingAndBorder(node, crossAxis);
+ float paddingAndBorderAxisMain = getPaddingAndBorderAxis(node, mainAxis);
+ float paddingAndBorderAxisCross = getPaddingAndBorderAxis(node, crossAxis);
+
+ bool isMainDimDefined = isLayoutDimDefined(node, mainAxis);
+ bool isCrossDimDefined = isLayoutDimDefined(node, crossAxis);
+ bool isMainRowDirection = isRowDirection(mainAxis);
+
+ int i;
+ int ii;
+ css_node_t* child;
+ css_flex_direction_t axis;
+
+ css_node_t* firstAbsoluteChild = NULL;
+ css_node_t* currentAbsoluteChild = NULL;
+
+ float definedMainDim = CSS_UNDEFINED;
+ if (isMainDimDefined) {
+ definedMainDim = node->layout.dimensions[dim[mainAxis]] - paddingAndBorderAxisMain;
+ }
+
+ // We want to execute the next two loops one per line with flex-wrap
+ int startLine = 0;
+ int endLine = 0;
+ // int nextOffset = 0;
+ int alreadyComputedNextLayout = 0;
+ // We aggregate the total dimensions of the container in those two variables
+ float linesCrossDim = 0;
+ float linesMainDim = 0;
+ int linesCount = 0;
+ while (endLine < childCount) {
+ // <Loop A> Layout non flexible children and count children by type
+
+ // mainContentDim is accumulation of the dimensions and margin of all the
+ // non flexible children. This will be used in order to either set the
+ // dimensions of the node if none already exist, or to compute the
+ // remaining space left for the flexible children.
+ float mainContentDim = 0;
+
+ // There are three kind of children, non flexible, flexible and absolute.
+ // We need to know how many there are in order to distribute the space.
+ int flexibleChildrenCount = 0;
+ float totalFlexible = 0;
+ int nonFlexibleChildrenCount = 0;
+
+ // Use the line loop to position children in the main axis for as long
+ // as they are using a simple stacking behaviour. Children that are
+ // immediately stacked in the initial loop will not be touched again
+ // in <Loop C>.
+ bool isSimpleStackMain =
+ (isMainDimDefined && justifyContent == CSS_JUSTIFY_FLEX_START) ||
+ (!isMainDimDefined && justifyContent != CSS_JUSTIFY_CENTER);
+ int firstComplexMain = (isSimpleStackMain ? childCount : startLine);
+
+ // Use the initial line loop to position children in the cross axis for
+ // as long as they are relatively positioned with alignment STRETCH or
+ // FLEX_START. Children that are immediately stacked in the initial loop
+ // will not be touched again in <Loop D>.
+ bool isSimpleStackCross = true;
+ int firstComplexCross = childCount;
+
+ css_node_t* firstFlexChild = NULL;
+ css_node_t* currentFlexChild = NULL;
+
+ float mainDim = leadingPaddingAndBorderMain;
+ float crossDim = 0;
+
+ float maxWidth = CSS_UNDEFINED;
+ float maxHeight = CSS_UNDEFINED;
+ for (i = startLine; i < childCount; ++i) {
+ child = node->get_child(node->context, i);
+ child->line_index = linesCount;
+
+ child->next_absolute_child = NULL;
+ child->next_flex_child = NULL;
+
+ css_align_t alignItem = getAlignItem(node, child);
+
+ // Pre-fill cross axis dimensions when the child is using stretch before
+ // we call the recursive layout pass
+ if (alignItem == CSS_ALIGN_STRETCH &&
+ child->style.position_type == CSS_POSITION_RELATIVE &&
+ isCrossDimDefined &&
+ !isStyleDimDefined(child, crossAxis)) {
+ child->layout.dimensions[dim[crossAxis]] = fmaxf(
+ boundAxis(child, crossAxis, node->layout.dimensions[dim[crossAxis]] -
+ paddingAndBorderAxisCross - getMarginAxis(child, crossAxis)),
+ // You never want to go smaller than padding
+ getPaddingAndBorderAxis(child, crossAxis)
+ );
+ } else if (child->style.position_type == CSS_POSITION_ABSOLUTE) {
+ // Store a private linked list of absolutely positioned children
+ // so that we can efficiently traverse them later.
+ if (firstAbsoluteChild == NULL) {
+ firstAbsoluteChild = child;
+ }
+ if (currentAbsoluteChild != NULL) {
+ currentAbsoluteChild->next_absolute_child = child;
+ }
+ currentAbsoluteChild = child;
+
+ // Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both
+ // left and right or top and bottom).
+ for (ii = 0; ii < 2; ii++) {
+ axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
+ if (isLayoutDimDefined(node, axis) &&
+ !isStyleDimDefined(child, axis) &&
+ isPosDefined(child, leading[axis]) &&
+ isPosDefined(child, trailing[axis])) {
+ child->layout.dimensions[dim[axis]] = fmaxf(
+ boundAxis(child, axis, node->layout.dimensions[dim[axis]] -
+ getPaddingAndBorderAxis(node, axis) -
+ getMarginAxis(child, axis) -
+ getPosition(child, leading[axis]) -
+ getPosition(child, trailing[axis])),
+ // You never want to go smaller than padding
+ getPaddingAndBorderAxis(child, axis)
+ );
+ }
+ }
+ }
+
+ float nextContentDim = 0;
+
+ // It only makes sense to consider a child flexible if we have a computed
+ // dimension for the node->
+ if (isMainDimDefined && isFlex(child)) {
+ flexibleChildrenCount++;
+ totalFlexible += child->style.flex;
+
+ // Store a private linked list of flexible children so that we can
+ // efficiently traverse them later.
+ if (firstFlexChild == NULL) {
+ firstFlexChild = child;
+ }
+ if (currentFlexChild != NULL) {
+ currentFlexChild->next_flex_child = child;
+ }
+ currentFlexChild = child;
+
+ // Even if we don't know its exact size yet, we already know the padding,
+ // border and margin. We'll use this partial information, which represents
+ // the smallest possible size for the child, to compute the remaining
+ // available space.
+ nextContentDim = getPaddingAndBorderAxis(child, mainAxis) +
+ getMarginAxis(child, mainAxis);
+
+ } else {
+ maxWidth = CSS_UNDEFINED;
+ maxHeight = CSS_UNDEFINED;
+
+ if (!isMainRowDirection) {
+ if (isLayoutDimDefined(node, resolvedRowAxis)) {
+ maxWidth = node->layout.dimensions[dim[resolvedRowAxis]] -
+ paddingAndBorderAxisResolvedRow;
+ } else {
+ maxWidth = parentMaxWidth -
+ getMarginAxis(node, resolvedRowAxis) -
+ paddingAndBorderAxisResolvedRow;
+ }
+ } else {
+ if (isLayoutDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) {
+ maxHeight = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] -
+ paddingAndBorderAxisColumn;
+ } else {
+ maxHeight = parentMaxHeight -
+ getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN) -
+ paddingAndBorderAxisColumn;
+ }
+ }
+
+ // This is the main recursive call. We layout non flexible children.
+ if (alreadyComputedNextLayout == 0) {
+ layoutNode(child, maxWidth, maxHeight, direction);
+ }
+
+ // Absolute positioned elements do not take part of the layout, so we
+ // don't use them to compute mainContentDim
+ if (child->style.position_type == CSS_POSITION_RELATIVE) {
+ nonFlexibleChildrenCount++;
+ // At this point we know the final size and margin of the element.
+ nextContentDim = getDimWithMargin(child, mainAxis);
+ }
+ }
+
+ // The element we are about to add would make us go to the next line
+ if (isNodeFlexWrap &&
+ isMainDimDefined &&
+ mainContentDim + nextContentDim > definedMainDim &&
+ // If there's only one element, then it's bigger than the content
+ // and needs its own line
+ i != startLine) {
+ nonFlexibleChildrenCount--;
+ alreadyComputedNextLayout = 1;
+ break;
+ }
+
+ // Disable simple stacking in the main axis for the current line as
+ // we found a non-trivial child-> The remaining children will be laid out
+ // in <Loop C>.
+ if (isSimpleStackMain &&
+ (child->style.position_type != CSS_POSITION_RELATIVE || isFlex(child))) {
+ isSimpleStackMain = false;
+ firstComplexMain = i;
+ }
+
+ // Disable simple stacking in the cross axis for the current line as
+ // we found a non-trivial child-> The remaining children will be laid out
+ // in <Loop D>.
+ if (isSimpleStackCross &&
+ (child->style.position_type != CSS_POSITION_RELATIVE ||
+ (alignItem != CSS_ALIGN_STRETCH && alignItem != CSS_ALIGN_FLEX_START) ||
+ (alignItem == CSS_ALIGN_STRETCH && !isCrossDimDefined))) {
+ isSimpleStackCross = false;
+ firstComplexCross = i;
+ }
+
+ if (isSimpleStackMain) {
+ child->layout.position[pos[mainAxis]] += mainDim;
+ if (isMainDimDefined) {
+ setTrailingPosition(node, child, mainAxis);
+ }
+
+ mainDim += getDimWithMargin(child, mainAxis);
+ crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis)));
+ }
+
+ if (isSimpleStackCross) {
+ child->layout.position[pos[crossAxis]] += linesCrossDim + leadingPaddingAndBorderCross;
+ if (isCrossDimDefined) {
+ setTrailingPosition(node, child, crossAxis);
+ }
+ }
+
+ alreadyComputedNextLayout = 0;
+ mainContentDim += nextContentDim;
+ endLine = i + 1;
+ }
+
+ // <Loop B> Layout flexible children and allocate empty space
+
+ // In order to position the elements in the main axis, we have two
+ // controls. The space between the beginning and the first element
+ // and the space between each two elements.
+ float leadingMainDim = 0;
+ float betweenMainDim = 0;
+
+ // The remaining available space that needs to be allocated
+ float remainingMainDim = 0;
+ if (isMainDimDefined) {
+ remainingMainDim = definedMainDim - mainContentDim;
+ } else {
+ remainingMainDim = fmaxf(mainContentDim, 0) - mainContentDim;
+ }
+
+ // If there are flexible children in the mix, they are going to fill the
+ // remaining space
+ if (flexibleChildrenCount != 0) {
+ float flexibleMainDim = remainingMainDim / totalFlexible;
+ float baseMainDim;
+ float boundMainDim;
+
+ // If the flex share of remaining space doesn't meet min/max bounds,
+ // remove this child from flex calculations.
+ currentFlexChild = firstFlexChild;
+ while (currentFlexChild != NULL) {
+ baseMainDim = flexibleMainDim * currentFlexChild->style.flex +
+ getPaddingAndBorderAxis(currentFlexChild, mainAxis);
+ boundMainDim = boundAxis(currentFlexChild, mainAxis, baseMainDim);
+
+ if (baseMainDim != boundMainDim) {
+ remainingMainDim -= boundMainDim;
+ totalFlexible -= currentFlexChild->style.flex;
+ }
+
+ currentFlexChild = currentFlexChild->next_flex_child;
+ }
+ flexibleMainDim = remainingMainDim / totalFlexible;
+
+ // The non flexible children can overflow the container, in this case
+ // we should just assume that there is no space available.
+ if (flexibleMainDim < 0) {
+ flexibleMainDim = 0;
+ }
+
+ currentFlexChild = firstFlexChild;
+ while (currentFlexChild != NULL) {
+ // At this point we know the final size of the element in the main
+ // dimension
+ currentFlexChild->layout.dimensions[dim[mainAxis]] = boundAxis(currentFlexChild, mainAxis,
+ flexibleMainDim * currentFlexChild->style.flex +
+ getPaddingAndBorderAxis(currentFlexChild, mainAxis)
+ );
+
+ maxWidth = CSS_UNDEFINED;
+ if (isLayoutDimDefined(node, resolvedRowAxis)) {
+ maxWidth = node->layout.dimensions[dim[resolvedRowAxis]] -
+ paddingAndBorderAxisResolvedRow;
+ } else if (!isMainRowDirection) {
+ maxWidth = parentMaxWidth -
+ getMarginAxis(node, resolvedRowAxis) -
+ paddingAndBorderAxisResolvedRow;
+ }
+ maxHeight = CSS_UNDEFINED;
+ if (isLayoutDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) {
+ maxHeight = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] -
+ paddingAndBorderAxisColumn;
+ } else if (isMainRowDirection) {
+ maxHeight = parentMaxHeight -
+ getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN) -
+ paddingAndBorderAxisColumn;
+ }
+
+ // And we recursively call the layout algorithm for this child
+ layoutNode(currentFlexChild, maxWidth, maxHeight, direction);
+
+ child = currentFlexChild;
+ currentFlexChild = currentFlexChild->next_flex_child;
+ child->next_flex_child = NULL;
+ }
+
+ // We use justifyContent to figure out how to allocate the remaining
+ // space available
+ } else if (justifyContent != CSS_JUSTIFY_FLEX_START) {
+ if (justifyContent == CSS_JUSTIFY_CENTER) {
+ leadingMainDim = remainingMainDim / 2;
+ } else if (justifyContent == CSS_JUSTIFY_FLEX_END) {
+ leadingMainDim = remainingMainDim;
+ } else if (justifyContent == CSS_JUSTIFY_SPACE_BETWEEN) {
+ remainingMainDim = fmaxf(remainingMainDim, 0);
+ if (flexibleChildrenCount + nonFlexibleChildrenCount - 1 != 0) {
+ betweenMainDim = remainingMainDim /
+ (flexibleChildrenCount + nonFlexibleChildrenCount - 1);
+ } else {
+ betweenMainDim = 0;
+ }
+ } else if (justifyContent == CSS_JUSTIFY_SPACE_AROUND) {
+ // Space on the edges is half of the space between elements
+ betweenMainDim = remainingMainDim /
+ (flexibleChildrenCount + nonFlexibleChildrenCount);
+ leadingMainDim = betweenMainDim / 2;
+ }
+ }
+
+ // <Loop C> Position elements in the main axis and compute dimensions
+
+ // At this point, all the children have their dimensions set. We need to
+ // find their position. In order to do that, we accumulate data in
+ // variables that are also useful to compute the total dimensions of the
+ // container!
+ mainDim += leadingMainDim;
+
+ for (i = firstComplexMain; i < endLine; ++i) {
+ child = node->get_child(node->context, i);
+
+ if (child->style.position_type == CSS_POSITION_ABSOLUTE &&
+ isPosDefined(child, leading[mainAxis])) {
+ // In case the child is position absolute and has left/top being
+ // defined, we override the position to whatever the user said
+ // (and margin/border).
+ child->layout.position[pos[mainAxis]] = getPosition(child, leading[mainAxis]) +
+ getLeadingBorder(node, mainAxis) +
+ getLeadingMargin(child, mainAxis);
+ } else {
+ // If the child is position absolute (without top/left) or relative,
+ // we put it at the current accumulated offset.
+ child->layout.position[pos[mainAxis]] += mainDim;
+
+ // Define the trailing position accordingly.
+ if (isMainDimDefined) {
+ setTrailingPosition(node, child, mainAxis);
+ }
+
+ // Now that we placed the element, we need to update the variables
+ // We only need to do that for relative elements. Absolute elements
+ // do not take part in that phase.
+ if (child->style.position_type == CSS_POSITION_RELATIVE) {
+ // The main dimension is the sum of all the elements dimension plus
+ // the spacing.
+ mainDim += betweenMainDim + getDimWithMargin(child, mainAxis);
+ // The cross dimension is the max of the elements dimension since there
+ // can only be one element in that cross dimension.
+ crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis)));
+ }
+ }
+ }
+
+ float containerCrossAxis = node->layout.dimensions[dim[crossAxis]];
+ if (!isCrossDimDefined) {
+ containerCrossAxis = fmaxf(
+ // For the cross dim, we add both sides at the end because the value
+ // is aggregate via a max function. Intermediate negative values
+ // can mess this computation otherwise
+ boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross),
+ paddingAndBorderAxisCross
+ );
+ }
+
+ // <Loop D> Position elements in the cross axis
+ for (i = firstComplexCross; i < endLine; ++i) {
+ child = node->get_child(node->context, i);
+
+ if (child->style.position_type == CSS_POSITION_ABSOLUTE &&
+ isPosDefined(child, leading[crossAxis])) {
+ // In case the child is absolutely positionned and has a
+ // top/left/bottom/right being set, we override all the previously
+ // computed positions to set it correctly.
+ child->layout.position[pos[crossAxis]] = getPosition(child, leading[crossAxis]) +
+ getLeadingBorder(node, crossAxis) +
+ getLeadingMargin(child, crossAxis);
+
+ } else {
+ float leadingCrossDim = leadingPaddingAndBorderCross;
+
+ // For a relative children, we're either using alignItems (parent) or
+ // alignSelf (child) in order to determine the position in the cross axis
+ if (child->style.position_type == CSS_POSITION_RELATIVE) {
+ /*eslint-disable */
+ // This variable is intentionally re-defined as the code is transpiled to a block scope language
+ css_align_t alignItem = getAlignItem(node, child);
+ /*eslint-enable */
+ if (alignItem == CSS_ALIGN_STRETCH) {
+ // You can only stretch if the dimension has not already been defined
+ // previously.
+ if (!isStyleDimDefined(child, crossAxis)) {
+ float dimCrossAxis = child->layout.dimensions[dim[crossAxis]];
+ child->layout.dimensions[dim[crossAxis]] = fmaxf(
+ boundAxis(child, crossAxis, containerCrossAxis -
+ paddingAndBorderAxisCross - getMarginAxis(child, crossAxis)),
+ // You never want to go smaller than padding
+ getPaddingAndBorderAxis(child, crossAxis)
+ );
+
+ // If the size has changed, and this child has children we need to re-layout this child
+ if (dimCrossAxis != child->layout.dimensions[dim[crossAxis]] && child->children_count > 0) {
+ // Reset child margins before re-layout as they are added back in layoutNode and would be doubled
+ child->layout.position[leading[mainAxis]] -= getLeadingMargin(child, mainAxis) +
+ getRelativePosition(child, mainAxis);
+ child->layout.position[trailing[mainAxis]] -= getTrailingMargin(child, mainAxis) +
+ getRelativePosition(child, mainAxis);
+ child->layout.position[leading[crossAxis]] -= getLeadingMargin(child, crossAxis) +
+ getRelativePosition(child, crossAxis);
+ child->layout.position[trailing[crossAxis]] -= getTrailingMargin(child, crossAxis) +
+ getRelativePosition(child, crossAxis);
+
+ layoutNode(child, maxWidth, maxHeight, direction);
+ }
+ }
+ } else if (alignItem != CSS_ALIGN_FLEX_START) {
+ // The remaining space between the parent dimensions+padding and child
+ // dimensions+margin.
+ float remainingCrossDim = containerCrossAxis -
+ paddingAndBorderAxisCross - getDimWithMargin(child, crossAxis);
+
+ if (alignItem == CSS_ALIGN_CENTER) {
+ leadingCrossDim += remainingCrossDim / 2;
+ } else { // CSS_ALIGN_FLEX_END
+ leadingCrossDim += remainingCrossDim;
+ }
+ }
+ }
+
+ // And we apply the position
+ child->layout.position[pos[crossAxis]] += linesCrossDim + leadingCrossDim;
+
+ // Define the trailing position accordingly.
+ if (isCrossDimDefined) {
+ setTrailingPosition(node, child, crossAxis);
+ }
+ }
+ }
+
+ linesCrossDim += crossDim;
+ linesMainDim = fmaxf(linesMainDim, mainDim);
+ linesCount += 1;
+ startLine = endLine;
+ }
+
+ // <Loop E>
+ //
+ // Note(prenaux): More than one line, we need to layout the crossAxis
+ // according to alignContent.
+ //
+ // Note that we could probably remove <Loop D> and handle the one line case
+ // here too, but for the moment this is safer since it won't interfere with
+ // previously working code.
+ //
+ // See specs:
+ // http://www.w3.org/TR/2012/CR-css3-flexbox-20120918/#layout-algorithm
+ // section 9.4
+ //
+ if (linesCount > 1 && isCrossDimDefined) {
+ float nodeCrossAxisInnerSize = node->layout.dimensions[dim[crossAxis]] -
+ paddingAndBorderAxisCross;
+ float remainingAlignContentDim = nodeCrossAxisInnerSize - linesCrossDim;
+
+ float crossDimLead = 0;
+ float currentLead = leadingPaddingAndBorderCross;
+
+ css_align_t alignContent = node->style.align_content;
+ if (alignContent == CSS_ALIGN_FLEX_END) {
+ currentLead += remainingAlignContentDim;
+ } else if (alignContent == CSS_ALIGN_CENTER) {
+ currentLead += remainingAlignContentDim / 2;
+ } else if (alignContent == CSS_ALIGN_STRETCH) {
+ if (nodeCrossAxisInnerSize > linesCrossDim) {
+ crossDimLead = (remainingAlignContentDim / linesCount);
+ }
+ }
+
+ int endIndex = 0;
+ for (i = 0; i < linesCount; ++i) {
+ int startIndex = endIndex;
+
+ // compute the line's height and find the endIndex
+ float lineHeight = 0;
+ for (ii = startIndex; ii < childCount; ++ii) {
+ child = node->get_child(node->context, ii);
+ if (child->style.position_type != CSS_POSITION_RELATIVE) {
+ continue;
+ }
+ if (child->line_index != i) {
+ break;
+ }
+ if (isLayoutDimDefined(child, crossAxis)) {
+ lineHeight = fmaxf(
+ lineHeight,
+ child->layout.dimensions[dim[crossAxis]] + getMarginAxis(child, crossAxis)
+ );
+ }
+ }
+ endIndex = ii;
+ lineHeight += crossDimLead;
+
+ for (ii = startIndex; ii < endIndex; ++ii) {
+ child = node->get_child(node->context, ii);
+ if (child->style.position_type != CSS_POSITION_RELATIVE) {
+ continue;
+ }
+
+ css_align_t alignContentAlignItem = getAlignItem(node, child);
+ if (alignContentAlignItem == CSS_ALIGN_FLEX_START) {
+ child->layout.position[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis);
+ } else if (alignContentAlignItem == CSS_ALIGN_FLEX_END) {
+ child->layout.position[pos[crossAxis]] = currentLead + lineHeight - getTrailingMargin(child, crossAxis) - child->layout.dimensions[dim[crossAxis]];
+ } else if (alignContentAlignItem == CSS_ALIGN_CENTER) {
+ float childHeight = child->layout.dimensions[dim[crossAxis]];
+ child->layout.position[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2;
+ } else if (alignContentAlignItem == CSS_ALIGN_STRETCH) {
+ child->layout.position[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis);
+ // TODO(prenaux): Correctly set the height of items with undefined
+ // (auto) crossAxis dimension.
+ }
+ }
+
+ currentLead += lineHeight;
+ }
+ }
+
+ bool needsMainTrailingPos = false;
+ bool needsCrossTrailingPos = false;
+
+ // If the user didn't specify a width or height, and it has not been set
+ // by the container, then we set it via the children.
+ if (!isMainDimDefined) {
+ node->layout.dimensions[dim[mainAxis]] = fmaxf(
+ // We're missing the last padding at this point to get the final
+ // dimension
+ boundAxis(node, mainAxis, linesMainDim + getTrailingPaddingAndBorder(node, mainAxis)),
+ // We can never assign a width smaller than the padding and borders
+ paddingAndBorderAxisMain
+ );
+
+ if (mainAxis == CSS_FLEX_DIRECTION_ROW_REVERSE ||
+ mainAxis == CSS_FLEX_DIRECTION_COLUMN_REVERSE) {
+ needsMainTrailingPos = true;
+ }
+ }
+
+ if (!isCrossDimDefined) {
+ node->layout.dimensions[dim[crossAxis]] = fmaxf(
+ // For the cross dim, we add both sides at the end because the value
+ // is aggregate via a max function. Intermediate negative values
+ // can mess this computation otherwise
+ boundAxis(node, crossAxis, linesCrossDim + paddingAndBorderAxisCross),
+ paddingAndBorderAxisCross
+ );
+
+ if (crossAxis == CSS_FLEX_DIRECTION_ROW_REVERSE ||
+ crossAxis == CSS_FLEX_DIRECTION_COLUMN_REVERSE) {
+ needsCrossTrailingPos = true;
+ }
+ }
+
+ // <Loop F> Set trailing position if necessary
+ if (needsMainTrailingPos || needsCrossTrailingPos) {
+ for (i = 0; i < childCount; ++i) {
+ child = node->get_child(node->context, i);
+
+ if (needsMainTrailingPos) {
+ setTrailingPosition(node, child, mainAxis);
+ }
+
+ if (needsCrossTrailingPos) {
+ setTrailingPosition(node, child, crossAxis);
+ }
+ }
+ }
+
+ // <Loop G> Calculate dimensions for absolutely positioned elements
+ currentAbsoluteChild = firstAbsoluteChild;
+ while (currentAbsoluteChild != NULL) {
+ // Pre-fill dimensions when using absolute position and both offsets for
+ // the axis are defined (either both left and right or top and bottom).
+ for (ii = 0; ii < 2; ii++) {
+ axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
+
+ if (isLayoutDimDefined(node, axis) &&
+ !isStyleDimDefined(currentAbsoluteChild, axis) &&
+ isPosDefined(currentAbsoluteChild, leading[axis]) &&
+ isPosDefined(currentAbsoluteChild, trailing[axis])) {
+ currentAbsoluteChild->layout.dimensions[dim[axis]] = fmaxf(
+ boundAxis(currentAbsoluteChild, axis, node->layout.dimensions[dim[axis]] -
+ getBorderAxis(node, axis) -
+ getMarginAxis(currentAbsoluteChild, axis) -
+ getPosition(currentAbsoluteChild, leading[axis]) -
+ getPosition(currentAbsoluteChild, trailing[axis])
+ ),
+ // You never want to go smaller than padding
+ getPaddingAndBorderAxis(currentAbsoluteChild, axis)
+ );
+ }
+
+ if (isPosDefined(currentAbsoluteChild, trailing[axis]) &&
+ !isPosDefined(currentAbsoluteChild, leading[axis])) {
+ currentAbsoluteChild->layout.position[leading[axis]] =
+ node->layout.dimensions[dim[axis]] -
+ currentAbsoluteChild->layout.dimensions[dim[axis]] -
+ getPosition(currentAbsoluteChild, trailing[axis]);
+ }
+ }
+
+ child = currentAbsoluteChild;
+ currentAbsoluteChild = currentAbsoluteChild->next_absolute_child;
+ child->next_absolute_child = NULL;
+ }
+ /** END_GENERATED **/
+}
+
+void layoutNode(css_node_t *node, float parentMaxWidth, float parentMaxHeight, css_direction_t parentDirection) {
+ css_layout_t *layout = &node->layout;
+ css_direction_t direction = node->style.direction;
+ layout->should_update = true;
+
+ bool skipLayout =
+ !node->is_dirty(node->context) &&
+ eq(layout->last_requested_dimensions[CSS_WIDTH], layout->dimensions[CSS_WIDTH]) &&
+ eq(layout->last_requested_dimensions[CSS_HEIGHT], layout->dimensions[CSS_HEIGHT]) &&
+ eq(layout->last_parent_max_width, parentMaxWidth) &&
+ eq(layout->last_parent_max_height, parentMaxHeight) &&
+ eq(layout->last_direction, direction);
+
+ if (skipLayout) {
+ layout->dimensions[CSS_WIDTH] = layout->last_dimensions[CSS_WIDTH];
+ layout->dimensions[CSS_HEIGHT] = layout->last_dimensions[CSS_HEIGHT];
+ layout->position[CSS_TOP] = layout->last_position[CSS_TOP];
+ layout->position[CSS_LEFT] = layout->last_position[CSS_LEFT];
+ } else {
+ layout->last_requested_dimensions[CSS_WIDTH] = layout->dimensions[CSS_WIDTH];
+ layout->last_requested_dimensions[CSS_HEIGHT] = layout->dimensions[CSS_HEIGHT];
+ layout->last_parent_max_width = parentMaxWidth;
+ layout->last_parent_max_height = parentMaxHeight;
+ layout->last_direction = direction;
+
+ for (int i = 0, childCount = node->children_count; i < childCount; i++) {
+ resetNodeLayout(node->get_child(node->context, i));
+ }
+
+ layoutNodeImpl(node, parentMaxWidth, parentMaxHeight, parentDirection);
+
+ layout->last_dimensions[CSS_WIDTH] = layout->dimensions[CSS_WIDTH];
+ layout->last_dimensions[CSS_HEIGHT] = layout->dimensions[CSS_HEIGHT];
+ layout->last_position[CSS_TOP] = layout->position[CSS_TOP];
+ layout->last_position[CSS_LEFT] = layout->position[CSS_LEFT];
+ }
+}
+
+void resetNodeLayout(css_node_t *node) {
+ node->layout.dimensions[CSS_WIDTH] = CSS_UNDEFINED;
+ node->layout.dimensions[CSS_HEIGHT] = CSS_UNDEFINED;
+ node->layout.position[CSS_LEFT] = 0;
+ node->layout.position[CSS_TOP] = 0;
+}