diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..d8e43e2 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +/Generated/** linguist-vendored diff --git a/SwiftBox.xcodeproj/project.pbxproj b/SwiftBox.xcodeproj/project.pbxproj index 5ae45ec..7daaa75 100644 --- a/SwiftBox.xcodeproj/project.pbxproj +++ b/SwiftBox.xcodeproj/project.pbxproj @@ -160,8 +160,8 @@ buildActionMask = 2147483647; files = ( 889865D21AAFD954009C19D1 /* SwiftBox.h in Headers */, - 889865D31AAFD954009C19D1 /* Layout.h in Headers */, 889865D41AAFD954009C19D1 /* NodeImpl.h in Headers */, + 889865D31AAFD954009C19D1 /* Layout.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -228,14 +228,16 @@ 882D605F1A7F05E1001C5181 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0620; + LastUpgradeCheck = 0800; ORGANIZATIONNAME = "Josh Abernathy"; TargetAttributes = { 882D60671A7F05E1001C5181 = { CreatedOnToolsVersion = 6.2; + LastSwiftMigration = 0800; }; 882D60721A7F05E1001C5181 = { CreatedOnToolsVersion = 6.2; + LastSwiftMigration = 0800; }; 889865B21AAFD89B009C19D1 = { CreatedOnToolsVersion = 6.3; @@ -342,15 +344,19 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", @@ -363,6 +369,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; @@ -386,8 +393,10 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = YES; @@ -396,15 +405,18 @@ ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -423,9 +435,11 @@ INFOPLIST_FILE = SwiftBox/Mac.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.joshaber.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = SwiftBox; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -442,8 +456,10 @@ INFOPLIST_FILE = SwiftBox/Mac.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.joshaber.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = SwiftBox; SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; }; name = Release; }; @@ -461,7 +477,9 @@ ); INFOPLIST_FILE = SwiftBoxTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.joshaber.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -475,14 +493,16 @@ ); INFOPLIST_FILE = SwiftBoxTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.joshaber.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; }; name = Release; }; 889865C61AAFD89B009C19D1 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; @@ -496,6 +516,7 @@ INFOPLIST_FILE = SwiftBox/iOS.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.joshaber.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = SwiftBox; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -506,7 +527,7 @@ 889865C71AAFD89B009C19D1 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; COPY_PHASE_STRIP = NO; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; @@ -516,6 +537,7 @@ INFOPLIST_FILE = SwiftBox/iOS.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.joshaber.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = SwiftBox; SDKROOT = iphoneos; SKIP_INSTALL = YES; diff --git a/SwiftBox.xcodeproj/xcshareddata/xcschemes/SwiftBox-Mac.xcscheme b/SwiftBox.xcodeproj/xcshareddata/xcschemes/SwiftBox-Mac.xcscheme index bd6c40e..6578dd7 100644 --- a/SwiftBox.xcodeproj/xcshareddata/xcschemes/SwiftBox-Mac.xcscheme +++ b/SwiftBox.xcodeproj/xcshareddata/xcschemes/SwiftBox-Mac.xcscheme @@ -1,6 +1,6 @@ @@ -57,11 +57,13 @@ + + @@ -94,7 +97,7 @@ diff --git a/SwiftBox.xcodeproj/xcshareddata/xcschemes/SwiftBox-iOS.xcscheme b/SwiftBox.xcodeproj/xcshareddata/xcschemes/SwiftBox-iOS.xcscheme index a5cf09a..a992e45 100644 --- a/SwiftBox.xcodeproj/xcshareddata/xcschemes/SwiftBox-iOS.xcscheme +++ b/SwiftBox.xcodeproj/xcshareddata/xcschemes/SwiftBox-iOS.xcscheme @@ -1,6 +1,6 @@ + + String { + fileprivate func descriptionForDepth(_ depth: Int) -> String { let selfDescription = "{origin={\(frame.origin.x), \(frame.origin.y)}, size={\(frame.size.width), \(frame.size.height)}}" if children.count > 0 { - let indentation = reduce(0...depth, "\n") { accum, _ in accum + "\t" } - let childrenDescription = indentation.join(children.map { $0.descriptionForDepth(depth + 1) }) + let indentation = (0...depth).reduce("\n") { accum, _ in accum + "\t" } + let childrenDescription = (children.map { $0.descriptionForDepth(depth + 1) }).joined(separator: indentation) return "\(selfDescription)\(indentation)\(childrenDescription)" } else { return selfDescription diff --git a/SwiftBox/Mac.plist b/SwiftBox/Mac.plist index c9bcebb..401f749 100644 --- a/SwiftBox/Mac.plist +++ b/SwiftBox/Mac.plist @@ -7,7 +7,7 @@ CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - com.joshaber.$(PRODUCT_NAME:rfc1034identifier) + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/SwiftBox/Node.swift b/SwiftBox/Node.swift index 743bec9..390a81c 100644 --- a/SwiftBox/Node.swift +++ b/SwiftBox/Node.swift @@ -14,7 +14,7 @@ public struct Edges { public let bottom: CGFloat public let top: CGFloat - private var asTuple: (Float, Float, Float, Float) { + fileprivate var asTuple: (Float, Float, Float, Float) { return (Float(left), Float(top), Float(right), Float(bottom)) } @@ -34,31 +34,31 @@ public struct Edges { } public enum Direction: UInt32 { - case Column = 0 - case Row = 1 + case column = 0 + case row = 1 } public enum Justification: UInt32 { - case FlexStart = 0 - case Center = 1 - case FlexEnd = 2 - case SpaceBetween = 3 - case SpaceAround = 4 + case flexStart = 0 + case center = 1 + case flexEnd = 2 + case spaceBetween = 3 + case spaceAround = 4 } public enum ChildAlignment: UInt32 { - case FlexStart = 1 - case Center = 2 - case FlexEnd = 3 - case Stretch = 4 + case flexStart = 1 + case center = 2 + case flexEnd = 3 + case stretch = 4 } public enum SelfAlignment: UInt32 { - case Auto = 0 - case FlexStart = 1 - case Center = 2 - case FlexEnd = 3 - case Stretch = 4 + case auto = 0 + case flexStart = 1 + case center = 2 + case flexEnd = 3 + case stretch = 4 } /// A node in a layout hierarchy. @@ -77,9 +77,9 @@ public struct Node { public let selfAlignment: SelfAlignment public let childAlignment: ChildAlignment public let flex: CGFloat - public let measure: (CGFloat -> CGSize)? + public let measure: ((CGFloat) -> CGSize)? - public init(size: CGSize = CGSize(width: Undefined, height: Undefined), children: [Node] = [], direction: Direction = .Column, margin: Edges = Edges(), padding: Edges = Edges(), wrap: Bool = false, justification: Justification = .FlexStart, selfAlignment: SelfAlignment = .Auto, childAlignment: ChildAlignment = .Stretch, flex: CGFloat = 0, measure: (CGFloat -> CGSize)? = nil) { + public init(size: CGSize = CGSize(width: Undefined, height: Undefined), children: [Node] = [], direction: Direction = .column, margin: Edges = Edges(), padding: Edges = Edges(), wrap: Bool = false, justification: Justification = .flexStart, selfAlignment: SelfAlignment = .auto, childAlignment: ChildAlignment = .stretch, flex: CGFloat = 0, measure: ((CGFloat) -> CGSize)? = nil) { self.size = size self.children = children self.direction = direction @@ -93,17 +93,17 @@ public struct Node { self.measure = measure } - private func createUnderlyingNode() -> NodeImpl { + fileprivate func createUnderlyingNode() -> NodeImpl { let node = NodeImpl() - node.node.memory.style.dimensions = (Float(size.width), Float(size.height)) - node.node.memory.style.margin = margin.asTuple - node.node.memory.style.padding = padding.asTuple - node.node.memory.style.flex = Float(flex) - node.node.memory.style.flex_direction = css_flex_direction_t(direction.rawValue) - node.node.memory.style.flex_wrap = css_wrap_type_t(wrap ? 1 : 0) - node.node.memory.style.justify_content = css_justify_t(justification.rawValue) - node.node.memory.style.align_self = css_align_t(selfAlignment.rawValue) - node.node.memory.style.align_items = css_align_t(childAlignment.rawValue) + node.node.pointee.style.dimensions = (Float(size.width), Float(size.height)) + node.node.pointee.style.margin = margin.asTuple + node.node.pointee.style.padding = padding.asTuple + node.node.pointee.style.flex = Float(flex) + node.node.pointee.style.flex_direction = css_flex_direction_t(direction.rawValue) + node.node.pointee.style.flex_wrap = css_wrap_type_t(wrap ? 1 : 0) + node.node.pointee.style.justify_content = css_justify_t(justification.rawValue) + node.node.pointee.style.align_self = css_align_t(selfAlignment.rawValue) + node.node.pointee.style.align_items = css_align_t(childAlignment.rawValue) if let measure = measure { node.measure = measure } @@ -112,10 +112,10 @@ public struct Node { } /// Lay out the receiver and all its children with an optional max width. - public func layout(maxWidth: CGFloat? = nil) -> Layout { + public func layout(maxWidth: CGFloat? = nil) -> Layout { let node = createUnderlyingNode() if let maxWidth = maxWidth { - node.layoutWithMaxWidth(maxWidth) + node.layout(withMaxWidth: maxWidth) } else { node.layout() } @@ -125,10 +125,28 @@ public struct Node { } } -private func createLayoutsFromChildren(node: NodeImpl) -> [Layout] { +private func createLayoutsFromChildren(_ node: NodeImpl) -> [Layout] { return node.children.map { let child = $0 as! NodeImpl let frame = child.frame return Layout(frame: frame, children: createLayoutsFromChildren(child)) } } + +public extension CGPoint { + var isUndefined: Bool { + return x.isNaN || y.isNaN + } +} + +public extension CGSize { + var isUndefined: Bool { + return width.isNaN || height.isNaN + } +} + +public extension CGRect { + var isUndefined: Bool { + return origin.isUndefined || size.isUndefined + } +} diff --git a/SwiftBox/iOS.plist b/SwiftBox/iOS.plist index 89d6ec8..d3de8ee 100644 --- a/SwiftBox/iOS.plist +++ b/SwiftBox/iOS.plist @@ -7,7 +7,7 @@ CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - com.joshaber.$(PRODUCT_NAME:rfc1034identifier) + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/SwiftBoxDemo/SwiftBoxDemo/AppDelegate.swift b/SwiftBoxDemo/SwiftBoxDemo/AppDelegate.swift index f2cf4ff..619d171 100644 --- a/SwiftBoxDemo/SwiftBoxDemo/AppDelegate.swift +++ b/SwiftBoxDemo/SwiftBoxDemo/AppDelegate.swift @@ -14,8 +14,9 @@ class AppDelegate: NSObject, NSApplicationDelegate { @IBOutlet weak var window: NSWindow! func applicationDidFinishLaunching(notification: NSNotification) { - let contentView = window.contentView as NSView + let contentView = window.contentView as! NSView let parent = Node(size: contentView.frame.size, + direction: .Row, childAlignment: .Center, children: [ Node(flex: 75, diff --git a/SwiftBoxTests/Info.plist b/SwiftBoxTests/Info.plist index bced151..ba72822 100644 --- a/SwiftBoxTests/Info.plist +++ b/SwiftBoxTests/Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - com.joshaber.$(PRODUCT_NAME:rfc1034identifier) + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/SwiftBoxTests/SwiftBoxTests.swift b/SwiftBoxTests/SwiftBoxTests.swift index 8a50659..820b90e 100644 --- a/SwiftBoxTests/SwiftBoxTests.swift +++ b/SwiftBoxTests/SwiftBoxTests.swift @@ -13,22 +13,22 @@ import SwiftBox class SwiftBoxTests: XCTestCase { func testDescription() { let parent = Node(size: CGSize(width: 300, height: 300), - childAlignment: .Center, - direction: .Row, - children: [ - Node(flex: 75, - margin: Edges(left: 10, right: 10), - size: CGSize(width: 0, height: 100)), - Node(flex: 15, - margin: Edges(right: 10), - size: CGSize(width: 0, height: 50)), - Node(flex: 10, - margin: Edges(right: 10), - size: CGSize(width: 0, height: 180)), - ]) + children: [ + Node(size: CGSize(width: 0, height: 100), + margin: Edges(left: 10, right: 10), + flex: 75), + Node(size: CGSize(width: 0, height: 50), + margin: Edges(right: 10), + flex: 15), + Node(size: CGSize(width: 0, height: 180), + margin: Edges(right: 10), + flex: 10), + ], + direction: .row, + childAlignment: .center) let layout = parent.layout() - XCTAssert("\(layout)".utf16Count > 0, "Has a description.") + XCTAssert("\(layout)".utf16.count > 0, "Has a description.") } func testSizeParentBasedOnChildren() { @@ -59,7 +59,27 @@ class SwiftBoxTests: XCTestCase { return CGSize(width: 1, height: 1) }) - let layout = node.layout(maxWidth: maxWidth) + _ = node.layout(maxWidth: maxWidth) XCTAssertEqual(maxWidthGiven, maxWidth) } + + func testUndefinedPoint() { + XCTAssertFalse(CGPoint.zero.isUndefined, "ordinary point is not undefined") + XCTAssert(CGPoint(x: 0, y: Node.Undefined).isUndefined, "detects undefined point.y") + XCTAssert(CGPoint(x: Node.Undefined, y: 0).isUndefined, "detects undefined point.x") + } + + func testUndefinedSize() { + XCTAssertFalse(CGSize.zero.isUndefined, "ordinary size is not undefined") + XCTAssert(CGSize(width: 0, height: Node.Undefined).isUndefined, "detects undefined size.height") + XCTAssert(CGSize(width: Node.Undefined, height: 0).isUndefined, "detects undefined size.width") + } + + func testUndefinedRect() { + XCTAssertFalse(CGRect.infinite.isUndefined, "infinite rect is not undefined") + XCTAssertFalse(CGRect.null.isUndefined, "null rect is not undefined") + XCTAssertFalse(CGRect.zero.isUndefined, "zero rect is not undefined") + XCTAssert(CGRect(x: 0, y: 0, width: Node.Undefined, height: 0).isUndefined, "detects undefined size in rect") + XCTAssert(CGRect(x: Node.Undefined, y: 0, width: 1, height: 1).isUndefined, "detects undefined origin in rect") + } }