Skip to content
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
73 changes: 73 additions & 0 deletions Sources/OpenSwiftUICore/Layout/LayoutPriorityLayout.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//
// LayoutPriorityLayout.swift
// OpenSwiftUICore
//
// Audited for iOS 18.0
// Status: WIP

extension View {
/// Sets the priority by which a parent layout should apportion space to
/// this child.
///
/// Views typically have a default priority of `0` which causes space to be
/// apportioned evenly to all sibling views. Raising a view's layout
/// priority encourages the higher priority view to shrink later when the
/// group is shrunk and stretch sooner when the group is stretched.
///
/// HStack {
/// Text("This is a moderately long string.")
/// .font(.largeTitle)
/// .border(Color.gray)
///
/// Spacer()
///
/// Text("This is a higher priority string.")
/// .font(.largeTitle)
/// .layoutPriority(1)
/// .border(Color.gray)
/// }
///
/// In the example above, the first ``Text`` element has the default
/// priority `0` which causes its view to shrink dramatically due to the
/// higher priority of the second ``Text`` element, even though all of their
/// other attributes (font, font size and character count) are the same.
///
/// ![A screenshot showing twoText views different layout
/// priorities.](OpenSwiftUI-View-layoutPriority.png)
///
/// A parent layout offers the child views with the highest layout priority
/// all the space offered to the parent minus the minimum space required for
/// all its lower-priority children.
///
/// - Parameter value: The priority by which a parent layout apportions
/// space to the child.
@inlinable
nonisolated public func layoutPriority(_ value: Double) -> some View {
_trait(LayoutPriorityTraitKey.self, value)
}
}

@usableFromInline
package struct LayoutPriorityTraitKey: _ViewTraitKey {
@inlinable
package static var defaultValue: Double { .zero }
}

@available(*, unavailable)
extension LayoutPriorityTraitKey: Sendable {}

// FIXME
protocol UnaryLayout: ViewModifier {
static func makeViewImpl(
modifier: _GraphValue<Self>,
inputs: _ViewInputs,
body: @escaping (_Graph, _ViewInputs) -> _ViewOutputs
) -> _ViewOutputs
}

