Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add basic layout system support #7

Merged
merged 7 commits into from
Dec 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Sources/OpenSwiftUI/Internal/Other/Defaultable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//
// Defaultable.swift
// OpenSwiftUI
//
// Created by Kyle on 2023/12/16.
// Lastest Version: iOS 15.5
// Status: Complete

protocol Defaultable {
associatedtype Value
static var defaultValue: Value { get }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
//
// AlignmentID.swift
// OpenSwiftUI
//
// Created by Kyle on 2023/12/16.
// Lastest Version: iOS 15.5
// Status: Complete

#if canImport(Darwin)
import CoreGraphics
#elseif os(Linux)
import Foundation
#endif

/// A type that you use to create custom alignment guides.
///
/// Every built-in alignment guide that ``VerticalAlignment`` or
/// ``HorizontalAlignment`` defines as a static property, like
/// ``VerticalAlignment/top`` or ``HorizontalAlignment/leading``, has a
/// unique alignment identifier type that produces the default offset for
/// that guide. To create a custom alignment guide, define your own alignment
/// identifier as a type that conforms to the `AlignmentID` protocol, and
/// implement the required ``AlignmentID/defaultValue(in:)`` method:
///
/// private struct FirstThirdAlignment: AlignmentID {
/// static func defaultValue(in context: ViewDimensions) -> CGFloat {
/// context.height / 3
/// }
/// }
///
/// When implementing the method, calculate the guide's default offset
/// from the view's origin. If it's helpful, you can use information from the
/// ``ViewDimensions`` input in the calculation. This parameter provides context
/// about the specific view that's using the guide. The above example creates an
/// identifier called `FirstThirdAlignment` and calculates a default value
/// that's one-third of the height of the aligned view.
///
/// Use the identifier's type to create a static property in an extension of
/// one of the alignment guide types, like ``VerticalAlignment``:
///
/// extension VerticalAlignment {
/// static let firstThird = VerticalAlignment(FirstThirdAlignment.self)
/// }
///
/// You can apply your custom guide like any of the built-in guides. For
/// example, you can use an ``HStack`` to align its views at one-third
/// of their height using the guide defined above:
///
/// struct StripesGroup: View {
/// var body: some View {
/// HStack(alignment: .firstThird, spacing: 1) {
/// HorizontalStripes().frame(height: 60)
/// HorizontalStripes().frame(height: 120)
/// HorizontalStripes().frame(height: 90)
/// }
/// }
/// }
///
/// struct HorizontalStripes: View {
/// var body: some View {
/// VStack(spacing: 1) {
/// ForEach(0..<3) { _ in Color.blue }
/// }
/// }
/// }
///
/// Because each set of stripes has three equal, vertically stacked
/// rectangles, they align at the bottom edge of the top rectangle. This
/// corresponds in each case to a third of the overall height, as
/// measured from the origin at the top of each set of stripes:
///
/// ![Three vertical stacks of rectangles, arranged in a row.
/// The rectangles in each stack have the same height as each other, but
/// different heights than the rectangles in the other stacks. The bottom edges
/// of the top-most rectangle in each stack are aligned with each
/// other.](AlignmentId-1-iOS)
///
/// You can also use the ``View/alignmentGuide(_:computeValue:)-6y3u2`` view
/// modifier to alter the behavior of your custom guide for a view, as you
/// might alter a built-in guide. For example, you can change
/// one of the stacks of stripes from the previous example to align its
/// `firstThird` guide at two thirds of the height instead:
///
/// struct StripesGroupModified: View {
/// var body: some View {
/// HStack(alignment: .firstThird, spacing: 1) {
/// HorizontalStripes().frame(height: 60)
/// HorizontalStripes().frame(height: 120)
/// HorizontalStripes().frame(height: 90)
/// .alignmentGuide(.firstThird) { context in
/// 2 * context.height / 3
/// }
/// }
/// }
/// }
///
/// The modified guide calculation causes the affected view to place the
/// bottom edge of its middle rectangle on the `firstThird` guide, which aligns
/// with the bottom edge of the top rectangle in the other two groups:
///
/// ![Three vertical stacks of rectangles, arranged in a row.
/// The rectangles in each stack have the same height as each other, but
/// different heights than the rectangles in the other stacks. The bottom edges
/// of the top-most rectangle in the first two stacks are aligned with each
/// other, and with the bottom edge of the middle rectangle in the third
/// stack.](AlignmentId-2-iOS)
///
public protocol AlignmentID {
/// Calculates a default value for the corresponding guide in the specified
/// context.
///
/// Implement this method when you create a type that conforms to the
/// ``AlignmentID`` protocol. Use the method to calculate the default
/// offset of the corresponding alignment guide. SwiftUI interprets the
/// value that you return as an offset in the coordinate space of the
/// view that's being laid out. For example, you can use the context to
/// return a value that's one-third of the height of the view:
///
/// private struct FirstThirdAlignment: AlignmentID {
/// static func defaultValue(in context: ViewDimensions) -> CGFloat {
/// context.height / 3
/// }
/// }
///
/// You can override the default value that this method returns for a
/// particular guide by adding the
/// ``View/alignmentGuide(_:computeValue:)-9mdoh`` view modifier to a
/// particular view.
///
/// - Parameter context: The context of the view that you apply
/// the alignment guide to. The context gives you the view's dimensions,
/// as well as the values of other alignment guides that apply to the
/// view, including both built-in and custom guides. You can use any of
/// these values, if helpful, to calculate the value for your custom
/// guide.
///
/// - Returns: The offset of the guide from the origin in the
/// view's coordinate space.
static func defaultValue(in context: ViewDimensions) -> CGFloat

static func _combineExplicit(childValue: CGFloat, _ n: Int, into parentValue: inout CGFloat?)
}

extension AlignmentID {
// n == 0:
// value = childValue = c0
// parentValue = childValue = c0
// n == 1:
// value = parentValue! = c0
// parentValue = (c0 + c1) / 2
// n == 2:
// value = parentValue! = (c0 + c1) / 2
// parentValue = (c0 + c1 + c2) / 3
public static func _combineExplicit(childValue: CGFloat, _ n: Int, into parentValue: inout CGFloat?) {
let value = (n == 0) ? childValue : parentValue!
let n = CGFloat(n)
parentValue = (value * n + childValue) / (n + 1.0)
}
}

protocol FrameAlignment: AlignmentID {}

extension FrameAlignment {
static func _combineExplicit(childValue _: CGFloat, _: Int, into _: inout CGFloat?) {}

Check warning on line 164 in Sources/OpenSwiftUI/Layout/LayoutAdjustments/Alignment/AlignmentID.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUI/Layout/LayoutAdjustments/Alignment/AlignmentID.swift#L164

Added line #L164 was not covered by tests
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//
// AlignmentKey.swift
// OpenSwiftUI
//
// Created by Kyle on 2023/12/17.
// Lastest Version: iOS 15.5
// Status: Complete
// ID: E20796D15DD3D417699102559E024115

@usableFromInline
@frozen
struct AlignmentKey: Hashable, Comparable {
private let bits: UInt

@usableFromInline
static func < (lhs: AlignmentKey, rhs: AlignmentKey) -> Bool {
lhs.bits < rhs.bits

Check warning on line 17 in Sources/OpenSwiftUI/Layout/LayoutAdjustments/Alignment/AlignmentKey.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUI/Layout/LayoutAdjustments/Alignment/AlignmentKey.swift#L16-L17

Added lines #L16 - L17 were not covered by tests
}

@UnsafeLockedPointer
private static var typeCache = TypeCache(typeIDs: [:], types: [])

struct TypeCache {
var typeIDs: [ObjectIdentifier: UInt]
var types: [AlignmentID.Type]
}

init(id: AlignmentID.Type, axis _: Axis) {
let index: UInt
if let value = AlignmentKey.typeCache.typeIDs[ObjectIdentifier(id)] {
index = value
} else {
index = UInt(AlignmentKey.typeCache.types.count)
AlignmentKey.typeCache.types.append(id)
AlignmentKey.typeCache.typeIDs[ObjectIdentifier(id)] = index

Check warning on line 35 in Sources/OpenSwiftUI/Layout/LayoutAdjustments/Alignment/AlignmentKey.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUI/Layout/LayoutAdjustments/Alignment/AlignmentKey.swift#L28-L35

Added lines #L28 - L35 were not covered by tests
}
bits = index * 2 + 3

Check warning on line 37 in Sources/OpenSwiftUI/Layout/LayoutAdjustments/Alignment/AlignmentKey.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUI/Layout/LayoutAdjustments/Alignment/AlignmentKey.swift#L37

Added line #L37 was not covered by tests
}

var id: AlignmentID.Type {
AlignmentKey.typeCache.types[Int(bits / 2 - 1)]

Check warning on line 41 in Sources/OpenSwiftUI/Layout/LayoutAdjustments/Alignment/AlignmentKey.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUI/Layout/LayoutAdjustments/Alignment/AlignmentKey.swift#L40-L41

Added lines #L40 - L41 were not covered by tests
}
}
Loading