Tokamak/Sources/TokamakCore/Fiber/AlignmentID.swift

154 lines
4.5 KiB
Swift

// Copyright 2022 Tokamak contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Created by Carson Katri on 2/18/22.
//
import Foundation
/// Used to identify an alignment guide.
///
/// Typically, you would define an alignment guide inside
/// an extension on `HorizontalAlignment` or `VerticalAlignment`:
///
/// extension HorizontalAlignment {
/// private enum MyAlignmentGuide: AlignmentID {
/// static func defaultValue(in context: ViewDimensions) -> CGFloat {
/// return 0.0
/// }
/// }
/// public static let myAlignmentGuide = Self(MyAlignmentGuide.self)
/// }
///
/// Which you can then use with the `alignmentGuide` modifier:
///
/// VStack(alignment: .myAlignmentGuide) {
/// Text("Align Leading")
/// .border(.red)
/// .alignmentGuide(.myAlignmentGuide) { $0[.leading] }
/// Text("Align Trailing")
/// .border(.blue)
/// .alignmentGuide(.myAlignmentGuide) { $0[.trailing] }
/// }
/// .border(.green)
public protocol AlignmentID {
/// The default value for this alignment guide
/// when not set via the `alignmentGuide` modifier.
static func defaultValue(in context: ViewDimensions) -> CGFloat
}
/// An alignment position along the horizontal axis.
@frozen
public struct HorizontalAlignment: Equatable {
public static func == (lhs: Self, rhs: Self) -> Bool {
lhs.id == rhs.id
}
let id: AlignmentID.Type
public init(_ id: AlignmentID.Type) {
self.id = id
}
}
extension HorizontalAlignment {
public static let leading = Self(Leading.self)
private enum Leading: AlignmentID {
static func defaultValue(in context: ViewDimensions) -> CGFloat {
0
}
}
public static let center = Self(Center.self)
private enum Center: AlignmentID {
static func defaultValue(in context: ViewDimensions) -> CGFloat {
context.width / 2
}
}
public static let trailing = Self(Trailing.self)
private enum Trailing: AlignmentID {
static func defaultValue(in context: ViewDimensions) -> CGFloat {
context.width
}
}
}
@frozen
public struct VerticalAlignment: Equatable {
public static func == (lhs: Self, rhs: Self) -> Bool {
lhs.id == rhs.id
}
let id: AlignmentID.Type
public init(_ id: AlignmentID.Type) {
self.id = id
}
}
extension VerticalAlignment {
public static let top = Self(Top.self)
private enum Top: AlignmentID {
static func defaultValue(in context: ViewDimensions) -> CGFloat {
0
}
}
public static let center = Self(Center.self)
private enum Center: AlignmentID {
static func defaultValue(in context: ViewDimensions) -> CGFloat {
context.height / 2
}
}
public static let bottom = Self(Bottom.self)
private enum Bottom: AlignmentID {
static func defaultValue(in context: ViewDimensions) -> CGFloat {
context.height
}
}
// TODO: Add baseline vertical alignment guides.
// public static let firstTextBaseline: VerticalAlignment
// public static let lastTextBaseline: VerticalAlignment
}
/// An alignment in both axes.
public struct Alignment: Equatable {
public var horizontal: HorizontalAlignment
public var vertical: VerticalAlignment
public init(
horizontal: HorizontalAlignment,
vertical: VerticalAlignment
) {
self.horizontal = horizontal
self.vertical = vertical
}
public static let topLeading = Self(horizontal: .leading, vertical: .top)
public static let top = Self(horizontal: .center, vertical: .top)
public static let topTrailing = Self(horizontal: .trailing, vertical: .top)
public static let leading = Self(horizontal: .leading, vertical: .center)
public static let center = Self(horizontal: .center, vertical: .center)
public static let trailing = Self(horizontal: .trailing, vertical: .center)
public static let bottomLeading = Self(horizontal: .leading, vertical: .bottom)
public static let bottom = Self(horizontal: .center, vertical: .bottom)
public static let bottomTrailing = Self(horizontal: .trailing, vertical: .bottom)
}