// FIXME
package struct LayoutPriorityLayout: UnaryLayout {
static func makeViewImpl(modifier: _GraphValue<LayoutPriorityLayout>, inputs: _ViewInputs, body: @escaping (_Graph, _ViewInputs) -> _ViewOutputs) -> _ViewOutputs {
.init()
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
//
// ViewTrait.swift
// OpenSwiftUI
// OpenSwiftUICore
//
// Audited for iOS 18.0
// Status: Blocked by ViewList
// Status: Complete
// ID: 9929B476764059557433A108298EE66F (SwiftUI)
// ID: 48526BA25CDCBF890FA91D018A5421B4 (SwiftUICore)

Expand All @@ -21,11 +21,11 @@ public protocol _ViewTraitKey {
static var defaultValue: Value { get }
}

// MARK: - _TraitWritingModifier [TODO]
// MARK: - _TraitWritingModifier

/// A view content adapter that associates a trait with its base content.
@frozen
public struct _TraitWritingModifier<Trait>: PrimitiveViewModifier where Trait : _ViewTraitKey {
public struct _TraitWritingModifier<Trait>: PrimitiveViewModifier where Trait: _ViewTraitKey {
public let value: Trait.Value

@inlinable
Expand All @@ -38,23 +38,47 @@ public struct _TraitWritingModifier<Trait>: PrimitiveViewModifier where Trait :
inputs: _ViewInputs,
body: @escaping (_Graph, _ViewInputs) -> _ViewOutputs
) -> _ViewOutputs {
preconditionFailure("TODO")
if Trait.self == LayoutPriorityTraitKey.self {
LayoutPriorityLayout.makeViewImpl(
modifier: modifier.unsafeCast(),
inputs: inputs,
body: body
)
} else {
body(_Graph(), inputs)
}
}

nonisolated public static func _makeViewList(
modifier: _GraphValue<Self>,
inputs: _ViewListInputs,
body: @escaping (_Graph, _ViewListInputs) -> _ViewListOutputs
) -> _ViewListOutputs {
preconditionFailure("TODO")
}

nonisolated public static func _viewListCount(inputs: _ViewListCountInputs, body: (_ViewListCountInputs) -> Int?) -> Int? {
preconditionFailure("TODO")
var inputs = inputs
if Trait.self == LayoutPriorityTraitKey.self,
!inputs.options.contains(.layoutPriorityIsTrait) {
let attribute = modifier.value.unsafeBitCast(to: _TraitWritingModifier<LayoutPriorityTraitKey>.self)
var outputs = body(_Graph(), inputs)
outputs.multiModifier(_GraphValue(attribute), inputs: inputs)
return outputs
} else {
let addTrait = AddTrait(modifier: modifier.value, traits: OptionalAttribute(inputs.traits))
let attribute = Attribute(addTrait)
inputs.addTraitKey(Trait.self)
inputs.traits = attribute
return body(_Graph(), inputs)
}
}

private struct AddTrait {
private struct AddTrait: Rule {
@Attribute var modifier: _TraitWritingModifier
@OptionalAttribute var traits: ViewTraitCollection?

var value: ViewTraitCollection {
var traits = traits ?? ViewTraitCollection()
traits[Trait.self] = modifier.value
return traits
}
}
}

Expand All @@ -65,16 +89,17 @@ extension View {
/// Associate a trait `value` for the given `key` for this view content.
@inlinable
nonisolated public func _trait<K>(_ key: K.Type, _ value: K.Value) -> some View where K: _ViewTraitKey {
return modifier(_TraitWritingModifier<K>(value: value))
modifier(_TraitWritingModifier<K>(value: value))
}
}

// MARK: - _ConditionalTraitWritingModifier [TODO]
// MARK: - _ConditionalTraitWritingModifier

/// Conditionally writes a trait.
@frozen
public struct _ConditionalTraitWritingModifier<Trait>: PrimitiveViewModifier where Trait : _ViewTraitKey {
public var value: Trait.Value

public var isEnabled: Bool

@_alwaysEmitIntoClient
Expand All @@ -88,26 +113,56 @@ public struct _ConditionalTraitWritingModifier<Trait>: PrimitiveViewModifier whe
inputs: _ViewInputs,
body: @escaping (_Graph, _ViewInputs) -> _ViewOutputs
) -> _ViewOutputs {
preconditionFailure("TODO")
_TraitWritingModifier<Trait>._makeView(
modifier: _GraphValue(Attribute(identifier: AnyAttribute.nil)),
inputs: inputs,
body: body
)
}

nonisolated public static func _makeViewList(
modifier: _GraphValue<Self>,
inputs: _ViewListInputs,
body: @escaping (_Graph, _ViewListInputs) -> _ViewListOutputs
) -> _ViewListOutputs {
preconditionFailure("TODO")
var inputs = inputs
if Trait.self == LayoutPriorityTraitKey.self,
!inputs.options.contains(.layoutPriorityIsTrait) {
let attribute = modifier.value.unsafeBitCast(to: _TraitWritingModifier<LayoutPriorityTraitKey>.self)
var outputs = body(_Graph(), inputs)
outputs.multiModifier(_GraphValue(attribute), inputs: inputs)
return outputs
} else {
let addTrait = ConditionalAddTrait(modifier: modifier.value, traits: OptionalAttribute(inputs.traits))
let attribute = Attribute(addTrait)
inputs.addTraitKey(Trait.self)
inputs.traits = attribute
return body(_Graph(), inputs)
}
}

nonisolated public static func _viewListCount(inputs: _ViewListCountInputs, body: (_ViewListCountInputs) -> Int?) -> Int? {
preconditionFailure("TODO")
private struct ConditionalAddTrait: Rule {
@Attribute var modifier: _ConditionalTraitWritingModifier
@OptionalAttribute var traits: ViewTraitCollection?

var value: ViewTraitCollection {
var traits = traits ?? ViewTraitCollection()
if modifier.isEnabled {
traits[Trait.self] = modifier.value
}
return traits
}
}
}

@available(*, unavailable)
extension _ConditionalTraitWritingModifier: Sendable {}

extension View {
/// Conditionally writes a trait.
@_alwaysEmitIntoClient
@MainActor
@preconcurrency
public func _trait<K>(_ key: K.Type = K.self, _ value: K.Value, isEnabled: Bool) -> some View where K: _ViewTraitKey {
modifier(_ConditionalTraitWritingModifier<K>(
value: value,
Expand All @@ -116,15 +171,49 @@ extension View {
}
}

// MARK: - TraitTransformerModifier [TODO]
// MARK: - TraitTransformerModifier

struct TraitTransformerModifier {

struct TraitTransformerModifier<Trait>: PrimitiveViewModifier where Trait: _ViewTraitKey {
var transform: (inout Trait.Value) -> Void

nonisolated public static func _makeView(
modifier: _GraphValue<Self>,
inputs: _ViewInputs,
body: @escaping (_Graph, _ViewInputs) -> _ViewOutputs
) -> _ViewOutputs {
body(_Graph(), inputs)
}

nonisolated public static func _makeViewList(
modifier: _GraphValue<Self>,
inputs: _ViewListInputs,
body: @escaping (_Graph, _ViewListInputs) -> _ViewListOutputs
) -> _ViewListOutputs {
var inputs = inputs
let trait = TransformTrait(modifier: modifier.value, traits: OptionalAttribute(inputs.traits))
let attribute = Attribute(trait)
inputs.traits = attribute
return body(_Graph(), inputs)
}

private struct TransformTrait: Rule {
@Attribute var modifier: TraitTransformerModifier
@OptionalAttribute var traits: ViewTraitCollection?

var value: ViewTraitCollection {
var traits = traits ?? ViewTraitCollection()
let transform = modifier.transform
var value = traits.value(for: Trait.self)
transform(&value)
traits[Trait.self] = value
return traits
}
}
}

extension View {
package func transformTrait<K>(_ key: K.Type = K.self, transform: @escaping (inout K.Value) -> Void) -> some View where K: _ViewTraitKey {
preconditionFailure("TODO")
modifier(TraitTransformerModifier<K>(transform: transform))
}
}

Expand Down
Loading