diff --git a/Benchmarks/LayoutPerformanceBenchmark/LayoutPerformanceBenchmark.swift b/Benchmarks/LayoutPerformanceBenchmark/LayoutPerformanceBenchmark.swift index 539f533013..dc34438718 100644 --- a/Benchmarks/LayoutPerformanceBenchmark/LayoutPerformanceBenchmark.swift +++ b/Benchmarks/LayoutPerformanceBenchmark/LayoutPerformanceBenchmark.swift @@ -34,9 +34,9 @@ struct Benchmarks { } @MainActor - func updateNode(_ node: ViewGraphNode, _ size: SIMD2) { - _ = node.update(proposedSize: size, environment: environment, dryRun: true) - _ = node.update(proposedSize: size, environment: environment, dryRun: false) + func updateNode(_ node: ViewGraphNode, _ size: ProposedViewSize) { + _ = node.computeLayout(proposedSize: size, environment: environment) + _ = node.commit() } #if BENCHMARK_VIZ @@ -44,7 +44,7 @@ struct Benchmarks { #endif @MainActor - func benchmarkLayout(of viewType: V.Type, _ size: SIMD2, _ label: String) { + func benchmarkLayout(of viewType: V.Type, _ size: ProposedViewSize, _ label: String) { #if BENCHMARK_VIZ benchmarkVisualizations.append(( label, @@ -62,8 +62,8 @@ struct Benchmarks { } // Register benchmarks - benchmarkLayout(of: GridView.self, SIMD2(800, 800), "grid") - benchmarkLayout(of: ScrollableMessageListView.self, SIMD2(800, 800), "message list") + benchmarkLayout(of: GridView.self, ProposedViewSize(800, 800), "grid") + benchmarkLayout(of: ScrollableMessageListView.self, ProposedViewSize(800, 800), "message list") #if BENCHMARK_VIZ let names = benchmarkVisualizations.map(\.name).joined(separator: " | ") diff --git a/Examples/Sources/WindowingExample/WindowingApp.swift b/Examples/Sources/WindowingExample/WindowingApp.swift index 4049151923..29056b0605 100644 --- a/Examples/Sources/WindowingExample/WindowingApp.swift +++ b/Examples/Sources/WindowingExample/WindowingApp.swift @@ -177,6 +177,8 @@ struct WindowingApp: App { } Image(Bundle.module.bundleURL.appendingPathComponent("Banner.png")) + .resizable() + .aspectRatio(contentMode: .fit) Divider() diff --git a/Sources/AppKitBackend/AppKitBackend.swift b/Sources/AppKitBackend/AppKitBackend.swift index 32adf784eb..6dbc4b7be3 100644 --- a/Sources/AppKitBackend/AppKitBackend.swift +++ b/Sources/AppKitBackend/AppKitBackend.swift @@ -31,12 +31,14 @@ public final class AppKitBackend: AppBackend { // We assume that all scrollers have their controlSize set to `.regular` by default. // The internet seems to indicate that this is true regardless of any system wide // preferences etc. - Int( + let result = Int( NSScroller.scrollerWidth( for: .regular, scrollerStyle: NSScroller.preferredScrollerStyle ).rounded(.awayFromZero) ) + print(result) + return result } private let appDelegate = NSCustomApplicationDelegate() @@ -528,7 +530,7 @@ public final class AppKitBackend: AppBackend { ) -> SIMD2 { if let proposedFrame, proposedFrame.x == 0 { // We want the text to have the same height as it would have if it were - // one pixel wide so that the layout doesn't suddely jump when the text + // one pixel wide so that the layout doesn't suddenly jump when the text // reaches zero width. let size = size( of: text, diff --git a/Sources/AppKitBackend/NSViewRepresentable.swift b/Sources/AppKitBackend/NSViewRepresentable.swift index 115b323a8a..07fdf356e9 100644 --- a/Sources/AppKitBackend/NSViewRepresentable.swift +++ b/Sources/AppKitBackend/NSViewRepresentable.swift @@ -1,228 +1,233 @@ -import AppKit -import SwiftCrossUI - -public struct NSViewRepresentableContext { - public let coordinator: Coordinator - public internal(set) var environment: EnvironmentValues -} - -/// A wrapper that you use to integrate an AppKit view into your SwiftCrossUI -/// view hierarchy. -public protocol NSViewRepresentable: View where Content == Never { - /// The underlying AppKit view. - associatedtype NSViewType: NSView - /// A type providing persistent storage for representable implementations. - associatedtype Coordinator = Void - - /// Create the initial NSView instance. - @MainActor - func makeNSView(context: NSViewRepresentableContext) -> NSViewType - - /// Update the view with new values. - /// - Parameters: - /// - nsView: The view to update. - /// - context: The context, including the coordinator and potentially new - /// environment values. - /// - Note: This may be called even when `context` has not changed. - @MainActor - func updateNSView( - _ nsView: NSViewType, - context: NSViewRepresentableContext - ) - - /// Make the coordinator for this view. - /// - /// The coordinator is used when the view needs to communicate changes to - /// the rest of the view hierarchy (i.e. through bindings), and is often the - /// view's delegate. - @MainActor - func makeCoordinator() -> Coordinator - - /// Compute the view's size. - /// - /// The default implementation uses `nsView.intrinsicContentSize` and - /// `nsView.sizeThatFits(_:)` to determine the return value. - /// - Parameters: - /// - proposal: The proposed frame for the view to render in. - /// - nsVIew: The view being queried for its preferred size. - /// - context: The context, including the coordinator and environment values. - /// - Returns: Information about the view's size. The ``SwiftCrossUI/ViewSize/size`` - /// property is what frame the view will actually be rendered with if the - /// current layout pass is not a dry run, while the other properties are - /// used to inform the layout engine how big or small the view can be. The - /// ``SwiftCrossUI/ViewSize/idealSize`` property should not vary with the - /// `proposal`, and should only depend on the view's contents. Pass `nil` - /// for the maximum width/height if the view has no maximum size (and - /// therefore may occupy the entire screen). - func determineViewSize( - for proposal: SIMD2, - nsView: NSViewType, - context: NSViewRepresentableContext - ) -> ViewSize - - /// Called to clean up the view when it's removed. - /// - /// This method is called after all AppKit lifecycle methods, such as - /// `nsView.didMoveToSuperview()`. The default implementation does nothing. - /// - Parameters: - /// - nsVIew: The view being dismantled. - /// - coordinator: The coordinator. - static func dismantleNSView(_ nsView: NSViewType, coordinator: Coordinator) -} - -extension NSViewRepresentable { - public static func dismantleNSView(_: NSViewType, coordinator _: Coordinator) { - // no-op - } - - public func determineViewSize( - for proposal: SIMD2, nsView: NSViewType, - context _: NSViewRepresentableContext - ) -> ViewSize { - let intrinsicSize = nsView.intrinsicContentSize - let sizeThatFits = nsView.fittingSize - - let roundedSizeThatFits = SIMD2( - Int(sizeThatFits.width.rounded(.up)), - Int(sizeThatFits.height.rounded(.up))) - let roundedIntrinsicSize = SIMD2( - Int(intrinsicSize.width.rounded(.awayFromZero)), - Int(intrinsicSize.height.rounded(.awayFromZero))) - - return ViewSize( - size: SIMD2( - intrinsicSize.width < 0.0 ? proposal.x : roundedSizeThatFits.x, - intrinsicSize.height < 0.0 ? proposal.y : roundedSizeThatFits.y - ), - // The 10 here is a somewhat arbitrary constant value so that it's always the same. - // See also `Color` and `Picker`, which use the same constant. - idealSize: SIMD2( - intrinsicSize.width < 0.0 ? 10 : roundedIntrinsicSize.x, - intrinsicSize.height < 0.0 ? 10 : roundedIntrinsicSize.y - ), - minimumWidth: max(0, roundedIntrinsicSize.x), - minimumHeight: max(0, roundedIntrinsicSize.x), - maximumWidth: nil, - maximumHeight: nil - ) - } -} - -extension View where Self: NSViewRepresentable { - public var body: Never { - preconditionFailure("This should never be called") - } - - public func children( - backend _: Backend, - snapshots _: [ViewGraphSnapshotter.NodeSnapshot]?, - environment _: EnvironmentValues - ) -> any ViewGraphNodeChildren { - EmptyViewChildren() - } - - public func layoutableChildren( - backend _: Backend, - children _: any ViewGraphNodeChildren - ) -> [LayoutSystem.LayoutableChild] { - [] - } - - public func asWidget( - _: any ViewGraphNodeChildren, - backend _: Backend - ) -> Backend.Widget { - if let widget = RepresentingWidget(representable: self) as? Backend.Widget { - return widget - } else { - fatalError("NSViewRepresentable requested by \(Backend.self)") - } - } - - public func update( - _ widget: Backend.Widget, - children: any ViewGraphNodeChildren, - proposedSize: SIMD2, - environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - guard let backend = backend as? AppKitBackend else { - fatalError("NSViewRepresentable updated by \(Backend.self)") - } - - let representingWidget = widget as! RepresentingWidget - representingWidget.update(with: environment) - - let size = representingWidget.representable.determineViewSize( - for: proposedSize, - nsView: representingWidget.subview, - context: representingWidget.context! - ) - - if !dryRun { - backend.setSize(of: representingWidget, to: size.size) - } - - return ViewUpdateResult.leafView(size: size) - } -} - -extension NSViewRepresentable where Coordinator == Void { - public func makeCoordinator() { - return () - } -} - -/// Exists to handle `deinit`, the rest of the stuff is just in here cause -/// it's a convenient location. -final class RepresentingWidget: NSView { - var representable: Representable - var context: NSViewRepresentableContext? - - init(representable: Representable) { - self.representable = representable - super.init(frame: .zero) - - self.translatesAutoresizingMaskIntoConstraints = false - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) is not used for this view") - } - - lazy var subview: Representable.NSViewType = { - let view = representable.makeNSView(context: context!) - - self.addSubview(view) - - view.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ - view.topAnchor.constraint(equalTo: self.topAnchor), - view.leadingAnchor.constraint(equalTo: self.leadingAnchor), - view.trailingAnchor.constraint(equalTo: self.trailingAnchor), - view.bottomAnchor.constraint(equalTo: self.bottomAnchor), - ]) - - return view - }() - - func update(with environment: EnvironmentValues) { - if context == nil { - context = .init( - coordinator: representable.makeCoordinator(), - environment: environment - ) - } else { - context!.environment = environment - representable.updateNSView(subview, context: context!) - } - } - - deinit { - if let context { - Representable.dismantleNSView(subview, coordinator: context.coordinator) - } - } -} +// import AppKit +// import SwiftCrossUI + +// public struct NSViewRepresentableContext { +// public let coordinator: Coordinator +// public internal(set) var environment: EnvironmentValues +// } + +// /// A wrapper that you use to integrate an AppKit view into your SwiftCrossUI +// /// view hierarchy. +// public protocol NSViewRepresentable: View where Content == Never { +// /// The underlying AppKit view. +// associatedtype NSViewType: NSView +// /// A type providing persistent storage for representable implementations. +// associatedtype Coordinator = Void + +// /// Create the initial NSView instance. +// @MainActor +// func makeNSView(context: NSViewRepresentableContext) -> NSViewType + +// /// Update the view with new values. +// /// - Parameters: +// /// - nsView: The view to update. +// /// - context: The context, including the coordinator and potentially new +// /// environment values. +// /// - Note: This may be called even when `context` has not changed. +// @MainActor +// func updateNSView( +// _ nsView: NSViewType, +// context: NSViewRepresentableContext +// ) + +// /// Make the coordinator for this view. +// /// +// /// The coordinator is used when the view needs to communicate changes to +// /// the rest of the view hierarchy (i.e. through bindings), and is often the +// /// view's delegate. +// @MainActor +// func makeCoordinator() -> Coordinator + +// /// Compute the view's size. +// /// +// /// The default implementation uses `nsView.intrinsicContentSize` and +// /// `nsView.sizeThatFits(_:)` to determine the return value. +// /// - Parameters: +// /// - proposal: The proposed frame for the view to render in. +// /// - nsVIew: The view being queried for its preferred size. +// /// - context: The context, including the coordinator and environment values. +// /// - Returns: Information about the view's size. The ``SwiftCrossUI/ViewSize/size`` +// /// property is what frame the view will actually be rendered with if the +// /// current layout pass is not a dry run, while the other properties are +// /// used to inform the layout engine how big or small the view can be. The +// /// ``SwiftCrossUI/ViewSize/idealSize`` property should not vary with the +// /// `proposal`, and should only depend on the view's contents. Pass `nil` +// /// for the maximum width/height if the view has no maximum size (and +// /// therefore may occupy the entire screen). +// func determineViewSize( +// for proposal: SIMD2, +// nsView: NSViewType, +// context: NSViewRepresentableContext +// ) -> ViewSize + +// /// Called to clean up the view when it's removed. +// /// +// /// This method is called after all AppKit lifecycle methods, such as +// /// `nsView.didMoveToSuperview()`. The default implementation does nothing. +// /// - Parameters: +// /// - nsVIew: The view being dismantled. +// /// - coordinator: The coordinator. +// static func dismantleNSView(_ nsView: NSViewType, coordinator: Coordinator) +// } + +// extension NSViewRepresentable { +// public static func dismantleNSView(_: NSViewType, coordinator _: Coordinator) { +// // no-op +// } + +// public func determineViewSize( +// for proposal: SIMD2, nsView: NSViewType, +// context _: NSViewRepresentableContext +// ) -> ViewSize { +// let intrinsicSize = nsView.intrinsicContentSize +// let sizeThatFits = nsView.fittingSize + +// let roundedSizeThatFits = SIMD2( +// Int(sizeThatFits.width.rounded(.up)), +// Int(sizeThatFits.height.rounded(.up))) +// let roundedIntrinsicSize = SIMD2( +// Int(intrinsicSize.width.rounded(.awayFromZero)), +// Int(intrinsicSize.height.rounded(.awayFromZero))) + +// return ViewSize( +// size: SIMD2( +// intrinsicSize.width < 0.0 ? proposal.x : roundedSizeThatFits.x, +// intrinsicSize.height < 0.0 ? proposal.y : roundedSizeThatFits.y +// ), +// // The 10 here is a somewhat arbitrary constant value so that it's always the same. +// // See also `Color` and `Picker`, which use the same constant. +// idealSize: SIMD2( +// intrinsicSize.width < 0.0 ? 10 : roundedIntrinsicSize.x, +// intrinsicSize.height < 0.0 ? 10 : roundedIntrinsicSize.y +// ), +// minimumWidth: max(0, roundedIntrinsicSize.x), +// minimumHeight: max(0, roundedIntrinsicSize.x), +// maximumWidth: nil, +// maximumHeight: nil +// ) +// } +// } + +// extension View where Self: NSViewRepresentable { +// public var body: Never { +// preconditionFailure("This should never be called") +// } + +// public func children( +// backend _: Backend, +// snapshots _: [ViewGraphSnapshotter.NodeSnapshot]?, +// environment _: EnvironmentValues +// ) -> any ViewGraphNodeChildren { +// EmptyViewChildren() +// } + +// public func layoutableChildren( +// backend _: Backend, +// children _: any ViewGraphNodeChildren +// ) -> [LayoutSystem.LayoutableChild] { +// [] +// } + +// public func asWidget( +// _: any ViewGraphNodeChildren, +// backend _: Backend +// ) -> Backend.Widget { +// if let widget = RepresentingWidget(representable: self) as? Backend.Widget { +// return widget +// } else { +// fatalError("NSViewRepresentable requested by \(Backend.self)") +// } +// } + +// public func computeLayout( +// _ widget: Backend.Widget, +// children: any ViewGraphNodeChildren, +// proposedSize: SIMD2, +// environment: EnvironmentValues, +// backend: Backend +// ) -> ViewLayoutResult { +// guard backend is AppKitBackend else { +// fatalError("NSViewRepresentable updated by \(Backend.self)") +// } + +// let representingWidget = widget as! RepresentingWidget +// representingWidget.update(with: environment) + +// let size = representingWidget.representable.determineViewSize( +// for: proposedSize, +// nsView: representingWidget.subview, +// context: representingWidget.context! +// ) + +// return ViewLayoutResult.leafView(size: size) +// } + +// public func commit( +// _ widget: Backend.Widget, +// children: any ViewGraphNodeChildren, +// layout: ViewLayoutResult, +// environment: EnvironmentValues, +// backend: Backend +// ) { +// backend.setSize(of: widget, to: layout.size.size) +// } +// } + +// extension NSViewRepresentable where Coordinator == Void { +// public func makeCoordinator() { +// return () +// } +// } + +// /// Exists to handle `deinit`, the rest of the stuff is just in here cause +// /// it's a convenient location. +// final class RepresentingWidget: NSView { +// var representable: Representable +// var context: NSViewRepresentableContext? + +// init(representable: Representable) { +// self.representable = representable +// super.init(frame: .zero) + +// self.translatesAutoresizingMaskIntoConstraints = false +// } + +// @available(*, unavailable) +// required init?(coder: NSCoder) { +// fatalError("init(coder:) is not used for this view") +// } + +// lazy var subview: Representable.NSViewType = { +// let view = representable.makeNSView(context: context!) + +// self.addSubview(view) + +// view.translatesAutoresizingMaskIntoConstraints = false +// NSLayoutConstraint.activate([ +// view.topAnchor.constraint(equalTo: self.topAnchor), +// view.leadingAnchor.constraint(equalTo: self.leadingAnchor), +// view.trailingAnchor.constraint(equalTo: self.trailingAnchor), +// view.bottomAnchor.constraint(equalTo: self.bottomAnchor), +// ]) + +// return view +// }() + +// func update(with environment: EnvironmentValues) { +// if context == nil { +// context = .init( +// coordinator: representable.makeCoordinator(), +// environment: environment +// ) +// } else { +// context!.environment = environment +// representable.updateNSView(subview, context: context!) +// } +// } + +// deinit { +// if let context { +// Representable.dismantleNSView(subview, coordinator: context.coordinator) +// } +// } +// } diff --git a/Sources/DummyBackend/DummyBackend.swift b/Sources/DummyBackend/DummyBackend.swift index ccc6cb9f72..2d007aebd0 100644 --- a/Sources/DummyBackend/DummyBackend.swift +++ b/Sources/DummyBackend/DummyBackend.swift @@ -386,7 +386,7 @@ public final class DummyBackend: AppBackend { let charactersPerLine = max(1, proposedFrame.x / characterWidth) let lineCount = (text.count + charactersPerLine - 1) / charactersPerLine return SIMD2( - characterWidth * charactersPerLine, + characterWidth * min(charactersPerLine, text.count), lineHeight * lineCount ) } diff --git a/Sources/Gtk/Generated/Entry.swift b/Sources/Gtk/Generated/Entry.swift index 26b74f887b..97cc334be7 100644 --- a/Sources/Gtk/Generated/Entry.swift +++ b/Sources/Gtk/Generated/Entry.swift @@ -336,439 +336,415 @@ open class Entry: Widget, CellEditable, Editable { SignalBox1.run(data, value1) } - addSignal(name: "notify::menu-entry-icon-primary-text", handler: gCallback(handler21)) { - [weak self] (param0: OpaquePointer) in - guard let self = self else { return } - self.notifyMenuEntryIconPrimaryText?(self, param0) - } - - let handler22: - @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = - { _, value1, data in - SignalBox1.run(data, value1) - } - - addSignal(name: "notify::menu-entry-icon-secondary-text", handler: gCallback(handler22)) { - [weak self] (param0: OpaquePointer) in - guard let self = self else { return } - self.notifyMenuEntryIconSecondaryText?(self, param0) - } - - let handler23: - @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = - { _, value1, data in - SignalBox1.run(data, value1) - } - - addSignal(name: "notify::overwrite-mode", handler: gCallback(handler23)) { + addSignal(name: "notify::overwrite-mode", handler: gCallback(handler21)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyOverwriteMode?(self, param0) } - let handler24: + let handler22: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::placeholder-text", handler: gCallback(handler24)) { + addSignal(name: "notify::placeholder-text", handler: gCallback(handler22)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyPlaceholderText?(self, param0) } - let handler25: + let handler23: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::primary-icon-activatable", handler: gCallback(handler25)) { + addSignal(name: "notify::primary-icon-activatable", handler: gCallback(handler23)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyPrimaryIconActivatable?(self, param0) } - let handler26: + let handler24: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::primary-icon-gicon", handler: gCallback(handler26)) { + addSignal(name: "notify::primary-icon-gicon", handler: gCallback(handler24)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyPrimaryIconGicon?(self, param0) } - let handler27: + let handler25: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::primary-icon-name", handler: gCallback(handler27)) { + addSignal(name: "notify::primary-icon-name", handler: gCallback(handler25)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyPrimaryIconName?(self, param0) } - let handler28: + let handler26: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::primary-icon-paintable", handler: gCallback(handler28)) { + addSignal(name: "notify::primary-icon-paintable", handler: gCallback(handler26)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyPrimaryIconPaintable?(self, param0) } - let handler29: + let handler27: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::primary-icon-sensitive", handler: gCallback(handler29)) { + addSignal(name: "notify::primary-icon-sensitive", handler: gCallback(handler27)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyPrimaryIconSensitive?(self, param0) } - let handler30: + let handler28: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::primary-icon-storage-type", handler: gCallback(handler30)) { + addSignal(name: "notify::primary-icon-storage-type", handler: gCallback(handler28)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyPrimaryIconStorageType?(self, param0) } - let handler31: + let handler29: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::primary-icon-tooltip-markup", handler: gCallback(handler31)) { + addSignal(name: "notify::primary-icon-tooltip-markup", handler: gCallback(handler29)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyPrimaryIconTooltipMarkup?(self, param0) } - let handler32: + let handler30: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::primary-icon-tooltip-text", handler: gCallback(handler32)) { + addSignal(name: "notify::primary-icon-tooltip-text", handler: gCallback(handler30)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyPrimaryIconTooltipText?(self, param0) } - let handler33: + let handler31: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::progress-fraction", handler: gCallback(handler33)) { + addSignal(name: "notify::progress-fraction", handler: gCallback(handler31)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyProgressFraction?(self, param0) } - let handler34: + let handler32: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::progress-pulse-step", handler: gCallback(handler34)) { + addSignal(name: "notify::progress-pulse-step", handler: gCallback(handler32)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyProgressPulseStep?(self, param0) } - let handler35: + let handler33: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::scroll-offset", handler: gCallback(handler35)) { + addSignal(name: "notify::scroll-offset", handler: gCallback(handler33)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyScrollOffset?(self, param0) } - let handler36: + let handler34: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::secondary-icon-activatable", handler: gCallback(handler36)) { + addSignal(name: "notify::secondary-icon-activatable", handler: gCallback(handler34)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifySecondaryIconActivatable?(self, param0) } - let handler37: + let handler35: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::secondary-icon-gicon", handler: gCallback(handler37)) { + addSignal(name: "notify::secondary-icon-gicon", handler: gCallback(handler35)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifySecondaryIconGicon?(self, param0) } - let handler38: + let handler36: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::secondary-icon-name", handler: gCallback(handler38)) { + addSignal(name: "notify::secondary-icon-name", handler: gCallback(handler36)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifySecondaryIconName?(self, param0) } - let handler39: + let handler37: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::secondary-icon-paintable", handler: gCallback(handler39)) { + addSignal(name: "notify::secondary-icon-paintable", handler: gCallback(handler37)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifySecondaryIconPaintable?(self, param0) } - let handler40: + let handler38: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::secondary-icon-sensitive", handler: gCallback(handler40)) { + addSignal(name: "notify::secondary-icon-sensitive", handler: gCallback(handler38)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifySecondaryIconSensitive?(self, param0) } - let handler41: + let handler39: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::secondary-icon-storage-type", handler: gCallback(handler41)) { + addSignal(name: "notify::secondary-icon-storage-type", handler: gCallback(handler39)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifySecondaryIconStorageType?(self, param0) } - let handler42: + let handler40: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::secondary-icon-tooltip-markup", handler: gCallback(handler42)) { + addSignal(name: "notify::secondary-icon-tooltip-markup", handler: gCallback(handler40)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifySecondaryIconTooltipMarkup?(self, param0) } - let handler43: + let handler41: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::secondary-icon-tooltip-text", handler: gCallback(handler43)) { + addSignal(name: "notify::secondary-icon-tooltip-text", handler: gCallback(handler41)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifySecondaryIconTooltipText?(self, param0) } - let handler44: + let handler42: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::show-emoji-icon", handler: gCallback(handler44)) { + addSignal(name: "notify::show-emoji-icon", handler: gCallback(handler42)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyShowEmojiIcon?(self, param0) } - let handler45: + let handler43: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::tabs", handler: gCallback(handler45)) { + addSignal(name: "notify::tabs", handler: gCallback(handler43)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyTabs?(self, param0) } - let handler46: + let handler44: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::text-length", handler: gCallback(handler46)) { + addSignal(name: "notify::text-length", handler: gCallback(handler44)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyTextLength?(self, param0) } - let handler47: + let handler45: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::truncate-multiline", handler: gCallback(handler47)) { + addSignal(name: "notify::truncate-multiline", handler: gCallback(handler45)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyTruncateMultiline?(self, param0) } - let handler48: + let handler46: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::visibility", handler: gCallback(handler48)) { + addSignal(name: "notify::visibility", handler: gCallback(handler46)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyVisibility?(self, param0) } - let handler49: + let handler47: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::editing-canceled", handler: gCallback(handler49)) { + addSignal(name: "notify::editing-canceled", handler: gCallback(handler47)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyEditingCanceled?(self, param0) } - let handler50: + let handler48: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::cursor-position", handler: gCallback(handler50)) { + addSignal(name: "notify::cursor-position", handler: gCallback(handler48)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyCursorPosition?(self, param0) } - let handler51: + let handler49: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::editable", handler: gCallback(handler51)) { + addSignal(name: "notify::editable", handler: gCallback(handler49)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyEditable?(self, param0) } - let handler52: + let handler50: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::enable-undo", handler: gCallback(handler52)) { + addSignal(name: "notify::enable-undo", handler: gCallback(handler50)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyEnableUndo?(self, param0) } - let handler53: + let handler51: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::max-width-chars", handler: gCallback(handler53)) { + addSignal(name: "notify::max-width-chars", handler: gCallback(handler51)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyMaxWidthChars?(self, param0) } - let handler54: + let handler52: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::selection-bound", handler: gCallback(handler54)) { + addSignal(name: "notify::selection-bound", handler: gCallback(handler52)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifySelectionBound?(self, param0) } - let handler55: + let handler53: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::text", handler: gCallback(handler55)) { + addSignal(name: "notify::text", handler: gCallback(handler53)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyText?(self, param0) } - let handler56: + let handler54: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::width-chars", handler: gCallback(handler56)) { + addSignal(name: "notify::width-chars", handler: gCallback(handler54)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyWidthChars?(self, param0) } - let handler57: + let handler55: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::xalign", handler: gCallback(handler57)) { + addSignal(name: "notify::xalign", handler: gCallback(handler55)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyXalign?(self, param0) @@ -937,10 +913,6 @@ open class Entry: Widget, CellEditable, Editable { public var notifyMaxLength: ((Entry, OpaquePointer) -> Void)? - public var notifyMenuEntryIconPrimaryText: ((Entry, OpaquePointer) -> Void)? - - public var notifyMenuEntryIconSecondaryText: ((Entry, OpaquePointer) -> Void)? - public var notifyOverwriteMode: ((Entry, OpaquePointer) -> Void)? public var notifyPlaceholderText: ((Entry, OpaquePointer) -> Void)? diff --git a/Sources/Gtk/Generated/FilterChange.swift b/Sources/Gtk/Generated/FilterChange.swift index ef229a3148..39c9f528a0 100644 --- a/Sources/Gtk/Generated/FilterChange.swift +++ b/Sources/Gtk/Generated/FilterChange.swift @@ -6,8 +6,6 @@ import CGtk /// If you are writing an implementation and are not sure which /// value to pass, `GTK_FILTER_CHANGE_DIFFERENT` is always a correct /// choice. -/// -/// New values may be added in the future. public enum FilterChange: GValueRepresentableEnum { public typealias GtkEnum = GtkFilterChange diff --git a/Sources/Gtk/Generated/GLArea.swift b/Sources/Gtk/Generated/GLArea.swift index 5247e75482..3d286c2204 100644 --- a/Sources/Gtk/Generated/GLArea.swift +++ b/Sources/Gtk/Generated/GLArea.swift @@ -41,13 +41,6 @@ import CGtk /// glClearColor (0, 0, 0, 0); /// glClear (GL_COLOR_BUFFER_BIT); /// -/// // record the active framebuffer ID, so we can return to it -/// // with `glBindFramebuffer (GL_FRAMEBUFFER, screen_fb)` should -/// // we, for instance, intend on utilizing the results of an -/// // intermediate render texture pass -/// GLuint screen_fb = 0; -/// glGetIntegerv (GL_FRAMEBUFFER_BINDING, &screen_fb); -/// /// // draw your object /// // draw_an_object (); /// diff --git a/Sources/Gtk/Generated/Image.swift b/Sources/Gtk/Generated/Image.swift index be138278de..455f4f9772 100644 --- a/Sources/Gtk/Generated/Image.swift +++ b/Sources/Gtk/Generated/Image.swift @@ -15,9 +15,9 @@ import CGtk /// If the file isn’t loaded successfully, the image will contain a /// “broken image” icon similar to that used in many web browsers. /// -/// If you want to handle errors in loading the file yourself, for example -/// by displaying an error message, then load the image with an image -/// loading framework such as libglycin, then create the `GtkImage` with +/// If you want to handle errors in loading the file yourself, +/// for example by displaying an error message, then load the image with +/// [ctor@Gdk.Texture.new_from_file], then create the `GtkImage` with /// [ctor@Gtk.Image.new_from_paintable]. /// /// Sometimes an application will want to avoid depending on external data @@ -53,9 +53,9 @@ open class Image: Widget { /// will display a “broken image” icon. This function never returns %NULL, /// it always returns a valid `GtkImage` widget. /// - /// If you need to detect failures to load the file, use an - /// image loading framework such as libglycin to load the file - /// yourself, then create the `GtkImage` from the texture. + /// If you need to detect failures to load the file, use + /// [ctor@Gdk.Texture.new_from_file] to load the file yourself, + /// then create the `GtkImage` from the texture. /// /// The storage type (see [method@Gtk.Image.get_storage_type]) /// of the returned image is not defined, it will be whatever @@ -96,13 +96,6 @@ open class Image: Widget { /// /// The `GtkImage` will track changes to the @paintable and update /// its size and contents in response to it. - /// - /// Note that paintables are still subject to the icon size that is - /// set on the image. If you want to display a paintable at its intrinsic - /// size, use [class@Gtk.Picture] instead. - /// - /// If @paintable is a [iface@Gtk.SymbolicPaintable], then it will be - /// recolored with the symbolic palette from the theme. public convenience init(paintable: OpaquePointer) { self.init( gtk_image_new_from_paintable(paintable) @@ -115,9 +108,9 @@ open class Image: Widget { /// display a “broken image” icon. This function never returns %NULL, /// it always returns a valid `GtkImage` widget. /// - /// If you need to detect failures to load the file, use an - /// image loading framework such as libglycin to load the file - /// yourself, then create the `GtkImage` from the texture. + /// If you need to detect failures to load the file, use + /// [ctor@GdkPixbuf.Pixbuf.new_from_file] to load the file yourself, + /// then create the `GtkImage` from the pixbuf. /// /// The storage type (see [method@Gtk.Image.get_storage_type]) of /// the returned image is not defined, it will be whatever is diff --git a/Sources/Gtk/Generated/Picture.swift b/Sources/Gtk/Generated/Picture.swift index 641cd3ecf3..df026896e3 100644 --- a/Sources/Gtk/Generated/Picture.swift +++ b/Sources/Gtk/Generated/Picture.swift @@ -16,8 +16,8 @@ import CGtk /// “broken image” icon similar to that used in many web browsers. /// If you want to handle errors in loading the file yourself, /// for example by displaying an error message, then load the image with -/// and image loading framework such as libglycin, then create the `GtkPicture` -/// with [ctor@Gtk.Picture.new_for_paintable]. +/// [ctor@Gdk.Texture.new_from_file], then create the `GtkPicture` with +/// [ctor@Gtk.Picture.new_for_paintable]. /// /// Sometimes an application will want to avoid depending on external data /// files, such as image files. See the documentation of `GResource` for details. diff --git a/Sources/Gtk/Generated/Popover.swift b/Sources/Gtk/Generated/Popover.swift index 0a68d9b281..03aee0c25b 100644 --- a/Sources/Gtk/Generated/Popover.swift +++ b/Sources/Gtk/Generated/Popover.swift @@ -5,17 +5,12 @@ import CGtk /// An example GtkPopover /// /// It is primarily meant to provide context-dependent information -/// or options. Popovers are attached to a parent widget. The parent widget -/// must support popover children, as [class@Gtk.MenuButton] and -/// [class@Gtk.PopoverMenuBar] do. If you want to make a custom widget that -/// has an attached popover, you need to call [method@Gtk.Popover.present] -/// in your [vfunc@Gtk.Widget.size_allocate] vfunc, in order to update the -/// positioning of the popover. +/// or options. Popovers are attached to a parent widget. By default, +/// they point to the whole widget area, although this behavior can be +/// changed with [method@Gtk.Popover.set_pointing_to]. /// /// The position of a popover relative to the widget it is attached to -/// can also be changed with [method@Gtk.Popover.set_position]. By default, -/// it points to the whole widget area, but it can be made to point to -/// a specific area using [method@Gtk.Popover.set_pointing_to]. +/// can also be changed with [method@Gtk.Popover.set_position] /// /// By default, `GtkPopover` performs a grab, in order to ensure input /// events get redirected to it while it is shown, and also so the popover diff --git a/Sources/Gtk3/Generated/Entry.swift b/Sources/Gtk3/Generated/Entry.swift index a7982980d9..c76a025c13 100644 --- a/Sources/Gtk3/Generated/Entry.swift +++ b/Sources/Gtk3/Generated/Entry.swift @@ -201,11 +201,6 @@ open class Entry: Widget, CellEditable, Editable { self.preeditChanged?(self, param0) } - addSignal(name: "toggle-direction") { [weak self] () in - guard let self = self else { return } - self.toggleDirection?(self) - } - addSignal(name: "toggle-overwrite") { [weak self] () in guard let self = self else { return } self.toggleOverwrite?(self) @@ -226,19 +221,19 @@ open class Entry: Widget, CellEditable, Editable { self.changed?(self) } - let handler17: + let handler16: @convention(c) (UnsafeMutableRawPointer, Int, Int, UnsafeMutableRawPointer) -> Void = { _, value1, value2, data in SignalBox2.run(data, value1, value2) } - addSignal(name: "delete-text", handler: gCallback(handler17)) { + addSignal(name: "delete-text", handler: gCallback(handler16)) { [weak self] (param0: Int, param1: Int) in guard let self = self else { return } self.deleteText?(self, param0, param1) } - let handler18: + let handler17: @convention(c) ( UnsafeMutableRawPointer, UnsafePointer, Int, gpointer, UnsafeMutableRawPointer @@ -248,631 +243,631 @@ open class Entry: Widget, CellEditable, Editable { data, value1, value2, value3) } - addSignal(name: "insert-text", handler: gCallback(handler18)) { + addSignal(name: "insert-text", handler: gCallback(handler17)) { [weak self] (param0: UnsafePointer, param1: Int, param2: gpointer) in guard let self = self else { return } self.insertText?(self, param0, param1, param2) } - let handler19: + let handler18: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::activates-default", handler: gCallback(handler19)) { + addSignal(name: "notify::activates-default", handler: gCallback(handler18)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyActivatesDefault?(self, param0) } - let handler20: + let handler19: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::attributes", handler: gCallback(handler20)) { + addSignal(name: "notify::attributes", handler: gCallback(handler19)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyAttributes?(self, param0) } - let handler21: + let handler20: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::buffer", handler: gCallback(handler21)) { + addSignal(name: "notify::buffer", handler: gCallback(handler20)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyBuffer?(self, param0) } - let handler22: + let handler21: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::caps-lock-warning", handler: gCallback(handler22)) { + addSignal(name: "notify::caps-lock-warning", handler: gCallback(handler21)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyCapsLockWarning?(self, param0) } - let handler23: + let handler22: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::completion", handler: gCallback(handler23)) { + addSignal(name: "notify::completion", handler: gCallback(handler22)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyCompletion?(self, param0) } - let handler24: + let handler23: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::cursor-position", handler: gCallback(handler24)) { + addSignal(name: "notify::cursor-position", handler: gCallback(handler23)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyCursorPosition?(self, param0) } - let handler25: + let handler24: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::editable", handler: gCallback(handler25)) { + addSignal(name: "notify::editable", handler: gCallback(handler24)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyEditable?(self, param0) } - let handler26: + let handler25: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::enable-emoji-completion", handler: gCallback(handler26)) { + addSignal(name: "notify::enable-emoji-completion", handler: gCallback(handler25)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyEnableEmojiCompletion?(self, param0) } - let handler27: + let handler26: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::has-frame", handler: gCallback(handler27)) { + addSignal(name: "notify::has-frame", handler: gCallback(handler26)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyHasFrame?(self, param0) } - let handler28: + let handler27: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::im-module", handler: gCallback(handler28)) { + addSignal(name: "notify::im-module", handler: gCallback(handler27)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyImModule?(self, param0) } - let handler29: + let handler28: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::inner-border", handler: gCallback(handler29)) { + addSignal(name: "notify::inner-border", handler: gCallback(handler28)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyInnerBorder?(self, param0) } - let handler30: + let handler29: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::input-hints", handler: gCallback(handler30)) { + addSignal(name: "notify::input-hints", handler: gCallback(handler29)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyInputHints?(self, param0) } - let handler31: + let handler30: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::input-purpose", handler: gCallback(handler31)) { + addSignal(name: "notify::input-purpose", handler: gCallback(handler30)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyInputPurpose?(self, param0) } - let handler32: + let handler31: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::invisible-char", handler: gCallback(handler32)) { + addSignal(name: "notify::invisible-char", handler: gCallback(handler31)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyInvisibleCharacter?(self, param0) } - let handler33: + let handler32: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::invisible-char-set", handler: gCallback(handler33)) { + addSignal(name: "notify::invisible-char-set", handler: gCallback(handler32)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyInvisibleCharacterSet?(self, param0) } - let handler34: + let handler33: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::max-length", handler: gCallback(handler34)) { + addSignal(name: "notify::max-length", handler: gCallback(handler33)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyMaxLength?(self, param0) } - let handler35: + let handler34: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::max-width-chars", handler: gCallback(handler35)) { + addSignal(name: "notify::max-width-chars", handler: gCallback(handler34)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyMaxWidthChars?(self, param0) } - let handler36: + let handler35: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::overwrite-mode", handler: gCallback(handler36)) { + addSignal(name: "notify::overwrite-mode", handler: gCallback(handler35)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyOverwriteMode?(self, param0) } - let handler37: + let handler36: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::placeholder-text", handler: gCallback(handler37)) { + addSignal(name: "notify::placeholder-text", handler: gCallback(handler36)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyPlaceholderText?(self, param0) } - let handler38: + let handler37: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::populate-all", handler: gCallback(handler38)) { + addSignal(name: "notify::populate-all", handler: gCallback(handler37)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyPopulateAll?(self, param0) } - let handler39: + let handler38: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::primary-icon-activatable", handler: gCallback(handler39)) { + addSignal(name: "notify::primary-icon-activatable", handler: gCallback(handler38)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyPrimaryIconActivatable?(self, param0) } - let handler40: + let handler39: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::primary-icon-gicon", handler: gCallback(handler40)) { + addSignal(name: "notify::primary-icon-gicon", handler: gCallback(handler39)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyPrimaryIconGicon?(self, param0) } - let handler41: + let handler40: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::primary-icon-name", handler: gCallback(handler41)) { + addSignal(name: "notify::primary-icon-name", handler: gCallback(handler40)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyPrimaryIconName?(self, param0) } - let handler42: + let handler41: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::primary-icon-pixbuf", handler: gCallback(handler42)) { + addSignal(name: "notify::primary-icon-pixbuf", handler: gCallback(handler41)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyPrimaryIconPixbuf?(self, param0) } - let handler43: + let handler42: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::primary-icon-sensitive", handler: gCallback(handler43)) { + addSignal(name: "notify::primary-icon-sensitive", handler: gCallback(handler42)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyPrimaryIconSensitive?(self, param0) } - let handler44: + let handler43: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::primary-icon-stock", handler: gCallback(handler44)) { + addSignal(name: "notify::primary-icon-stock", handler: gCallback(handler43)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyPrimaryIconStock?(self, param0) } - let handler45: + let handler44: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::primary-icon-storage-type", handler: gCallback(handler45)) { + addSignal(name: "notify::primary-icon-storage-type", handler: gCallback(handler44)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyPrimaryIconStorageType?(self, param0) } - let handler46: + let handler45: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::primary-icon-tooltip-markup", handler: gCallback(handler46)) { + addSignal(name: "notify::primary-icon-tooltip-markup", handler: gCallback(handler45)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyPrimaryIconTooltipMarkup?(self, param0) } - let handler47: + let handler46: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::primary-icon-tooltip-text", handler: gCallback(handler47)) { + addSignal(name: "notify::primary-icon-tooltip-text", handler: gCallback(handler46)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyPrimaryIconTooltipText?(self, param0) } - let handler48: + let handler47: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::progress-fraction", handler: gCallback(handler48)) { + addSignal(name: "notify::progress-fraction", handler: gCallback(handler47)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyProgressFraction?(self, param0) } - let handler49: + let handler48: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::progress-pulse-step", handler: gCallback(handler49)) { + addSignal(name: "notify::progress-pulse-step", handler: gCallback(handler48)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyProgressPulseStep?(self, param0) } - let handler50: + let handler49: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::scroll-offset", handler: gCallback(handler50)) { + addSignal(name: "notify::scroll-offset", handler: gCallback(handler49)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyScrollOffset?(self, param0) } - let handler51: + let handler50: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::secondary-icon-activatable", handler: gCallback(handler51)) { + addSignal(name: "notify::secondary-icon-activatable", handler: gCallback(handler50)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifySecondaryIconActivatable?(self, param0) } - let handler52: + let handler51: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::secondary-icon-gicon", handler: gCallback(handler52)) { + addSignal(name: "notify::secondary-icon-gicon", handler: gCallback(handler51)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifySecondaryIconGicon?(self, param0) } - let handler53: + let handler52: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::secondary-icon-name", handler: gCallback(handler53)) { + addSignal(name: "notify::secondary-icon-name", handler: gCallback(handler52)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifySecondaryIconName?(self, param0) } - let handler54: + let handler53: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::secondary-icon-pixbuf", handler: gCallback(handler54)) { + addSignal(name: "notify::secondary-icon-pixbuf", handler: gCallback(handler53)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifySecondaryIconPixbuf?(self, param0) } - let handler55: + let handler54: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::secondary-icon-sensitive", handler: gCallback(handler55)) { + addSignal(name: "notify::secondary-icon-sensitive", handler: gCallback(handler54)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifySecondaryIconSensitive?(self, param0) } - let handler56: + let handler55: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::secondary-icon-stock", handler: gCallback(handler56)) { + addSignal(name: "notify::secondary-icon-stock", handler: gCallback(handler55)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifySecondaryIconStock?(self, param0) } - let handler57: + let handler56: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::secondary-icon-storage-type", handler: gCallback(handler57)) { + addSignal(name: "notify::secondary-icon-storage-type", handler: gCallback(handler56)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifySecondaryIconStorageType?(self, param0) } - let handler58: + let handler57: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::secondary-icon-tooltip-markup", handler: gCallback(handler58)) { + addSignal(name: "notify::secondary-icon-tooltip-markup", handler: gCallback(handler57)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifySecondaryIconTooltipMarkup?(self, param0) } - let handler59: + let handler58: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::secondary-icon-tooltip-text", handler: gCallback(handler59)) { + addSignal(name: "notify::secondary-icon-tooltip-text", handler: gCallback(handler58)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifySecondaryIconTooltipText?(self, param0) } - let handler60: + let handler59: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::selection-bound", handler: gCallback(handler60)) { + addSignal(name: "notify::selection-bound", handler: gCallback(handler59)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifySelectionBound?(self, param0) } - let handler61: + let handler60: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::shadow-type", handler: gCallback(handler61)) { + addSignal(name: "notify::shadow-type", handler: gCallback(handler60)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyShadowType?(self, param0) } - let handler62: + let handler61: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::show-emoji-icon", handler: gCallback(handler62)) { + addSignal(name: "notify::show-emoji-icon", handler: gCallback(handler61)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyShowEmojiIcon?(self, param0) } - let handler63: + let handler62: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::tabs", handler: gCallback(handler63)) { + addSignal(name: "notify::tabs", handler: gCallback(handler62)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyTabs?(self, param0) } - let handler64: + let handler63: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::text", handler: gCallback(handler64)) { + addSignal(name: "notify::text", handler: gCallback(handler63)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyText?(self, param0) } - let handler65: + let handler64: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::text-length", handler: gCallback(handler65)) { + addSignal(name: "notify::text-length", handler: gCallback(handler64)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyTextLength?(self, param0) } - let handler66: + let handler65: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::truncate-multiline", handler: gCallback(handler66)) { + addSignal(name: "notify::truncate-multiline", handler: gCallback(handler65)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyTruncateMultiline?(self, param0) } - let handler67: + let handler66: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::visibility", handler: gCallback(handler67)) { + addSignal(name: "notify::visibility", handler: gCallback(handler66)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyVisibility?(self, param0) } - let handler68: + let handler67: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::width-chars", handler: gCallback(handler68)) { + addSignal(name: "notify::width-chars", handler: gCallback(handler67)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyWidthChars?(self, param0) } - let handler69: + let handler68: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::xalign", handler: gCallback(handler69)) { + addSignal(name: "notify::xalign", handler: gCallback(handler68)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyXalign?(self, param0) } - let handler70: + let handler69: @convention(c) (UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer) -> Void = { _, value1, data in SignalBox1.run(data, value1) } - addSignal(name: "notify::editing-canceled", handler: gCallback(handler70)) { + addSignal(name: "notify::editing-canceled", handler: gCallback(handler69)) { [weak self] (param0: OpaquePointer) in guard let self = self else { return } self.notifyEditingCanceled?(self, param0) @@ -999,8 +994,6 @@ open class Entry: Widget, CellEditable, Editable { /// connect to this signal. public var preeditChanged: ((Entry, UnsafePointer) -> Void)? - public var toggleDirection: ((Entry) -> Void)? - /// The ::toggle-overwrite signal is a /// [keybinding signal][GtkBindingSignal] /// which gets emitted to toggle the overwrite mode of the entry. diff --git a/Sources/SwiftCrossUI/Builders/SceneBuilder.swift b/Sources/SwiftCrossUI/Builders/SceneBuilder.swift index ee690388e7..c40e832119 100644 --- a/Sources/SwiftCrossUI/Builders/SceneBuilder.swift +++ b/Sources/SwiftCrossUI/Builders/SceneBuilder.swift @@ -9,258 +9,79 @@ public struct SceneBuilder { return content } - public static func buildBlock(_ scene0: Scene0, _ scene1: Scene1) - -> TupleScene2 - { + public static func buildBlock(_ scene0: Scene0, _ scene1: Scene1) -> TupleScene2 { return TupleScene2(scene0, scene1) } - public static func buildBlock( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2 - ) -> TupleScene3 { + public static func buildBlock(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2) -> TupleScene3 { return TupleScene3(scene0, scene1, scene2) } - public static func buildBlock( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3 - ) -> TupleScene4 { + public static func buildBlock(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3) -> TupleScene4 { return TupleScene4(scene0, scene1, scene2, scene3) } - public static func buildBlock< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene - >(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4) - -> TupleScene5 - { + public static func buildBlock(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4) -> TupleScene5 { return TupleScene5(scene0, scene1, scene2, scene3, scene4) } - public static func buildBlock< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene - >( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, - _ scene5: Scene5 - ) -> TupleScene6 { + public static func buildBlock(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5) -> TupleScene6 { return TupleScene6(scene0, scene1, scene2, scene3, scene4, scene5) } - public static func buildBlock< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene - >( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, - _ scene5: Scene5, _ scene6: Scene6 - ) -> TupleScene7 { + public static func buildBlock(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5, _ scene6: Scene6) -> TupleScene7 { return TupleScene7(scene0, scene1, scene2, scene3, scene4, scene5, scene6) } - public static func buildBlock< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene - >( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, - _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7 - ) -> TupleScene8 { + public static func buildBlock(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7) -> TupleScene8 { return TupleScene8(scene0, scene1, scene2, scene3, scene4, scene5, scene6, scene7) } - public static func buildBlock< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene - >( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, - _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8 - ) -> TupleScene9 { + public static func buildBlock(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8) -> TupleScene9 { return TupleScene9(scene0, scene1, scene2, scene3, scene4, scene5, scene6, scene7, scene8) } - public static func buildBlock< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene - >( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, - _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9 - ) -> TupleScene10< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9 - > { - return TupleScene10( - scene0, scene1, scene2, scene3, scene4, scene5, scene6, scene7, scene8, scene9) + public static func buildBlock(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9) -> TupleScene10 { + return TupleScene10(scene0, scene1, scene2, scene3, scene4, scene5, scene6, scene7, scene8, scene9) } - public static func buildBlock< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene, Scene10: Scene - >( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, - _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, - _ scene10: Scene10 - ) -> TupleScene11< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9, Scene10 - > { - return TupleScene11( - scene0, scene1, scene2, scene3, scene4, scene5, scene6, scene7, scene8, scene9, scene10) + public static func buildBlock(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, _ scene10: Scene10) -> TupleScene11 { + return TupleScene11(scene0, scene1, scene2, scene3, scene4, scene5, scene6, scene7, scene8, scene9, scene10) } - public static func buildBlock< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene, Scene10: Scene, Scene11: Scene - >( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, - _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, - _ scene10: Scene10, _ scene11: Scene11 - ) -> TupleScene12< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9, Scene10, - Scene11 - > { - return TupleScene12( - scene0, scene1, scene2, scene3, scene4, scene5, scene6, scene7, scene8, scene9, scene10, - scene11) + public static func buildBlock(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, _ scene10: Scene10, _ scene11: Scene11) -> TupleScene12 { + return TupleScene12(scene0, scene1, scene2, scene3, scene4, scene5, scene6, scene7, scene8, scene9, scene10, scene11) } - public static func buildBlock< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene, Scene10: Scene, Scene11: Scene, - Scene12: Scene - >( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, - _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, - _ scene10: Scene10, _ scene11: Scene11, _ scene12: Scene12 - ) -> TupleScene13< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9, Scene10, - Scene11, Scene12 - > { - return TupleScene13( - scene0, scene1, scene2, scene3, scene4, scene5, scene6, scene7, scene8, scene9, scene10, - scene11, scene12) + public static func buildBlock(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, _ scene10: Scene10, _ scene11: Scene11, _ scene12: Scene12) -> TupleScene13 { + return TupleScene13(scene0, scene1, scene2, scene3, scene4, scene5, scene6, scene7, scene8, scene9, scene10, scene11, scene12) } - public static func buildBlock< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene, Scene10: Scene, Scene11: Scene, - Scene12: Scene, Scene13: Scene - >( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, - _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, - _ scene10: Scene10, _ scene11: Scene11, _ scene12: Scene12, _ scene13: Scene13 - ) -> TupleScene14< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9, Scene10, - Scene11, Scene12, Scene13 - > { - return TupleScene14( - scene0, scene1, scene2, scene3, scene4, scene5, scene6, scene7, scene8, scene9, scene10, - scene11, scene12, scene13) + public static func buildBlock(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, _ scene10: Scene10, _ scene11: Scene11, _ scene12: Scene12, _ scene13: Scene13) -> TupleScene14 { + return TupleScene14(scene0, scene1, scene2, scene3, scene4, scene5, scene6, scene7, scene8, scene9, scene10, scene11, scene12, scene13) } - public static func buildBlock< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene, Scene10: Scene, Scene11: Scene, - Scene12: Scene, Scene13: Scene, Scene14: Scene - >( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, - _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, - _ scene10: Scene10, _ scene11: Scene11, _ scene12: Scene12, _ scene13: Scene13, - _ scene14: Scene14 - ) -> TupleScene15< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9, Scene10, - Scene11, Scene12, Scene13, Scene14 - > { - return TupleScene15( - scene0, scene1, scene2, scene3, scene4, scene5, scene6, scene7, scene8, scene9, scene10, - scene11, scene12, scene13, scene14) + public static func buildBlock(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, _ scene10: Scene10, _ scene11: Scene11, _ scene12: Scene12, _ scene13: Scene13, _ scene14: Scene14) -> TupleScene15 { + return TupleScene15(scene0, scene1, scene2, scene3, scene4, scene5, scene6, scene7, scene8, scene9, scene10, scene11, scene12, scene13, scene14) } - public static func buildBlock< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene, Scene10: Scene, Scene11: Scene, - Scene12: Scene, Scene13: Scene, Scene14: Scene, Scene15: Scene - >( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, - _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, - _ scene10: Scene10, _ scene11: Scene11, _ scene12: Scene12, _ scene13: Scene13, - _ scene14: Scene14, _ scene15: Scene15 - ) -> TupleScene16< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9, Scene10, - Scene11, Scene12, Scene13, Scene14, Scene15 - > { - return TupleScene16( - scene0, scene1, scene2, scene3, scene4, scene5, scene6, scene7, scene8, scene9, scene10, - scene11, scene12, scene13, scene14, scene15) + public static func buildBlock(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, _ scene10: Scene10, _ scene11: Scene11, _ scene12: Scene12, _ scene13: Scene13, _ scene14: Scene14, _ scene15: Scene15) -> TupleScene16 { + return TupleScene16(scene0, scene1, scene2, scene3, scene4, scene5, scene6, scene7, scene8, scene9, scene10, scene11, scene12, scene13, scene14, scene15) } - public static func buildBlock< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene, Scene10: Scene, Scene11: Scene, - Scene12: Scene, Scene13: Scene, Scene14: Scene, Scene15: Scene, Scene16: Scene - >( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, - _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, - _ scene10: Scene10, _ scene11: Scene11, _ scene12: Scene12, _ scene13: Scene13, - _ scene14: Scene14, _ scene15: Scene15, _ scene16: Scene16 - ) -> TupleScene17< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9, Scene10, - Scene11, Scene12, Scene13, Scene14, Scene15, Scene16 - > { - return TupleScene17( - scene0, scene1, scene2, scene3, scene4, scene5, scene6, scene7, scene8, scene9, scene10, - scene11, scene12, scene13, scene14, scene15, scene16) + public static func buildBlock(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, _ scene10: Scene10, _ scene11: Scene11, _ scene12: Scene12, _ scene13: Scene13, _ scene14: Scene14, _ scene15: Scene15, _ scene16: Scene16) -> TupleScene17 { + return TupleScene17(scene0, scene1, scene2, scene3, scene4, scene5, scene6, scene7, scene8, scene9, scene10, scene11, scene12, scene13, scene14, scene15, scene16) } - public static func buildBlock< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene, Scene10: Scene, Scene11: Scene, - Scene12: Scene, Scene13: Scene, Scene14: Scene, Scene15: Scene, Scene16: Scene, - Scene17: Scene - >( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, - _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, - _ scene10: Scene10, _ scene11: Scene11, _ scene12: Scene12, _ scene13: Scene13, - _ scene14: Scene14, _ scene15: Scene15, _ scene16: Scene16, _ scene17: Scene17 - ) -> TupleScene18< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9, Scene10, - Scene11, Scene12, Scene13, Scene14, Scene15, Scene16, Scene17 - > { - return TupleScene18( - scene0, scene1, scene2, scene3, scene4, scene5, scene6, scene7, scene8, scene9, scene10, - scene11, scene12, scene13, scene14, scene15, scene16, scene17) + public static func buildBlock(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, _ scene10: Scene10, _ scene11: Scene11, _ scene12: Scene12, _ scene13: Scene13, _ scene14: Scene14, _ scene15: Scene15, _ scene16: Scene16, _ scene17: Scene17) -> TupleScene18 { + return TupleScene18(scene0, scene1, scene2, scene3, scene4, scene5, scene6, scene7, scene8, scene9, scene10, scene11, scene12, scene13, scene14, scene15, scene16, scene17) } - public static func buildBlock< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene, Scene10: Scene, Scene11: Scene, - Scene12: Scene, Scene13: Scene, Scene14: Scene, Scene15: Scene, Scene16: Scene, - Scene17: Scene, Scene18: Scene - >( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, - _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, - _ scene10: Scene10, _ scene11: Scene11, _ scene12: Scene12, _ scene13: Scene13, - _ scene14: Scene14, _ scene15: Scene15, _ scene16: Scene16, _ scene17: Scene17, - _ scene18: Scene18 - ) -> TupleScene19< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9, Scene10, - Scene11, Scene12, Scene13, Scene14, Scene15, Scene16, Scene17, Scene18 - > { - return TupleScene19( - scene0, scene1, scene2, scene3, scene4, scene5, scene6, scene7, scene8, scene9, scene10, - scene11, scene12, scene13, scene14, scene15, scene16, scene17, scene18) + public static func buildBlock(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, _ scene10: Scene10, _ scene11: Scene11, _ scene12: Scene12, _ scene13: Scene13, _ scene14: Scene14, _ scene15: Scene15, _ scene16: Scene16, _ scene17: Scene17, _ scene18: Scene18) -> TupleScene19 { + return TupleScene19(scene0, scene1, scene2, scene3, scene4, scene5, scene6, scene7, scene8, scene9, scene10, scene11, scene12, scene13, scene14, scene15, scene16, scene17, scene18) } - public static func buildBlock< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene, Scene10: Scene, Scene11: Scene, - Scene12: Scene, Scene13: Scene, Scene14: Scene, Scene15: Scene, Scene16: Scene, - Scene17: Scene, Scene18: Scene, Scene19: Scene - >( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, - _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, - _ scene10: Scene10, _ scene11: Scene11, _ scene12: Scene12, _ scene13: Scene13, - _ scene14: Scene14, _ scene15: Scene15, _ scene16: Scene16, _ scene17: Scene17, - _ scene18: Scene18, _ scene19: Scene19 - ) -> TupleScene20< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9, Scene10, - Scene11, Scene12, Scene13, Scene14, Scene15, Scene16, Scene17, Scene18, Scene19 - > { - return TupleScene20( - scene0, scene1, scene2, scene3, scene4, scene5, scene6, scene7, scene8, scene9, scene10, - scene11, scene12, scene13, scene14, scene15, scene16, scene17, scene18, scene19) + public static func buildBlock(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, _ scene10: Scene10, _ scene11: Scene11, _ scene12: Scene12, _ scene13: Scene13, _ scene14: Scene14, _ scene15: Scene15, _ scene16: Scene16, _ scene17: Scene17, _ scene18: Scene18, _ scene19: Scene19) -> TupleScene20 { + return TupleScene20(scene0, scene1, scene2, scene3, scene4, scene5, scene6, scene7, scene8, scene9, scene10, scene11, scene12, scene13, scene14, scene15, scene16, scene17, scene18, scene19) } } diff --git a/Sources/SwiftCrossUI/Builders/TableRowBuilder.swift b/Sources/SwiftCrossUI/Builders/TableRowBuilder.swift deleted file mode 100644 index 2db59f9fb2..0000000000 --- a/Sources/SwiftCrossUI/Builders/TableRowBuilder.swift +++ /dev/null @@ -1,379 +0,0 @@ -// This file was generated using gyb. Do not edit it directly. Edit -// TableRowBuilder.swift.gyb instead. - -/// A result builder for constructing a collection of table columns. -@resultBuilder -public struct TableRowBuilder { - public static func buildBlock() -> EmptyTableRowContent { - EmptyTableRowContent() - } - - public static func buildBlock< - Content0: View - >( - _ column0: TableColumn - ) -> TupleTableRowContent1< - RowValue, Content0 - > { - TupleTableRowContent1( - column0 - ) - } - public static func buildBlock< - Content0: View, Content1: View - >( - _ column0: TableColumn, _ column1: TableColumn - ) -> TupleTableRowContent2< - RowValue, Content0, Content1 - > { - TupleTableRowContent2( - column0, column1 - ) - } - public static func buildBlock< - Content0: View, Content1: View, Content2: View - >( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn - ) -> TupleTableRowContent3< - RowValue, Content0, Content1, Content2 - > { - TupleTableRowContent3( - column0, column1, column2 - ) - } - public static func buildBlock< - Content0: View, Content1: View, Content2: View, Content3: View - >( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn - ) -> TupleTableRowContent4< - RowValue, Content0, Content1, Content2, Content3 - > { - TupleTableRowContent4( - column0, column1, column2, column3 - ) - } - public static func buildBlock< - Content0: View, Content1: View, Content2: View, Content3: View, Content4: View - >( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn - ) -> TupleTableRowContent5< - RowValue, Content0, Content1, Content2, Content3, Content4 - > { - TupleTableRowContent5( - column0, column1, column2, column3, column4 - ) - } - public static func buildBlock< - Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View - >( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn, _ column5: TableColumn - ) -> TupleTableRowContent6< - RowValue, Content0, Content1, Content2, Content3, Content4, Content5 - > { - TupleTableRowContent6( - column0, column1, column2, column3, column4, column5 - ) - } - public static func buildBlock< - Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View, Content6: View - >( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn, _ column5: TableColumn, - _ column6: TableColumn - ) -> TupleTableRowContent7< - RowValue, Content0, Content1, Content2, Content3, Content4, Content5, Content6 - > { - TupleTableRowContent7( - column0, column1, column2, column3, column4, column5, column6 - ) - } - public static func buildBlock< - Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View, Content6: View, Content7: View - >( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn, _ column5: TableColumn, - _ column6: TableColumn, _ column7: TableColumn - ) -> TupleTableRowContent8< - RowValue, Content0, Content1, Content2, Content3, Content4, Content5, Content6, Content7 - > { - TupleTableRowContent8( - column0, column1, column2, column3, column4, column5, column6, column7 - ) - } - public static func buildBlock< - Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View, Content6: View, Content7: View, Content8: View - >( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn, _ column5: TableColumn, - _ column6: TableColumn, _ column7: TableColumn, - _ column8: TableColumn - ) -> TupleTableRowContent9< - RowValue, Content0, Content1, Content2, Content3, Content4, Content5, Content6, Content7, - Content8 - > { - TupleTableRowContent9( - column0, column1, column2, column3, column4, column5, column6, column7, column8 - ) - } - public static func buildBlock< - Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View, Content6: View, Content7: View, Content8: View, Content9: View - >( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn, _ column5: TableColumn, - _ column6: TableColumn, _ column7: TableColumn, - _ column8: TableColumn, _ column9: TableColumn - ) -> TupleTableRowContent10< - RowValue, Content0, Content1, Content2, Content3, Content4, Content5, Content6, Content7, - Content8, Content9 - > { - TupleTableRowContent10( - column0, column1, column2, column3, column4, column5, column6, column7, column8, column9 - ) - } - public static func buildBlock< - Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View, Content6: View, Content7: View, Content8: View, Content9: View, - Content10: View - >( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn, _ column5: TableColumn, - _ column6: TableColumn, _ column7: TableColumn, - _ column8: TableColumn, _ column9: TableColumn, - _ column10: TableColumn - ) -> TupleTableRowContent11< - RowValue, Content0, Content1, Content2, Content3, Content4, Content5, Content6, Content7, - Content8, Content9, Content10 - > { - TupleTableRowContent11( - column0, column1, column2, column3, column4, column5, column6, column7, column8, - column9, column10 - ) - } - public static func buildBlock< - Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View, Content6: View, Content7: View, Content8: View, Content9: View, - Content10: View, Content11: View - >( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn, _ column5: TableColumn, - _ column6: TableColumn, _ column7: TableColumn, - _ column8: TableColumn, _ column9: TableColumn, - _ column10: TableColumn, _ column11: TableColumn - ) -> TupleTableRowContent12< - RowValue, Content0, Content1, Content2, Content3, Content4, Content5, Content6, Content7, - Content8, Content9, Content10, Content11 - > { - TupleTableRowContent12( - column0, column1, column2, column3, column4, column5, column6, column7, column8, - column9, column10, column11 - ) - } - public static func buildBlock< - Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View, Content6: View, Content7: View, Content8: View, Content9: View, - Content10: View, Content11: View, Content12: View - >( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn, _ column5: TableColumn, - _ column6: TableColumn, _ column7: TableColumn, - _ column8: TableColumn, _ column9: TableColumn, - _ column10: TableColumn, _ column11: TableColumn, - _ column12: TableColumn - ) -> TupleTableRowContent13< - RowValue, Content0, Content1, Content2, Content3, Content4, Content5, Content6, Content7, - Content8, Content9, Content10, Content11, Content12 - > { - TupleTableRowContent13( - column0, column1, column2, column3, column4, column5, column6, column7, column8, - column9, column10, column11, column12 - ) - } - public static func buildBlock< - Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View, Content6: View, Content7: View, Content8: View, Content9: View, - Content10: View, Content11: View, Content12: View, Content13: View - >( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn, _ column5: TableColumn, - _ column6: TableColumn, _ column7: TableColumn, - _ column8: TableColumn, _ column9: TableColumn, - _ column10: TableColumn, _ column11: TableColumn, - _ column12: TableColumn, _ column13: TableColumn - ) -> TupleTableRowContent14< - RowValue, Content0, Content1, Content2, Content3, Content4, Content5, Content6, Content7, - Content8, Content9, Content10, Content11, Content12, Content13 - > { - TupleTableRowContent14( - column0, column1, column2, column3, column4, column5, column6, column7, column8, - column9, column10, column11, column12, column13 - ) - } - public static func buildBlock< - Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View, Content6: View, Content7: View, Content8: View, Content9: View, - Content10: View, Content11: View, Content12: View, Content13: View, Content14: View - >( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn, _ column5: TableColumn, - _ column6: TableColumn, _ column7: TableColumn, - _ column8: TableColumn, _ column9: TableColumn, - _ column10: TableColumn, _ column11: TableColumn, - _ column12: TableColumn, _ column13: TableColumn, - _ column14: TableColumn - ) -> TupleTableRowContent15< - RowValue, Content0, Content1, Content2, Content3, Content4, Content5, Content6, Content7, - Content8, Content9, Content10, Content11, Content12, Content13, Content14 - > { - TupleTableRowContent15( - column0, column1, column2, column3, column4, column5, column6, column7, column8, - column9, column10, column11, column12, column13, column14 - ) - } - public static func buildBlock< - Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View, Content6: View, Content7: View, Content8: View, Content9: View, - Content10: View, Content11: View, Content12: View, Content13: View, Content14: View, - Content15: View - >( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn, _ column5: TableColumn, - _ column6: TableColumn, _ column7: TableColumn, - _ column8: TableColumn, _ column9: TableColumn, - _ column10: TableColumn, _ column11: TableColumn, - _ column12: TableColumn, _ column13: TableColumn, - _ column14: TableColumn, _ column15: TableColumn - ) -> TupleTableRowContent16< - RowValue, Content0, Content1, Content2, Content3, Content4, Content5, Content6, Content7, - Content8, Content9, Content10, Content11, Content12, Content13, Content14, Content15 - > { - TupleTableRowContent16( - column0, column1, column2, column3, column4, column5, column6, column7, column8, - column9, column10, column11, column12, column13, column14, column15 - ) - } - public static func buildBlock< - Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View, Content6: View, Content7: View, Content8: View, Content9: View, - Content10: View, Content11: View, Content12: View, Content13: View, Content14: View, - Content15: View, Content16: View - >( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn, _ column5: TableColumn, - _ column6: TableColumn, _ column7: TableColumn, - _ column8: TableColumn, _ column9: TableColumn, - _ column10: TableColumn, _ column11: TableColumn, - _ column12: TableColumn, _ column13: TableColumn, - _ column14: TableColumn, _ column15: TableColumn, - _ column16: TableColumn - ) -> TupleTableRowContent17< - RowValue, Content0, Content1, Content2, Content3, Content4, Content5, Content6, Content7, - Content8, Content9, Content10, Content11, Content12, Content13, Content14, Content15, - Content16 - > { - TupleTableRowContent17( - column0, column1, column2, column3, column4, column5, column6, column7, column8, - column9, column10, column11, column12, column13, column14, column15, column16 - ) - } - public static func buildBlock< - Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View, Content6: View, Content7: View, Content8: View, Content9: View, - Content10: View, Content11: View, Content12: View, Content13: View, Content14: View, - Content15: View, Content16: View, Content17: View - >( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn, _ column5: TableColumn, - _ column6: TableColumn, _ column7: TableColumn, - _ column8: TableColumn, _ column9: TableColumn, - _ column10: TableColumn, _ column11: TableColumn, - _ column12: TableColumn, _ column13: TableColumn, - _ column14: TableColumn, _ column15: TableColumn, - _ column16: TableColumn, _ column17: TableColumn - ) -> TupleTableRowContent18< - RowValue, Content0, Content1, Content2, Content3, Content4, Content5, Content6, Content7, - Content8, Content9, Content10, Content11, Content12, Content13, Content14, Content15, - Content16, Content17 - > { - TupleTableRowContent18( - column0, column1, column2, column3, column4, column5, column6, column7, column8, - column9, column10, column11, column12, column13, column14, column15, column16, column17 - ) - } - public static func buildBlock< - Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View, Content6: View, Content7: View, Content8: View, Content9: View, - Content10: View, Content11: View, Content12: View, Content13: View, Content14: View, - Content15: View, Content16: View, Content17: View, Content18: View - >( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn, _ column5: TableColumn, - _ column6: TableColumn, _ column7: TableColumn, - _ column8: TableColumn, _ column9: TableColumn, - _ column10: TableColumn, _ column11: TableColumn, - _ column12: TableColumn, _ column13: TableColumn, - _ column14: TableColumn, _ column15: TableColumn, - _ column16: TableColumn, _ column17: TableColumn, - _ column18: TableColumn - ) -> TupleTableRowContent19< - RowValue, Content0, Content1, Content2, Content3, Content4, Content5, Content6, Content7, - Content8, Content9, Content10, Content11, Content12, Content13, Content14, Content15, - Content16, Content17, Content18 - > { - TupleTableRowContent19( - column0, column1, column2, column3, column4, column5, column6, column7, column8, - column9, column10, column11, column12, column13, column14, column15, column16, column17, - column18 - ) - } - public static func buildBlock< - Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View, Content6: View, Content7: View, Content8: View, Content9: View, - Content10: View, Content11: View, Content12: View, Content13: View, Content14: View, - Content15: View, Content16: View, Content17: View, Content18: View, Content19: View - >( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn, _ column5: TableColumn, - _ column6: TableColumn, _ column7: TableColumn, - _ column8: TableColumn, _ column9: TableColumn, - _ column10: TableColumn, _ column11: TableColumn, - _ column12: TableColumn, _ column13: TableColumn, - _ column14: TableColumn, _ column15: TableColumn, - _ column16: TableColumn, _ column17: TableColumn, - _ column18: TableColumn, _ column19: TableColumn - ) -> TupleTableRowContent20< - RowValue, Content0, Content1, Content2, Content3, Content4, Content5, Content6, Content7, - Content8, Content9, Content10, Content11, Content12, Content13, Content14, Content15, - Content16, Content17, Content18, Content19 - > { - TupleTableRowContent20( - column0, column1, column2, column3, column4, column5, column6, column7, column8, - column9, column10, column11, column12, column13, column14, column15, column16, column17, - column18, column19 - ) - } -} diff --git a/Sources/SwiftCrossUI/Builders/TableRowBuilder.swift.gyb b/Sources/SwiftCrossUI/Builders/TableRowBuilder.swift.gyb deleted file mode 100644 index f47814a32c..0000000000 --- a/Sources/SwiftCrossUI/Builders/TableRowBuilder.swift.gyb +++ /dev/null @@ -1,27 +0,0 @@ -// This file was generated using gyb. Do not edit it directly. Edit -// TableRowBuilder.swift.gyb instead. -%{ -maximum_column_count = 20 -}% - -/// A result builder for constructing a collection of table columns. -@resultBuilder -public struct TableRowBuilder { - public static func buildBlock() -> EmptyTableRowContent { - EmptyTableRowContent() - } - - %for i in range(1, maximum_column_count + 1): - public static func buildBlock< - ${", ".join("Content%d: View" % j for j in range(i))} - >( - ${", ".join("_ column%d: TableColumn" % (j, j) for j in range(i))} - ) -> TupleTableRowContent${i}< - RowValue, ${", ".join("Content%d" % j for j in range(i))} - > { - TupleTableRowContent${i}( - ${", ".join("column%d" % j for j in range(i))} - ) - } - %end -} diff --git a/Sources/SwiftCrossUI/Builders/ViewBuilder.swift b/Sources/SwiftCrossUI/Builders/ViewBuilder.swift index 2b5252d367..81d052d982 100644 --- a/Sources/SwiftCrossUI/Builders/ViewBuilder.swift +++ b/Sources/SwiftCrossUI/Builders/ViewBuilder.swift @@ -13,216 +13,80 @@ public struct ViewBuilder { return TupleView1(view0) } - public static func buildBlock(_ view0: V0, _ view1: V1) -> TupleView2< - V0, V1 - > { + public static func buildBlock(_ view0: V0, _ view1: V1) -> TupleView2 { return TupleView2(view0, view1) } - public static func buildBlock( - _ view0: V0, _ view1: V1, _ view2: V2 - ) -> TupleView3 { + public static func buildBlock(_ view0: V0, _ view1: V1, _ view2: V2) -> TupleView3 { return TupleView3(view0, view1, view2) } - public static func buildBlock( - _ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3 - ) -> TupleView4 { + public static func buildBlock(_ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3) -> TupleView4 { return TupleView4(view0, view1, view2, view3) } - public static func buildBlock( - _ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3, _ view4: V4 - ) -> TupleView5 { + public static func buildBlock(_ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3, _ view4: V4) -> TupleView5 { return TupleView5(view0, view1, view2, view3, view4) } - public static func buildBlock( - _ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3, _ view4: V4, _ view5: V5 - ) -> TupleView6 { + public static func buildBlock(_ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3, _ view4: V4, _ view5: V5) -> TupleView6 { return TupleView6(view0, view1, view2, view3, view4, view5) } - public static func buildBlock< - V0: View, V1: View, V2: View, V3: View, V4: View, V5: View, V6: View - >(_ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3, _ view4: V4, _ view5: V5, _ view6: V6) - -> TupleView7 - { - return TupleView7( - view0, view1, view2, view3, view4, view5, view6) - } - - public static func buildBlock< - V0: View, V1: View, V2: View, V3: View, V4: View, V5: View, V6: View, V7: View - >( - _ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3, _ view4: V4, _ view5: V5, _ view6: V6, - _ view7: V7 - ) -> TupleView8 { - return TupleView8( - view0, view1, view2, view3, view4, view5, view6, view7) - } - - public static func buildBlock< - V0: View, V1: View, V2: View, V3: View, V4: View, V5: View, V6: View, V7: View, V8: View - >( - _ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3, _ view4: V4, _ view5: V5, _ view6: V6, - _ view7: V7, _ view8: V8 - ) -> TupleView9 { - return TupleView9( - view0, view1, view2, view3, view4, view5, view6, view7, view8) - } - - public static func buildBlock< - V0: View, V1: View, V2: View, V3: View, V4: View, V5: View, V6: View, V7: View, V8: View, - V9: View - >( - _ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3, _ view4: V4, _ view5: V5, _ view6: V6, - _ view7: V7, _ view8: V8, _ view9: V9 - ) -> TupleView10 { - return TupleView10( - view0, view1, view2, view3, view4, view5, view6, view7, view8, view9) - } - - public static func buildBlock< - V0: View, V1: View, V2: View, V3: View, V4: View, V5: View, V6: View, V7: View, V8: View, - V9: View, V10: View - >( - _ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3, _ view4: V4, _ view5: V5, _ view6: V6, - _ view7: V7, _ view8: V8, _ view9: V9, _ view10: V10 - ) -> TupleView11 { - return TupleView11( - view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10) - } - - public static func buildBlock< - V0: View, V1: View, V2: View, V3: View, V4: View, V5: View, V6: View, V7: View, V8: View, - V9: View, V10: View, V11: View - >( - _ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3, _ view4: V4, _ view5: V5, _ view6: V6, - _ view7: V7, _ view8: V8, _ view9: V9, _ view10: V10, _ view11: V11 - ) -> TupleView12 { - return TupleView12( - view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11) - } - - public static func buildBlock< - V0: View, V1: View, V2: View, V3: View, V4: View, V5: View, V6: View, V7: View, V8: View, - V9: View, V10: View, V11: View, V12: View - >( - _ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3, _ view4: V4, _ view5: V5, _ view6: V6, - _ view7: V7, _ view8: V8, _ view9: V9, _ view10: V10, _ view11: V11, _ view12: V12 - ) -> TupleView13 { - return TupleView13( - view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11, - view12) - } - - public static func buildBlock< - V0: View, V1: View, V2: View, V3: View, V4: View, V5: View, V6: View, V7: View, V8: View, - V9: View, V10: View, V11: View, V12: View, V13: View - >( - _ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3, _ view4: V4, _ view5: V5, _ view6: V6, - _ view7: V7, _ view8: V8, _ view9: V9, _ view10: V10, _ view11: V11, _ view12: V12, - _ view13: V13 - ) -> TupleView14 { - return TupleView14( - view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11, - view12, view13) - } - - public static func buildBlock< - V0: View, V1: View, V2: View, V3: View, V4: View, V5: View, V6: View, V7: View, V8: View, - V9: View, V10: View, V11: View, V12: View, V13: View, V14: View - >( - _ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3, _ view4: V4, _ view5: V5, _ view6: V6, - _ view7: V7, _ view8: V8, _ view9: V9, _ view10: V10, _ view11: V11, _ view12: V12, - _ view13: V13, _ view14: V14 - ) -> TupleView15 { - return TupleView15( - view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11, - view12, view13, view14) - } - - public static func buildBlock< - V0: View, V1: View, V2: View, V3: View, V4: View, V5: View, V6: View, V7: View, V8: View, - V9: View, V10: View, V11: View, V12: View, V13: View, V14: View, V15: View - >( - _ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3, _ view4: V4, _ view5: V5, _ view6: V6, - _ view7: V7, _ view8: V8, _ view9: V9, _ view10: V10, _ view11: V11, _ view12: V12, - _ view13: V13, _ view14: V14, _ view15: V15 - ) -> TupleView16 { - return TupleView16( - view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11, - view12, view13, view14, view15) - } - - public static func buildBlock< - V0: View, V1: View, V2: View, V3: View, V4: View, V5: View, V6: View, V7: View, V8: View, - V9: View, V10: View, V11: View, V12: View, V13: View, V14: View, V15: View, V16: View - >( - _ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3, _ view4: V4, _ view5: V5, _ view6: V6, - _ view7: V7, _ view8: V8, _ view9: V9, _ view10: V10, _ view11: V11, _ view12: V12, - _ view13: V13, _ view14: V14, _ view15: V15, _ view16: V16 - ) -> TupleView17 { - return TupleView17< - V0, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15, V16 - >( - view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11, - view12, view13, view14, view15, view16) - } - - public static func buildBlock< - V0: View, V1: View, V2: View, V3: View, V4: View, V5: View, V6: View, V7: View, V8: View, - V9: View, V10: View, V11: View, V12: View, V13: View, V14: View, V15: View, V16: View, - V17: View - >( - _ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3, _ view4: V4, _ view5: V5, _ view6: V6, - _ view7: V7, _ view8: V8, _ view9: V9, _ view10: V10, _ view11: V11, _ view12: V12, - _ view13: V13, _ view14: V14, _ view15: V15, _ view16: V16, _ view17: V17 - ) -> TupleView18 - { - return TupleView18< - V0, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15, V16, V17 - >( - view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11, - view12, view13, view14, view15, view16, view17) - } - - public static func buildBlock< - V0: View, V1: View, V2: View, V3: View, V4: View, V5: View, V6: View, V7: View, V8: View, - V9: View, V10: View, V11: View, V12: View, V13: View, V14: View, V15: View, V16: View, - V17: View, V18: View - >( - _ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3, _ view4: V4, _ view5: V5, _ view6: V6, - _ view7: V7, _ view8: V8, _ view9: V9, _ view10: V10, _ view11: V11, _ view12: V12, - _ view13: V13, _ view14: V14, _ view15: V15, _ view16: V16, _ view17: V17, _ view18: V18 - ) -> TupleView19< - V0, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15, V16, V17, V18 - > { - return TupleView19< - V0, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15, V16, V17, V18 - >( - view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11, - view12, view13, view14, view15, view16, view17, view18) - } - - public static func buildBlock< - V0: View, V1: View, V2: View, V3: View, V4: View, V5: View, V6: View, V7: View, V8: View, - V9: View, V10: View, V11: View, V12: View, V13: View, V14: View, V15: View, V16: View, - V17: View, V18: View, V19: View - >( - _ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3, _ view4: V4, _ view5: V5, _ view6: V6, - _ view7: V7, _ view8: V8, _ view9: V9, _ view10: V10, _ view11: V11, _ view12: V12, - _ view13: V13, _ view14: V14, _ view15: V15, _ view16: V16, _ view17: V17, _ view18: V18, - _ view19: V19 - ) -> TupleView20< - V0, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15, V16, V17, V18, V19 - > { - return TupleView20< - V0, V1, V2, V3, V4, V5, V6, V7, V8, V9, V10, V11, V12, V13, V14, V15, V16, V17, V18, V19 - >( - view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11, - view12, view13, view14, view15, view16, view17, view18, view19) + public static func buildBlock(_ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3, _ view4: V4, _ view5: V5, _ view6: V6) -> TupleView7 { + return TupleView7(view0, view1, view2, view3, view4, view5, view6) + } + + public static func buildBlock(_ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3, _ view4: V4, _ view5: V5, _ view6: V6, _ view7: V7) -> TupleView8 { + return TupleView8(view0, view1, view2, view3, view4, view5, view6, view7) + } + + public static func buildBlock(_ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3, _ view4: V4, _ view5: V5, _ view6: V6, _ view7: V7, _ view8: V8) -> TupleView9 { + return TupleView9(view0, view1, view2, view3, view4, view5, view6, view7, view8) + } + + public static func buildBlock(_ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3, _ view4: V4, _ view5: V5, _ view6: V6, _ view7: V7, _ view8: V8, _ view9: V9) -> TupleView10 { + return TupleView10(view0, view1, view2, view3, view4, view5, view6, view7, view8, view9) + } + + public static func buildBlock(_ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3, _ view4: V4, _ view5: V5, _ view6: V6, _ view7: V7, _ view8: V8, _ view9: V9, _ view10: V10) -> TupleView11 { + return TupleView11(view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10) + } + + public static func buildBlock(_ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3, _ view4: V4, _ view5: V5, _ view6: V6, _ view7: V7, _ view8: V8, _ view9: V9, _ view10: V10, _ view11: V11) -> TupleView12 { + return TupleView12(view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11) + } + + public static func buildBlock(_ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3, _ view4: V4, _ view5: V5, _ view6: V6, _ view7: V7, _ view8: V8, _ view9: V9, _ view10: V10, _ view11: V11, _ view12: V12) -> TupleView13 { + return TupleView13(view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11, view12) + } + + public static func buildBlock(_ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3, _ view4: V4, _ view5: V5, _ view6: V6, _ view7: V7, _ view8: V8, _ view9: V9, _ view10: V10, _ view11: V11, _ view12: V12, _ view13: V13) -> TupleView14 { + return TupleView14(view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11, view12, view13) + } + + public static func buildBlock(_ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3, _ view4: V4, _ view5: V5, _ view6: V6, _ view7: V7, _ view8: V8, _ view9: V9, _ view10: V10, _ view11: V11, _ view12: V12, _ view13: V13, _ view14: V14) -> TupleView15 { + return TupleView15(view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11, view12, view13, view14) + } + + public static func buildBlock(_ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3, _ view4: V4, _ view5: V5, _ view6: V6, _ view7: V7, _ view8: V8, _ view9: V9, _ view10: V10, _ view11: V11, _ view12: V12, _ view13: V13, _ view14: V14, _ view15: V15) -> TupleView16 { + return TupleView16(view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11, view12, view13, view14, view15) + } + + public static func buildBlock(_ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3, _ view4: V4, _ view5: V5, _ view6: V6, _ view7: V7, _ view8: V8, _ view9: V9, _ view10: V10, _ view11: V11, _ view12: V12, _ view13: V13, _ view14: V14, _ view15: V15, _ view16: V16) -> TupleView17 { + return TupleView17(view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11, view12, view13, view14, view15, view16) + } + + public static func buildBlock(_ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3, _ view4: V4, _ view5: V5, _ view6: V6, _ view7: V7, _ view8: V8, _ view9: V9, _ view10: V10, _ view11: V11, _ view12: V12, _ view13: V13, _ view14: V14, _ view15: V15, _ view16: V16, _ view17: V17) -> TupleView18 { + return TupleView18(view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11, view12, view13, view14, view15, view16, view17) + } + + public static func buildBlock(_ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3, _ view4: V4, _ view5: V5, _ view6: V6, _ view7: V7, _ view8: V8, _ view9: V9, _ view10: V10, _ view11: V11, _ view12: V12, _ view13: V13, _ view14: V14, _ view15: V15, _ view16: V16, _ view17: V17, _ view18: V18) -> TupleView19 { + return TupleView19(view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11, view12, view13, view14, view15, view16, view17, view18) + } + + public static func buildBlock(_ view0: V0, _ view1: V1, _ view2: V2, _ view3: V3, _ view4: V4, _ view5: V5, _ view6: V6, _ view7: V7, _ view8: V8, _ view9: V9, _ view10: V10, _ view11: V11, _ view12: V12, _ view13: V13, _ view14: V14, _ view15: V15, _ view16: V16, _ view17: V17, _ view18: V18, _ view19: V19) -> TupleView20 { + return TupleView20(view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11, view12, view13, view14, view15, view16, view17, view18, view19) } public static func buildEither(first component: A) -> EitherView { diff --git a/Sources/SwiftCrossUI/Environment/EnvironmentValues.swift b/Sources/SwiftCrossUI/Environment/EnvironmentValues.swift index cafe0dea63..264e8e741a 100644 --- a/Sources/SwiftCrossUI/Environment/EnvironmentValues.swift +++ b/Sources/SwiftCrossUI/Environment/EnvironmentValues.swift @@ -92,8 +92,8 @@ public struct EnvironmentValues { /// The style of list to use. package var listStyle: ListStyle - /// The style of toggle to use. - public var toggleStyle: ToggleStyle + // /// The style of toggle to use. + // public var toggleStyle: ToggleStyle /// Whether the text should be selectable. Set by ``View/textSelectionEnabled(_:)``. public var isTextSelectionEnabled: Bool @@ -213,7 +213,7 @@ public struct EnvironmentValues { window = nil extraValues = [:] listStyle = .default - toggleStyle = .button + // toggleStyle = .button isEnabled = true scrollDismissesKeyboardMode = .automatic isTextSelectionEnabled = false diff --git a/Sources/SwiftCrossUI/HotReloadingMacros.swift b/Sources/SwiftCrossUI/HotReloadingMacros.swift index 80a8e22baa..4c6daa8534 100644 --- a/Sources/SwiftCrossUI/HotReloadingMacros.swift +++ b/Sources/SwiftCrossUI/HotReloadingMacros.swift @@ -1,17 +1,17 @@ import Foundation -@attached( - peer, - names: named(hotReloadingExportedEntryPoint), - named(hotReloadingImportedEntryPoint), - named(hotReloadingHasConnectedToServer)) -@attached(member, names: named(entryPoint), named(hotReloadingExprIds)) -public macro HotReloadable() = - #externalMacro(module: "HotReloadingMacrosPlugin", type: "HotReloadableAppMacro") +// @attached( +// peer, +// names: named(hotReloadingExportedEntryPoint), +// named(hotReloadingImportedEntryPoint), +// named(hotReloadingHasConnectedToServer)) +// @attached(member, names: named(entryPoint), named(hotReloadingExprIds)) +// public macro HotReloadable() = +// #externalMacro(module: "HotReloadingMacrosPlugin", type: "HotReloadableAppMacro") -@freestanding(expression) -public macro hotReloadable(@ViewBuilder _ expr: () -> T) -> HotReloadableView = - #externalMacro(module: "HotReloadingMacrosPlugin", type: "HotReloadableExprMacro") +// @freestanding(expression) +// public macro hotReloadable(@ViewBuilder _ expr: () -> T) -> HotReloadableView = +// #externalMacro(module: "HotReloadingMacrosPlugin", type: "HotReloadableExprMacro") @_documentation(visibility: internal) public struct ExprLocation: Hashable { diff --git a/Sources/SwiftCrossUI/Layout/LayoutSystem.swift b/Sources/SwiftCrossUI/Layout/LayoutSystem.swift index 4618efff1e..89cd3a5399 100644 --- a/Sources/SwiftCrossUI/Layout/LayoutSystem.swift +++ b/Sources/SwiftCrossUI/Layout/LayoutSystem.swift @@ -8,7 +8,25 @@ public enum LayoutSystem { } static func roundSize(_ size: Double) -> Int { - Int(size.rounded(.towardZero)) + let size = size.rounded(.towardZero) + return if size >= Double(Int.max) { + Int.max + } else if size <= Double(Int.min) { + Int.min + } else { + Int(size) + } + } + + static func clamp(_ value: Double, minimum: Double?, maximum: Double?) -> Double { + var value = value + if let minimum { + value = max(minimum, value) + } + if let maximum { + value = min(maximum, value) + } + return value } static func aspectRatio(of frame: SIMD2) -> Double { @@ -45,29 +63,36 @@ public enum LayoutSystem { } public struct LayoutableChild { - private var update: + private var computeLayout: @MainActor ( - _ proposedSize: SIMD2, - _ environment: EnvironmentValues, - _ dryRun: Bool - ) -> ViewUpdateResult + _ proposedSize: ProposedViewSize, + _ environment: EnvironmentValues + ) -> ViewLayoutResult + private var _commit: @MainActor () -> ViewLayoutResult var tag: String? public init( - update: @escaping @MainActor (SIMD2, EnvironmentValues, Bool) -> ViewUpdateResult, + computeLayout: @escaping @MainActor (ProposedViewSize, EnvironmentValues) -> ViewLayoutResult, + commit: @escaping @MainActor () -> ViewLayoutResult, tag: String? = nil ) { - self.update = update + self.computeLayout = computeLayout + self._commit = commit self.tag = tag } @MainActor - public func update( - proposedSize: SIMD2, + public func computeLayout( + proposedSize: ProposedViewSize, environment: EnvironmentValues, dryRun: Bool = false - ) -> ViewUpdateResult { - update(proposedSize, environment, dryRun) + ) -> ViewLayoutResult { + computeLayout(proposedSize, environment) + } + + @MainActor + public func commit() -> ViewLayoutResult { + _commit() } } @@ -77,68 +102,94 @@ public enum LayoutSystem { /// ``Group`` to avoid changing stack layout participation (since ``Group`` /// is meant to appear completely invisible to the layout system). @MainActor - public static func updateStackLayout( + static func computeStackLayout( container: Backend.Widget, children: [LayoutableChild], - proposedSize: SIMD2, + cache: inout StackLayoutCache, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend, - dryRun: Bool, inheritStackLayoutParticipation: Bool = false - ) -> ViewUpdateResult { + ) -> ViewLayoutResult { let spacing = environment.layoutSpacing - let alignment = environment.layoutAlignment let orientation = environment.layoutOrientation + let perpendicularOrientation = orientation.perpendicular - var renderedChildren: [ViewUpdateResult] = Array( - repeating: ViewUpdateResult.leafView(size: .empty), + var renderedChildren: [ViewLayoutResult] = Array( + repeating: ViewLayoutResult.leafView(size: .zero), count: children.count ) - // Figure out which views to treat as hidden. This could be the cause - // of issues if a view has some threshold at which it suddenly becomes - // invisible. - var isHidden = [Bool](repeating: false, count: children.count) - for (i, child) in children.enumerated() { - let result = child.update( - proposedSize: proposedSize, - environment: environment, - dryRun: true + let stackLength = proposedSize[component: orientation] + if stackLength == 0 || stackLength == .infinity || stackLength == nil { + var resultLength: Double = 0 + var resultWidth: Double = 0 + var results: [ViewLayoutResult] = [] + for child in children { + let result = child.computeLayout( + proposedSize: proposedSize, + environment: environment + ) + resultLength += result.size[component: orientation] + resultWidth = max(resultWidth, result.size[component: perpendicularOrientation]) + results.append(result) + } + var size = ViewSize.zero + size[component: orientation] = resultLength + size[component: perpendicularOrientation] = resultWidth + + // In this case, flexibility doesn't matter. We set the ordering to + // nil to signal to commitStackLayout that it can ignore flexibility. + cache.lastFlexibilityOrdering = nil + cache.lastHiddenChildren = results.map(\.participatesInStackLayouts).map(!) + cache.redistributeSpaceOnCommit = false + + return ViewLayoutResult( + size: size, + childResults: results, + participateInStackLayoutsWhenEmpty: + results.contains(where: \.participateInStackLayoutsWhenEmpty), + preferencesOverlay: nil ) - isHidden[i] = !result.participatesInStackLayouts + } + + guard let stackLength else { + fatalError("unreachable") } // My thanks go to this great article for investigating and explaining // how SwiftUI determines child view 'flexibility': // https://www.objc.io/blog/2020/11/10/hstacks-child-ordering/ + var isHidden = [Bool](repeating: false, count: children.count) + var minimumProposedSize = proposedSize + minimumProposedSize[component: orientation] = 0 + var maximumProposedSize = proposedSize + maximumProposedSize[component: orientation] = .infinity + let flexibilities = children.enumerated().map { i, child in + let minimumResult = child.computeLayout( + proposedSize: minimumProposedSize, + environment: environment + ) + let maximumResult = child.computeLayout( + proposedSize: maximumProposedSize, + environment: environment + ) + isHidden[i] = !minimumResult.participatesInStackLayouts + let maximum = maximumResult.size[component: orientation] + let minimum = minimumResult.size[component: orientation] + return maximum - minimum + } let visibleChildrenCount = isHidden.filter { hidden in !hidden }.count - let totalSpacing = max(visibleChildrenCount - 1, 0) * spacing - let proposedSizeWithoutSpacing = SIMD2( - proposedSize.x - (orientation == .horizontal ? totalSpacing : 0), - proposedSize.y - (orientation == .vertical ? totalSpacing : 0) - ) - let flexibilities = children.map { child in - let size = child.update( - proposedSize: proposedSizeWithoutSpacing, - environment: environment, - dryRun: true - ).size - return switch orientation { - case .horizontal: - size.maximumWidth - Double(size.minimumWidth) - case .vertical: - size.maximumHeight - Double(size.minimumHeight) - } - } + let totalSpacing = Double(max(visibleChildrenCount - 1, 0) * spacing) let sortedChildren = zip(children.enumerated(), flexibilities) .sorted { first, second in first.1 <= second.1 } .map(\.0) - var spaceUsedAlongStackAxis = 0 + var spaceUsedAlongStackAxis: Double = 0 var childrenRemaining = visibleChildrenCount for (index, child) in sortedChildren { // No need to render visible children. @@ -146,10 +197,9 @@ public enum LayoutSystem { // Update child in case it has just changed from visible to hidden, // and to make sure that the view is still hidden (if it's not then // it's a bug with either the view or the layout system). - let result = child.update( + let result = child.computeLayout( proposedSize: .zero, - environment: environment, - dryRun: dryRun + environment: environment ) if result.participatesInStackLayouts { print( @@ -160,154 +210,130 @@ public enum LayoutSystem { ) } renderedChildren[index] = result - renderedChildren[index].size = .hidden + renderedChildren[index].participateInStackLayoutsWhenEmpty = false + renderedChildren[index].size = .zero continue } - let proposedWidth: Double - let proposedHeight: Double - switch orientation { - case .horizontal: - proposedWidth = - Double(max(proposedSize.x - spaceUsedAlongStackAxis - totalSpacing, 0)) - / Double(childrenRemaining) - proposedHeight = Double(proposedSize.y) - case .vertical: - proposedHeight = - Double(max(proposedSize.y - spaceUsedAlongStackAxis - totalSpacing, 0)) - / Double(childrenRemaining) - proposedWidth = Double(proposedSize.x) - } + var proposedChildSize = proposedSize + proposedChildSize[component: orientation] = + max(stackLength - spaceUsedAlongStackAxis - totalSpacing, 0) / Double(childrenRemaining) - let childResult = child.update( - proposedSize: SIMD2( - Int(proposedWidth.rounded(.towardZero)), - Int(proposedHeight.rounded(.towardZero)) - ), - environment: environment, - dryRun: dryRun + let childResult = child.computeLayout( + proposedSize: proposedChildSize, + environment: environment ) renderedChildren[index] = childResult childrenRemaining -= 1 - switch orientation { - case .horizontal: - spaceUsedAlongStackAxis += childResult.size.size.x - case .vertical: - spaceUsedAlongStackAxis += childResult.size.size.y - } + spaceUsedAlongStackAxis += childResult.size[component: orientation] } - let size: SIMD2 - let idealSize: SIMD2 - let idealWidthForProposedHeight: Int - let idealHeightForProposedWidth: Int - let minimumWidth: Int - let minimumHeight: Int - let maximumWidth: Double? - let maximumHeight: Double? - switch orientation { - case .horizontal: - size = SIMD2( - renderedChildren.map(\.size.size.x).reduce(0, +) + totalSpacing, - renderedChildren.map(\.size.size.y).max() ?? 0 - ) - idealSize = SIMD2( - renderedChildren.map(\.size.idealSize.x).reduce(0, +) + totalSpacing, - renderedChildren.map(\.size.idealSize.y).max() ?? 0 - ) - minimumWidth = renderedChildren.map(\.size.minimumWidth).reduce(0, +) + totalSpacing - minimumHeight = renderedChildren.map(\.size.minimumHeight).max() ?? 0 - maximumWidth = - renderedChildren.map(\.size.maximumWidth).reduce(0, +) + Double(totalSpacing) - maximumHeight = renderedChildren.map(\.size.maximumHeight).max() - idealWidthForProposedHeight = - renderedChildren.map(\.size.idealWidthForProposedHeight).reduce(0, +) - + totalSpacing - idealHeightForProposedWidth = - renderedChildren.map(\.size.idealHeightForProposedWidth).max() ?? 0 - case .vertical: - size = SIMD2( - renderedChildren.map(\.size.size.x).max() ?? 0, - renderedChildren.map(\.size.size.y).reduce(0, +) + totalSpacing - ) - idealSize = SIMD2( - renderedChildren.map(\.size.idealSize.x).max() ?? 0, - renderedChildren.map(\.size.idealSize.y).reduce(0, +) + totalSpacing - ) - minimumWidth = renderedChildren.map(\.size.minimumWidth).max() ?? 0 - minimumHeight = - renderedChildren.map(\.size.minimumHeight).reduce(0, +) + totalSpacing - maximumWidth = renderedChildren.map(\.size.maximumWidth).max() - maximumHeight = - renderedChildren.map(\.size.maximumHeight).reduce(0, +) + Double(totalSpacing) - idealWidthForProposedHeight = - renderedChildren.map(\.size.idealWidthForProposedHeight).max() ?? 0 - idealHeightForProposedWidth = - renderedChildren.map(\.size.idealHeightForProposedWidth).reduce(0, +) - + totalSpacing - } + var size = ViewSize.zero + size[component: orientation] = + renderedChildren.map(\.size[component: orientation]).reduce(0, +) + totalSpacing + size[component: perpendicularOrientation] = + renderedChildren.map(\.size[component: orientation]).max() ?? 0 - if !dryRun { - backend.setSize(of: container, to: size) + cache.lastFlexibilityOrdering = sortedChildren.map(\.offset) + cache.lastHiddenChildren = isHidden - var x = 0 - var y = 0 - for (index, childSize) in renderedChildren.enumerated() { - // Avoid the whole iteration if the child is hidden. If there - // are weird positioning issues for views that do strange things - // then this could be the cause. - if isHidden[index] { - continue - } + // When the length along the stacking axis is concrete (i.e. flexibility + // matters) and the perpendicular axis is unspecified (nil), then we need + // to re-run the space distribution algorithm with our final size during + // the commit phase. This opens the door to certain edge cases, but SwiftUI + // has them too, and there's not a good general solution to these edge + // cases, even if you assume that you have unlimited compute. + cache.redistributeSpaceOnCommit = + proposedSize[component: orientation] != nil + && proposedSize[component: perpendicularOrientation] == nil - // Compute alignment - switch (orientation, alignment) { - case (.vertical, .leading): - x = 0 - case (.horizontal, .leading): - y = 0 - case (.vertical, .center): - x = (size.x - childSize.size.size.x) / 2 - case (.horizontal, .center): - y = (size.y - childSize.size.size.y) / 2 - case (.vertical, .trailing): - x = (size.x - childSize.size.size.x) - case (.horizontal, .trailing): - y = (size.y - childSize.size.size.y) - } + return ViewLayoutResult( + size: size, + childResults: renderedChildren, + participateInStackLayoutsWhenEmpty: + renderedChildren.contains(where: \.participateInStackLayoutsWhenEmpty), + ) + } + + @MainActor + static func commitStackLayout( + container: Backend.Widget, + children: [LayoutableChild], + cache: inout StackLayoutCache, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + let size = layout.size + backend.setSize(of: container, to: size.vector) + + let alignment = environment.layoutAlignment + let spacing = environment.layoutSpacing + let orientation = environment.layoutOrientation + let perpendicularOrientation = orientation.perpendicular - backend.setPosition(ofChildAt: index, in: container, to: SIMD2(x, y)) + if cache.redistributeSpaceOnCommit { + guard let ordering = cache.lastFlexibilityOrdering else { + fatalError("Expected flexibility ordering in order to redistribute space during commit") + } + + var spaceUsedAlongStackAxis: Double = 0 + let visibleChildrenCount = cache.lastHiddenChildren.filter { isHidden in + !isHidden + }.count + let totalSpacing = Double(visibleChildrenCount * spacing) + var childrenRemaining = visibleChildrenCount - switch orientation { - case .horizontal: - x += childSize.size.size.x + spacing - case .vertical: - y += childSize.size.size.y + spacing + // TODO: Reuse the corresponding loop from computeStackLayout if + // possible to avoid the possibility for a behaviour mismatch. + for index in ordering { + if cache.lastHiddenChildren[index] { + continue } + + var proposedChildSize = layout.size + proposedChildSize[component: orientation] -= spaceUsedAlongStackAxis + totalSpacing + proposedChildSize[component: orientation] /= Double(childrenRemaining) + let result = children[index].computeLayout( + proposedSize: ProposedViewSize(proposedChildSize), + environment: environment + ) + + spaceUsedAlongStackAxis += result.size[component: orientation] + childrenRemaining -= 1 } } - // If the stack has been told to inherit its stack layout participation - // and all of its children are hidden, then the stack itself also - // shouldn't participate in stack layouts. - let shouldGetIgnoredInStackLayouts = - inheritStackLayoutParticipation && isHidden.allSatisfy { $0 } + let renderedChildren = children.map { $0.commit() } - return ViewUpdateResult( - size: ViewSize( - size: size, - idealSize: idealSize, - idealWidthForProposedHeight: idealWidthForProposedHeight, - idealHeightForProposedWidth: idealHeightForProposedWidth, - minimumWidth: minimumWidth, - minimumHeight: minimumHeight, - maximumWidth: maximumWidth, - maximumHeight: maximumHeight, - participateInStackLayoutsWhenEmpty: !shouldGetIgnoredInStackLayouts - ), - childResults: renderedChildren - ) + var position = Position.zero + for (index, child) in renderedChildren.enumerated() { + // Avoid the whole iteration if the child is hidden. If there + // are weird positioning issues for views that do strange things + // then this could be the cause. + if !child.participatesInStackLayouts { + continue + } + + // Compute alignment + switch alignment { + case .leading: + position[component: perpendicularOrientation] = 0 + case .center: + let outer = size[component: perpendicularOrientation] + let inner = child.size[component: perpendicularOrientation] + position[component: perpendicularOrientation] = (outer - inner) / 2 + case .trailing: + let outer = size[component: perpendicularOrientation] + let inner = child.size[component: perpendicularOrientation] + position[component: perpendicularOrientation] = outer - inner + } + + backend.setPosition(ofChildAt: index, in: container, to: position.vector) + + position[component: orientation] += child.size[component: orientation] + Double(spacing) + } } } diff --git a/Sources/SwiftCrossUI/Layout/Position.swift b/Sources/SwiftCrossUI/Layout/Position.swift new file mode 100644 index 0000000000..1d9682397b --- /dev/null +++ b/Sources/SwiftCrossUI/Layout/Position.swift @@ -0,0 +1,53 @@ +/// A positon. +struct Position: Hashable, Sendable { + /// The zero position (aka the origin). + static let zero = Self(0, 0) + + /// The position's x component. + var x: Double + /// The position's y component. + var y: Double + + var vector: SIMD2 { + SIMD2( + LayoutSystem.roundSize(x), + LayoutSystem.roundSize(y) + ) + } + + /// Creates a new position. + init(_ x: Double, _ y: Double) { + self.x = x + self.y = y + } + + /// The position component associated with the given orientation's main axis. + public subscript(component orientation: Orientation) -> Double { + get { + switch orientation { + case .horizontal: + x + case .vertical: + y + } + } + set { + switch orientation { + case .horizontal: + x = newValue + case .vertical: + y = newValue + } + } + } + + /// The position component associated with the given axis. + public subscript(component axis: Axis) -> Double { + get { + self[component: axis.orientation] + } + set { + self[component: axis.orientation] = newValue + } + } +} diff --git a/Sources/SwiftCrossUI/Layout/ProposedViewSize.swift b/Sources/SwiftCrossUI/Layout/ProposedViewSize.swift new file mode 100644 index 0000000000..df85e8f8ac --- /dev/null +++ b/Sources/SwiftCrossUI/Layout/ProposedViewSize.swift @@ -0,0 +1,69 @@ +/// The proposed size for a view. `nil` signifies an unspecified dimension. +public struct ProposedViewSize: Hashable, Sendable { + /// The zero proposal. + public static let zero = Self(0, 0) + /// The infinite proposal. + public static let infinity = Self(.infinity, .infinity) + /// The unspecified/ideal proposal. + public static let unspecified = Self(nil, nil) + + /// The proposed width (if any). + public var width: Double? + /// The proposed height (if any). + public var height: Double? + + /// Creates a view size proposal. + public init(_ width: Double?, _ height: Double?) { + self.width = width + self.height = height + } + + public init(_ viewSize: ViewSize) { + self.width = viewSize.width + self.height = viewSize.height + } + + init(_ vector: SIMD2) { + self.width = Double(vector.x) + self.height = Double(vector.y) + } + + /// Replaces unspecified dimensions of a proposed view size with dimensions + /// from a concrete view size to get a concrete proposal. + public func replacingUnspecifiedDimensions(by size: ViewSize) -> ViewSize { + ViewSize( + width ?? size.width, + height ?? size.height + ) + } + + /// The component associated with the given orientation. + public subscript(component orientation: Orientation) -> Double? { + get { + switch orientation { + case .horizontal: + width + case .vertical: + height + } + } + set { + switch orientation { + case .horizontal: + width = newValue + case .vertical: + height = newValue + } + } + } + + /// The component associated with the given axis. + public subscript(component axis: Axis) -> Double? { + get { + self[component: axis.orientation] + } + set { + self[component: axis.orientation] = newValue + } + } +} diff --git a/Sources/SwiftCrossUI/Layout/ViewLayoutResult.swift b/Sources/SwiftCrossUI/Layout/ViewLayoutResult.swift new file mode 100644 index 0000000000..7dc1e01596 --- /dev/null +++ b/Sources/SwiftCrossUI/Layout/ViewLayoutResult.swift @@ -0,0 +1,53 @@ +/// The result of a call to ``View/computeLayout(_:children:proposedSize:environment:backend:)``. +public struct ViewLayoutResult { + /// The size that the view has chosen for itself based off of the proposed view size. + public var size: ViewSize + /// Whether the view participates in stack layouts when empty (i.e. has its own spacing). + /// + /// This will be removed once we properly support dynamic alignment and spacing. + public var participateInStackLayoutsWhenEmpty: Bool + /// The preference values produced by the view and its children. + public var preferences: PreferenceValues + + public init( + size: ViewSize, + participateInStackLayoutsWhenEmpty: Bool = false, + preferences: PreferenceValues + ) { + self.size = size + self.participateInStackLayoutsWhenEmpty = participateInStackLayoutsWhenEmpty + self.preferences = preferences + } + + /// Creates a layout result by combining a parent view's sizing and its + /// children's preference values. + public init( + size: ViewSize, + childResults: [ViewLayoutResult], + participateInStackLayoutsWhenEmpty: Bool = false, + preferencesOverlay: PreferenceValues? = nil + ) { + self.size = size + self.participateInStackLayoutsWhenEmpty = participateInStackLayoutsWhenEmpty + + preferences = PreferenceValues( + merging: childResults.map(\.preferences) + + [preferencesOverlay].compactMap { $0 } + ) + } + + /// Creates the layout result of a leaf view (one with no children and no + /// special preference behaviour). Uses ``PreferenceValues/default``. + public static func leafView(size: ViewSize) -> Self { + ViewLayoutResult( + size: size, + participateInStackLayoutsWhenEmpty: true, + preferences: .default + ) + } + + /// Whether the view should participate in stack layouts (i.e. get its own spacing). + public var participatesInStackLayouts: Bool { + size != .zero || participateInStackLayoutsWhenEmpty + } +} diff --git a/Sources/SwiftCrossUI/Layout/ViewSize.swift b/Sources/SwiftCrossUI/Layout/ViewSize.swift index cd1a478979..aae94422e2 100644 --- a/Sources/SwiftCrossUI/Layout/ViewSize.swift +++ b/Sources/SwiftCrossUI/Layout/ViewSize.swift @@ -1,117 +1,60 @@ -/// The size of a view. Includes ideal size, and minimum/maximum width and height -/// along with the size you'd expect. -/// -/// The width and height components of the view's minimum and maximum sizes are -/// stored separately to make it extra clear that they don't always form some -/// sort of achievable minimum/maximum size. The provided minimum/maximum bounds -/// may only be achievable along a single axis at a time. -public struct ViewSize: Equatable, Sendable { - /// The view update result for an empty view. - public static let empty = ViewSize( - size: .zero, - idealSize: .zero, - minimumWidth: 0, - minimumHeight: 0, - maximumWidth: 0, - maximumHeight: 0 - ) +/// The size of a view. +public struct ViewSize: Hashable, Sendable { + /// The zero view size. + public static let zero = Self(0, 0) - /// The view update result for a hidden view. Differs from ``ViewSize/empty`` - /// by stopping hidden views from participating in stack layouts (i.e. - /// getting spacing between the previous child and the hidden child). - public static let hidden = ViewSize( - size: .zero, - idealSize: .zero, - minimumWidth: 0, - minimumHeight: 0, - maximumWidth: 0, - maximumHeight: 0, - participateInStackLayoutsWhenEmpty: false - ) + /// The view's width. + public var width: Double + /// The view's height. + public var height: Double - /// The size that the view now takes up. - public var size: SIMD2 - /// The size that the view ideally wants to take up. - public var idealSize: SIMD2 - /// The width that the view ideally wants to take up assuming that the - /// proposed height doesn't change. Only really differs from `idealSize` for - /// views that have a trade-off between width and height (such as `Text`). - public var idealWidthForProposedHeight: Int - /// The height that the view ideally wants to take up assuming that the - /// proposed width doesn't change. Only really differs from `idealSize` for - /// views that have a trade-off between width and height (such as `Text`). - public var idealHeightForProposedWidth: Int - /// The minimum width that the view can take (if its height remains the same). - public var minimumWidth: Int - /// The minimum height that the view can take (if its width remains the same). - public var minimumHeight: Int - /// The maximum width that the view can take (if its height remains the same). - public var maximumWidth: Double - /// The maximum height that the view can take (if its width remains the same). - public var maximumHeight: Double - /// Whether the view should participate in stack layouts when empty. - /// - /// If `false`, the view won't get any spacing before or after it in stack - /// layouts. For example, this is used by ``OptionalView`` when its - /// underlying view is `nil` to avoid having spacing between views that are - /// semantically 'not present'. - /// - /// Only takes effect when ``ViewSize/size`` is zero, to avoid any ambiguity - /// when the view has non-zero size as this option is really only intended - /// to be used for visually hidden views (what would it mean for a non-empty - /// view to not participate in the layout? would the spacing between the - /// previous view and the next go before or after the view? would the view - /// get forced to zero size?). - public var participateInStackLayoutsWhenEmpty: Bool + /// Creates a view size. + public init(_ width: Double, _ height: Double) { + self.width = width + self.height = height + } + + /// Creates a view size from an integer vector. + init(_ vector: SIMD2) { + width = Double(vector.x) + height = Double(vector.y) + } - /// The view's ideal aspect ratio, computed from ``ViewSize/idealSize``. If - /// either of the view's ideal dimensions are 0, then the aspect ratio - /// defaults to 1. - public var idealAspectRatio: Double { - LayoutSystem.aspectRatio(of: SIMD2(idealSize)) + /// Gets the view size as a vector. + var vector: SIMD2 { + SIMD2( + LayoutSystem.roundSize(width), + LayoutSystem.roundSize(height) + ) } - public init( - size: SIMD2, - idealSize: SIMD2, - idealWidthForProposedHeight: Int? = nil, - idealHeightForProposedWidth: Int? = nil, - minimumWidth: Int, - minimumHeight: Int, - maximumWidth: Double?, - maximumHeight: Double?, - participateInStackLayoutsWhenEmpty: Bool = true - ) { - self.size = size - self.idealSize = idealSize - self.idealWidthForProposedHeight = idealWidthForProposedHeight ?? idealSize.x - self.idealHeightForProposedWidth = idealHeightForProposedWidth ?? idealSize.y - self.minimumWidth = minimumWidth - self.minimumHeight = minimumHeight - // Using `Double(1 << 53)` as the default allows us to differentiate between views - // with unlimited size and different minimum sizes when calculating view flexibility. - // If we use `Double.infinity` then all views with unlimited size have infinite - // flexibility, meaning that there's no difference when sorting, even though the - // minimum size should still affect view layout. Similarly, if we use - // `Double.greatestFiniteMagnitude` we don't have enough precision to get different results - // when subtracting reasonable minimum dimensions. The chosen value for 'unlimited' - // width/height is in the range where the gap between consecutive Doubles is `1`, which - // I believe is a good compromise. - self.maximumWidth = maximumWidth ?? Double(1 << 53) - self.maximumHeight = maximumHeight ?? Double(1 << 53) - self.participateInStackLayoutsWhenEmpty = - participateInStackLayoutsWhenEmpty + /// The size component associated with the given orientation. + public subscript(component orientation: Orientation) -> Double { + get { + switch orientation { + case .horizontal: + width + case .vertical: + height + } + } + set { + switch orientation { + case .horizontal: + width = newValue + case .vertical: + height = newValue + } + } } - public init(fixedSize: SIMD2) { - size = fixedSize - idealSize = fixedSize - idealWidthForProposedHeight = fixedSize.x - idealHeightForProposedWidth = fixedSize.y - minimumWidth = fixedSize.x - minimumHeight = fixedSize.y - maximumWidth = Double(fixedSize.x) - maximumHeight = Double(fixedSize.y) - participateInStackLayoutsWhenEmpty = true + /// The size component associated with the given axis. + public subscript(component axis: Axis) -> Double { + get { + self[component: axis.orientation] + } + set { + self[component: axis.orientation] = newValue + } } } diff --git a/Sources/SwiftCrossUI/Layout/ViewUpdateResult.swift b/Sources/SwiftCrossUI/Layout/ViewUpdateResult.swift deleted file mode 100644 index bd907bc728..0000000000 --- a/Sources/SwiftCrossUI/Layout/ViewUpdateResult.swift +++ /dev/null @@ -1,33 +0,0 @@ -public struct ViewUpdateResult { - public var size: ViewSize - public var preferences: PreferenceValues - - public init( - size: ViewSize, - preferences: PreferenceValues - ) { - self.size = size - self.preferences = preferences - } - - public init( - size: ViewSize, - childResults: [ViewUpdateResult], - preferencesOverlay: PreferenceValues? = nil - ) { - self.size = size - - preferences = PreferenceValues( - merging: childResults.map(\.preferences) - + [preferencesOverlay].compactMap { $0 } - ) - } - - public static func leafView(size: ViewSize) -> Self { - ViewUpdateResult(size: size, preferences: .default) - } - - public var participatesInStackLayouts: Bool { - size.size != .zero || size.participateInStackLayoutsWhenEmpty - } -} diff --git a/Sources/SwiftCrossUI/Scenes/TupleScene.swift b/Sources/SwiftCrossUI/Scenes/TupleScene.swift index e5301fc3ba..7578d95757 100644 --- a/Sources/SwiftCrossUI/Scenes/TupleScene.swift +++ b/Sources/SwiftCrossUI/Scenes/TupleScene.swift @@ -115,9 +115,7 @@ public struct TupleScene4: - SceneGraphNode -{ +public final class TupleSceneNode4: SceneGraphNode { public typealias NodeScene = TupleScene4 var node0: Scene0.Node @@ -147,9 +145,7 @@ public final class TupleSceneNode4: Scene { +public struct TupleScene5: Scene { public typealias Node = TupleSceneNode5 var scene0: Scene0 @@ -160,9 +156,7 @@ public struct TupleScene5< public var commands: Commands - public init( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4 - ) { + public init(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4) { self.scene0 = scene0 self.scene1 = scene1 self.scene2 = scene2 @@ -178,9 +172,7 @@ public struct TupleScene5< } } -public final class TupleSceneNode5< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene ->: SceneGraphNode { +public final class TupleSceneNode5: SceneGraphNode { public typealias NodeScene = TupleScene5 var node0: Scene0.Node @@ -213,9 +205,7 @@ public final class TupleSceneNode5< node4.update(newScene?.scene4, backend: backend, environment: environment) } } -public struct TupleScene6< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene ->: Scene { +public struct TupleScene6: Scene { public typealias Node = TupleSceneNode6 var scene0: Scene0 @@ -227,10 +217,7 @@ public struct TupleScene6< public var commands: Commands - public init( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, - _ scene5: Scene5 - ) { + public init(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5) { self.scene0 = scene0 self.scene1 = scene1 self.scene2 = scene2 @@ -248,9 +235,7 @@ public struct TupleScene6< } } -public final class TupleSceneNode6< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene ->: SceneGraphNode { +public final class TupleSceneNode6: SceneGraphNode { public typealias NodeScene = TupleScene6 var node0: Scene0.Node @@ -286,10 +271,7 @@ public final class TupleSceneNode6< node5.update(newScene?.scene5, backend: backend, environment: environment) } } -public struct TupleScene7< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene ->: Scene { +public struct TupleScene7: Scene { public typealias Node = TupleSceneNode7 var scene0: Scene0 @@ -302,10 +284,7 @@ public struct TupleScene7< public var commands: Commands - public init( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, - _ scene5: Scene5, _ scene6: Scene6 - ) { + public init(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5, _ scene6: Scene6) { self.scene0 = scene0 self.scene1 = scene1 self.scene2 = scene2 @@ -325,10 +304,7 @@ public struct TupleScene7< } } -public final class TupleSceneNode7< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene ->: SceneGraphNode { +public final class TupleSceneNode7: SceneGraphNode { public typealias NodeScene = TupleScene7 var node0: Scene0.Node @@ -367,13 +343,8 @@ public final class TupleSceneNode7< node6.update(newScene?.scene6, backend: backend, environment: environment) } } -public struct TupleScene8< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene ->: Scene { - public typealias Node = TupleSceneNode8< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7 - > +public struct TupleScene8: Scene { + public typealias Node = TupleSceneNode8 var scene0: Scene0 var scene1: Scene1 @@ -386,10 +357,7 @@ public struct TupleScene8< public var commands: Commands - public init( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, - _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7 - ) { + public init(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7) { self.scene0 = scene0 self.scene1 = scene1 self.scene2 = scene2 @@ -411,13 +379,8 @@ public struct TupleScene8< } } -public final class TupleSceneNode8< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene ->: SceneGraphNode { - public typealias NodeScene = TupleScene8< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7 - > +public final class TupleSceneNode8: SceneGraphNode { + public typealias NodeScene = TupleScene8 var node0: Scene0.Node var node1: Scene1.Node @@ -458,13 +421,8 @@ public final class TupleSceneNode8< node7.update(newScene?.scene7, backend: backend, environment: environment) } } -public struct TupleScene9< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene ->: Scene { - public typealias Node = TupleSceneNode9< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8 - > +public struct TupleScene9: Scene { + public typealias Node = TupleSceneNode9 var scene0: Scene0 var scene1: Scene1 @@ -478,10 +436,7 @@ public struct TupleScene9< public var commands: Commands - public init( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, - _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8 - ) { + public init(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8) { self.scene0 = scene0 self.scene1 = scene1 self.scene2 = scene2 @@ -505,13 +460,8 @@ public struct TupleScene9< } } -public final class TupleSceneNode9< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene ->: SceneGraphNode { - public typealias NodeScene = TupleScene9< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8 - > +public final class TupleSceneNode9: SceneGraphNode { + public typealias NodeScene = TupleScene9 var node0: Scene0.Node var node1: Scene1.Node @@ -555,13 +505,8 @@ public final class TupleSceneNode9< node8.update(newScene?.scene8, backend: backend, environment: environment) } } -public struct TupleScene10< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene ->: Scene { - public typealias Node = TupleSceneNode10< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9 - > +public struct TupleScene10: Scene { + public typealias Node = TupleSceneNode10 var scene0: Scene0 var scene1: Scene1 @@ -576,10 +521,7 @@ public struct TupleScene10< public var commands: Commands - public init( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, - _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9 - ) { + public init(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9) { self.scene0 = scene0 self.scene1 = scene1 self.scene2 = scene2 @@ -605,13 +547,8 @@ public struct TupleScene10< } } -public final class TupleSceneNode10< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene ->: SceneGraphNode { - public typealias NodeScene = TupleScene10< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9 - > +public final class TupleSceneNode10: SceneGraphNode { + public typealias NodeScene = TupleScene10 var node0: Scene0.Node var node1: Scene1.Node @@ -658,13 +595,8 @@ public final class TupleSceneNode10< node9.update(newScene?.scene9, backend: backend, environment: environment) } } -public struct TupleScene11< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene, Scene10: Scene ->: Scene { - public typealias Node = TupleSceneNode11< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9, Scene10 - > +public struct TupleScene11: Scene { + public typealias Node = TupleSceneNode11 var scene0: Scene0 var scene1: Scene1 @@ -680,11 +612,7 @@ public struct TupleScene11< public var commands: Commands - public init( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, - _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, - _ scene10: Scene10 - ) { + public init(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, _ scene10: Scene10) { self.scene0 = scene0 self.scene1 = scene1 self.scene2 = scene2 @@ -712,13 +640,8 @@ public struct TupleScene11< } } -public final class TupleSceneNode11< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene, Scene10: Scene ->: SceneGraphNode { - public typealias NodeScene = TupleScene11< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9, Scene10 - > +public final class TupleSceneNode11: SceneGraphNode { + public typealias NodeScene = TupleScene11 var node0: Scene0.Node var node1: Scene1.Node @@ -768,14 +691,8 @@ public final class TupleSceneNode11< node10.update(newScene?.scene10, backend: backend, environment: environment) } } -public struct TupleScene12< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene, Scene10: Scene, Scene11: Scene ->: Scene { - public typealias Node = TupleSceneNode12< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9, Scene10, - Scene11 - > +public struct TupleScene12: Scene { + public typealias Node = TupleSceneNode12 var scene0: Scene0 var scene1: Scene1 @@ -792,11 +709,7 @@ public struct TupleScene12< public var commands: Commands - public init( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, - _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, - _ scene10: Scene10, _ scene11: Scene11 - ) { + public init(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, _ scene10: Scene10, _ scene11: Scene11) { self.scene0 = scene0 self.scene1 = scene1 self.scene2 = scene2 @@ -826,14 +739,8 @@ public struct TupleScene12< } } -public final class TupleSceneNode12< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene, Scene10: Scene, Scene11: Scene ->: SceneGraphNode { - public typealias NodeScene = TupleScene12< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9, Scene10, - Scene11 - > +public final class TupleSceneNode12: SceneGraphNode { + public typealias NodeScene = TupleScene12 var node0: Scene0.Node var node1: Scene1.Node @@ -886,15 +793,8 @@ public final class TupleSceneNode12< node11.update(newScene?.scene11, backend: backend, environment: environment) } } -public struct TupleScene13< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene, Scene10: Scene, Scene11: Scene, - Scene12: Scene ->: Scene { - public typealias Node = TupleSceneNode13< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9, Scene10, - Scene11, Scene12 - > +public struct TupleScene13: Scene { + public typealias Node = TupleSceneNode13 var scene0: Scene0 var scene1: Scene1 @@ -912,11 +812,7 @@ public struct TupleScene13< public var commands: Commands - public init( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, - _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, - _ scene10: Scene10, _ scene11: Scene11, _ scene12: Scene12 - ) { + public init(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, _ scene10: Scene10, _ scene11: Scene11, _ scene12: Scene12) { self.scene0 = scene0 self.scene1 = scene1 self.scene2 = scene2 @@ -948,15 +844,8 @@ public struct TupleScene13< } } -public final class TupleSceneNode13< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene, Scene10: Scene, Scene11: Scene, - Scene12: Scene ->: SceneGraphNode { - public typealias NodeScene = TupleScene13< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9, Scene10, - Scene11, Scene12 - > +public final class TupleSceneNode13: SceneGraphNode { + public typealias NodeScene = TupleScene13 var node0: Scene0.Node var node1: Scene1.Node @@ -1012,15 +901,8 @@ public final class TupleSceneNode13< node12.update(newScene?.scene12, backend: backend, environment: environment) } } -public struct TupleScene14< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene, Scene10: Scene, Scene11: Scene, - Scene12: Scene, Scene13: Scene ->: Scene { - public typealias Node = TupleSceneNode14< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9, Scene10, - Scene11, Scene12, Scene13 - > +public struct TupleScene14: Scene { + public typealias Node = TupleSceneNode14 var scene0: Scene0 var scene1: Scene1 @@ -1039,11 +921,7 @@ public struct TupleScene14< public var commands: Commands - public init( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, - _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, - _ scene10: Scene10, _ scene11: Scene11, _ scene12: Scene12, _ scene13: Scene13 - ) { + public init(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, _ scene10: Scene10, _ scene11: Scene11, _ scene12: Scene12, _ scene13: Scene13) { self.scene0 = scene0 self.scene1 = scene1 self.scene2 = scene2 @@ -1077,15 +955,8 @@ public struct TupleScene14< } } -public final class TupleSceneNode14< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene, Scene10: Scene, Scene11: Scene, - Scene12: Scene, Scene13: Scene ->: SceneGraphNode { - public typealias NodeScene = TupleScene14< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9, Scene10, - Scene11, Scene12, Scene13 - > +public final class TupleSceneNode14: SceneGraphNode { + public typealias NodeScene = TupleScene14 var node0: Scene0.Node var node1: Scene1.Node @@ -1144,15 +1015,8 @@ public final class TupleSceneNode14< node13.update(newScene?.scene13, backend: backend, environment: environment) } } -public struct TupleScene15< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene, Scene10: Scene, Scene11: Scene, - Scene12: Scene, Scene13: Scene, Scene14: Scene ->: Scene { - public typealias Node = TupleSceneNode15< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9, Scene10, - Scene11, Scene12, Scene13, Scene14 - > +public struct TupleScene15: Scene { + public typealias Node = TupleSceneNode15 var scene0: Scene0 var scene1: Scene1 @@ -1172,12 +1036,7 @@ public struct TupleScene15< public var commands: Commands - public init( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, - _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, - _ scene10: Scene10, _ scene11: Scene11, _ scene12: Scene12, _ scene13: Scene13, - _ scene14: Scene14 - ) { + public init(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, _ scene10: Scene10, _ scene11: Scene11, _ scene12: Scene12, _ scene13: Scene13, _ scene14: Scene14) { self.scene0 = scene0 self.scene1 = scene1 self.scene2 = scene2 @@ -1213,15 +1072,8 @@ public struct TupleScene15< } } -public final class TupleSceneNode15< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene, Scene10: Scene, Scene11: Scene, - Scene12: Scene, Scene13: Scene, Scene14: Scene ->: SceneGraphNode { - public typealias NodeScene = TupleScene15< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9, Scene10, - Scene11, Scene12, Scene13, Scene14 - > +public final class TupleSceneNode15: SceneGraphNode { + public typealias NodeScene = TupleScene15 var node0: Scene0.Node var node1: Scene1.Node @@ -1283,15 +1135,8 @@ public final class TupleSceneNode15< node14.update(newScene?.scene14, backend: backend, environment: environment) } } -public struct TupleScene16< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene, Scene10: Scene, Scene11: Scene, - Scene12: Scene, Scene13: Scene, Scene14: Scene, Scene15: Scene ->: Scene { - public typealias Node = TupleSceneNode16< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9, Scene10, - Scene11, Scene12, Scene13, Scene14, Scene15 - > +public struct TupleScene16: Scene { + public typealias Node = TupleSceneNode16 var scene0: Scene0 var scene1: Scene1 @@ -1312,12 +1157,7 @@ public struct TupleScene16< public var commands: Commands - public init( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, - _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, - _ scene10: Scene10, _ scene11: Scene11, _ scene12: Scene12, _ scene13: Scene13, - _ scene14: Scene14, _ scene15: Scene15 - ) { + public init(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, _ scene10: Scene10, _ scene11: Scene11, _ scene12: Scene12, _ scene13: Scene13, _ scene14: Scene14, _ scene15: Scene15) { self.scene0 = scene0 self.scene1 = scene1 self.scene2 = scene2 @@ -1355,15 +1195,8 @@ public struct TupleScene16< } } -public final class TupleSceneNode16< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene, Scene10: Scene, Scene11: Scene, - Scene12: Scene, Scene13: Scene, Scene14: Scene, Scene15: Scene ->: SceneGraphNode { - public typealias NodeScene = TupleScene16< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9, Scene10, - Scene11, Scene12, Scene13, Scene14, Scene15 - > +public final class TupleSceneNode16: SceneGraphNode { + public typealias NodeScene = TupleScene16 var node0: Scene0.Node var node1: Scene1.Node @@ -1428,15 +1261,8 @@ public final class TupleSceneNode16< node15.update(newScene?.scene15, backend: backend, environment: environment) } } -public struct TupleScene17< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene, Scene10: Scene, Scene11: Scene, - Scene12: Scene, Scene13: Scene, Scene14: Scene, Scene15: Scene, Scene16: Scene ->: Scene { - public typealias Node = TupleSceneNode17< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9, Scene10, - Scene11, Scene12, Scene13, Scene14, Scene15, Scene16 - > +public struct TupleScene17: Scene { + public typealias Node = TupleSceneNode17 var scene0: Scene0 var scene1: Scene1 @@ -1458,12 +1284,7 @@ public struct TupleScene17< public var commands: Commands - public init( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, - _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, - _ scene10: Scene10, _ scene11: Scene11, _ scene12: Scene12, _ scene13: Scene13, - _ scene14: Scene14, _ scene15: Scene15, _ scene16: Scene16 - ) { + public init(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, _ scene10: Scene10, _ scene11: Scene11, _ scene12: Scene12, _ scene13: Scene13, _ scene14: Scene14, _ scene15: Scene15, _ scene16: Scene16) { self.scene0 = scene0 self.scene1 = scene1 self.scene2 = scene2 @@ -1503,15 +1324,8 @@ public struct TupleScene17< } } -public final class TupleSceneNode17< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene, Scene10: Scene, Scene11: Scene, - Scene12: Scene, Scene13: Scene, Scene14: Scene, Scene15: Scene, Scene16: Scene ->: SceneGraphNode { - public typealias NodeScene = TupleScene17< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9, Scene10, - Scene11, Scene12, Scene13, Scene14, Scene15, Scene16 - > +public final class TupleSceneNode17: SceneGraphNode { + public typealias NodeScene = TupleScene17 var node0: Scene0.Node var node1: Scene1.Node @@ -1579,15 +1393,8 @@ public final class TupleSceneNode17< node16.update(newScene?.scene16, backend: backend, environment: environment) } } -public struct TupleScene18< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene, Scene10: Scene, Scene11: Scene, - Scene12: Scene, Scene13: Scene, Scene14: Scene, Scene15: Scene, Scene16: Scene, Scene17: Scene ->: Scene { - public typealias Node = TupleSceneNode18< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9, Scene10, - Scene11, Scene12, Scene13, Scene14, Scene15, Scene16, Scene17 - > +public struct TupleScene18: Scene { + public typealias Node = TupleSceneNode18 var scene0: Scene0 var scene1: Scene1 @@ -1610,12 +1417,7 @@ public struct TupleScene18< public var commands: Commands - public init( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, - _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, - _ scene10: Scene10, _ scene11: Scene11, _ scene12: Scene12, _ scene13: Scene13, - _ scene14: Scene14, _ scene15: Scene15, _ scene16: Scene16, _ scene17: Scene17 - ) { + public init(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, _ scene10: Scene10, _ scene11: Scene11, _ scene12: Scene12, _ scene13: Scene13, _ scene14: Scene14, _ scene15: Scene15, _ scene16: Scene16, _ scene17: Scene17) { self.scene0 = scene0 self.scene1 = scene1 self.scene2 = scene2 @@ -1657,15 +1459,8 @@ public struct TupleScene18< } } -public final class TupleSceneNode18< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene, Scene10: Scene, Scene11: Scene, - Scene12: Scene, Scene13: Scene, Scene14: Scene, Scene15: Scene, Scene16: Scene, Scene17: Scene ->: SceneGraphNode { - public typealias NodeScene = TupleScene18< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9, Scene10, - Scene11, Scene12, Scene13, Scene14, Scene15, Scene16, Scene17 - > +public final class TupleSceneNode18: SceneGraphNode { + public typealias NodeScene = TupleScene18 var node0: Scene0.Node var node1: Scene1.Node @@ -1736,16 +1531,8 @@ public final class TupleSceneNode18< node17.update(newScene?.scene17, backend: backend, environment: environment) } } -public struct TupleScene19< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene, Scene10: Scene, Scene11: Scene, - Scene12: Scene, Scene13: Scene, Scene14: Scene, Scene15: Scene, Scene16: Scene, Scene17: Scene, - Scene18: Scene ->: Scene { - public typealias Node = TupleSceneNode19< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9, Scene10, - Scene11, Scene12, Scene13, Scene14, Scene15, Scene16, Scene17, Scene18 - > +public struct TupleScene19: Scene { + public typealias Node = TupleSceneNode19 var scene0: Scene0 var scene1: Scene1 @@ -1769,13 +1556,7 @@ public struct TupleScene19< public var commands: Commands - public init( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, - _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, - _ scene10: Scene10, _ scene11: Scene11, _ scene12: Scene12, _ scene13: Scene13, - _ scene14: Scene14, _ scene15: Scene15, _ scene16: Scene16, _ scene17: Scene17, - _ scene18: Scene18 - ) { + public init(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, _ scene10: Scene10, _ scene11: Scene11, _ scene12: Scene12, _ scene13: Scene13, _ scene14: Scene14, _ scene15: Scene15, _ scene16: Scene16, _ scene17: Scene17, _ scene18: Scene18) { self.scene0 = scene0 self.scene1 = scene1 self.scene2 = scene2 @@ -1819,16 +1600,8 @@ public struct TupleScene19< } } -public final class TupleSceneNode19< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene, Scene10: Scene, Scene11: Scene, - Scene12: Scene, Scene13: Scene, Scene14: Scene, Scene15: Scene, Scene16: Scene, Scene17: Scene, - Scene18: Scene ->: SceneGraphNode { - public typealias NodeScene = TupleScene19< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9, Scene10, - Scene11, Scene12, Scene13, Scene14, Scene15, Scene16, Scene17, Scene18 - > +public final class TupleSceneNode19: SceneGraphNode { + public typealias NodeScene = TupleScene19 var node0: Scene0.Node var node1: Scene1.Node @@ -1902,16 +1675,8 @@ public final class TupleSceneNode19< node18.update(newScene?.scene18, backend: backend, environment: environment) } } -public struct TupleScene20< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene, Scene10: Scene, Scene11: Scene, - Scene12: Scene, Scene13: Scene, Scene14: Scene, Scene15: Scene, Scene16: Scene, Scene17: Scene, - Scene18: Scene, Scene19: Scene ->: Scene { - public typealias Node = TupleSceneNode20< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9, Scene10, - Scene11, Scene12, Scene13, Scene14, Scene15, Scene16, Scene17, Scene18, Scene19 - > +public struct TupleScene20: Scene { + public typealias Node = TupleSceneNode20 var scene0: Scene0 var scene1: Scene1 @@ -1936,13 +1701,7 @@ public struct TupleScene20< public var commands: Commands - public init( - _ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, - _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, - _ scene10: Scene10, _ scene11: Scene11, _ scene12: Scene12, _ scene13: Scene13, - _ scene14: Scene14, _ scene15: Scene15, _ scene16: Scene16, _ scene17: Scene17, - _ scene18: Scene18, _ scene19: Scene19 - ) { + public init(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8, _ scene9: Scene9, _ scene10: Scene10, _ scene11: Scene11, _ scene12: Scene12, _ scene13: Scene13, _ scene14: Scene14, _ scene15: Scene15, _ scene16: Scene16, _ scene17: Scene17, _ scene18: Scene18, _ scene19: Scene19) { self.scene0 = scene0 self.scene1 = scene1 self.scene2 = scene2 @@ -1988,16 +1747,8 @@ public struct TupleScene20< } } -public final class TupleSceneNode20< - Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, - Scene6: Scene, Scene7: Scene, Scene8: Scene, Scene9: Scene, Scene10: Scene, Scene11: Scene, - Scene12: Scene, Scene13: Scene, Scene14: Scene, Scene15: Scene, Scene16: Scene, Scene17: Scene, - Scene18: Scene, Scene19: Scene ->: SceneGraphNode { - public typealias NodeScene = TupleScene20< - Scene0, Scene1, Scene2, Scene3, Scene4, Scene5, Scene6, Scene7, Scene8, Scene9, Scene10, - Scene11, Scene12, Scene13, Scene14, Scene15, Scene16, Scene17, Scene18, Scene19 - > +public final class TupleSceneNode20: SceneGraphNode { + public typealias NodeScene = TupleScene20 var node0: Scene0.Node var node1: Scene1.Node diff --git a/Sources/SwiftCrossUI/Scenes/WindowGroupNode.swift b/Sources/SwiftCrossUI/Scenes/WindowGroupNode.swift index 717906b484..191d831c40 100644 --- a/Sources/SwiftCrossUI/Scenes/WindowGroupNode.swift +++ b/Sources/SwiftCrossUI/Scenes/WindowGroupNode.swift @@ -102,7 +102,7 @@ public final class WindowGroupNode: SceneGraphNode { backend: Backend, environment: EnvironmentValues, windowSizeIsFinal: Bool = false - ) -> ViewUpdateResult { + ) -> ViewLayoutResult { guard let window = window as? Backend.Window else { fatalError("Scene updated with a backend incompatible with the window it was given") } @@ -136,119 +136,52 @@ public final class WindowGroupNode: SceneGraphNode { } .with(\.window, window) - let dryRunResult: ViewUpdateResult? - if !windowSizeIsFinal { - // Perform a dry-run update of the root view to check if the window - // needs to change size. - let contentResult = viewGraph.update( + let finalContentResult: ViewLayoutResult + if scene.resizability.isResizable { + let minimumWindowSize = viewGraph.computeLayout( with: newScene?.body, - proposedSize: proposedWindowSize, - environment: environment, - dryRun: true - ) - dryRunResult = contentResult - - let newWindowSize = computeNewWindowSize( - currentProposedSize: proposedWindowSize, - backend: backend, - contentSize: contentResult.size, + proposedSize: .zero, environment: environment + ).size + + let clampedWindowSize = ViewSize( + max(minimumWindowSize.width, Double(proposedWindowSize.x)), + max(minimumWindowSize.height, Double(proposedWindowSize.y)) ) - // Restart the window update if the content has caused the window to - // change size. To avoid infinite recursion, we take the view's word - // and assume that it will take on the minimum/maximum size it claimed. - if let newWindowSize { + if clampedWindowSize.vector != proposedWindowSize && !windowSizeIsFinal { + // Restart the window update if the content has caused the window to + // change size. return update( scene, - proposedWindowSize: newWindowSize, + proposedWindowSize: clampedWindowSize.vector, backend: backend, environment: environment, - windowSizeIsFinal: false + windowSizeIsFinal: true ) } - } else { - dryRunResult = nil - } - let finalContentResult = viewGraph.update( - with: newScene?.body, - proposedSize: proposedWindowSize, - environment: environment, - dryRun: false - ) + // Set this even if the window isn't programmatically resizable + // because the window may still be user resizable. + backend.setMinimumSize(ofWindow: window, to: minimumWindowSize.vector) - // The Gtk 3 backend has some broken sizing code that can't really be - // fixed due to the design of Gtk 3. Our layout system underestimates - // the size of the new view due to the button not being in the Gtk 3 - // widget hierarchy yet (which prevents Gtk 3 from computing the - // natural sizes of the new buttons). One fix seems to be removing - // view size reuse (currently the second check in ViewGraphNode.update) - // and I'm not exactly sure why, but that makes things awfully slow. - // The other fix is to add an alternative path to - // Gtk3Backend.naturalSize(of:) for buttons that moves non-realized - // buttons to a secondary window before measuring their natural size, - // but that's super janky, easy to break if the button in the real - // window is inheriting styles from its ancestors, and I'm not sure - // how to hide the window (it's probably terrible for performance too). - // - // I still have no clue why this size underestimation (and subsequent - // mis-sizing of the window) had the symptom of all buttons losing - // their labels temporarily; Gtk 3 is a temperamental beast. - // - // Anyway, Gtk3Backend isn't really intended to be a recommended - // backend so I think this is a fine solution for now (people should - // only use Gtk3Backend if they can't use GtkBackend). - if let dryRunResult, finalContentResult.size != dryRunResult.size { - print( - """ - warning: Final window content size didn't match dry-run size. This is a sign that - either view size caching is broken or that backend.naturalSize(of:) is - broken (or both). - -> dryRunResult.size: \(dryRunResult.size) - -> finalContentResult.size: \(finalContentResult.size) - """ + finalContentResult = viewGraph.computeLayout( + proposedSize: ProposedViewSize(proposedWindowSize), + environment: environment ) - - // Give the view graph one more chance to sort itself out to fail - // as gracefully as possible. - let newWindowSize = computeNewWindowSize( - currentProposedSize: proposedWindowSize, - backend: backend, - contentSize: finalContentResult.size, + } else { + finalContentResult = viewGraph.computeLayout( + proposedSize: .unspecified, environment: environment ) - - if let newWindowSize { - return update( - scene, - proposedWindowSize: newWindowSize, - backend: backend, - environment: environment, - windowSizeIsFinal: true - ) - } } - // Set this even if the window isn't programmatically resizable - // because the window may still be user resizable. - if scene.resizability.isResizable { - backend.setMinimumSize( - ofWindow: window, - to: SIMD2( - finalContentResult.size.minimumWidth, - finalContentResult.size.minimumHeight - ) - ) - } + viewGraph.commit() backend.setPosition( ofChildAt: 0, in: containerWidget.into(), - to: SIMD2( - (proposedWindowSize.x - finalContentResult.size.size.x) / 2, - (proposedWindowSize.y - finalContentResult.size.size.y) / 2 - ) + to: (proposedWindowSize &- finalContentResult.size.vector) / 2 ) let currentWindowSize = backend.size(ofWindow: window) @@ -263,29 +196,4 @@ public final class WindowGroupNode: SceneGraphNode { return finalContentResult } - - public func computeNewWindowSize( - currentProposedSize: SIMD2, - backend: Backend, - contentSize: ViewSize, - environment: EnvironmentValues - ) -> SIMD2? { - if scene.resizability.isResizable { - if currentProposedSize.x < contentSize.minimumWidth - || currentProposedSize.y < contentSize.minimumHeight - { - let newSize = SIMD2( - max(currentProposedSize.x, contentSize.minimumWidth), - max(currentProposedSize.y, contentSize.minimumHeight) - ) - return newSize - } else { - return nil - } - } else if contentSize.idealSize != currentProposedSize { - return contentSize.idealSize - } else { - return nil - } - } } diff --git a/Sources/SwiftCrossUI/Values/Axis.swift b/Sources/SwiftCrossUI/Values/Axis.swift index 16f14a00e6..eaf1a95a0c 100644 --- a/Sources/SwiftCrossUI/Values/Axis.swift +++ b/Sources/SwiftCrossUI/Values/Axis.swift @@ -1,10 +1,20 @@ /// An axis in a 2D coordinate system. -public enum Axis: Sendable { +public enum Axis: Sendable, CaseIterable { /// The horizontal axis. case horizontal /// The vertical axis. case vertical + /// Gets the orientation with this axis as its main axis. + var orientation: Orientation { + switch self { + case .horizontal: + .horizontal + case .vertical: + .vertical + } + } + /// A set of axes represented as an efficient bit field. public struct Set: OptionSet, Sendable { /// The horizontal axis. @@ -17,5 +27,15 @@ public enum Axis: Sendable { public init(rawValue: UInt8) { self.rawValue = rawValue } + + /// Gets whether a given member is a member of the option set. + public func contains(_ member: Axis) -> Bool { + switch member { + case .horizontal: + contains(Axis.Set.horizontal) + case .vertical: + contains(Axis.Set.vertical) + } + } } } diff --git a/Sources/SwiftCrossUI/Values/Color.swift b/Sources/SwiftCrossUI/Values/Color.swift index aa9c18f6d0..ef67f26d41 100644 --- a/Sources/SwiftCrossUI/Values/Color.swift +++ b/Sources/SwiftCrossUI/Values/Color.swift @@ -1,5 +1,8 @@ /// An RGBA representation of a color. public struct Color: Sendable, Equatable, Hashable { + /// The ideal size of a color view. + private static let idealSize = ViewSize(10, 10) + /// The red component (from 0 to 1). public var red: Float /// The green component (from 0 to 1). @@ -69,26 +72,24 @@ extension Color: ElementaryView { backend.createColorableRectangle() } - func update( + func computeLayout( _ widget: Backend.Widget, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - if !dryRun { - backend.setSize(of: widget, to: proposedSize) - backend.setColor(ofColorableRectangle: widget, to: self) - } - return ViewUpdateResult.leafView( - size: ViewSize( - size: proposedSize, - idealSize: SIMD2(10, 10), - minimumWidth: 0, - minimumHeight: 0, - maximumWidth: nil, - maximumHeight: nil - ) + backend: Backend + ) -> ViewLayoutResult { + ViewLayoutResult.leafView( + size: proposedSize.replacingUnspecifiedDimensions(by: Self.idealSize) ) } + + func commit( + _ widget: Backend.Widget, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + backend.setSize(of: widget, to: layout.size.vector) + backend.setColor(ofColorableRectangle: widget, to: self) + } } diff --git a/Sources/SwiftCrossUI/Values/Orientation.swift b/Sources/SwiftCrossUI/Values/Orientation.swift index 0139a6d721..01ac11dac3 100644 --- a/Sources/SwiftCrossUI/Values/Orientation.swift +++ b/Sources/SwiftCrossUI/Values/Orientation.swift @@ -2,4 +2,14 @@ public enum Orientation: Sendable { case horizontal case vertical + + /// The orientation perpendicular to this one. + var perpendicular: Orientation { + switch self { + case .horizontal: + .vertical + case .vertical: + .horizontal + } + } } diff --git a/Sources/SwiftCrossUI/ViewGraph/AnyViewGraphNode.swift b/Sources/SwiftCrossUI/ViewGraph/AnyViewGraphNode.swift index 2edc517100..886a140baf 100644 --- a/Sources/SwiftCrossUI/ViewGraph/AnyViewGraphNode.swift +++ b/Sources/SwiftCrossUI/ViewGraph/AnyViewGraphNode.swift @@ -12,14 +12,15 @@ public class AnyViewGraphNode { _getWidget() } - /// The node's type-erased update method for update the view. - private var _updateWithNewView: + /// The node's type-erased layout computing method. + private var _computeLayoutWithNewView: ( _ newView: NodeView?, - _ proposedSize: SIMD2, - _ environment: EnvironmentValues, - _ dryRun: Bool - ) -> ViewUpdateResult + _ proposedSize: ProposedViewSize, + _ environment: EnvironmentValues + ) -> ViewLayoutResult + /// The node's type-erased commit method. + private var _commit: () -> ViewLayoutResult /// The type-erased getter for the node's widget. private var _getWidget: () -> AnyWidget /// The type-erased getter for the node's view. @@ -32,7 +33,8 @@ public class AnyViewGraphNode { /// Type-erases a view graph node. public init(_ node: ViewGraphNode) { self.node = node - _updateWithNewView = node.update(with:proposedSize:environment:dryRun:) + _computeLayoutWithNewView = node.computeLayout(with:proposedSize:environment:) + _commit = node.commit _getWidget = { AnyWidget(node.widget) } @@ -64,16 +66,20 @@ public class AnyViewGraphNode { ) } - /// Updates the view after it got recomputed (e.g. due to the parent's state changing) - /// or after its own state changed (depending on the presence of `newView`). - /// - Parameter dryRun: If `true`, only compute sizing and don't update the underlying widget. - public func update( + /// Computes a view's layout. Propagates to the view's children unless + /// the given size proposal already has a cached result. + public func computeLayout( with newView: NodeView?, - proposedSize: SIMD2, - environment: EnvironmentValues, - dryRun: Bool - ) -> ViewUpdateResult { - _updateWithNewView(newView, proposedSize, environment, dryRun) + proposedSize: ProposedViewSize, + environment: EnvironmentValues + ) -> ViewLayoutResult { + _computeLayoutWithNewView(newView, proposedSize, environment) + } + + /// Commits the view's most recently computed layout. Propagates to the + /// view's children. Also commits any view state changes. + public func commit() -> ViewLayoutResult { + _commit() } /// Gets the node's wrapped view. diff --git a/Sources/SwiftCrossUI/ViewGraph/ErasedViewGraphNode.swift b/Sources/SwiftCrossUI/ViewGraph/ErasedViewGraphNode.swift index 23bba12d40..0293bd32d9 100644 --- a/Sources/SwiftCrossUI/ViewGraph/ErasedViewGraphNode.swift +++ b/Sources/SwiftCrossUI/ViewGraph/ErasedViewGraphNode.swift @@ -8,13 +8,14 @@ public struct ErasedViewGraphNode { /// value will have `viewTypeMatched` set to `false`, allowing views such as `AnyView` /// to choose how to react to a mismatch. In `AnyView`'s case this means throwing away /// the current view graph node and creating a new one for the new view type. - public var updateWithNewView: + public var computeLayoutWithNewView: ( _ newView: Any?, - _ proposedSize: SIMD2, - _ environment: EnvironmentValues, - _ dryRun: Bool - ) -> (viewTypeMatched: Bool, size: ViewUpdateResult) + _ proposedSize: ProposedViewSize, + _ environment: EnvironmentValues + ) -> (viewTypeMatched: Bool, size: ViewLayoutResult) + /// The underlying view graph node's commit method. + public var commit: () -> ViewLayoutResult public var getWidget: () -> AnyWidget public var viewType: any View.Type @@ -42,28 +43,27 @@ public struct ErasedViewGraphNode { self.node = node backendType = Backend.self viewType = V.self - updateWithNewView = { view, proposedSize, environment, dryRun in + computeLayoutWithNewView = { view, proposedSize, environment in if let view { guard let view = view as? V else { - return (false, ViewUpdateResult.leafView(size: .empty)) + return (false, ViewLayoutResult.leafView(size: .zero)) } - let size = node.update( + let size = node.computeLayout( with: view, proposedSize: proposedSize, - environment: environment, - dryRun: dryRun + environment: environment ) return (true, size) } else { - let size = node.update( + let size = node.computeLayout( with: nil, proposedSize: proposedSize, - environment: environment, - dryRun: dryRun + environment: environment ) return (true, size) } } + commit = node.commit getWidget = { return AnyWidget(node.widget) } diff --git a/Sources/SwiftCrossUI/ViewGraph/ViewGraph.swift b/Sources/SwiftCrossUI/ViewGraph/ViewGraph.swift index b868d9ab7e..10d2c7908b 100644 --- a/Sources/SwiftCrossUI/ViewGraph/ViewGraph.swift +++ b/Sources/SwiftCrossUI/ViewGraph/ViewGraph.swift @@ -18,11 +18,13 @@ public class ViewGraph { private var cancellable: Cancellable? /// The root view being managed by this view graph. private var view: Root - /// The most recent size of the window (used when updated the root view due to a state - /// change as opposed to a window resizing event). - private var windowSize: SIMD2 + /// The latest size proposal. + private var latestProposal: ProposedViewSize + /// The latest proposal as of the last commit (used when updated the root + /// view due to a state change as opposed to a window resizing event). + private var committedProposal: ProposedViewSize /// The current size of the root view. - private var currentRootViewResult: ViewUpdateResult + private var currentRootViewResult: ViewLayoutResult /// The environment most recently provided by this node's parent scene. private var parentEnvironment: EnvironmentValues @@ -39,37 +41,44 @@ public class ViewGraph { rootNode = AnyViewGraphNode(for: view, backend: backend, environment: environment) self.view = view - windowSize = .zero + latestProposal = .zero + committedProposal = .zero parentEnvironment = environment - currentRootViewResult = ViewUpdateResult.leafView(size: .empty) + currentRootViewResult = ViewLayoutResult.leafView(size: .zero) setIncomingURLHandler = backend.setIncomingURLHandler(to:) } /// Recomputes the entire UI (e.g. due to the root view's state updating). /// If the update is due to the parent scene getting updated then the view /// is recomputed and passed as `newView`. - public func update( + public func computeLayout( with newView: Root? = nil, - proposedSize: SIMD2, - environment: EnvironmentValues, - dryRun: Bool - ) -> ViewUpdateResult { + proposedSize: ProposedViewSize, + environment: EnvironmentValues + ) -> ViewLayoutResult { parentEnvironment = environment - windowSize = proposedSize - let result = rootNode.update( + latestProposal = proposedSize + + let result = rootNode.computeLayout( with: newView ?? view, proposedSize: proposedSize, - environment: parentEnvironment, - dryRun: dryRun + environment: parentEnvironment ) self.currentRootViewResult = result - if isFirstUpdate, !dryRun { + return result + } + + /// Commits the result of the last computeLayout call to the underlying + /// widget hierarchy. + public func commit() { + committedProposal = latestProposal + self.currentRootViewResult = rootNode.commit() + if isFirstUpdate { setIncomingURLHandler { url in self.currentRootViewResult.preferences.onOpenURL?(url) } isFirstUpdate = false } - return result } public func snapshot() -> ViewGraphSnapshotter.NodeSnapshot { diff --git a/Sources/SwiftCrossUI/ViewGraph/ViewGraphNode.swift b/Sources/SwiftCrossUI/ViewGraph/ViewGraphNode.swift index b80b848699..d79a6b4333 100644 --- a/Sources/SwiftCrossUI/ViewGraph/ViewGraphNode.swift +++ b/Sources/SwiftCrossUI/ViewGraph/ViewGraphNode.swift @@ -1,6 +1,7 @@ import Foundation -/// A view graph node storing a view, its widget, and its children (likely a collection of more nodes). +/// A view graph node storing a view, its widget, and its children (likely a +/// collection of more nodes). /// /// This is where updates are initiated when a view's state updates, and where state is persisted /// even when a view gets recomputed by its parent. @@ -17,9 +18,9 @@ public class ViewGraphNode: Sendable { /// The view's children (usually just contains more view graph nodes, but can handle extra logic /// such as figuring out how to update variable length array of children efficiently). /// - /// It's type-erased because otherwise complex implementation details would be forced to the user - /// or other compromises would have to be made. I believe that this is the best option with Swift's - /// current generics landscape. + /// It's type-erased because otherwise complex implementation details would + /// be forced to the user or other compromises would have to be made. I + /// believe that this is the best option with Swift's current generics landscape. public var children: any ViewGraphNodeChildren { get { _children! @@ -37,13 +38,13 @@ public class ViewGraphNode: Sendable { public var backend: Backend /// The most recent update result for the wrapped view. - var currentResult: ViewUpdateResult? + public var currentLayout: ViewLayoutResult? /// A cache of update results keyed by the proposed size they were for. Gets cleared before the /// results' sizes become invalid. - var resultCache: [SIMD2: ViewUpdateResult] + var resultCache: [ProposedViewSize: ViewLayoutResult] /// The most recent size proposed by the parent view. Used when updating the wrapped /// view as a result of a state change rather than the parent view updating. - private var lastProposedSize: SIMD2 + private var lastProposedSize: ProposedViewSize /// A cancellable handle to the view's state property observations. private var cancellables: [Cancellable] @@ -70,7 +71,7 @@ public class ViewGraphNode: Sendable { snapshot?.isValid(for: NodeView.self) == true ? snapshot?.children : snapshot.map { [$0] } - currentResult = nil + currentLayout = nil resultCache = [:] lastProposedSize = .zero parentEnvironment = environment @@ -136,34 +137,18 @@ public class ViewGraphNode: Sendable { private func bottomUpUpdate() { // First we compute what size the view will be after the update. If it will change size, // propagate the update to this node's parent instead of updating straight away. - let currentSize = currentResult?.size - let newResult = self.update( + let currentSize = currentLayout?.size + let newLayout = self.computeLayout( proposedSize: lastProposedSize, - environment: parentEnvironment, - dryRun: true + environment: parentEnvironment ) - if newResult.size != currentSize { - self.currentResult = newResult - resultCache[lastProposedSize] = newResult - parentEnvironment.onResize(newResult.size) + self.currentLayout = newLayout + if newLayout.size != currentSize { + resultCache[lastProposedSize] = newLayout + parentEnvironment.onResize(newLayout.size) } else { - let finalResult = self.update( - proposedSize: lastProposedSize, - environment: parentEnvironment, - dryRun: false - ) - if finalResult.size != newResult.size { - print( - """ - warning: State-triggered view update had mismatch \ - between dry-run size and final size. - -> dry-run size: \(newResult.size) - -> final size: \(finalResult.size) - """ - ) - } - self.currentResult = finalResult + _ = self.commit() } } @@ -174,17 +159,15 @@ public class ViewGraphNode: Sendable { } } - /// Recomputes the view's body, and updates its widget accordingly. The view may or may not - /// propagate the update to its children depending on the nature of the update. If `newView` - /// is provided (in the case that the parent's body got updated) then it simply replaces the - /// old view while inheriting the old view's state. - /// - Parameter dryRun: If `true`, only compute sizing and don't update the underlying widget. - public func update( + /// Recomputes the view's body and computes the layout of it and all its children + /// if necessary. If `newView` is provided (in the case that the parent's body got + /// updated) then it simply replaces the old view while inheriting the old view's + /// state. + public func computeLayout( with newView: NodeView? = nil, - proposedSize: SIMD2, - environment: EnvironmentValues, - dryRun: Bool - ) -> ViewUpdateResult { + proposedSize: ProposedViewSize, + environment: EnvironmentValues + ) -> ViewLayoutResult { // Defensively ensure that all future scene implementations obey this // precondition. By putting the check here instead of only in views // that require `environment.window` (such as the alert modifier view), @@ -194,46 +177,11 @@ public class ViewGraphNode: Sendable { "View graph updated without parent window present in environment" ) - if dryRun, let cachedResult = resultCache[proposedSize] { + if let cachedResult = resultCache[proposedSize] { + currentLayout = cachedResult return cachedResult } - // Attempt to cleverly reuse the current size if we can know that it - // won't change. We must of course be in a dry run, have a known - // current size, and must've run at least one proper dry run update - // since the last update cycle (checked via`!sizeCache.isEmpty`) to - // ensure that the view has been updated at least once with the - // current view state. - if dryRun, let currentResult, !resultCache.isEmpty { - // If both the previous and current proposed sizes are larger than - // the view's previously computed maximum size, reuse the previous - // result (currentResult). - if ((Double(lastProposedSize.x) >= currentResult.size.maximumWidth - && Double(proposedSize.x) >= currentResult.size.maximumWidth) - || proposedSize.x == lastProposedSize.x) - && ((Double(lastProposedSize.y) >= currentResult.size.maximumHeight - && Double(proposedSize.y) >= currentResult.size.maximumHeight) - || proposedSize.y == lastProposedSize.y) - { - return currentResult - } - - // If the view has already been updated this update cycle and claims - // to be fixed size (maximumSize == minimumSize) then reuse the current - // result. - let maximumSize = SIMD2( - currentResult.size.maximumWidth, - currentResult.size.maximumHeight - ) - let minimumSize = SIMD2( - Double(currentResult.size.minimumWidth), - Double(currentResult.size.minimumHeight) - ) - if maximumSize == minimumSize { - return currentResult - } - } - parentEnvironment = environment lastProposedSize = proposedSize @@ -253,30 +201,45 @@ public class ViewGraphNode: Sendable { environment: viewEnvironment ) - if !dryRun { - backend.show(widget: widget) - } - let result = view.update( + let result = view.computeLayout( widget, children: children, proposedSize: proposedSize, environment: viewEnvironment, - backend: backend, - dryRun: dryRun + backend: backend ) - // We assume that the view's sizing behaviour won't change between consecutive dry run updates - // and the following real update because groups of updates following that pattern are assumed to - // be occurring within a single overarching view update. It may seem weird that we set it - // to false after real updates, but that's because it may get invalidated between a real - // update and the next dry-run update. - if !dryRun { - resultCache = [:] - } else { - resultCache[proposedSize] = result - } + // We assume that the view's sizing behaviour won't change between consecutive + // layout computations and the following commit, because groups of updates + // following that pattern are assumed to be occurring within a single overarching + // view update. Under that assumption, we can cache view layout results. + resultCache[proposedSize] = result - currentResult = result + currentLayout = result return result } + + /// Commits the view's most recently computed layout and any view state changes + /// that have occurred since the last update (e.g. text content changes or font + /// size changes). Returns the most recently computed layout for convenience, + /// although it's guaranteed to match the result of the last call to computeLayout. + public func commit() -> ViewLayoutResult { + backend.show(widget: widget) + + guard let currentLayout else { + print("warning: layout committed before being computed, ignoring") + return .leafView(size: .zero) + } + + view.commit( + widget, + children: children, + layout: currentLayout, + environment: parentEnvironment, + backend: backend + ) + resultCache = [:] + + return currentLayout + } } diff --git a/Sources/SwiftCrossUI/Views/AnyView.swift b/Sources/SwiftCrossUI/Views/AnyView.swift deleted file mode 100644 index 7004374fad..0000000000 --- a/Sources/SwiftCrossUI/Views/AnyView.swift +++ /dev/null @@ -1,136 +0,0 @@ -import Foundation - -/// A view which erases the type of its child. Useful in dynamic -/// use-cases such as hot reloading, but not recommended if there -/// are alternate strongly-typed solutions to your problem since -/// ``AnyView`` has significantly more overhead than strongly -/// typed views. -public struct AnyView: TypeSafeView { - typealias Children = AnyViewChildren - - public var body = EmptyView() - - var child: any View - - public init(_ child: any View) { - self.child = child - } - - func children( - backend: Backend, - snapshots: [ViewGraphSnapshotter.NodeSnapshot]?, - environment: EnvironmentValues - ) -> AnyViewChildren { - let snapshot = snapshots?.count == 1 ? snapshots?.first : nil - return AnyViewChildren( - from: self, - backend: backend, - snapshot: snapshot, - environment: environment - ) - } - - func layoutableChildren( - backend: Backend, - children: AnyViewChildren - ) -> [LayoutSystem.LayoutableChild] { - // TODO: Figure out a convention for views like this where ``layoutableChildren`` will - // never get used unless something has already gone pretty wrong. - body.layoutableChildren(backend: backend, children: children) - } - - func asWidget( - _ children: AnyViewChildren, - backend: Backend - ) -> Backend.Widget { - let container = backend.createContainer() - backend.addChild(children.node.getWidget().into(), to: container) - backend.setPosition(ofChildAt: 0, in: container, to: .zero) - return container - } - - /// Attempts to update the child. If the initial update fails then it means that the child's - /// concrete type has changed and we must recreate the child node and swap out our current - /// child widget with the new view's widget. - func update( - _ widget: Backend.Widget, - children: AnyViewChildren, - proposedSize: SIMD2, - environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - var (viewTypesMatched, result) = children.node.updateWithNewView( - child, - proposedSize, - environment, - dryRun - ) - - // If the new view's type doesn't match the old view's type then we need to create a new - // view graph node for the new view. - if !viewTypesMatched { - children.widgetToReplace = children.node.getWidget() - children.node = ErasedViewGraphNode( - for: child, - backend: backend, - environment: environment - ) - - // We can just assume that the update succeeded because we just created the node - // a few lines earlier (so it's guaranteed that the view types match). - let (_, newResult) = children.node.updateWithNewView( - child, - proposedSize, - environment, - dryRun - ) - result = newResult - } - - // If the child view has changed types and this isn't a dry-run then switch to displaying - // the new child widget. - if !dryRun, let widgetToReplace = children.widgetToReplace { - backend.removeChild(widgetToReplace.into(), from: widget) - backend.addChild(children.node.getWidget().into(), to: widget) - backend.setPosition(ofChildAt: 0, in: widget, to: .zero) - children.widgetToReplace = nil - } - - if !dryRun { - backend.setSize(of: widget, to: result.size.size) - } - - return result - } -} - -class AnyViewChildren: ViewGraphNodeChildren { - /// The erased underlying node. - var node: ErasedViewGraphNode - /// If the displayed view changed during a dry-run update then this stores the widget of the replaced view. - var widgetToReplace: AnyWidget? - - var widgets: [AnyWidget] { - return [node.getWidget()] - } - - var erasedNodes: [ErasedViewGraphNode] { - [node] - } - - /// Creates the erased child node and wraps the child's widget in a single-child container. - init( - from view: AnyView, - backend: Backend, - snapshot: ViewGraphSnapshotter.NodeSnapshot?, - environment: EnvironmentValues - ) { - node = ErasedViewGraphNode( - for: view.child, - backend: backend, - snapshot: snapshot, - environment: environment - ) - } -} diff --git a/Sources/SwiftCrossUI/Views/Button.swift b/Sources/SwiftCrossUI/Views/Button.swift index 739d1ba407..dc86fdc694 100644 --- a/Sources/SwiftCrossUI/Views/Button.swift +++ b/Sources/SwiftCrossUI/Views/Button.swift @@ -29,15 +29,14 @@ extension Button: ElementaryView { return backend.createButton() } - public func update( + public func computeLayout( _ widget: Backend.Widget, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - // TODO: Implement button sizing within SwiftCrossUI so that we can properly implement - // `dryRun`. Relying on the backend for button sizing also makes the Gtk 3 backend + backend: Backend + ) -> ViewLayoutResult { + // TODO: Implement button sizing within SwiftCrossUI so that we can move this to + // commit. Relying on the backend for button sizing also makes the Gtk 3 backend // basically impossible to implement correctly, hence the // `finalContentSize != contentSize` check in WindowGroupNode to catch any weird // behaviour. Without that extra safety net logic, buttons all end up label-less @@ -58,10 +57,15 @@ extension Button: ElementaryView { naturalSize.y ) - if !dryRun { - backend.setSize(of: widget, to: size) - } + return ViewLayoutResult.leafView(size: ViewSize(size)) + } - return ViewUpdateResult.leafView(size: ViewSize(fixedSize: size)) + public func commit( + _ widget: Backend.Widget, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + backend.setSize(of: widget, to: layout.size.vector) } } diff --git a/Sources/SwiftCrossUI/Views/Checkbox.swift b/Sources/SwiftCrossUI/Views/Checkbox.swift deleted file mode 100644 index 7c80d354f7..0000000000 --- a/Sources/SwiftCrossUI/Views/Checkbox.swift +++ /dev/null @@ -1,33 +0,0 @@ -/// A checkbox control that is either on or off. -struct Checkbox: ElementaryView, View { - /// Whether the checkbox is active or not. - private var active: Binding - - /// Creates a checkbox. - public init(active: Binding) { - self.active = active - } - - public func asWidget(backend: Backend) -> Backend.Widget { - return backend.createCheckbox() - } - - public func update( - _ widget: Backend.Widget, - proposedSize: SIMD2, - environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - if !dryRun { - backend.updateCheckbox(widget, environment: environment) { newActiveState in - active.wrappedValue = newActiveState - } - backend.setState(ofCheckbox: widget, to: active.wrappedValue) - } - - return ViewUpdateResult.leafView( - size: ViewSize(fixedSize: backend.naturalSize(of: widget)) - ) - } -} diff --git a/Sources/SwiftCrossUI/Views/Divider.swift b/Sources/SwiftCrossUI/Views/Divider.swift deleted file mode 100644 index f57182703b..0000000000 --- a/Sources/SwiftCrossUI/Views/Divider.swift +++ /dev/null @@ -1,25 +0,0 @@ -/// A divider that expands along the minor axis of the containing stack layout -/// (or horizontally otherwise). In dark mode it's white with 10% opacity, and -/// in light mode it's black with 10% opacity. -public struct Divider: View { - @Environment(\.colorScheme) var colorScheme - @Environment(\.layoutOrientation) var layoutOrientation - - var color: Color { - switch colorScheme { - case .dark: - Color(1, 1, 1, 0.1) - case .light: - Color(0, 0, 0, 0.1) - } - } - - public init() {} - - public var body: some View { - color.frame( - width: layoutOrientation == .horizontal ? 1 : nil, - height: layoutOrientation == .vertical ? 1 : nil - ) - } -} diff --git a/Sources/SwiftCrossUI/Views/EitherView.swift b/Sources/SwiftCrossUI/Views/EitherView.swift index fac32056ff..685ca68246 100644 --- a/Sources/SwiftCrossUI/Views/EitherView.swift +++ b/Sources/SwiftCrossUI/Views/EitherView.swift @@ -47,25 +47,23 @@ extension EitherView: TypeSafeView { return backend.createContainer() } - func update( + func computeLayout( _ widget: Backend.Widget, children: EitherViewChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - let result: ViewUpdateResult + backend: Backend + ) -> ViewLayoutResult { + let result: ViewLayoutResult let hasSwitchedCase: Bool switch storage { case .a(let a): switch children.node { - case .a(let nodeA): - result = nodeA.update( + case let .a(nodeA): + result = nodeA.computeLayout( with: a, proposedSize: proposedSize, - environment: environment, - dryRun: dryRun + environment: environment ) hasSwitchedCase = false case .b: @@ -75,22 +73,20 @@ extension EitherView: TypeSafeView { environment: environment ) children.node = .a(nodeA) - result = nodeA.update( + result = nodeA.computeLayout( with: a, proposedSize: proposedSize, - environment: environment, - dryRun: dryRun + environment: environment ) hasSwitchedCase = true } case .b(let b): switch children.node { - case .b(let nodeB): - result = nodeB.update( + case let .b(nodeB): + result = nodeB.computeLayout( with: b, proposedSize: proposedSize, - environment: environment, - dryRun: dryRun + environment: environment ) hasSwitchedCase = false case .a: @@ -100,29 +96,36 @@ extension EitherView: TypeSafeView { environment: environment ) children.node = .b(nodeB) - result = nodeB.update( + result = nodeB.computeLayout( with: b, proposedSize: proposedSize, - environment: environment, - dryRun: dryRun + environment: environment ) hasSwitchedCase = true } } children.hasSwitchedCase = children.hasSwitchedCase || hasSwitchedCase - if !dryRun && children.hasSwitchedCase { + return result + } + + func commit( + _ widget: Backend.Widget, + children: EitherViewChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + if children.hasSwitchedCase { backend.removeAllChildren(of: widget) backend.addChild(children.node.widget.into(), to: widget) backend.setPosition(ofChildAt: 0, in: widget, to: .zero) children.hasSwitchedCase = false } - if !dryRun { - backend.setSize(of: widget, to: result.size.size) - } + _ = children.node.erasedNode.commit() - return result + backend.setSize(of: widget, to: layout.size.vector) } } diff --git a/Sources/SwiftCrossUI/Views/ElementaryView.swift b/Sources/SwiftCrossUI/Views/ElementaryView.swift index df835d0e01..3e93b86f22 100644 --- a/Sources/SwiftCrossUI/Views/ElementaryView.swift +++ b/Sources/SwiftCrossUI/Views/ElementaryView.swift @@ -7,13 +7,19 @@ protocol ElementaryView: View where Content == EmptyView { backend: Backend ) -> Backend.Widget - func update( + func computeLayout( _ widget: Backend.Widget, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult + backend: Backend + ) -> ViewLayoutResult + + func commit( + _ widget: Backend.Widget, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) } extension ElementaryView { @@ -30,20 +36,33 @@ extension ElementaryView { } /// Do not implement yourself, implement ``ElementaryView/update(_:proposedSize:environment:backend:)`` instead. - public func update( + public func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - update( + backend: Backend + ) -> ViewLayoutResult { + computeLayout( widget, proposedSize: proposedSize, environment: environment, - backend: backend, - dryRun: dryRun + backend: backend + ) + } + + public func commit( + _ widget: Backend.Widget, + children: any ViewGraphNodeChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + commit( + widget, + layout: layout, + environment: environment, + backend: backend ) } } diff --git a/Sources/SwiftCrossUI/Views/EmptyView.swift b/Sources/SwiftCrossUI/Views/EmptyView.swift index b541a72366..90cc881507 100644 --- a/Sources/SwiftCrossUI/Views/EmptyView.swift +++ b/Sources/SwiftCrossUI/Views/EmptyView.swift @@ -36,16 +36,23 @@ public struct EmptyView: View, Sendable { backend.createContainer() } - public func update( + public func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - ViewUpdateResult.leafView(size: .empty) + backend: Backend + ) -> ViewLayoutResult { + ViewLayoutResult.leafView(size: .zero) } + + public func commit( + _ widget: Backend.Widget, + children: any ViewGraphNodeChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) {} } /// The children of a node with no children. diff --git a/Sources/SwiftCrossUI/Views/ForEach.swift b/Sources/SwiftCrossUI/Views/ForEach.swift index 81ad975c1a..b050be0658 100644 --- a/Sources/SwiftCrossUI/Views/ForEach.swift +++ b/Sources/SwiftCrossUI/Views/ForEach.swift @@ -1,3 +1,5 @@ +import Foundation + /// A view that displays a variable amount of children. public struct ForEach where Items.Index == Int { /// A variable-length collection of elements to display. @@ -76,40 +78,19 @@ extension ForEach: TypeSafeView, View where Child: View { return backend.createContainer() } - func update( + func computeLayout( _ widget: Backend.Widget, children: ForEachViewChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { + backend: Backend + ) -> ViewLayoutResult { func addChild(_ child: Backend.Widget) { - if dryRun { - children.queuedChanges.append(.addChild(AnyWidget(child))) - } else { - backend.addChild(child, to: widget) - } + children.queuedChanges.append(.addChild(AnyWidget(child))) } func removeChild(_ child: Backend.Widget) { - if dryRun { - children.queuedChanges.append(.removeChild(AnyWidget(child))) - } else { - backend.removeChild(child, from: widget) - } - } - - if !dryRun { - for change in children.queuedChanges { - switch change { - case .addChild(let child): - backend.addChild(child.into(), to: widget) - case .removeChild(let child): - backend.removeChild(child.into(), from: widget) - } - } - children.queuedChanges = [] + children.queuedChanges.append(.removeChild(AnyWidget(child))) } // TODO: The way we're reusing nodes for technically different elements means that if @@ -128,14 +109,14 @@ extension ForEach: TypeSafeView, View where Child: View { } layoutableChildren.append( LayoutSystem.LayoutableChild( - update: { proposedSize, environment, dryRun in - node.update( + computeLayout: { proposedSize, environment in + node.computeLayout( with: childContent, proposedSize: proposedSize, - environment: environment, - dryRun: dryRun + environment: environment ) - } + }, + commit: node.commit ) ) } @@ -156,14 +137,14 @@ extension ForEach: TypeSafeView, View where Child: View { addChild(node.widget.into()) layoutableChildren.append( LayoutSystem.LayoutableChild( - update: { proposedSize, environment, dryRun in - node.update( + computeLayout: { proposedSize, environment in + node.computeLayout( with: childContent, proposedSize: proposedSize, - environment: environment, - dryRun: dryRun + environment: environment ) - } + }, + commit: node.commit ) ) } @@ -175,13 +156,51 @@ extension ForEach: TypeSafeView, View where Child: View { children.nodes.removeLast(unused) } - return LayoutSystem.updateStackLayout( + return LayoutSystem.computeStackLayout( container: widget, children: layoutableChildren, + cache: &children.stackLayoutCache, proposedSize: proposedSize, environment: environment, - backend: backend, - dryRun: dryRun + backend: backend + ) + } + + func commit( + _ widget: Backend.Widget, + children: ForEachViewChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + for change in children.queuedChanges { + switch change { + case .addChild(let child): + backend.addChild(child.into(), to: widget) + case .removeChild(let child): + backend.removeChild(child.into(), from: widget) + } + } + children.queuedChanges = [] + + LayoutSystem.commitStackLayout( + container: widget, + children: children.nodes.map { node in + LayoutSystem.LayoutableChild( + computeLayout: { proposedSize, environment in + node.computeLayout( + with: nil, + proposedSize: proposedSize, + environment: environment + ) + }, + commit: node.commit + ) + }, + cache: &children.stackLayoutCache, + layout: layout, + environment: environment, + backend: backend ) } } @@ -220,6 +239,8 @@ class ForEachViewChildren< nodes.map(ErasedViewGraphNode.init(wrapping:)) } + var stackLayoutCache = StackLayoutCache() + /// Gets a variable length view's children as view graph node children. init( from view: ForEach, diff --git a/Sources/SwiftCrossUI/Views/GeometryReader.swift b/Sources/SwiftCrossUI/Views/GeometryReader.swift deleted file mode 100644 index 79ff46d3d1..0000000000 --- a/Sources/SwiftCrossUI/Views/GeometryReader.swift +++ /dev/null @@ -1,124 +0,0 @@ -/// A container view that allows its content to read the size proposed to it. -/// -/// Geometry readers always take up the size proposed to them; no more, no less. -/// This is to decouple the geometry reader's size from the size of its content -/// in order to avoid feedback loops. -/// -/// ```swift -/// struct MeasurementView: View { -/// var body: some View { -/// GeometryReader { proxy in -/// Text("Width: \(proxy.size.x)") -/// Text("Height: \(proxy.size.y)") -/// } -/// } -/// } -/// ``` -/// -/// > Note: Geometry reader content may get evaluated multiple times with various -/// > sizes before the layout system settles on a size. Do not depend on the size -/// > proposal always being final. -public struct GeometryReader: TypeSafeView, View { - var content: (GeometryProxy) -> Content - - public var body = EmptyView() - - public init(@ViewBuilder content: @escaping (GeometryProxy) -> Content) { - self.content = content - } - - func children( - backend: Backend, - snapshots: [ViewGraphSnapshotter.NodeSnapshot]?, - environment: EnvironmentValues - ) -> GeometryReaderChildren { - GeometryReaderChildren() - } - - func layoutableChildren( - backend: Backend, - children: GeometryReaderChildren - ) -> [LayoutSystem.LayoutableChild] { - [] - } - - func asWidget( - _ children: GeometryReaderChildren, - backend: Backend - ) -> Backend.Widget { - // This is a little different to our usual wrapper implementations - // because we want to avoid calling the user's content closure before - // we actually have to. - return backend.createContainer() - } - - func update( - _ widget: Backend.Widget, - children: GeometryReaderChildren, - proposedSize: SIMD2, - environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - let view = content(GeometryProxy(size: proposedSize)) - - let environment = environment.with(\.layoutAlignment, .leading) - - let contentNode: AnyViewGraphNode - if let node = children.node { - contentNode = node - } else { - contentNode = AnyViewGraphNode( - for: view, - backend: backend, - environment: environment - ) - children.node = contentNode - - // It's ok to add the child here even though it's not a dry run - // because this is guaranteed to only happen once. Dry runs are - // more about 'commit' actions that happen every single update. - backend.addChild(contentNode.widget.into(), to: widget) - } - - // TODO: Look into moving this to the final non-dry run update. In order - // to do so we'd have to give up on preferences being allowed to affect - // layout (which is probably something we don't want to support anyway - // because it sounds like feedback loop central). - let contentResult = contentNode.update( - with: view, - proposedSize: proposedSize, - environment: environment, - dryRun: dryRun - ) - - if !dryRun { - backend.setPosition(ofChildAt: 0, in: widget, to: .zero) - backend.setSize(of: widget, to: proposedSize) - } - - return ViewUpdateResult( - size: ViewSize( - size: proposedSize, - idealSize: SIMD2(10, 10), - minimumWidth: 0, - minimumHeight: 0, - maximumWidth: nil, - maximumHeight: nil - ), - childResults: [contentResult] - ) - } -} - -class GeometryReaderChildren: ViewGraphNodeChildren { - var node: AnyViewGraphNode? - - var widgets: [AnyWidget] { - [node?.widget].compactMap { $0 } - } - - var erasedNodes: [ErasedViewGraphNode] { - [node.map(ErasedViewGraphNode.init(wrapping:))].compactMap { $0 } - } -} diff --git a/Sources/SwiftCrossUI/Views/Group.swift b/Sources/SwiftCrossUI/Views/Group.swift index 7d967f91e1..1e92224e47 100644 --- a/Sources/SwiftCrossUI/Views/Group.swift +++ b/Sources/SwiftCrossUI/Views/Group.swift @@ -23,22 +23,46 @@ public struct Group: View { return container } - public func update( + public func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - LayoutSystem.updateStackLayout( + backend: Backend + ) -> ViewLayoutResult { + if !(children is TupleViewChildren) { + print("warning: VStack will not function correctly non-TupleView Content") + } + var cache = (children as? TupleViewChildren)?.stackLayoutCache ?? StackLayoutCache() + let result = LayoutSystem.computeStackLayout( container: widget, children: layoutableChildren(backend: backend, children: children), + cache: &cache, proposedSize: proposedSize, environment: environment, backend: backend, - dryRun: dryRun, inheritStackLayoutParticipation: true ) + (children as? TupleViewChildren)?.stackLayoutCache = cache + return result + } + + public func commit( + _ widget: Backend.Widget, + children: any ViewGraphNodeChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + var cache = (children as? TupleViewChildren)?.stackLayoutCache ?? StackLayoutCache() + LayoutSystem.commitStackLayout( + container: widget, + children: layoutableChildren(backend: backend, children: children), + cache: &cache, + layout: layout, + environment: environment, + backend: backend + ) + (children as? TupleViewChildren)?.stackLayoutCache = cache } } diff --git a/Sources/SwiftCrossUI/Views/HStack.swift b/Sources/SwiftCrossUI/Views/HStack.swift index 097df08c0c..36fe1f788d 100644 --- a/Sources/SwiftCrossUI/Views/HStack.swift +++ b/Sources/SwiftCrossUI/Views/HStack.swift @@ -29,25 +29,53 @@ public struct HStack: View { return vStack } - public func update( + public func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - return LayoutSystem.updateStackLayout( + backend: Backend + ) -> ViewLayoutResult { + if !(children is TupleViewChildren) { + print("warning: VStack will not function correctly non-TupleView Content") + } + var cache = (children as? TupleViewChildren)?.stackLayoutCache ?? StackLayoutCache() + let result = LayoutSystem.computeStackLayout( container: widget, children: layoutableChildren(backend: backend, children: children), + cache: &cache, proposedSize: proposedSize, environment: environment .with(\.layoutOrientation, .horizontal) .with(\.layoutAlignment, alignment.asStackAlignment) .with(\.layoutSpacing, spacing), - backend: backend, - dryRun: dryRun + backend: backend + ) + (children as? TupleViewChildren)?.stackLayoutCache = cache + return result + } + + public func commit( + _ widget: Backend.Widget, + children: any ViewGraphNodeChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + var cache = (children as? TupleViewChildren)?.stackLayoutCache ?? StackLayoutCache() + LayoutSystem.commitStackLayout( + container: widget, + children: layoutableChildren(backend: backend, children: children), + cache: &cache, + layout: layout, + environment: + environment + .with(\.layoutOrientation, .horizontal) + .with(\.layoutAlignment, alignment.asStackAlignment) + .with(\.layoutSpacing, spacing), + backend: backend ) + (children as? TupleViewChildren)?.stackLayoutCache = cache } } diff --git a/Sources/SwiftCrossUI/Views/HotReloadableView.swift b/Sources/SwiftCrossUI/Views/HotReloadableView.swift deleted file mode 100644 index 5c00505748..0000000000 --- a/Sources/SwiftCrossUI/Views/HotReloadableView.swift +++ /dev/null @@ -1,134 +0,0 @@ -import Foundation - -/// A view which attempts to persist the state of its view subtree even -/// when the subtree's structure changes. Uses state serialization (via -/// view graph snapshotting) to persist view state even when a child -/// view's implementation gets swapped out with an implementation from -/// a newly-loaded dylib (this is what makes this useful for hot reloading). -/// -/// Only expected to be used directly by SwiftCrossUI itself or third -/// party libraries extending SwiftCrossUI's hot reloading capabilities. -public struct HotReloadableView: TypeSafeView { - typealias Children = HotReloadableViewChildren - - public var body = EmptyView() - - var child: any View - - public init(_ child: any View) { - self.child = child - } - - public init(@ViewBuilder _ child: () -> some View) { - self.child = child() - } - - func children( - backend: Backend, - snapshots: [ViewGraphSnapshotter.NodeSnapshot]?, - environment: EnvironmentValues - ) -> HotReloadableViewChildren { - let snapshot = snapshots?.count == 1 ? snapshots?.first : nil - return HotReloadableViewChildren( - from: self, - backend: backend, - snapshot: snapshot, - environment: environment - ) - } - - func asWidget( - _ children: HotReloadableViewChildren, - backend: Backend - ) -> Backend.Widget { - backend.createContainer() - } - - /// Attempts to update the child. If the initial update succeeds then the child's concrete type - /// hasn't changed and the ViewGraph has handled state persistence on our behalf. Otherwise, - /// we must recreate the child node and swap out our current child widget with the new view's - /// widget. Before displaying the child, we also attempt to transfer a snapshot of the old - /// view graph sub tree's state onto the new view graph sub tree. This is not possible to do - /// perfectly by definition, so if we can't successfully transfer the state of the sub tree - /// we just fall back on the failing view's default state. - func update( - _ widget: Backend.Widget, - children: HotReloadableViewChildren, - proposedSize: SIMD2, - environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - var (viewTypeMatched, result) = children.node.updateWithNewView( - child, - proposedSize, - environment, - dryRun - ) - - if !viewTypeMatched { - let snapshotter = ViewGraphSnapshotter() - let snapshot = children.node.transform(with: snapshotter) - children.node = ErasedViewGraphNode( - for: child, - backend: backend, - snapshot: snapshot, - environment: environment - ) - - // We can assume that the view types match since we just recreated the view - // on the line above. - let (_, newResult) = children.node.updateWithNewView( - child, - proposedSize, - environment, - dryRun - ) - result = newResult - children.hasChangedChild = true - } - - if !dryRun { - if children.hasChangedChild { - backend.removeAllChildren(of: widget) - backend.addChild(children.node.getWidget().into(), to: widget) - backend.setPosition(ofChildAt: 0, in: widget, to: .zero) - children.hasChangedChild = false - } - - backend.setSize(of: widget, to: result.size.size) - } - - return result - } -} - -class HotReloadableViewChildren: ViewGraphNodeChildren { - /// The erased underlying node. - var node: ErasedViewGraphNode - - var widgets: [AnyWidget] { - [node.getWidget()] - } - - var erasedNodes: [ErasedViewGraphNode] { - [node] - } - - var hasChangedChild = true - - /// Creates the erased child node and wraps the child's widget in a single-child container. - init( - from view: HotReloadableView, - backend: Backend, - snapshot: ViewGraphSnapshotter.NodeSnapshot?, - environment: EnvironmentValues - ) { - node = ErasedViewGraphNode( - for: view.child, - backend: backend, - snapshot: snapshot, - environment: environment - ) - } -} diff --git a/Sources/SwiftCrossUI/Views/Image.swift b/Sources/SwiftCrossUI/Views/Image.swift deleted file mode 100644 index 2f3be3e6f8..0000000000 --- a/Sources/SwiftCrossUI/Views/Image.swift +++ /dev/null @@ -1,179 +0,0 @@ -import Foundation -import ImageFormats - -/// A view that displays an image. -public struct Image: Sendable { - private var isResizable = false - private var source: Source - - enum Source: Equatable { - case url(URL, useFileExtension: Bool) - case image(ImageFormats.Image) - } - - /// Displays an image file. `png`, `jpg`, and `webp` are supported. - /// - Parameters: - /// - url: The url of the file to display. - /// - useFileExtension: If `true`, the file extension is used to determine the file type, - /// otherwise the first few ('magic') bytes of the file are used. - public init(_ url: URL, useFileExtension: Bool = true) { - source = .url(url, useFileExtension: useFileExtension) - } - - /// Displays an image from raw pixel data. - /// - Parameter image: The image data to display. - public init(_ image: ImageFormats.Image) { - source = .image(image) - } - - /// Makes the image resize to fit the available space. - public func resizable() -> Self { - var image = self - image.isResizable = true - return image - } - - init(_ source: Source, resizable: Bool) { - self.source = source - self.isResizable = resizable - } -} - -extension Image: View { - public var body: some View { return EmptyView() } -} - -extension Image: TypeSafeView { - func layoutableChildren( - backend: Backend, - children: _ImageChildren - ) -> [LayoutSystem.LayoutableChild] { - [] - } - - func children( - backend: Backend, - snapshots: [ViewGraphSnapshotter.NodeSnapshot]?, - environment: EnvironmentValues - ) -> _ImageChildren { - _ImageChildren(backend: backend) - } - - func asWidget( - _ children: _ImageChildren, - backend: Backend - ) -> Backend.Widget { - children.container.into() - } - - func update( - _ widget: Backend.Widget, - children: _ImageChildren, - proposedSize: SIMD2, - environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - let image: ImageFormats.Image? - if source != children.cachedImageSource { - switch source { - case .url(let url, let useFileExtension): - if let data = try? Data(contentsOf: url) { - let bytes = Array(data) - if useFileExtension { - image = try? ImageFormats.Image.load( - from: bytes, - usingFileExtension: url.pathExtension - ) - } else { - image = try? ImageFormats.Image.load(from: bytes) - } - } else { - image = nil - } - case .image(let sourceImage): - image = sourceImage - } - - children.cachedImageSource = source - children.cachedImage = image - children.imageChanged = true - } else { - image = children.cachedImage - } - - let idealSize = SIMD2(image?.width ?? 0, image?.height ?? 0) - let size: ViewSize - if isResizable { - size = ViewSize( - size: image == nil ? .zero : proposedSize, - idealSize: idealSize, - minimumWidth: 0, - minimumHeight: 0, - maximumWidth: image == nil ? 0 : nil, - maximumHeight: image == nil ? 0 : nil - ) - } else { - size = ViewSize(fixedSize: idealSize) - } - - let hasResized = children.cachedImageDisplaySize != size.size - if !dryRun - && (children.imageChanged - || hasResized - || (backend.requiresImageUpdateOnScaleFactorChange - && children.lastScaleFactor != environment.windowScaleFactor)) - { - if let image { - backend.updateImageView( - children.imageWidget.into(), - rgbaData: image.bytes, - width: image.width, - height: image.height, - targetWidth: size.size.x, - targetHeight: size.size.y, - dataHasChanged: children.imageChanged, - environment: environment - ) - if children.isContainerEmpty { - backend.addChild(children.imageWidget.into(), to: children.container.into()) - backend.setPosition(ofChildAt: 0, in: children.container.into(), to: .zero) - } - children.isContainerEmpty = false - } else { - backend.removeAllChildren(of: children.container.into()) - children.isContainerEmpty = true - } - children.imageChanged = false - children.lastScaleFactor = environment.windowScaleFactor - } - - children.cachedImageDisplaySize = size.size - - if !dryRun { - backend.setSize(of: children.container.into(), to: size.size) - backend.setSize(of: children.imageWidget.into(), to: size.size) - } - - return ViewUpdateResult.leafView(size: size) - } -} - -class _ImageChildren: ViewGraphNodeChildren { - var cachedImageSource: Image.Source? = nil - var cachedImage: ImageFormats.Image? = nil - var cachedImageDisplaySize: SIMD2 = .zero - var container: AnyWidget - var imageWidget: AnyWidget - var imageChanged = false - var isContainerEmpty = true - var lastScaleFactor: Double = 1 - - init(backend: Backend) { - container = AnyWidget(backend.createContainer()) - imageWidget = AnyWidget(backend.createImageView()) - } - - var widgets: [AnyWidget] = [] - var erasedNodes: [ErasedViewGraphNode] = [] -} diff --git a/Sources/SwiftCrossUI/Views/List.swift b/Sources/SwiftCrossUI/Views/List.swift deleted file mode 100644 index 4283e5249e..0000000000 --- a/Sources/SwiftCrossUI/Views/List.swift +++ /dev/null @@ -1,234 +0,0 @@ -public struct List: TypeSafeView, View { - typealias Children = ListViewChildren> - - public let body = EmptyView() - - var selection: Binding - var rowContent: (Int) -> RowView - var associatedSelectionValue: (Int) -> SelectionValue - var find: (SelectionValue) -> Int? - var rowCount: Int - - public init( - _ data: Data, - selection: Binding, - @ViewBuilder rowContent: @escaping (Data.Element) -> RowView - ) where Data.Element: Identifiable, Data.Element.ID == SelectionValue, Data.Index == Int { - self.init(data, id: \.id, selection: selection, rowContent: rowContent) - } - - public init( - _ data: Data, - selection: Binding - ) - where - Data.Element: CustomStringConvertible & Identifiable, - Data.Element.ID == SelectionValue, - Data.Index == Int, - RowView == Text - { - self.init(data, selection: selection) { item in - return Text(item.description) - } - } - - public init( - _ data: Data, - id: @escaping (Data.Element) -> SelectionValue, - selection: Binding - ) where Data.Element: CustomStringConvertible, RowView == Text, Data.Index == Int { - self.init(data, id: id, selection: selection) { item in - return Text(item.description) - } - } - - public init( - _ data: Data, - id: KeyPath, - selection: Binding - ) where Data.Element: CustomStringConvertible, RowView == Text, Data.Index == Int { - self.init(data, id: id, selection: selection) { item in - return Text(item.description) - } - } - - public init( - _ data: Data, - id: KeyPath, - selection: Binding, - @ViewBuilder rowContent: @escaping (Data.Element) -> RowView - ) where Data.Index == Int { - self.init(data, id: { $0[keyPath: id] }, selection: selection, rowContent: rowContent) - } - - public init( - _ data: Data, - id: @escaping (Data.Element) -> SelectionValue, - selection: Binding, - @ViewBuilder rowContent: @escaping (Data.Element) -> RowView - ) where Data.Index == Int { - self.selection = selection - self.rowContent = { index in - rowContent(data[index]) - } - associatedSelectionValue = { index in - id(data[index]) - } - find = { selection in - data.firstIndex { item in - id(item) == selection - } - } - rowCount = data.count - } - - func children( - backend: Backend, - snapshots: [ViewGraphSnapshotter.NodeSnapshot]?, - environment: EnvironmentValues - ) -> Children { - // TODO: Implement snapshotting - Children() - } - - func asWidget( - _ children: Children, - backend: Backend - ) -> Backend.Widget { - backend.createSelectableListView() - } - - func update( - _ widget: Backend.Widget, - children: Children, - proposedSize: SIMD2, - environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - // Padding that the backend could not remove (some frameworks have a small - // constant amount of required padding within each row). - let baseRowPadding = backend.baseItemPadding(ofSelectableListView: widget) - let minimumRowSize = backend.minimumRowSize(ofSelectableListView: widget) - let horizontalBasePadding = baseRowPadding.axisTotals.x - let verticalBasePadding = baseRowPadding.axisTotals.y - - let rowViews = (0.. children.nodes.count { - for rowView in rowViews.dropFirst(children.nodes.count) { - let node = AnyViewGraphNode( - for: rowView, - backend: backend, - environment: environment - ) - children.nodes.append(node) - } - } else if children.nodes.count > rowCount { - children.nodes.removeLast(children.nodes.count - rowCount) - } - - var childResults: [ViewUpdateResult] = [] - for (rowView, node) in zip(rowViews, children.nodes) { - let preferredSize = node.update( - with: rowView, - proposedSize: SIMD2( - max(proposedSize.x, minimumRowSize.x) - baseRowPadding.axisTotals.x, - max(proposedSize.y, minimumRowSize.y) - baseRowPadding.axisTotals.y - ), - environment: environment, - dryRun: true - ).size - let childResult = node.update( - with: nil, - proposedSize: SIMD2( - max(proposedSize.x, minimumRowSize.x) - horizontalBasePadding, - max( - preferredSize.idealHeightForProposedWidth, - minimumRowSize.y - baseRowPadding.axisTotals.y - ) - ), - environment: environment, - dryRun: dryRun - ) - childResults.append(childResult) - } - - let size = SIMD2( - max( - (childResults.map(\.size.size.x).max() ?? 0) + horizontalBasePadding, - max(minimumRowSize.x, proposedSize.x) - ), - childResults.map(\.size.size.y).map { rowHeight in - max( - rowHeight + verticalBasePadding, - minimumRowSize.y - ) - }.reduce(0, +) - ) - - if !dryRun { - backend.setItems( - ofSelectableListView: widget, - to: children.widgets.map { $0.into() }, - withRowHeights: childResults.map(\.size.size.y).map { height in - height + verticalBasePadding - } - ) - backend.setSize(of: widget, to: size) - backend.setSelectionHandler(forSelectableListView: widget) { selectedIndex in - selection.wrappedValue = associatedSelectionValue(selectedIndex) - } - let selectedIndex: Int? - if let selectedItem = selection.wrappedValue { - selectedIndex = find(selectedItem) - } else { - selectedIndex = nil - } - backend.setSelectedItem(ofSelectableListView: widget, toItemAt: selectedIndex) - } - - return ViewUpdateResult( - size: ViewSize( - size: size, - idealSize: SIMD2( - (childResults.map(\.size.idealSize.x).max() ?? 0) - + horizontalBasePadding, - size.y - ), - minimumWidth: (childResults.map(\.size.minimumWidth).max() ?? 0) - + horizontalBasePadding, - minimumHeight: size.y, - maximumWidth: nil, - maximumHeight: Double(size.y) - ), - childResults: childResults - ) - } -} - -class ListViewChildren: ViewGraphNodeChildren { - var nodes: [AnyViewGraphNode] - - init() { - nodes = [] - } - - var erasedNodes: [ErasedViewGraphNode] { - nodes.map(ErasedViewGraphNode.init) - } - - var widgets: [AnyWidget] { - nodes.map(\.widget) - } -} diff --git a/Sources/SwiftCrossUI/Views/Menu.swift b/Sources/SwiftCrossUI/Views/Menu.swift index 7773db5215..cf9a78b80c 100644 --- a/Sources/SwiftCrossUI/Views/Menu.swift +++ b/Sources/SwiftCrossUI/Views/Menu.swift @@ -64,18 +64,29 @@ extension Menu: TypeSafeView { [] } - func update( + func computeLayout( _ widget: Backend.Widget, children: MenuStorage, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { + backend: Backend + ) -> ViewLayoutResult { // TODO: Store popped menu in view graph node children so that we can // continue updating it even once it's open. var size = backend.naturalSize(of: widget) size.x = buttonWidth ?? size.x + return ViewLayoutResult.leafView(size: ViewSize(size)) + } + + func commit( + _ widget: Backend.Widget, + children: MenuStorage, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + let size = layout.size + backend.setSize(of: widget, to: size.vector) let content = resolve().content switch backend.menuImplementationStyle { @@ -94,7 +105,7 @@ extension Menu: TypeSafeView { ) backend.showPopoverMenu( menu, - at: SIMD2(0, size.y + 2), + at: SIMD2(0, LayoutSystem.roundSize(size.width) + 2), relativeTo: widget ) { children.menu = nil @@ -102,14 +113,11 @@ extension Menu: TypeSafeView { } ) - if !dryRun { - backend.setSize(of: widget, to: size) - children.updateMenuIfShown( - content: content, - environment: environment, - backend: backend - ) - } + children.updateMenuIfShown( + content: content, + environment: environment, + backend: backend + ) case .menuButton: let menu = children.menu as? Backend.Menu ?? backend.createPopoverMenu() children.menu = menu @@ -119,13 +127,7 @@ extension Menu: TypeSafeView { environment: environment ) backend.updateButton(widget, label: label, menu: menu, environment: environment) - - if !dryRun { - backend.setSize(of: widget, to: size) - } } - - return ViewUpdateResult.leafView(size: ViewSize(fixedSize: size)) } /// A temporary button width solution until arbitrary labels are supported. diff --git a/Sources/SwiftCrossUI/Views/Modifiers/AlertModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/AlertModifier.swift deleted file mode 100644 index 3f3b9e5dfa..0000000000 --- a/Sources/SwiftCrossUI/Views/Modifiers/AlertModifier.swift +++ /dev/null @@ -1,128 +0,0 @@ -extension View { - public func alert( - _ title: String, - isPresented: Binding, - @AlertActionsBuilder actions: () -> [AlertAction] - ) -> some View { - AlertModifierView( - child: self, - title: title, - isPresented: isPresented, - actions: actions() - ) - } - - public func alert( - _ title: Binding, - @AlertActionsBuilder actions: () -> [AlertAction] - ) -> some View { - AlertModifierView( - child: self, - title: title.wrappedValue ?? "", - isPresented: Binding { - title.wrappedValue != nil - } set: { newValue in - if !newValue { - title.wrappedValue = nil - } - }, - actions: actions() - ) - } -} - -struct AlertModifierView: TypeSafeView { - typealias Children = AlertModifierViewChildren - - var body = EmptyView() - - var child: Child - var title: String - var isPresented: Binding - var actions: [AlertAction] - - func children( - backend: Backend, - snapshots: [ViewGraphSnapshotter.NodeSnapshot]?, - environment: EnvironmentValues - ) -> Children { - AlertModifierViewChildren( - childNode: AnyViewGraphNode( - ViewGraphNode( - for: child, - backend: backend, - environment: environment - ) - ), - alert: nil - ) - } - - func asWidget(_ children: Children, backend: Backend) -> Backend.Widget { - children.childNode.widget.into() - } - - func update( - _ widget: Backend.Widget, - children: Children, - proposedSize: SIMD2, - environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - let childResult = children.childNode.update( - with: child, - proposedSize: proposedSize, - environment: environment, - dryRun: dryRun - ) - - if isPresented.wrappedValue && children.alert == nil { - let alert = backend.createAlert() - backend.updateAlert( - alert, - title: title, - actionLabels: actions.map(\.label), - environment: environment - ) - backend.showAlert( - alert, - window: .some(environment.window! as! Backend.Window) - ) { responseId in - children.alert = nil - isPresented.wrappedValue = false - actions[responseId].action() - } - children.alert = alert - } else if isPresented.wrappedValue == false && children.alert != nil { - backend.dismissAlert( - children.alert as! Backend.Alert, - window: .some(environment.window! as! Backend.Window) - ) - children.alert = nil - } - - return childResult - } -} - -class AlertModifierViewChildren: ViewGraphNodeChildren { - var childNode: AnyViewGraphNode - var alert: Any? - - var widgets: [AnyWidget] { - [childNode.widget] - } - - var erasedNodes: [ErasedViewGraphNode] { - [ErasedViewGraphNode(wrapping: childNode)] - } - - init( - childNode: AnyViewGraphNode, - alert: Any? - ) { - self.childNode = childNode - self.alert = alert - } -} diff --git a/Sources/SwiftCrossUI/Views/Modifiers/ConditionalApplicationModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/ConditionalApplicationModifier.swift deleted file mode 100644 index 2edc31b42e..0000000000 --- a/Sources/SwiftCrossUI/Views/Modifiers/ConditionalApplicationModifier.swift +++ /dev/null @@ -1,23 +0,0 @@ -extension View { - public func `if`( - _ condition: Bool, - apply modifier: (Self) -> Result - ) -> some View { - if condition { - EitherView(modifier(self)) - } else { - EitherView(self) - } - } - - public func ifLet( - _ value: Value?, - apply modifier: (Self, Value) -> Result - ) -> some View { - if let value { - EitherView(modifier(self, value)) - } else { - EitherView(self) - } - } -} diff --git a/Sources/SwiftCrossUI/Views/Modifiers/DisabledModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/DisabledModifier.swift deleted file mode 100644 index 8d95d2a6e2..0000000000 --- a/Sources/SwiftCrossUI/Views/Modifiers/DisabledModifier.swift +++ /dev/null @@ -1,9 +0,0 @@ -extension View { - /// Disables user interaction in any subviews that support disabling - /// interaction. - public func disabled(_ disabled: Bool = true) -> some View { - EnvironmentModifier(self) { environment in - environment.with(\.isEnabled, !disabled) - } - } -} diff --git a/Sources/SwiftCrossUI/Views/Modifiers/EnvironmentModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/EnvironmentModifier.swift index 801354c213..216a214244 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/EnvironmentModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/EnvironmentModifier.swift @@ -19,21 +19,35 @@ package struct EnvironmentModifier: View { ) } - package func update( + package func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - body.update( + backend: Backend + ) -> ViewLayoutResult { + body.computeLayout( widget, children: children, proposedSize: proposedSize, environment: modification(environment), - backend: backend, - dryRun: dryRun + backend: backend + ) + } + + package func commit( + _ widget: Backend.Widget, + children: any ViewGraphNodeChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + body.commit( + widget, + children: children, + layout: layout, + environment: modification(environment), + backend: backend ) } } diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnChangeModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnChangeModifier.swift deleted file mode 100644 index 9c226532d3..0000000000 --- a/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnChangeModifier.swift +++ /dev/null @@ -1,54 +0,0 @@ -extension View { - public func onChange( - of value: Value, - initial: Bool = false, - perform action: @escaping () -> Void - ) -> some View { - OnChangeModifier( - body: TupleView1(self), - value: value, - action: action, - initial: initial - ) - } -} - -struct OnChangeModifier: View { - // TODO: This probably doesn't have to trigger view updates. We're only - // really using @State here to persist the data. - @State var previousValue: Value? - - var body: TupleView1 - - var value: Value - var action: () -> Void - var initial: Bool - - func update( - _ widget: Backend.Widget, - children: any ViewGraphNodeChildren, - proposedSize: SIMD2, - environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - if let previousValue = previousValue, value != previousValue { - action() - } else if initial && previousValue == nil { - action() - } - - if previousValue != value { - previousValue = value - } - - return defaultUpdate( - widget, - children: children, - proposedSize: proposedSize, - environment: environment, - backend: backend, - dryRun: dryRun - ) - } -} diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnHoverModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnHoverModifier.swift deleted file mode 100644 index 0d1d000f84..0000000000 --- a/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnHoverModifier.swift +++ /dev/null @@ -1,59 +0,0 @@ -extension View { - /// Adds an action to perform when the user's pointer enters/leaves this view. - public func onHover(perform action: @escaping (_ hovering: Bool) -> Void) - -> some View - { - OnHoverModifier(body: TupleView1(self), action: action) - } -} - -struct OnHoverModifier: TypeSafeView { - typealias Children = TupleView1.Children - - var body: TupleView1 - var action: (Bool) -> Void - - func children( - backend: Backend, - snapshots: [ViewGraphSnapshotter.NodeSnapshot]?, - environment: EnvironmentValues - ) -> Children { - body.children( - backend: backend, - snapshots: snapshots, - environment: environment - ) - } - - func asWidget( - _ children: Children, - backend: Backend - ) -> Backend.Widget { - backend.createHoverTarget(wrapping: children.child0.widget.into()) - } - - func update( - _ widget: Backend.Widget, - children: Children, - proposedSize: SIMD2, - environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - let childResult = children.child0.update( - with: body.view0, - proposedSize: proposedSize, - environment: environment, - dryRun: dryRun - ) - if !dryRun { - backend.setSize(of: widget, to: childResult.size.size) - backend.updateHoverTarget( - widget, - environment: environment, - action: action - ) - } - return childResult - } -} diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnOpenURLModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnOpenURLModifier.swift deleted file mode 100644 index 9b52440bd1..0000000000 --- a/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnOpenURLModifier.swift +++ /dev/null @@ -1,18 +0,0 @@ -import Foundation - -extension View { - public func onOpenURL(perform action: @escaping (URL) -> Void) -> some View { - PreferenceModifier(self) { preferences, environment in - var newPreferences = preferences - newPreferences.onOpenURL = { url in - action(url) - if let innerHandler = preferences.onOpenURL { - innerHandler(url) - } else { - environment.bringWindowForward() - } - } - return newPreferences - } - } -} diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnSubmitModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnSubmitModifier.swift deleted file mode 100644 index 9f79a02fdb..0000000000 --- a/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnSubmitModifier.swift +++ /dev/null @@ -1,23 +0,0 @@ -extension View { - /// Adds an action to perform when the user submits a text field within this - /// view (generally via pressing the Enter/Return key). Outer `onSubmit` - /// handlers get called before inner `onSubmit` handlers. To prevent - /// submissions from propagating upwards, use ``View/submitScope()`` after - /// adding the handler. - public func onSubmit(perform action: @escaping () -> Void) -> some View { - EnvironmentModifier(self) { environment in - environment.with(\.onSubmit) { - environment.onSubmit?() - action() - } - } - } - - /// Prevents text field submissions from propagating to this view's - /// ancestors. - public func submitScope() -> some View { - EnvironmentModifier(self) { environment in - environment.with(\.onSubmit, nil) - } - } -} diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnTapGestureModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnTapGestureModifier.swift index b8688eabbd..1feb583a42 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnTapGestureModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnTapGestureModifier.swift @@ -61,29 +61,34 @@ struct OnTapGestureModifier: TypeSafeView { backend.createTapGestureTarget(wrapping: children.child0.widget.into(), gesture: gesture) } - func update( + func computeLayout( _ widget: Backend.Widget, children: Children, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - let childResult = children.child0.update( + backend: Backend + ) -> ViewLayoutResult { + children.child0.computeLayout( with: body.view0, proposedSize: proposedSize, + environment: environment + ) + } + + func commit( + _ widget: Backend.Widget, + children: TupleView1.Children, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + let size = children.child0.commit().size + backend.setSize(of: widget, to: size.vector) + backend.updateTapGestureTarget( + widget, + gesture: gesture, environment: environment, - dryRun: dryRun + action: action ) - if !dryRun { - backend.setSize(of: widget, to: childResult.size.size) - backend.updateTapGestureTarget( - widget, - gesture: gesture, - environment: environment, - action: action - ) - } - return childResult } } diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Layout/AspectRatioModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Layout/AspectRatioModifier.swift deleted file mode 100644 index 2fc864a04e..0000000000 --- a/Sources/SwiftCrossUI/Views/Modifiers/Layout/AspectRatioModifier.swift +++ /dev/null @@ -1,148 +0,0 @@ -extension View { - // TODO: Figure out why SwiftUI's window gets significantly shorter than - // SwiftCrossUI's with the following content; - // - // VStack { - // Text("Hello, World!") - // Divider() - // Color.red - // .aspectRatio(1, contentMode: .fill) - // .frame(maxWidth: 300) - // Divider() - // Text("Footer") - // } - - /// Constrains a view to maintain a specific aspect ratio. - /// - Parameter aspectRatio: The aspect ratio to maintain. Use `nil` to - /// maintain the view's ideal aspect ratio. - /// - Parameter contentMode: How the view should fill available space. - public func aspectRatio(_ aspectRatio: Double? = nil, contentMode: ContentMode) -> some View { - AspectRatioView(self, aspectRatio: aspectRatio, contentMode: contentMode) - } - - /// Constrains a view to maintain an aspect ratio matching that of the - /// provided size. - /// - Parameter aspectRatio: The aspect ratio to maintain, specified as a - /// size with the desired aspect ratio. - /// - Parameter contentMode: How the view should fill available space. - public func aspectRatio(_ aspectRatio: SIMD2, contentMode: ContentMode) -> some View { - AspectRatioView( - self, - aspectRatio: LayoutSystem.aspectRatio(of: aspectRatio), - contentMode: contentMode - ) - } -} - -struct AspectRatioView: TypeSafeView { - var body: TupleView1 - - var aspectRatio: Double? - var contentMode: ContentMode - - init(_ child: Child, aspectRatio: Double?, contentMode: ContentMode) { - body = TupleView1(child) - self.aspectRatio = aspectRatio - self.contentMode = contentMode - } - - func children( - backend: Backend, - snapshots: [ViewGraphSnapshotter.NodeSnapshot]?, - environment: EnvironmentValues - ) -> TupleViewChildren1 { - body.children(backend: backend, snapshots: snapshots, environment: environment) - } - - func asWidget( - _ children: TupleViewChildren1, - backend: Backend - ) -> Backend.Widget { - let container = backend.createContainer() - backend.addChild(children.child0.widget.into(), to: container) - return container - } - - func update( - _ widget: Backend.Widget, - children: TupleViewChildren1, - proposedSize: SIMD2, - environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - let evaluatedAspectRatio: Double - if let aspectRatio { - evaluatedAspectRatio = aspectRatio == 0 ? 1 : aspectRatio - } else { - let childResult = children.child0.update( - with: body.view0, - proposedSize: proposedSize, - environment: environment, - dryRun: true - ) - evaluatedAspectRatio = childResult.size.idealAspectRatio - } - - let proposedFrameSize = LayoutSystem.frameSize( - forProposedSize: proposedSize, - aspectRatio: evaluatedAspectRatio, - contentMode: contentMode - ) - - let childResult = children.child0.update( - with: nil, - proposedSize: proposedFrameSize, - environment: environment, - dryRun: dryRun - ) - - let frameSize = LayoutSystem.frameSize( - forProposedSize: childResult.size.size, - aspectRatio: evaluatedAspectRatio, - contentMode: contentMode.opposite - ) - - if !dryRun { - // Center child in frame for cases where it's smaller or bigger than - // aspect ratio locked frame (not all views can achieve every aspect - // ratio). - let childPosition = Alignment.center.position( - ofChild: childResult.size.size, - in: frameSize - ) - backend.setPosition(ofChildAt: 0, in: widget, to: childPosition) - backend.setSize(of: widget, to: frameSize) - } - - return ViewUpdateResult( - size: ViewSize( - size: frameSize, - idealSize: LayoutSystem.frameSize( - forProposedSize: childResult.size.idealSize, - aspectRatio: evaluatedAspectRatio, - contentMode: .fill - ), - idealWidthForProposedHeight: LayoutSystem.height( - forWidth: frameSize.x, - aspectRatio: evaluatedAspectRatio - ), - idealHeightForProposedWidth: LayoutSystem.width( - forHeight: frameSize.y, - aspectRatio: evaluatedAspectRatio - ), - // TODO: These minimum and maximum size calculations are - // incorrect. I don't think we have enough information to - // compute these properly at the moment because the `minimumWidth` - // and `minimumHeight` properties are the minimum sizes assuming - // that the other dimension stays constant, which isn't very - // useful when trying to maintain aspect ratio. - minimumWidth: childResult.size.minimumWidth, - minimumHeight: childResult.size.minimumHeight, - maximumWidth: childResult.size.maximumWidth, - maximumHeight: childResult.size.maximumHeight - ), - childResults: [childResult] - ) - } -} diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Layout/BackgroundModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Layout/BackgroundModifier.swift index 8b34d55c80..c408871021 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/Layout/BackgroundModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/Layout/BackgroundModifier.swift @@ -34,65 +34,62 @@ struct BackgroundModifier: TypeSafeView { body.asWidget(children, backend: backend) } - func update( + func computeLayout( _ widget: Backend.Widget, children: TupleView2.Children, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - let foregroundResult = children.child1.update( + backend: Backend + ) -> ViewLayoutResult { + let foregroundResult = children.child1.computeLayout( with: body.view1, proposedSize: proposedSize, - environment: environment, - dryRun: dryRun + environment: environment ) let foregroundSize = foregroundResult.size - let backgroundResult = children.child0.update( + let backgroundResult = children.child0.computeLayout( with: body.view0, - proposedSize: foregroundSize.size, - environment: environment, - dryRun: dryRun + proposedSize: ProposedViewSize(foregroundSize), + environment: environment ) let backgroundSize = backgroundResult.size - let frameSize = SIMD2( - max(backgroundSize.size.x, foregroundSize.size.x), - max(backgroundSize.size.y, foregroundSize.size.y) + let frameSize = ViewSize( + max(backgroundSize.width, foregroundSize.width), + max(backgroundSize.height, foregroundSize.height) ) - if !dryRun { - let backgroundPosition = (frameSize &- backgroundSize.size) / 2 - let foregroundPosition = (frameSize &- foregroundSize.size) / 2 - - backend.setPosition(ofChildAt: 0, in: widget, to: backgroundPosition) - backend.setPosition(ofChildAt: 1, in: widget, to: foregroundPosition) + // TODO: Investigate the ordering of SwiftUI's preference merging for + // the background modifier. + return ViewLayoutResult( + size: frameSize, + childResults: [backgroundResult, foregroundResult] + ) + } - backend.setSize(of: widget, to: frameSize) - } + public func commit( + _ widget: Backend.Widget, + children: TupleView2.Children, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + let frameSize = layout.size + let backgroundSize = children.child0.commit().size + let foregroundSize = children.child1.commit().size - return ViewUpdateResult( - size: ViewSize( - size: frameSize, - idealSize: SIMD2( - max(foregroundSize.idealSize.x, backgroundSize.minimumWidth), - max(foregroundSize.idealSize.y, backgroundSize.minimumHeight) - ), - idealWidthForProposedHeight: max( - foregroundSize.idealWidthForProposedHeight, - backgroundSize.minimumWidth - ), - idealHeightForProposedWidth: max( - foregroundSize.idealHeightForProposedWidth, - backgroundSize.minimumHeight - ), - minimumWidth: max(backgroundSize.minimumWidth, foregroundSize.minimumWidth), - minimumHeight: max(backgroundSize.minimumHeight, foregroundSize.minimumHeight), - maximumWidth: min(backgroundSize.maximumWidth, foregroundSize.maximumWidth), - maximumHeight: min(backgroundSize.maximumHeight, foregroundSize.maximumHeight) - ), - childResults: [backgroundResult, foregroundResult] + let backgroundPosition = Alignment.center.position( + ofChild: backgroundSize.vector, + in: frameSize.vector ) + let foregroundPosition = Alignment.center.position( + ofChild: foregroundSize.vector, + in: frameSize.vector + ) + + backend.setPosition(ofChildAt: 0, in: widget, to: backgroundPosition) + backend.setPosition(ofChildAt: 1, in: widget, to: foregroundPosition) + + backend.setSize(of: widget, to: frameSize.vector) } } diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Layout/FixedSizeModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Layout/FixedSizeModifier.swift index fa6300781c..9000ff2a18 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/Layout/FixedSizeModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/Layout/FixedSizeModifier.swift @@ -37,58 +37,45 @@ struct FixedSizeModifier: TypeSafeView { return container } - func update( + func computeLayout( _ widget: Backend.Widget, children: TupleViewChildren1, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - let probingChildResult = children.child0.update( + backend: Backend + ) -> ViewLayoutResult { + var childProposal = proposedSize + if horizontal { + childProposal.width = nil + } + if vertical { + childProposal.height = nil + } + let childResult = children.child0.computeLayout( with: body.view0, proposedSize: proposedSize, - environment: environment, - dryRun: true + environment: environment ) - var frameSize = probingChildResult.size.size - if horizontal && vertical { - frameSize = probingChildResult.size.idealSize - } else if horizontal { - frameSize.x = probingChildResult.size.idealWidthForProposedHeight - } else if vertical { - frameSize.y = probingChildResult.size.idealHeightForProposedWidth - } - - let childResult = children.child0.update( - with: body.view0, - proposedSize: frameSize, - environment: environment, - dryRun: dryRun + return ViewLayoutResult( + size: childResult.size, + childResults: [childResult] ) + } - if !dryRun { - let childPosition = Alignment.center.position( - ofChild: childResult.size.size, - in: frameSize - ) - backend.setPosition(ofChildAt: 0, in: widget, to: childPosition) - backend.setSize(of: widget, to: frameSize) - } - - return ViewUpdateResult( - size: ViewSize( - size: frameSize, - idealSize: childResult.size.idealSize, - idealWidthForProposedHeight: childResult.size.idealWidthForProposedHeight, - idealHeightForProposedWidth: childResult.size.idealHeightForProposedWidth, - minimumWidth: horizontal ? frameSize.x : childResult.size.minimumWidth, - minimumHeight: vertical ? frameSize.y : childResult.size.minimumHeight, - maximumWidth: horizontal ? Double(frameSize.x) : childResult.size.maximumWidth, - maximumHeight: vertical ? Double(frameSize.y) : childResult.size.maximumHeight - ), - childResults: [childResult] + func commit( + _ widget: Backend.Widget, + children: TupleViewChildren1, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + let childResult = children.child0.commit() + let childPosition = Alignment.center.position( + ofChild: childResult.size.vector, + in: layout.size.vector ) + backend.setPosition(ofChildAt: 0, in: widget, to: childPosition) + backend.setSize(of: widget, to: layout.size.vector) } } diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Layout/FrameModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Layout/FrameModifier.swift index eebfc350c8..3c297c2858 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/Layout/FrameModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/Layout/FrameModifier.swift @@ -72,82 +72,53 @@ struct StrictFrameView: TypeSafeView { return container } - func update( + func computeLayout( _ widget: Backend.Widget, children: TupleViewChildren1, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - let proposedSize = SIMD2( - width ?? proposedSize.x, - height ?? proposedSize.y - ) + backend: Backend + ) -> ViewLayoutResult { + let width = width.map(Double.init) + let height = height.map(Double.init) - let childResult = children.child0.update( + let childResult = children.child0.computeLayout( with: body.view0, - proposedSize: proposedSize, - environment: environment, - dryRun: dryRun + proposedSize: ProposedViewSize( + width ?? proposedSize.width, + height ?? proposedSize.height + ), + environment: environment ) let childSize = childResult.size - let frameSize = SIMD2( - width ?? childSize.size.x, - height ?? childSize.size.y + let frameSize = ViewSize( + width ?? childSize.width, + height ?? childSize.height ) - if !dryRun { - let childPosition = alignment.position( - ofChild: childSize.size, - in: frameSize - ) - backend.setSize(of: widget, to: frameSize) - backend.setPosition(ofChildAt: 0, in: widget, to: childPosition) - } - let idealWidth: Int - let idealHeight: Int - if let width, let height { - idealWidth = width - idealHeight = height - } else if let width, height == nil { - idealWidth = width - idealHeight = childSize.idealHeightForProposedWidth - } else if let height, width == nil { - idealHeight = height - idealWidth = childSize.idealWidthForProposedHeight - } else { - idealWidth = childSize.idealSize.x - idealHeight = childSize.idealSize.y - } + return ViewLayoutResult( + size: frameSize, + childResults: [childResult] + ) + } - let idealWidthForProposedHeight: Int - let idealHeightForProposedWidth: Int - if width == nil && height == nil { - idealWidthForProposedHeight = childSize.idealWidthForProposedHeight - idealHeightForProposedWidth = childSize.idealHeightForProposedWidth - } else { - idealWidthForProposedHeight = idealWidth - idealHeightForProposedWidth = idealHeight - } + func commit( + _ widget: Backend.Widget, + children: TupleViewChildren1, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + let frameSize = layout.size + let childSize = children.child0.commit().size - return ViewUpdateResult( - size: ViewSize( - size: frameSize, - idealSize: SIMD2( - idealWidth, - idealHeight - ), - idealWidthForProposedHeight: idealWidthForProposedHeight, - idealHeightForProposedWidth: idealHeightForProposedWidth, - minimumWidth: width ?? childSize.minimumWidth, - minimumHeight: height ?? childSize.minimumHeight, - maximumWidth: width.map(Double.init) ?? childSize.maximumWidth, - maximumHeight: height.map(Double.init) ?? childSize.maximumHeight - ), - childResults: [childResult] + let childPosition = alignment.position( + ofChild: childSize.vector, + in: frameSize.vector ) + backend.setSize(of: widget, to: frameSize.vector) + backend.setPosition(ofChildAt: 0, in: widget, to: childPosition) } } @@ -202,37 +173,35 @@ struct FlexibleFrameView: TypeSafeView { return container } - func update( + func computeLayout( _ widget: Backend.Widget, children: TupleViewChildren1, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { + backend: Backend + ) -> ViewLayoutResult { var proposedFrameSize = proposedSize - if let minWidth { - proposedFrameSize.x = max(proposedFrameSize.x, minWidth) - } - if let maxWidth { - proposedFrameSize.x = LayoutSystem.roundSize( - min(Double(proposedFrameSize.x), maxWidth) + + if let proposedWidth = proposedSize.width { + proposedFrameSize.width = LayoutSystem.clamp( + proposedWidth, + minimum: minWidth.map(Double.init), + maximum: maxWidth ) } - if let minHeight { - proposedFrameSize.y = max(proposedFrameSize.y, minHeight) - } - if let maxHeight { - proposedFrameSize.y = LayoutSystem.roundSize( - min(Double(proposedFrameSize.y), maxHeight) + + if let proposedHeight = proposedSize.height { + proposedFrameSize.height = LayoutSystem.clamp( + proposedHeight, + minimum: minHeight.map(Double.init), + maximum: maxHeight ) } - let childResult = children.child0.update( + let childResult = children.child0.computeLayout( with: body.view0, proposedSize: proposedFrameSize, - environment: environment, - dryRun: dryRun + environment: environment ) let childSize = childResult.size @@ -242,73 +211,45 @@ struct FlexibleFrameView: TypeSafeView { // perform an additional dryRun update to probe the child view. var frameSize = childSize - if let minWidth { - frameSize.size.x = max(frameSize.size.x, minWidth) - frameSize.minimumWidth = minWidth - frameSize.idealSize.x = max(frameSize.idealSize.x, minWidth) - frameSize.idealWidthForProposedHeight = max( - frameSize.idealWidthForProposedHeight, - minWidth - ) - } - if let maxWidth { - if maxWidth == .infinity { - frameSize.size.x = proposedSize.x - } else { - frameSize.size.x = min(frameSize.size.x, LayoutSystem.roundSize(maxWidth)) - } - frameSize.idealSize.x = LayoutSystem.roundSize( - min(Double(frameSize.idealSize.x), maxWidth) - ) - frameSize.maximumWidth = min(childSize.maximumWidth, Double(maxWidth)) - frameSize.idealWidthForProposedHeight = LayoutSystem.roundSize( - min(Double(frameSize.idealWidthForProposedHeight), maxWidth) - ) - } - - if let minHeight { - frameSize.size.y = max(frameSize.size.y, minHeight) - frameSize.minimumHeight = minHeight - frameSize.idealSize.y = max(frameSize.idealSize.y, minHeight) - frameSize.idealHeightForProposedWidth = max( - frameSize.idealHeightForProposedWidth, - minHeight - ) - } - if let maxHeight { - if maxHeight == .infinity { - frameSize.size.y = proposedSize.y - } else { - frameSize.size.y = min(frameSize.size.y, LayoutSystem.roundSize(maxHeight)) - } - frameSize.idealSize.y = LayoutSystem.roundSize( - min(Double(frameSize.idealSize.y), maxHeight) - ) - frameSize.maximumHeight = min(childSize.maximumHeight, Double(maxHeight)) - frameSize.idealHeightForProposedWidth = LayoutSystem.roundSize( - min(Double(frameSize.idealHeightForProposedWidth), maxHeight) - ) - } + frameSize.width = LayoutSystem.clamp( + frameSize.width, + minimum: minWidth.map(Double.init), + maximum: maxWidth + ) + frameSize.height = LayoutSystem.clamp( + frameSize.height, + minimum: minHeight.map(Double.init), + maximum: maxHeight + ) - if let idealWidth { - frameSize.idealSize.x = idealWidth + if maxWidth == .infinity, let proposedWidth = proposedSize.width { + frameSize.width = proposedWidth } - if let idealHeight { - frameSize.idealSize.y = idealHeight + if maxHeight == .infinity, let proposedHeight = proposedSize.height { + frameSize.height = proposedHeight } - if !dryRun { - let childPosition = alignment.position( - ofChild: childSize.size, - in: frameSize.size - ) - backend.setSize(of: widget, to: frameSize.size) - backend.setPosition(ofChildAt: 0, in: widget, to: childPosition) - } - - return ViewUpdateResult( + return ViewLayoutResult( size: frameSize, childResults: [childResult] ) } + + func commit( + _ widget: Backend.Widget, + children: TupleViewChildren1, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + let frameSize = layout.size + let childSize = children.child0.commit().size + + let childPosition = alignment.position( + ofChild: childSize.vector, + in: frameSize.vector + ) + backend.setSize(of: widget, to: frameSize.vector) + backend.setPosition(ofChildAt: 0, in: widget, to: childPosition) + } } diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Layout/MultilineTextAlignmentModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Layout/MultilineTextAlignmentModifier.swift deleted file mode 100644 index 7907863a1a..0000000000 --- a/Sources/SwiftCrossUI/Views/Modifiers/Layout/MultilineTextAlignmentModifier.swift +++ /dev/null @@ -1,9 +0,0 @@ -extension View { - /// Sets the alignment of lines of text relative to each other in multiline - /// text views. - public func multilineTextAlignment(_ alignment: HorizontalAlignment) -> some View { - return EnvironmentModifier(self) { environment in - return environment.with(\.multilineTextAlignment, alignment) - } - } -} diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Layout/OverlayModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Layout/OverlayModifier.swift deleted file mode 100644 index 34edb7438d..0000000000 --- a/Sources/SwiftCrossUI/Views/Modifiers/Layout/OverlayModifier.swift +++ /dev/null @@ -1,91 +0,0 @@ -extension View { - public func overlay(@ViewBuilder content: () -> some View) -> some View { - OverlayModifier(content: self, overlay: content()) - } -} - -struct OverlayModifier: TypeSafeView { - typealias Children = TupleView2.Children - - var body: TupleView2 - - init(content: Content, overlay: Overlay) { - body = TupleView2(content, overlay) - } - - func children( - backend: Backend, - snapshots: [ViewGraphSnapshotter.NodeSnapshot]?, - environment: EnvironmentValues - ) -> TupleView2.Children { - body.children( - backend: backend, - snapshots: snapshots, - environment: environment - ) - } - - func layoutableChildren( - backend: Backend, - children: TupleView2.Children - ) -> [LayoutSystem.LayoutableChild] { - [] - } - - func asWidget( - _ children: TupleView2.Children, backend: Backend - ) -> Backend.Widget { - body.asWidget(children, backend: backend) - } - - func update( - _ widget: Backend.Widget, - children: TupleView2.Children, - proposedSize: SIMD2, - environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - let contentResult = children.child0.update( - with: body.view0, - proposedSize: proposedSize, - environment: environment, - dryRun: dryRun - ) - let contentSize = contentResult.size - let overlayResult = children.child1.update( - with: body.view1, - proposedSize: contentSize.size, - environment: environment, - dryRun: dryRun - ) - let overlaySize = overlayResult.size - - let frameSize = SIMD2( - max(contentSize.size.x, overlaySize.size.x), - max(contentSize.size.y, overlaySize.size.y) - ) - - if !dryRun { - let contentPosition = (frameSize &- contentSize.size) / 2 - let overlayPosition = (frameSize &- overlaySize.size) / 2 - - backend.setPosition(ofChildAt: 0, in: widget, to: contentPosition) - backend.setPosition(ofChildAt: 1, in: widget, to: overlayPosition) - - backend.setSize(of: widget, to: frameSize) - } - - return ViewUpdateResult( - size: ViewSize( - size: frameSize, - idealSize: contentSize.idealSize, - minimumWidth: max(contentSize.minimumWidth, overlaySize.minimumWidth), - minimumHeight: max(contentSize.minimumHeight, overlaySize.minimumHeight), - maximumWidth: min(contentSize.maximumWidth, overlaySize.maximumWidth), - maximumHeight: min(contentSize.maximumHeight, overlaySize.maximumHeight) - ), - childResults: [contentResult, overlayResult] - ) - } -} diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Layout/PaddingModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Layout/PaddingModifier.swift index c5441093ac..d2429b6942 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/Layout/PaddingModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/Layout/PaddingModifier.swift @@ -97,50 +97,57 @@ struct PaddingModifierView: TypeSafeView { return container } - func update( + func computeLayout( _ container: Backend.Widget, children: TupleViewChildren1, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { + backend: Backend + ) -> ViewLayoutResult { + // This first block of calculations is somewhat repeated in `commit`, + // make sure to update things in both places. let insets = EdgeInsets(insets, defaultAmount: backend.defaultPaddingAmount) + let horizontalPadding = Double(insets.leading + insets.trailing) + let verticalPadding = Double(insets.top + insets.bottom) + + var childProposal = proposedSize + if let proposedWidth = proposedSize.width { + childProposal.width = max(proposedWidth - horizontalPadding, 0) + } + if let proposedHeight = proposedSize.height { + childProposal.height = max(proposedHeight - verticalPadding, 0) + } - let childResult = children.child0.update( + let childResult = children.child0.computeLayout( with: body.view0, - proposedSize: SIMD2( - max(proposedSize.x - insets.leading - insets.trailing, 0), - max(proposedSize.y - insets.top - insets.bottom, 0) - ), - environment: environment, - dryRun: dryRun + proposedSize: childProposal, + environment: environment ) - let childSize = childResult.size - - let paddingSize = SIMD2(insets.leading + insets.trailing, insets.top + insets.bottom) - let size = - SIMD2( - childSize.size.x, - childSize.size.y - ) &+ paddingSize - if !dryRun { - backend.setSize(of: container, to: size) - backend.setPosition(ofChildAt: 0, in: container, to: SIMD2(insets.leading, insets.top)) - } - return ViewUpdateResult( - size: ViewSize( - size: size, - idealSize: childSize.idealSize &+ paddingSize, - idealWidthForProposedHeight: childSize.idealWidthForProposedHeight + paddingSize.x, - idealHeightForProposedWidth: childSize.idealHeightForProposedWidth + paddingSize.y, - minimumWidth: childSize.minimumWidth + paddingSize.x, - minimumHeight: childSize.minimumHeight + paddingSize.y, - maximumWidth: childSize.maximumWidth + Double(paddingSize.x), - maximumHeight: childSize.maximumHeight + Double(paddingSize.y) - ), + var size = childResult.size + size.width += horizontalPadding + size.height += verticalPadding + + return ViewLayoutResult( + size: size, childResults: [childResult] ) } + + func commit( + _ container: Backend.Widget, + children: TupleViewChildren1, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + _ = children.child0.commit() + + let size = layout.size + backend.setSize(of: container, to: size.vector) + + let insets = EdgeInsets(insets, defaultAmount: backend.defaultPaddingAmount) + let childPosition = SIMD2(insets.leading, insets.top) + backend.setPosition(ofChildAt: 0, in: container, to: childPosition) + } } diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Lifecycle/OnAppearModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Lifecycle/OnAppearModifier.swift deleted file mode 100644 index 79c6479aca..0000000000 --- a/Sources/SwiftCrossUI/Views/Modifiers/Lifecycle/OnAppearModifier.swift +++ /dev/null @@ -1,25 +0,0 @@ -extension View { - /// Adds an action to be performed before this view appears. - /// - /// The exact moment that the action gets called is an internal detail and - /// may change at any time, but it is guaranteed to be after accessing the - /// view's ``View/body`` and before the view appears on screen. Currently, - /// if these docs have been kept up to date, the action gets called just - /// before creating the view's widget. - public func onAppear(perform action: @escaping @MainActor () -> Void) -> some View { - OnAppearModifier(body: TupleView1(self), action: action) - } -} - -struct OnAppearModifier: View { - var body: TupleView1 - var action: @MainActor () -> Void - - func asWidget( - _ children: any ViewGraphNodeChildren, - backend: Backend - ) -> Backend.Widget { - action() - return defaultAsWidget(children, backend: backend) - } -} diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Lifecycle/OnDisappearModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Lifecycle/OnDisappearModifier.swift deleted file mode 100644 index 6c9ce558c9..0000000000 --- a/Sources/SwiftCrossUI/Views/Modifiers/Lifecycle/OnDisappearModifier.swift +++ /dev/null @@ -1,93 +0,0 @@ -extension View { - /// Adds an action to be performed after this view disappears. - /// - /// `onDisappear` actions on outermost views are called first and propagate - /// down to the leaf views due to essentially relying on the `deinit` of the - /// modifier view's ``ViewGraphNode``. - public func onDisappear(perform action: @escaping @Sendable @MainActor () -> Void) -> some View - { - OnDisappearModifier(body: TupleView1(self), action: action) - } -} - -struct OnDisappearModifier: TypeSafeView { - var body: TupleView1 - var action: @Sendable @MainActor () -> Void - - func children( - backend: Backend, - snapshots: [ViewGraphSnapshotter.NodeSnapshot]?, - environment: EnvironmentValues - ) -> OnDisappearModifierChildren { - OnDisappearModifierChildren( - wrappedChildren: defaultChildren( - backend: backend, - snapshots: snapshots, - environment: environment - ), - action: action - ) - } - - func layoutableChildren( - backend: Backend, - children: OnDisappearModifierChildren - ) -> [LayoutSystem.LayoutableChild] { - defaultLayoutableChildren( - backend: backend, - children: children.wrappedChildren - ) - } - - func asWidget( - _ children: OnDisappearModifierChildren, - backend: Backend - ) -> Backend.Widget { - defaultAsWidget(children.wrappedChildren, backend: backend) - } - - func update( - _ widget: Backend.Widget, - children: OnDisappearModifierChildren, - proposedSize: SIMD2, - environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - defaultUpdate( - widget, - children: children.wrappedChildren, - proposedSize: proposedSize, - environment: environment, - backend: backend, - dryRun: dryRun - ) - } -} - -class OnDisappearModifierChildren: ViewGraphNodeChildren { - var wrappedChildren: any ViewGraphNodeChildren - var action: @Sendable @MainActor () -> Void - - var widgets: [AnyWidget] { - wrappedChildren.widgets - } - - var erasedNodes: [ErasedViewGraphNode] { - wrappedChildren.erasedNodes - } - - init( - wrappedChildren: any ViewGraphNodeChildren, - action: @escaping @Sendable @MainActor () -> Void - ) { - self.wrappedChildren = wrappedChildren - self.action = action - } - - deinit { - Task { @MainActor [action] in - action() - } - } -} diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Lifecycle/TaskModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Lifecycle/TaskModifier.swift deleted file mode 100644 index 62726cf32a..0000000000 --- a/Sources/SwiftCrossUI/Views/Modifiers/Lifecycle/TaskModifier.swift +++ /dev/null @@ -1,62 +0,0 @@ -extension View { - /// Starts a task before a view appears (but after ``View/body`` has been - /// accessed), and cancels the task when the view disappears. Additionally, - /// if `id` changes the current task is cancelled and a new one is started. - /// - /// This variant of `task` can be useful when the lifetime of the task - /// must be linked to a value with a potentially shorter lifetime than the - /// view. - public nonisolated func task( - id: Id, - priority: TaskPriority = .userInitiated, - _ action: @escaping () async -> Void - ) -> some View { - TaskModifier( - id: id, - content: TupleView1(self), - priority: priority, - action: action - ) - } - - /// Starts a task before a view appears (but after ``View/body`` has been - /// accessed), and cancels the task when the view disappears. - public nonisolated func task( - priority: TaskPriority = .userInitiated, - _ action: @escaping () async -> Void - ) -> some View { - TaskModifier( - id: 0, - content: TupleView1(self), - priority: priority, - action: action - ) - } -} - -struct TaskModifier { - @State var task: Task<(), any Error>? = nil - - var id: Id - var content: Content - var priority: TaskPriority - var action: () async -> Void -} - -extension TaskModifier: View { - var body: some View { - // Explicitly return to disable result builder (we don't want an extra - // layer of views). - return - content - .onChange(of: id, initial: true) { - task?.cancel() - task = Task(priority: priority) { - await action() - } - } - .onDisappear { - task?.cancel() - } - } -} diff --git a/Sources/SwiftCrossUI/Views/Modifiers/PreferenceModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/PreferenceModifier.swift deleted file mode 100644 index 90ec013d16..0000000000 --- a/Sources/SwiftCrossUI/Views/Modifiers/PreferenceModifier.swift +++ /dev/null @@ -1,45 +0,0 @@ -extension View { - public func preference( - key: WritableKeyPath, - value: V - ) -> some View { - PreferenceModifier(self) { preferences, _ in - var preferences = preferences - preferences[keyPath: key] = value - return preferences - } - } -} - -struct PreferenceModifier: View { - var body: TupleView1 - var modification: (PreferenceValues, EnvironmentValues) -> PreferenceValues - - init( - _ child: Child, - modification: @escaping (PreferenceValues, EnvironmentValues) -> PreferenceValues - ) { - self.body = TupleView1(child) - self.modification = modification - } - - func update( - _ widget: Backend.Widget, - children: any ViewGraphNodeChildren, - proposedSize: SIMD2, - environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - var result = defaultUpdate( - widget, - children: children, - proposedSize: proposedSize, - environment: environment, - backend: backend, - dryRun: dryRun - ) - result.preferences = modification(result.preferences, environment) - return result - } -} diff --git a/Sources/SwiftCrossUI/Views/Modifiers/PresentationModifiers.swift b/Sources/SwiftCrossUI/Views/Modifiers/PresentationModifiers.swift deleted file mode 100644 index 3b5738a615..0000000000 --- a/Sources/SwiftCrossUI/Views/Modifiers/PresentationModifiers.swift +++ /dev/null @@ -1,64 +0,0 @@ -extension View { - /// Sets the detents (heights) for the enclosing sheet presentation to snap to - /// when the user resizes it interactively. - /// - /// Detents are only respected on platforms that support sheet resizing, - /// and sheet resizing is generally only supported on mobile. - /// - /// If no detents are specified, then a single default detent is used. The - /// default is platform-specific. On iOS the default is `.large`. - /// - /// - Supported platforms: iOS & Mac Catalyst 15+ (ignored on unsupported platforms) - /// - `.fraction` and `.height` fall back to `.medium` on iOS 15 and earlier - /// - /// - Parameter detents: A set of detents that the sheet can be resized to. - /// - Returns: A view with the presentationDetents preference set. - public func presentationDetents(_ detents: Set) -> some View { - preference(key: \.presentationDetents, value: Array(detents)) - } - - /// Sets the corner radius for the enclosing sheet presentation. - /// - /// - Supported platforms: iOS & Mac Catalyst 15+, Gtk4 (ignored on unsupported platforms) - /// - /// - Parameter radius: The corner radius in points. - /// - Returns: A view with the presentationCornerRadius preference set. - public func presentationCornerRadius(_ radius: Double) -> some View { - preference(key: \.presentationCornerRadius, value: radius) - } - - /// Sets the visibility of the enclosing sheet presentation's drag indicator. - /// Drag indicators are only supported on platforms that support sheet - /// resizing, and sheet resizing is generally only support on mobile. - /// - /// - Supported platforms: iOS & Mac Catalyst 15+ (ignored on unsupported platforms) - /// - /// - Parameter visibility: The visibility to use for the drag indicator of - /// the enclosing sheet. - /// - Returns: A view with the presentationDragIndicatorVisibility preference set. - public func presentationDragIndicatorVisibility( - _ visibility: Visibility - ) -> some View { - preference(key: \.presentationDragIndicatorVisibility, value: visibility) - } - - /// Sets the background of the enclosing sheet presentation. - /// - /// - Parameter color: The background color to use for the enclosing sheet presentation. - /// - Returns: A view with the presentationBackground preference set. - public func presentationBackground(_ color: Color) -> some View { - preference(key: \.presentationBackground, value: color) - } - - /// Prevents the user from dismissing the enclosing sheet presentation. - /// - /// When interactive dismissal is disabled, users can only dismiss sheets by - /// performing actions that your code handles and turns into programmatic - /// dismissals. - /// - /// - Parameter isDisabled: Whether interactive dismissal is disabled. - /// - Returns: A view with the interactiveDismissDisabled preference set. - public func interactiveDismissDisabled(_ isDisabled: Bool = true) -> some View { - preference(key: \.interactiveDismissDisabled, value: isDisabled) - } -} diff --git a/Sources/SwiftCrossUI/Views/Modifiers/ScrollDismissesKeyboardModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/ScrollDismissesKeyboardModifier.swift deleted file mode 100644 index 0655a8e907..0000000000 --- a/Sources/SwiftCrossUI/Views/Modifiers/ScrollDismissesKeyboardModifier.swift +++ /dev/null @@ -1,41 +0,0 @@ -extension View { - /// Configures the behavior in which scrollable content interacts with - /// the software keyboard. - /// - /// You use this modifier to customize how scrollable content interacts - /// with the software keyboard. For example, you can specify a value of - /// ``ScrollDismissesKeyboardMode/immediately`` to indicate that you - /// would like scrollable content to immediately dismiss the keyboard if - /// present when a scroll drag gesture begins. - /// - /// @State private var text = "" - /// - /// ScrollView { - /// TextField("Prompt", text: $text) - /// ForEach(0 ..< 50) { index in - /// Text("\(index)") - /// .padding() - /// } - /// } - /// .scrollDismissesKeyboard(.immediately) - /// - /// You can also use this modifier to customize the keyboard dismissal - /// behavior for other kinds of scrollable views, like a ``List`` or a - /// ``TextEditor``. - /// - /// By default, scrollable content dismisses the keyboard interactively as the user scrolls. - /// Pass a different value of ``ScrollDismissesKeyboardMode`` to change this behavior. - /// For example, use ``ScrollDismissesKeyboardMode/never`` to prevent the keyboard from - /// dismissing automatically. Note that ``TextEditor`` may still use a different - /// default to preserve expected editing behavior. - /// - /// - Parameter mode: The keyboard dismissal mode that scrollable content - /// uses. - /// - /// - Returns: A view that uses the specified keyboard dismissal mode. - public func scrollDismissesKeyboard(_ mode: ScrollDismissesKeyboardMode) -> some View { - EnvironmentModifier(self) { environment in - environment.with(\.scrollDismissesKeyboardMode, mode) - } - } -} diff --git a/Sources/SwiftCrossUI/Views/Modifiers/SheetModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/SheetModifier.swift deleted file mode 100644 index 00d87b182c..0000000000 --- a/Sources/SwiftCrossUI/Views/Modifiers/SheetModifier.swift +++ /dev/null @@ -1,199 +0,0 @@ -extension View { - /// Presents a conditional modal overlay. `onDismiss` gets invoked when the sheet is dismissed. - /// - /// On most platforms sheets appear as form-style modals. On tvOS, sheets - /// appear as full screen overlays (non-opaque). - /// - /// `onDismiss` isn't called when the sheet gets dismissed programmatically - /// (i.e. by setting `isPresented` to `false`). - /// - /// `onDismiss` gets called *after* the sheet has been dismissed by the - /// underlying UI framework, and *before* `isPresented` gets set to false. - /// - /// - Parameters: - /// - isPresented: A binding controlling whether the sheet is presented. - /// - onDismiss: An action to perform when the sheet is dismissed - /// by the user. - public func sheet( - isPresented: Binding, - onDismiss: (() -> Void)? = nil, - @ViewBuilder content: @escaping () -> SheetContent - ) -> some View { - SheetModifier( - isPresented: isPresented, - body: TupleView1(self), - onDismiss: onDismiss, - sheetContent: content - ) - } -} - -struct SheetModifier: TypeSafeView { - typealias Children = SheetModifierViewChildren - - var isPresented: Binding - var body: TupleView1 - var onDismiss: (() -> Void)? - var sheetContent: () -> SheetContent - - var sheet: Any? - - func children( - backend: Backend, - snapshots: [ViewGraphSnapshotter.NodeSnapshot]?, - environment: EnvironmentValues - ) -> Children { - let bodyViewGraphNode = ViewGraphNode( - for: body.view0, - backend: backend, - environment: environment - ) - let bodyNode = AnyViewGraphNode(bodyViewGraphNode) - - return SheetModifierViewChildren( - childNode: bodyNode, - sheetContentNode: nil, - sheet: nil - ) - } - - func asWidget( - _ children: Children, - backend: Backend - ) -> Backend.Widget { - children.childNode.widget.into() - } - - func update( - _ widget: Backend.Widget, - children: Children, - proposedSize: SIMD2, - environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - let childResult = children.childNode.update( - with: body.view0, - proposedSize: proposedSize, - environment: environment, - dryRun: dryRun - ) - - if isPresented.wrappedValue && children.sheet == nil { - let sheetViewGraphNode = ViewGraphNode( - for: sheetContent(), - backend: backend, - environment: environment - ) - let sheetContentNode = AnyViewGraphNode(sheetViewGraphNode) - children.sheetContentNode = sheetContentNode - - let sheet = backend.createSheet( - content: children.sheetContentNode!.widget.into() - ) - - let dismissAction = DismissAction(action: { [isPresented] in - isPresented.wrappedValue = false - }) - - let sheetEnvironment = - environment - .with(\.dismiss, dismissAction) - .with(\.sheet, sheet) - - let result = children.sheetContentNode!.update( - with: sheetContent(), - proposedSize: SIMD2(x: 10_000, y: 0), - environment: sheetEnvironment, - dryRun: false - ) - - let window = environment.window! as! Backend.Window - let preferences = result.preferences - backend.updateSheet( - sheet, - window: window, - // We intentionally use the outer environment rather than - // sheetEnvironment here, because this is meant to be the sheet's - // environment, not that of its content. - environment: environment, - size: result.size.size, - onDismiss: { handleDismiss(children: children) }, - cornerRadius: preferences.presentationCornerRadius, - detents: preferences.presentationDetents ?? [], - dragIndicatorVisibility: - preferences.presentationDragIndicatorVisibility ?? .automatic, - backgroundColor: preferences.presentationBackground, - interactiveDismissDisabled: preferences.interactiveDismissDisabled ?? false - ) - - let parentSheet = environment.sheet.map { $0 as! Backend.Sheet } - backend.presentSheet( - sheet, - window: window, - parentSheet: parentSheet - ) - children.sheet = sheet - children.window = window - children.parentSheet = parentSheet - } else if !isPresented.wrappedValue && children.sheet != nil { - backend.dismissSheet( - children.sheet as! Backend.Sheet, - window: children.window! as! Backend.Window, - parentSheet: children.parentSheet.map { $0 as! Backend.Sheet } - ) - children.sheet = nil - children.window = nil - children.parentSheet = nil - children.sheetContentNode = nil - } - - // Reset presentation preferences so that they don't leak to enclosing sheets. - var modifiedResult = childResult - modifiedResult.preferences.interactiveDismissDisabled = nil - modifiedResult.preferences.presentationBackground = nil - modifiedResult.preferences.presentationCornerRadius = nil - modifiedResult.preferences.presentationDetents = nil - modifiedResult.preferences.presentationDragIndicatorVisibility = nil - return modifiedResult - } - - func handleDismiss(children: Children) { - onDismiss?() - children.sheet = nil - children.window = nil - children.parentSheet = nil - children.sheetContentNode = nil - isPresented.wrappedValue = false - } -} - -class SheetModifierViewChildren: ViewGraphNodeChildren { - var widgets: [AnyWidget] { - [childNode.widget] - } - - var erasedNodes: [ErasedViewGraphNode] { - var nodes: [ErasedViewGraphNode] = [ErasedViewGraphNode(wrapping: childNode)] - if let sheetContentNode = sheetContentNode { - nodes.append(ErasedViewGraphNode(wrapping: sheetContentNode)) - } - return nodes - } - - var childNode: AnyViewGraphNode - var sheetContentNode: AnyViewGraphNode? - var sheet: Any? - var window: Any? - var parentSheet: Any? - - init( - childNode: AnyViewGraphNode, - sheetContentNode: AnyViewGraphNode?, - sheet: Any? - ) { - self.childNode = childNode - self.sheetContentNode = sheetContentNode - self.sheet = sheet - } -} diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Style/CornerRadiusModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Style/CornerRadiusModifier.swift index 1dd3b3ff0e..98e350eadd 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/Style/CornerRadiusModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/Style/CornerRadiusModifier.swift @@ -30,14 +30,29 @@ struct CornerRadiusModifier: View { body.layoutableChildren(backend: backend, children: children) } - func update( + func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { + backend: Backend + ) -> ViewLayoutResult { + body.computeLayout( + widget, + children: children, + proposedSize: proposedSize, + environment: environment, + backend: backend + ) + } + + func commit( + _ widget: Backend.Widget, + children: any ViewGraphNodeChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { // We used to wrap the child content in a container and then set the corner // radius on that, since it was the simplest approach. But Gtk3Backend has // extremely poor corner radius support and only applies the corner radius @@ -46,17 +61,13 @@ struct CornerRadiusModifier: View { // implement the modifier this way then you can at the very least set the // cornerRadius of a coloured rectangle, which is quite a common thing to // want to do. - let contentResult = body.update( + body.commit( widget, children: children, - proposedSize: proposedSize, + layout: layout, environment: environment, - backend: backend, - dryRun: dryRun + backend: backend ) - if !dryRun { - backend.setCornerRadius(of: widget, to: cornerRadius) - } - return contentResult + backend.setCornerRadius(of: widget, to: cornerRadius) } } diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Style/ToggleStyleModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Style/ToggleStyleModifier.swift deleted file mode 100644 index e6258ea45d..0000000000 --- a/Sources/SwiftCrossUI/Views/Modifiers/Style/ToggleStyleModifier.swift +++ /dev/null @@ -1,8 +0,0 @@ -extension View { - /// Sets the style of the toggle. - public func toggleStyle(_ toggleStyle: ToggleStyle) -> some View { - return EnvironmentModifier(self) { environment in - return environment.with(\.toggleStyle, toggleStyle) - } - } -} diff --git a/Sources/SwiftCrossUI/Views/Modifiers/TextContentTypeModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/TextContentTypeModifier.swift deleted file mode 100644 index b6d267d4ef..0000000000 --- a/Sources/SwiftCrossUI/Views/Modifiers/TextContentTypeModifier.swift +++ /dev/null @@ -1,11 +0,0 @@ -extension View { - /// Set the content type of text fields. - /// - /// This controls autocomplete suggestions, and on mobile devices, which on-screen keyboard - /// is shown. - public func textContentType(_ type: TextContentType) -> some View { - EnvironmentModifier(self) { environment in - environment.with(\.textContentType, type) - } - } -} diff --git a/Sources/SwiftCrossUI/Views/Modifiers/TextSelectionModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/TextSelectionModifier.swift deleted file mode 100644 index 07b0ec1df6..0000000000 --- a/Sources/SwiftCrossUI/Views/Modifiers/TextSelectionModifier.swift +++ /dev/null @@ -1,10 +0,0 @@ -extension View { - /// Set selectability of contained text. Ignored on tvOS. - public func textSelectionEnabled(_ isEnabled: Bool = true) -> some View { - EnvironmentModifier( - self, - modification: { environment in - environment.with(\.isTextSelectionEnabled, isEnabled) - }) - } -} diff --git a/Sources/SwiftCrossUI/Views/NavigationLink.swift b/Sources/SwiftCrossUI/Views/NavigationLink.swift deleted file mode 100644 index 388e704c4e..0000000000 --- a/Sources/SwiftCrossUI/Views/NavigationLink.swift +++ /dev/null @@ -1,29 +0,0 @@ -// TODO: This documentation could probably be clarified a bit more (potentially with -// some practical examples). -/// A navigation primitive that appends a value to the current navigation path on click. -/// -/// Unlike Apples SwiftUI API a `NavigationLink` can be outside of a `NavigationStack` -/// as long as they share the same `NavigationPath`. -public struct NavigationLink: View { - public var body: some View { - Button(label) { - path.wrappedValue.append(value) - } - } - - /// The label to display on the button. - private let label: String - /// The value to append to the navigation path when clicked. - private let value: any Codable - /// The navigation path to append to when clicked. - private let path: Binding - - /// Creates a navigation link that presents the view corresponding to a value. - /// The link is handled by whatever ``NavigationStack`` is sharing the same - /// navigation path. - public init(_ label: String, value: C, path: Binding) { - self.label = label - self.value = value - self.path = path - } -} diff --git a/Sources/SwiftCrossUI/Views/NavigationPath.swift b/Sources/SwiftCrossUI/Views/NavigationPath.swift deleted file mode 100644 index fbe50d0f69..0000000000 --- a/Sources/SwiftCrossUI/Views/NavigationPath.swift +++ /dev/null @@ -1,150 +0,0 @@ -import Foundation - -/// A type-erased list of data representing the content of a navigation stack. -/// -/// If you are persisting a path using the ``Codable`` implementation, you must -/// not change type definitions in a non-backwards compatible way. Otherwise, -/// the path may fail to decode. -public struct NavigationPath { - /// A storage class used so that we have control over exactly which changes are published (to - /// avoid infinite loops). - private class Storage { - /// An entry that will be decoded next time it is used by a ``NavigationStack`` (we need to - /// wait until we know what concrete entry types are available). - struct EncodedEntry: Codable { - var type: String - var value: Data - } - - /// The current path. If both this and `encodedEntries` are non-empty, the elements in path - /// were added before the navigation path was even used to render a view. By design they - /// come after the encodedEntries (because they can only be the result of appending and - /// maybe popping). - var path: [any Codable] = [] - /// Entries that will be encoded when this navigation path is first used by a - /// ``NavigationStack``. It is not possible to decode the entries without first knowing - /// what types the path can possibly contain (which only the ``NavigationStack`` will know). - var encodedEntries: [EncodedEntry] = [] - } - - /// The path and any elements waiting to be decoded are stored in a class so that changes are - /// triggered from within NavigationStack when decoding the elements (which causes an infinite - /// loop of updates). - private var storage = Storage() - - /// Indicates whether this path is empty. - var isEmpty: Bool { - storage.encodedEntries.isEmpty && storage.path.isEmpty - } - - /// The number of elements in the path. - var count: Int { - storage.encodedEntries.count + storage.path.count - } - - /// Creates an empty navigation path. - public init() {} - - /// Appends a new value to the end of the path. - public mutating func append(_ component: C) { - storage.path.append(component) - } - - /// Removes values from the end of this path. - /// - /// - Parameter k: The number of elements to remove from the path. ``k`` must be greater than or equal to zero. - public mutating func removeLast(_ k: Int = 1) { - precondition(k >= 0, "`k` must be greater than or equal to zero") - if k < storage.path.count { - storage.path.removeLast(k) - } else if k < count { - storage.encodedEntries.removeLast(k - storage.path.count) - storage.path.removeAll() - } else { - removeAll() - } - } - - /// Removes all values from this path. - public mutating func removeAll() { - storage.path.removeAll() - storage.encodedEntries.removeAll() - } - - /// Gets the path's current entries. If the path was decoded from a stored representation and - /// has not been used by a ``NavigationStack`` yet, the ``destinationTypes`` will be used to - /// decode all elements in the path. Without knowing the ``destinationTypes``, the entries - /// cannot be decoded (after macOS 11 they can be decoded by using `_typeByName`, but we can't - /// use that because of backwards compatibility). - func path(destinationTypes: [any Codable.Type]) -> [any Codable] { - guard !storage.encodedEntries.isEmpty else { - return storage.path - } - - var decodedEntries: [Int: any Codable] = [:] - for destinationType in destinationTypes { - let type = String(reflecting: destinationType) - for (i, entry) in storage.encodedEntries.enumerated() where entry.type == type { - do { - let value = try JSONDecoder().decode( - destinationType, - from: entry.value - ) - decodedEntries[i] = value - } catch { - let data = String(data: entry.value, encoding: .utf8) ?? "Invalid encoding" - fatalError("Failed to decode item in encoded navigation path: '\(data)'") - } - } - } - - var entries: [any Codable] = [] - for i in 0..: View { - public var body: some View { - SplitView( - sidebar: { - return sidebar - }, - detail: { - if MiddleBar.self == EmptyView.self { - detail - } else { - SplitView( - sidebar: { - return content - }, - detail: { - return detail - } - ) - } - } - ) - } - - public var sidebar: Sidebar - public var content: MiddleBar - public var detail: Detail - - /// Creates a three column split view. - public init( - @ViewBuilder sidebar: () -> Sidebar, - @ViewBuilder content: () -> MiddleBar, - @ViewBuilder detail: () -> Detail - ) { - self.sidebar = sidebar() - self.content = content() - self.detail = detail() - } -} - -extension NavigationSplitView where MiddleBar == EmptyView { - /// Creates a two column split view. - public init( - @ViewBuilder sidebar: () -> Sidebar, - @ViewBuilder detail: () -> Detail - ) { - self.sidebar = sidebar() - content = EmptyView() - self.detail = detail() - } -} diff --git a/Sources/SwiftCrossUI/Views/NavigationStack.swift b/Sources/SwiftCrossUI/Views/NavigationStack.swift deleted file mode 100644 index 2dd5e0ae17..0000000000 --- a/Sources/SwiftCrossUI/Views/NavigationStack.swift +++ /dev/null @@ -1,122 +0,0 @@ -/// Type to indicate the root of the NavigationStack. This is internal to prevent root accidentally showing instead -/// of a detail view. -struct NavigationStackRootPath: Codable {} - -/// A view that displays a root view and enables you to present additional views over the root view. -/// -/// Use .navigationDestination(for:destination:) on this view instead of its children unlike Apples SwiftUI API. -public struct NavigationStack: View { - public var body: some View { - if let element = elements.last { - if let content = child(element) { - content - } else { - fatalError( - "Failed to find detail view for \"\(element)\", make sure you have called .navigationDestination for this type." - ) - } - } else { - Text("Empty navigation path") - } - } - - /// A binding to the current navigation path. - var path: Binding - /// The types handled by each destination (in the same order as their - /// corresponding views in the stack). - var destinationTypes: [any Codable.Type] - /// Gets a recursive ``EitherView`` structure which will have a single view - /// visible suitable for displaying the given path element (based on its - /// type). - /// - /// It's implemented as a recursive structure because that's the best way to keep this - /// typesafe without introducing some crazy generated pseudo-variadic storage types of - /// some sort. This way we can easily have unlimited navigation destinations and there's - /// just a single simple method for adding a navigation destination. - var child: (any Codable) -> Detail? - /// The elements of the navigation path. The result can depend on - /// ``NavigationStack/destinationTypes`` which determines how the keys are - /// decoded if they haven't yet been decoded (this happens if they're loaded - /// from disk for persistence). - var elements: [any Codable] { - let resolvedPath = path.wrappedValue.path( - destinationTypes: destinationTypes - ) - return [NavigationStackRootPath()] + resolvedPath - } - - /// Creates a navigation stack with heterogeneous navigation state that you can control. - /// - Parameters: - /// - path: A `Binding` to the navigation state for this stack. - /// - root: The view to display when the stack is empty. - public init( - path: Binding, - @ViewBuilder _ root: @escaping () -> Detail - ) { - self.path = path - destinationTypes = [] - child = { element in - if element is NavigationStackRootPath { - return root() - } else { - return nil - } - } - } - - /// Associates a destination view with a presented data type for use within a navigation stack. - /// - /// Add this view modifer to describe the view that the stack displays when presenting a particular - /// kind of data. Use a `NavigationLink` to present the data. You can add more than one navigation - /// destination modifier to the stack if it needs to present more than one kind of data. - /// - Parameters: - /// - data: The type of data that this destination matches. - /// - destination: A view builder that defines a view to display when the stack’s navigation - /// state contains a value of type data. The closure takes one argument, which is the value - /// of the data to present. - public func navigationDestination( - for data: D.Type, - @ViewBuilder destination: @escaping (D) -> C - ) -> NavigationStack> { - // Adds another detail view by adding to the recursive structure of either views created - // to display details in a type-safe manner. See NavigationStack.child for details. - return NavigationStack>( - previous: self, - destination: destination - ) - } - - /// Add a destination for a specific path element (by adding another layer of ``EitherView``). - private init( - previous: NavigationStack, - destination: @escaping (Component) -> NewDetail? - ) where Detail == EitherView { - path = previous.path - destinationTypes = previous.destinationTypes + [Component.self] - child = { - if let previous = previous.child($0) { - // Either root or previously defined destination returned a view - return EitherView(previous) - } else if let component = $0 as? Component, let new = destination(component) { - // This destination returned a detail view for the current element - return EitherView(new) - } else { - // Possibly a future .navigationDestination will handle this path element - return nil - } - } - } - - /// Attempts to compute the detail view for the given element (the type of - /// the element decides which detail is shown). Crashes if no suitable detail - /// view is found. - func childOrCrash(for element: any Codable) -> Detail { - guard let child = child(element) else { - fatalError( - "Failed to find detail view for \"\(element)\", make sure you have called .navigationDestination for this type." - ) - } - - return child - } -} diff --git a/Sources/SwiftCrossUI/Views/OptionalView.swift b/Sources/SwiftCrossUI/Views/OptionalView.swift index 97394a0be1..c39dfcbd6b 100644 --- a/Sources/SwiftCrossUI/Views/OptionalView.swift +++ b/Sources/SwiftCrossUI/Views/OptionalView.swift @@ -39,23 +39,21 @@ extension OptionalView: TypeSafeView { return backend.createContainer() } - func update( + func computeLayout( _ widget: Backend.Widget, children: OptionalViewChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { + backend: Backend + ) -> ViewLayoutResult { let hasToggled: Bool - let result: ViewUpdateResult + let result: ViewLayoutResult if let view = view { if let node = children.node { - result = node.update( + result = node.computeLayout( with: view, proposedSize: proposedSize, - environment: environment, - dryRun: dryRun + environment: environment ) hasToggled = false } else { @@ -65,35 +63,42 @@ extension OptionalView: TypeSafeView { environment: environment ) children.node = node - result = node.update( + result = node.computeLayout( with: view, proposedSize: proposedSize, - environment: environment, - dryRun: dryRun + environment: environment ) hasToggled = true } } else { hasToggled = children.node != nil children.node = nil - result = ViewUpdateResult.leafView(size: .hidden) + result = ViewLayoutResult.leafView(size: .zero) } children.hasToggled = children.hasToggled || hasToggled - if !dryRun { - if children.hasToggled { - backend.removeAllChildren(of: widget) - if let node = children.node { - backend.addChild(node.widget.into(), to: widget) - backend.setPosition(ofChildAt: 0, in: widget, to: .zero) - } - children.hasToggled = false - } + return result + } - backend.setSize(of: widget, to: result.size.size) + func commit( + _ widget: Backend.Widget, + children: OptionalViewChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + if children.hasToggled { + backend.removeAllChildren(of: widget) + if let node = children.node { + backend.addChild(node.widget.into(), to: widget) + backend.setPosition(ofChildAt: 0, in: widget, to: .zero) + } + children.hasToggled = false } - return result + _ = children.node?.commit() + + backend.setSize(of: widget, to: layout.size.vector) } } diff --git a/Sources/SwiftCrossUI/Views/Picker.swift b/Sources/SwiftCrossUI/Views/Picker.swift deleted file mode 100644 index 89efa8424d..0000000000 --- a/Sources/SwiftCrossUI/Views/Picker.swift +++ /dev/null @@ -1,72 +0,0 @@ -/// A control for selecting from a set of values. -public struct Picker: ElementaryView, View { - /// The options to be offered by the picker. - private var options: [Value] - /// The picker's selected option. - private var value: Binding - - /// The index of the selected option (if any). - private var selectedOptionIndex: Int? { - return options.firstIndex { option in - return option == value.wrappedValue - } - } - - /// Creates a new picker with the given options and a binding for the selected value. - public init(of options: [Value], selection value: Binding) { - self.options = options - self.value = value - } - - public func asWidget(backend: Backend) -> Backend.Widget { - return backend.createPicker() - } - - public func update( - _ widget: Backend.Widget, - proposedSize: SIMD2, - environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - backend.updatePicker( - widget, - options: options.map { "\($0)" }, - environment: environment - ) { - selectedIndex in - guard let selectedIndex = selectedIndex else { - value.wrappedValue = nil - return - } - value.wrappedValue = options[selectedIndex] - } - backend.setSelectedOption(ofPicker: widget, to: selectedOptionIndex) - - // Special handling for UIKitBackend: - // When backed by a UITableView, its natural size is -1 x -1, - // but it can and should be as large as reasonable - let size = backend.naturalSize(of: widget) - if size == SIMD2(-1, -1) { - if !dryRun { - backend.setSize(of: widget, to: proposedSize) - } - - return ViewUpdateResult.leafView( - size: ViewSize( - size: proposedSize, - idealSize: SIMD2(10, 10), - minimumWidth: 0, - minimumHeight: 0, - maximumWidth: nil, - maximumHeight: nil - ) - ) - } else { - // TODO: Implement picker sizing within SwiftCrossUI so that we can properly implement `dryRun`. - return ViewUpdateResult.leafView( - size: ViewSize(fixedSize: size) - ) - } - } -} diff --git a/Sources/SwiftCrossUI/Views/ProgressView.swift b/Sources/SwiftCrossUI/Views/ProgressView.swift deleted file mode 100644 index 1653410ba6..0000000000 --- a/Sources/SwiftCrossUI/Views/ProgressView.swift +++ /dev/null @@ -1,163 +0,0 @@ -import Foundation - -public struct ProgressView: View { - private var label: Label - private var progress: Double? - private var kind: Kind - - private enum Kind { - case spinner - case bar - } - - public var body: some View { - if label as? EmptyView == nil { - progressIndicator - label - } else { - progressIndicator - } - } - - @ViewBuilder - private var progressIndicator: some View { - switch kind { - case .spinner: - ProgressSpinnerView() - case .bar: - ProgressBarView(value: progress) - } - } - - public init(_ label: Label) { - self.label = label - self.kind = .spinner - } - - public init(_ label: Label, _ progress: Progress) { - self.label = label - self.kind = .bar - - if !progress.isIndeterminate { - self.progress = progress.fractionCompleted - } - } - - /// Creates a progress bar view. If `value` is `nil`, an indeterminate progress - /// bar will be shown. - public init(_ label: Label, value: Value?) { - self.label = label - self.kind = .bar - self.progress = value.map(Double.init) - } -} - -extension ProgressView where Label == EmptyView { - public init() { - self.label = EmptyView() - self.kind = .spinner - } - - public init(_ progress: Progress) { - self.label = EmptyView() - self.kind = .bar - - if !progress.isIndeterminate { - self.progress = progress.fractionCompleted - } - } - - /// Creates a progress bar view. If `value` is `nil`, an indeterminate progress - /// bar will be shown. - public init(value: Value?) { - self.label = EmptyView() - self.kind = .bar - self.progress = value.map(Double.init) - } -} - -extension ProgressView where Label == Text { - public init(_ label: String) { - self.label = Text(label) - self.kind = .spinner - } - - public init(_ label: String, _ progress: Progress) { - self.label = Text(label) - self.kind = .bar - - if !progress.isIndeterminate { - self.progress = progress.fractionCompleted - } - } - - /// Creates a progress bar view. If `value` is `nil`, an indeterminate progress - /// bar will be shown. - public init(_ label: String, value: Value?) { - self.label = Text(label) - self.kind = .bar - self.progress = value.map(Double.init) - } -} - -struct ProgressSpinnerView: ElementaryView { - init() {} - - func asWidget(backend: Backend) -> Backend.Widget { - backend.createProgressSpinner() - } - - func update( - _ widget: Backend.Widget, - proposedSize: SIMD2, - environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - ViewUpdateResult.leafView( - size: ViewSize(fixedSize: backend.naturalSize(of: widget)) - ) - } -} - -struct ProgressBarView: ElementaryView { - var value: Double? - - init(value: Double?) { - self.value = value - } - - func asWidget(backend: Backend) -> Backend.Widget { - backend.createProgressBar() - } - - func update( - _ widget: Backend.Widget, - proposedSize: SIMD2, - environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - let height = backend.naturalSize(of: widget).y - let size = SIMD2( - proposedSize.x, - height - ) - - if !dryRun { - backend.updateProgressBar(widget, progressFraction: value, environment: environment) - backend.setSize(of: widget, to: size) - } - - return ViewUpdateResult.leafView( - size: ViewSize( - size: size, - idealSize: SIMD2(100, height), - minimumWidth: 0, - minimumHeight: height, - maximumWidth: nil, - maximumHeight: Double(height) - ) - ) - } -} diff --git a/Sources/SwiftCrossUI/Views/ScrollView.swift b/Sources/SwiftCrossUI/Views/ScrollView.swift index b3d7f1bc51..af581480ed 100644 --- a/Sources/SwiftCrossUI/Views/ScrollView.swift +++ b/Sources/SwiftCrossUI/Views/ScrollView.swift @@ -1,3 +1,5 @@ +import Foundation + /// A view that is scrollable when it would otherwise overflow available space. Use the /// ``View/frame`` modifier to constrain height if necessary. public struct ScrollView: TypeSafeView, View { @@ -41,124 +43,118 @@ public struct ScrollView: TypeSafeView, View { return backend.createScrollContainer(for: children.innerContainer.into()) } - func update( + func computeLayout( _ widget: Backend.Widget, children: ScrollViewChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { + backend: Backend + ) -> ViewLayoutResult { // Probe how big the child would like to be - let childResult = children.child.update( + var childProposal = proposedSize + for axis in Axis.allCases where axes.contains(axis) { + childProposal[component: axis] = nil + } + let childResult = children.child.computeLayout( with: body, - proposedSize: proposedSize, - environment: environment, - dryRun: true + proposedSize: childProposal, + environment: environment ) + + // If all scroll axes are unspecified, then our size is exactly that of + // the child view. This includes when we have no scroll axes. + if Axis.allCases.allSatisfy({ axis in + !axes.contains(axis) || proposedSize[component: axis] == nil + }) { + return childResult + } + let contentSize = childResult.size - let scrollBarWidth = backend.scrollBarWidth + // An axis is present when its a scroll axis AND the corresponding + // child content size is bigger then the proposed size. If the proposed + // size along the axis is nil then we don't have a scroll bar. + let hasHorizontalScrollBar: Bool + if axes.contains(.horizontal), let proposedWidth = proposedSize.width { + hasHorizontalScrollBar = contentSize.width > proposedWidth + } else { + hasHorizontalScrollBar = false + } - let hasHorizontalScrollBar = - axes.contains(.horizontal) && contentSize.idealWidthForProposedHeight > proposedSize.x - let hasVerticalScrollBar = - axes.contains(.vertical) && contentSize.idealHeightForProposedWidth > proposedSize.y + let hasVerticalScrollBar: Bool + if axes.contains(.vertical), let proposedHeight = proposedSize.height { + hasVerticalScrollBar = contentSize.height > proposedHeight + } else { + hasVerticalScrollBar = false + } + let scrollBarWidth = Double(backend.scrollBarWidth) let verticalScrollBarWidth = hasVerticalScrollBar ? scrollBarWidth : 0 let horizontalScrollBarHeight = hasHorizontalScrollBar ? scrollBarWidth : 0 - let scrollViewWidth: Int - let scrollViewHeight: Int - let minimumWidth: Int - let minimumHeight: Int - if axes.contains(.horizontal) { - scrollViewWidth = max(proposedSize.x, verticalScrollBarWidth) - minimumWidth = verticalScrollBarWidth - } else { - scrollViewWidth = min( - contentSize.size.x + verticalScrollBarWidth, - max(proposedSize.x, contentSize.minimumWidth + verticalScrollBarWidth) - ) - minimumWidth = contentSize.minimumWidth + verticalScrollBarWidth + // Compute the final size to propose to the child view. Subtract off + // scroll bar sizes from non-scrolling axes. + var finalContentSizeProposal = childProposal + if !axes.contains(.horizontal), let proposedWidth = childProposal.width { + finalContentSizeProposal.width = proposedWidth - verticalScrollBarWidth } - if axes.contains(.vertical) { - scrollViewHeight = max(proposedSize.y, horizontalScrollBarHeight) - minimumHeight = horizontalScrollBarHeight - } else { - scrollViewHeight = min( - contentSize.size.y + horizontalScrollBarHeight, - max(proposedSize.y, contentSize.minimumHeight + horizontalScrollBarHeight) - ) - minimumHeight = contentSize.minimumHeight + horizontalScrollBarHeight + + if !axes.contains(.vertical), let proposedHeight = childProposal.height { + finalContentSizeProposal.height = proposedHeight - horizontalScrollBarHeight } - let scrollViewSize = SIMD2( - scrollViewWidth, - scrollViewHeight + // Propose a final size to the child view. + let finalChildResult = children.child.computeLayout( + with: nil, + proposedSize: finalContentSizeProposal, + environment: environment ) - let finalResult: ViewUpdateResult - if !dryRun { - // TODO: scroll bar presence shouldn't affect whether we use current - // or ideal size. Only the presence of the given axis in the user's - // list of scroll axes should affect that. - let proposedContentSize = SIMD2( - hasHorizontalScrollBar - ? (hasVerticalScrollBar - ? contentSize.idealSize.x : contentSize.idealWidthForProposedHeight) - : min(contentSize.size.x, proposedSize.x - verticalScrollBarWidth), - hasVerticalScrollBar - ? (hasHorizontalScrollBar - ? contentSize.idealSize.y : contentSize.idealHeightForProposedWidth) - : min(contentSize.size.y, proposedSize.y - horizontalScrollBarHeight) + // Compute the outer size. + var outerSize = finalChildResult.size + if axes.contains(.horizontal) { + outerSize.width = max( + finalChildResult.size.width + verticalScrollBarWidth, + proposedSize.width ?? 0 ) + } else { + outerSize.width += verticalScrollBarWidth + } - finalResult = children.child.update( - with: body, - proposedSize: proposedContentSize, - environment: environment, - dryRun: false - ) - let finalContentSize = finalResult.size - - let clipViewWidth = scrollViewSize.x - verticalScrollBarWidth - let clipViewHeight = scrollViewSize.y - horizontalScrollBarHeight - var childPosition: SIMD2 = .zero - var innerContainerSize: SIMD2 = finalContentSize.size - if axes.contains(.vertical) && finalContentSize.size.x < clipViewWidth { - childPosition.x = (clipViewWidth - finalContentSize.size.x) / 2 - innerContainerSize.x = clipViewWidth - } - if axes.contains(.horizontal) && finalContentSize.size.y < clipViewHeight { - childPosition.y = (clipViewHeight - finalContentSize.size.y) / 2 - innerContainerSize.y = clipViewHeight - } - - backend.setSize(of: widget, to: scrollViewSize) - backend.setSize(of: children.innerContainer.into(), to: innerContainerSize) - backend.setPosition(ofChildAt: 0, in: children.innerContainer.into(), to: childPosition) - backend.setScrollBarPresence( - ofScrollContainer: widget, - hasVerticalScrollBar: hasVerticalScrollBar, - hasHorizontalScrollBar: hasHorizontalScrollBar + if axes.contains(.vertical) { + outerSize.height = max( + finalChildResult.size.height + horizontalScrollBarHeight, + proposedSize.height ?? 0 ) - backend.updateScrollContainer(widget, environment: environment) } else { - finalResult = childResult + outerSize.height += horizontalScrollBarHeight } - return ViewUpdateResult( - size: ViewSize( - size: scrollViewSize, - idealSize: contentSize.idealSize, - minimumWidth: minimumWidth, - minimumHeight: minimumHeight, - maximumWidth: nil, - maximumHeight: nil - ), - childResults: [finalResult] + return ViewLayoutResult( + size: outerSize, + childResults: [finalChildResult] + ) + } + + func commit( + _ widget: Backend.Widget, + children: ScrollViewChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + let scrollViewSize = layout.size + let finalContentSize = children.child.commit().size + + backend.setSize(of: widget, to: scrollViewSize.vector) + backend.setSize(of: children.innerContainer.into(), to: finalContentSize.vector) + backend.setPosition(ofChildAt: 0, in: children.innerContainer.into(), to: .zero) + backend.setScrollBarPresence( + ofScrollContainer: widget, + hasVerticalScrollBar: children.hasVerticalScrollBar, + hasHorizontalScrollBar: children.hasHorizontalScrollBar ) + backend.updateScrollContainer(widget, environment: environment) } } @@ -166,6 +162,9 @@ class ScrollViewChildren: ViewGraphNodeChildren { var children: TupleView1>.Children var innerContainer: AnyWidget + var hasVerticalScrollBar = false + var hasHorizontalScrollBar = false + var child: AnyViewGraphNode> { children.child0 } diff --git a/Sources/SwiftCrossUI/Views/Shapes/Capsule.swift b/Sources/SwiftCrossUI/Views/Shapes/Capsule.swift deleted file mode 100644 index cdc02e05a1..0000000000 --- a/Sources/SwiftCrossUI/Views/Shapes/Capsule.swift +++ /dev/null @@ -1,9 +0,0 @@ -/// A rounded rectangle whose corner radius is equal to half the length of its shortest side. -public struct Capsule: Shape { - public nonisolated init() {} - - public nonisolated func path(in bounds: Path.Rect) -> Path { - let radius = min(bounds.width, bounds.height) / 2.0 - return RoundedRectangle(cornerRadius: radius).path(in: bounds) - } -} diff --git a/Sources/SwiftCrossUI/Views/Shapes/Circle.swift b/Sources/SwiftCrossUI/Views/Shapes/Circle.swift deleted file mode 100644 index 4eb1cfba9f..0000000000 --- a/Sources/SwiftCrossUI/Views/Shapes/Circle.swift +++ /dev/null @@ -1,23 +0,0 @@ -public struct Circle: Shape { - public nonisolated init() {} - - public nonisolated func path(in bounds: Path.Rect) -> Path { - Path() - .addCircle(center: bounds.center, radius: min(bounds.width, bounds.height) / 2.0) - } - - public nonisolated func size(fitting proposal: SIMD2) -> ViewSize { - let diameter = min(proposal.x, proposal.y) - - return ViewSize( - size: SIMD2(x: diameter, y: diameter), - idealSize: SIMD2(x: 10, y: 10), - idealWidthForProposedHeight: proposal.y, - idealHeightForProposedWidth: proposal.x, - minimumWidth: 0, - minimumHeight: 0, - maximumWidth: nil, - maximumHeight: nil - ) - } -} diff --git a/Sources/SwiftCrossUI/Views/Shapes/Ellipse.swift b/Sources/SwiftCrossUI/Views/Shapes/Ellipse.swift deleted file mode 100644 index eb6c6794ea..0000000000 --- a/Sources/SwiftCrossUI/Views/Shapes/Ellipse.swift +++ /dev/null @@ -1,19 +0,0 @@ -public struct Ellipse: Shape { - public nonisolated init() {} - - public nonisolated func path(in bounds: Path.Rect) -> Path { - Path() - .addCircle(center: .zero, radius: bounds.width / 2.0) - .applyTransform( - AffineTransform( - linearTransform: SIMD4( - x: 1.0, - y: 0.0, - z: 0.0, - w: bounds.height / bounds.width - ), - translation: bounds.center - ) - ) - } -} diff --git a/Sources/SwiftCrossUI/Views/Shapes/Rectangle.swift b/Sources/SwiftCrossUI/Views/Shapes/Rectangle.swift deleted file mode 100644 index 21ce292fca..0000000000 --- a/Sources/SwiftCrossUI/Views/Shapes/Rectangle.swift +++ /dev/null @@ -1,7 +0,0 @@ -public struct Rectangle: Shape { - public nonisolated init() {} - - public nonisolated func path(in bounds: Path.Rect) -> Path { - Path().addRectangle(bounds) - } -} diff --git a/Sources/SwiftCrossUI/Views/Shapes/RoundedRectangle.swift b/Sources/SwiftCrossUI/Views/Shapes/RoundedRectangle.swift deleted file mode 100644 index 285249bcbc..0000000000 --- a/Sources/SwiftCrossUI/Views/Shapes/RoundedRectangle.swift +++ /dev/null @@ -1,449 +0,0 @@ -/// A rounded rectangle. -/// -/// This is not necessarily four line segments and four circular arcs. If possible, this shape -/// uses smoother curves to make the transition between the edges and corners less abrupt. -public struct RoundedRectangle { - public var cornerRadius: Double - - public init(cornerRadius: Double) { - assert( - cornerRadius >= 0.0 && cornerRadius.isFinite, - "Corner radius must be a positive finite value") - self.cornerRadius = cornerRadius - } - - // This shape tries to mimic an order 5 superellipse, extending the sides with line segments. - // Since paths don't support quintic curves, I'm using an approximation consisting of - // two cubic curves and a line segment. This constant is the list of control points for - // the cubic curves. See https://www.desmos.com/calculator/chwx3ddx6u . - // - // Preconditions: - // - points.0 is the same as if a line segment and a circular arc were used - // - points.6.y == 0.0 - fileprivate static let points = ( - SIMD2(0.292893218813, 0.292893218813), - SIMD2(0.517, 0.0687864376269), - SIMD2(0.87, 0.0337), - SIMD2(1.13130356636, 0.0139677719414), - SIMD2(1.1973, 0.0089), - SIMD2(1.5038, 0.0002), - SIMD2(1.7, 0.0) - ) - - // This corresponds to r_{min} in the above Desmos link. This is the minimum ratio of - // cornerRadius to half the side length at which the superellipse is not applicable. Above this, - // line segments and circular arcs are used. - fileprivate static let rMin = 0.441968022436 -} - -extension RoundedRectangle: Shape { - public func path(in bounds: Path.Rect) -> Path { - // just to avoid `RoundedRectangle.` qualifiers - let rMin = RoundedRectangle.rMin - let points = RoundedRectangle.points - - let effectiveRadius = min(cornerRadius, bounds.width / 2.0, bounds.height / 2.0) - let xRatio = effectiveRadius / (bounds.width / 2.0) - let yRatio = effectiveRadius / (bounds.height / 2.0) - - // MARK: Early exits - // These code paths are guaranteed to not use the approximations of the quintic curves. - - // Optimization: just a circle - if bounds.width == bounds.height && bounds.width <= cornerRadius * 2.0 { - return Circle().path(in: bounds) - } - - // Optimization: just a rectangle - if effectiveRadius == 0.0 { - return Rectangle().path(in: bounds) - } - - // Optimization: corner radius is too large to use quintic curves - if xRatio >= rMin && yRatio >= rMin { - return Path() - .move(to: SIMD2(x: bounds.x + effectiveRadius, y: bounds.y)) - .addLine(to: SIMD2(x: bounds.maxX - effectiveRadius, y: bounds.y)) - .addArc( - center: SIMD2(x: bounds.maxX - effectiveRadius, y: bounds.y + effectiveRadius), - radius: effectiveRadius, - startAngle: .pi * 1.5, - endAngle: 0.0, - clockwise: true - ) - .addLine(to: SIMD2(x: bounds.maxX, y: bounds.maxY - effectiveRadius)) - .addArc( - center: SIMD2( - x: bounds.maxX - effectiveRadius, y: bounds.maxY - effectiveRadius), - radius: effectiveRadius, - startAngle: 0.0, - endAngle: .pi * 0.5, - clockwise: true - ) - .addLine(to: SIMD2(x: bounds.x + effectiveRadius, y: bounds.maxY)) - .addArc( - center: SIMD2(x: bounds.x + effectiveRadius, y: bounds.maxY - effectiveRadius), - radius: effectiveRadius, - startAngle: .pi * 0.5, - endAngle: .pi, - clockwise: true - ) - .addLine(to: SIMD2(x: bounds.x, y: bounds.y + effectiveRadius)) - .addArc( - center: SIMD2(x: bounds.x + effectiveRadius, y: bounds.y + effectiveRadius), - radius: effectiveRadius, - startAngle: .pi, - endAngle: .pi * 1.5, - clockwise: true - ) - } - - return Path() - // MARK: Top edge, right side - .move(to: SIMD2(x: bounds.center.x, y: bounds.y)) - .if(xRatio >= rMin) { - $0 - .addLine(to: SIMD2(x: bounds.maxX - effectiveRadius, y: bounds.y)) - .addArc( - center: SIMD2( - x: bounds.maxX - effectiveRadius, y: bounds.y + effectiveRadius), - radius: effectiveRadius, - startAngle: .pi * 1.5, - endAngle: .pi * 1.75, - clockwise: true - ) - } else: { - $0 - .addLine( - to: SIMD2( - x: bounds.maxX - points.6.x * effectiveRadius, - y: bounds.y + points.6.y * effectiveRadius - ) - ) - .addCubicCurve( - control1: SIMD2( - x: bounds.maxX - points.5.x * effectiveRadius, - y: bounds.y + points.5.y * effectiveRadius - ), - control2: SIMD2( - x: bounds.maxX - points.4.x * effectiveRadius, - y: bounds.y + points.4.y * effectiveRadius - ), - to: SIMD2( - x: bounds.maxX - points.3.x * effectiveRadius, - y: bounds.y + points.3.y * effectiveRadius - ) - ) - .addCubicCurve( - control1: SIMD2( - x: bounds.maxX - points.2.x * effectiveRadius, - y: bounds.y + points.2.y * effectiveRadius - ), - control2: SIMD2( - x: bounds.maxX - points.1.x * effectiveRadius, - y: bounds.y + points.1.y * effectiveRadius - ), - to: SIMD2( - x: bounds.maxX - points.0.x * effectiveRadius, - y: bounds.y + points.0.y * effectiveRadius - ) - ) - } - // MARK: Right edge - .if(yRatio >= rMin) { - $0 - .addArc( - center: SIMD2( - x: bounds.maxX - effectiveRadius, y: bounds.y + effectiveRadius), - radius: effectiveRadius, - startAngle: .pi * 1.75, - endAngle: 0.0, - clockwise: true - ) - .addLine(to: SIMD2(x: bounds.maxX, y: bounds.maxY - effectiveRadius)) - .addArc( - center: SIMD2( - x: bounds.maxX - effectiveRadius, y: bounds.maxY - effectiveRadius), - radius: effectiveRadius, - startAngle: 0.0, - endAngle: .pi * 0.25, - clockwise: true - ) - } else: { - $0 - .addCubicCurve( - control1: SIMD2( - x: bounds.maxX - points.1.y * effectiveRadius, - y: bounds.y + points.1.x * effectiveRadius - ), - control2: SIMD2( - x: bounds.maxX - points.2.y * effectiveRadius, - y: bounds.y + points.2.x * effectiveRadius - ), - to: SIMD2( - x: bounds.maxX - points.3.y * effectiveRadius, - y: bounds.y + points.3.x * effectiveRadius - ) - ) - .addCubicCurve( - control1: SIMD2( - x: bounds.maxX - points.4.y * effectiveRadius, - y: bounds.y + points.4.x * effectiveRadius - ), - control2: SIMD2( - x: bounds.maxX - points.5.y * effectiveRadius, - y: bounds.y + points.5.x * effectiveRadius - ), - to: SIMD2( - x: bounds.maxX - points.6.y * effectiveRadius, - y: bounds.y + points.6.x * effectiveRadius - ) - ) - .addLine( - to: SIMD2( - x: bounds.maxX - points.6.y * effectiveRadius, - y: bounds.maxY - points.6.x * effectiveRadius - ) - ) - .addCubicCurve( - control1: SIMD2( - x: bounds.maxX - points.5.y * effectiveRadius, - y: bounds.maxY - points.5.x * effectiveRadius - ), - control2: SIMD2( - x: bounds.maxX - points.4.y * effectiveRadius, - y: bounds.maxY - points.4.x * effectiveRadius - ), - to: SIMD2( - x: bounds.maxX - points.3.y * effectiveRadius, - y: bounds.maxY - points.3.x * effectiveRadius - ) - ) - .addCubicCurve( - control1: SIMD2( - x: bounds.maxX - points.2.y * effectiveRadius, - y: bounds.maxY - points.2.x * effectiveRadius - ), - control2: SIMD2( - x: bounds.maxX - points.1.y * effectiveRadius, - y: bounds.maxY - points.1.x * effectiveRadius - ), - to: SIMD2( - x: bounds.maxX - points.0.y * effectiveRadius, - y: bounds.maxY - points.0.x * effectiveRadius - ) - ) - } - // MARK: Bottom edge - .if(xRatio >= rMin) { - $0 - .addArc( - center: SIMD2( - x: bounds.maxX - effectiveRadius, y: bounds.maxY - effectiveRadius), - radius: effectiveRadius, - startAngle: .pi * 0.25, - endAngle: .pi * 0.5, - clockwise: true - ) - .addLine(to: SIMD2(x: bounds.x + effectiveRadius, y: bounds.maxY)) - .addArc( - center: SIMD2( - x: bounds.x + effectiveRadius, y: bounds.maxY - effectiveRadius), - radius: effectiveRadius, - startAngle: .pi * 0.5, - endAngle: .pi * 0.75, - clockwise: true - ) - } else: { - $0 - .addCubicCurve( - control1: SIMD2( - x: bounds.maxX - points.1.x * effectiveRadius, - y: bounds.maxY - points.1.y * effectiveRadius - ), - control2: SIMD2( - x: bounds.maxX - points.2.x * effectiveRadius, - y: bounds.maxY - points.2.y * effectiveRadius - ), - to: SIMD2( - x: bounds.maxX - points.3.x * effectiveRadius, - y: bounds.maxY - points.3.y * effectiveRadius - ) - ) - .addCubicCurve( - control1: SIMD2( - x: bounds.maxX - points.4.x * effectiveRadius, - y: bounds.maxY - points.4.y * effectiveRadius - ), - control2: SIMD2( - x: bounds.maxX - points.5.x * effectiveRadius, - y: bounds.maxY - points.5.y * effectiveRadius - ), - to: SIMD2( - x: bounds.maxX - points.6.x * effectiveRadius, - y: bounds.maxY - points.6.y * effectiveRadius - ) - ) - .addLine( - to: SIMD2( - x: bounds.x + points.6.x * effectiveRadius, - y: bounds.maxY - points.6.y * effectiveRadius - ) - ) - .addCubicCurve( - control1: SIMD2( - x: bounds.x + points.5.x * effectiveRadius, - y: bounds.maxY - points.5.y * effectiveRadius - ), - control2: SIMD2( - x: bounds.x + points.4.x * effectiveRadius, - y: bounds.maxY - points.4.y * effectiveRadius - ), - to: SIMD2( - x: bounds.x + points.3.x * effectiveRadius, - y: bounds.maxY - points.3.y * effectiveRadius - ) - ) - .addCubicCurve( - control1: SIMD2( - x: bounds.x + points.2.x * effectiveRadius, - y: bounds.maxY - points.2.y * effectiveRadius - ), - control2: SIMD2( - x: bounds.x + points.1.x * effectiveRadius, - y: bounds.maxY - points.1.y * effectiveRadius - ), - to: SIMD2( - x: bounds.x + points.0.x * effectiveRadius, - y: bounds.maxY - points.0.y * effectiveRadius - ) - ) - } - // MARK: Left edge - .if(yRatio >= rMin) { - $0 - .addArc( - center: SIMD2( - x: bounds.x + effectiveRadius, y: bounds.maxY - effectiveRadius), - radius: effectiveRadius, - startAngle: .pi * 0.75, - endAngle: .pi, - clockwise: true - ) - .addLine(to: SIMD2(x: bounds.x, y: bounds.y + effectiveRadius)) - .addArc( - center: SIMD2(x: bounds.x + effectiveRadius, y: bounds.y + effectiveRadius), - radius: effectiveRadius, - startAngle: .pi, - endAngle: .pi * 1.25, - clockwise: true - ) - } else: { - $0 - .addCubicCurve( - control1: SIMD2( - x: bounds.x + points.1.y * effectiveRadius, - y: bounds.maxY - points.1.x * effectiveRadius - ), - control2: SIMD2( - x: bounds.x + points.2.y * effectiveRadius, - y: bounds.maxY - points.2.x * effectiveRadius - ), - to: SIMD2( - x: bounds.x + points.3.y * effectiveRadius, - y: bounds.maxY - points.3.x * effectiveRadius - ) - ) - .addCubicCurve( - control1: SIMD2( - x: bounds.x + points.4.y * effectiveRadius, - y: bounds.maxY - points.4.x * effectiveRadius - ), - control2: SIMD2( - x: bounds.x + points.5.y * effectiveRadius, - y: bounds.maxY - points.5.x * effectiveRadius - ), - to: SIMD2( - x: bounds.x + points.6.y * effectiveRadius, - y: bounds.maxY - points.6.x * effectiveRadius - ) - ) - .addLine( - to: SIMD2( - x: bounds.x + points.6.y * effectiveRadius, - y: bounds.y + points.6.x * effectiveRadius - ) - ) - .addCubicCurve( - control1: SIMD2( - x: bounds.x + points.5.y * effectiveRadius, - y: bounds.y + points.5.x * effectiveRadius - ), - control2: SIMD2( - x: bounds.x + points.4.y * effectiveRadius, - y: bounds.y + points.4.x * effectiveRadius - ), - to: SIMD2( - x: bounds.x + points.3.y * effectiveRadius, - y: bounds.y + points.3.x * effectiveRadius - ) - ) - .addCubicCurve( - control1: SIMD2( - x: bounds.x + points.2.y * effectiveRadius, - y: bounds.y + points.2.x * effectiveRadius - ), - control2: SIMD2( - x: bounds.x + points.1.y * effectiveRadius, - y: bounds.y + points.1.x * effectiveRadius - ), - to: SIMD2( - x: bounds.x + points.0.y * effectiveRadius, - y: bounds.y + points.0.x * effectiveRadius - ) - ) - } - // MARK: Top edge, left side - .if(xRatio >= rMin) { - $0 - .addArc( - center: SIMD2(x: bounds.x + effectiveRadius, y: bounds.y + effectiveRadius), - radius: effectiveRadius, - startAngle: .pi * 1.25, - endAngle: .pi * 1.5, - clockwise: true - ) - } else: { - $0 - .addCubicCurve( - control1: SIMD2( - x: bounds.x + points.1.x * effectiveRadius, - y: bounds.y + points.1.y * effectiveRadius - ), - control2: SIMD2( - x: bounds.x + points.2.x * effectiveRadius, - y: bounds.y + points.2.y * effectiveRadius - ), - to: SIMD2( - x: bounds.x + points.3.x * effectiveRadius, - y: bounds.y + points.3.y * effectiveRadius - ) - ) - .addCubicCurve( - control1: SIMD2( - x: bounds.x + points.4.x * effectiveRadius, - y: bounds.y + points.4.y * effectiveRadius - ), - control2: SIMD2( - x: bounds.x + points.5.x * effectiveRadius, - y: bounds.y + points.5.y * effectiveRadius - ), - to: SIMD2( - x: bounds.x + points.6.x * effectiveRadius, - y: bounds.y + points.6.y * effectiveRadius - ) - ) - } - .addLine(to: SIMD2(x: bounds.center.x, y: bounds.y)) - } -} diff --git a/Sources/SwiftCrossUI/Views/Shapes/Shape.swift b/Sources/SwiftCrossUI/Views/Shapes/Shape.swift deleted file mode 100644 index e266588cbe..0000000000 --- a/Sources/SwiftCrossUI/Views/Shapes/Shape.swift +++ /dev/null @@ -1,139 +0,0 @@ -/// A 2-D shape that can be drawn as a view. -/// -/// If no stroke color or fill color is specified, the default is no stroke and a fill of the -/// current foreground color. -public protocol Shape: View, Sendable where Content == EmptyView { - /// Draw the path for this shape. - /// - /// The bounds passed to a shape that is immediately drawn as a view will always have an - /// origin of (0, 0). However, you may pass a different bounding box to subpaths. For example, - /// this code draws a rectangle in the left half of the bounds and an ellipse in the right half: - /// ```swift - /// func path(in bounds: Path.Rect) -> Path { - /// Path() - /// .addSubpath( - /// Rectangle().path( - /// in: Path.Rect( - /// x: bounds.x, - /// y: bounds.y, - /// width: bounds.width / 2.0, - /// height: bounds.height - /// ) - /// ) - /// ) - /// .addSubpath( - /// Ellipse().path( - /// in: Path.Rect( - /// x: bounds.center.x, - /// y: bounds.y, - /// width: bounds.width / 2.0, - /// height: bounds.height - /// ) - /// ) - /// ) - /// } - /// ``` - func path(in bounds: Path.Rect) -> Path - /// Determine the ideal size of this shape given the proposed bounds. - /// - /// The default implementation accepts the proposal and imposes no practical limit on - /// the shape's size. - /// - Returns: Information about the shape's size. The ``ViewSize/size`` property is what - /// frame the shape will actually be rendered with if the current layout pass is not - /// a dry run, while the other properties are used to inform the layout engine how big - /// or small the shape can be. The ``ViewSize/idealSize`` property should not vary with - /// the `proposal`, and should only depend on the shape's contents. Pass `nil` for the - /// maximum width/height if the shape has no maximum size. - func size(fitting proposal: SIMD2) -> ViewSize -} - -extension Shape { - public var body: EmptyView { return EmptyView() } - - public func size(fitting proposal: SIMD2) -> ViewSize { - return ViewSize( - size: proposal, - idealSize: SIMD2(x: 10, y: 10), - minimumWidth: 0, - minimumHeight: 0, - maximumWidth: nil, - maximumHeight: nil - ) - } - - @MainActor - public func children( - backend _: Backend, - snapshots _: [ViewGraphSnapshotter.NodeSnapshot]?, - environment _: EnvironmentValues - ) -> any ViewGraphNodeChildren { - ShapeStorage() - } - - @MainActor - public func asWidget( - _ children: any ViewGraphNodeChildren, backend: Backend - ) -> Backend.Widget { - let container = backend.createPathWidget() - let storage = children as! ShapeStorage - storage.backendPath = backend.createPath() - storage.oldPath = nil - return container - } - - @MainActor - public func update( - _ widget: Backend.Widget, - children: any ViewGraphNodeChildren, - proposedSize: SIMD2, - environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - let storage = children as! ShapeStorage - let size = size(fitting: proposedSize) - - let bounds = Path.Rect( - x: 0.0, - y: 0.0, - width: Double(size.size.x), - height: Double(size.size.y) - ) - let path = path(in: bounds) - - storage.pointsChanged = - storage.pointsChanged || storage.oldPath?.actions != path.actions - storage.oldPath = path - - let backendPath = storage.backendPath as! Backend.Path - if !dryRun { - backend.updatePath( - backendPath, - path, - bounds: bounds, - pointsChanged: storage.pointsChanged, - environment: environment - ) - storage.pointsChanged = false - - backend.setSize(of: widget, to: size.size) - backend.renderPath( - backendPath, - container: widget, - strokeColor: .clear, - fillColor: environment.suggestedForegroundColor, - overrideStrokeStyle: nil - ) - } - - return ViewUpdateResult.leafView(size: size) - } -} - -final class ShapeStorage: ViewGraphNodeChildren { - let widgets: [AnyWidget] = [] - let erasedNodes: [ErasedViewGraphNode] = [] - var backendPath: Any! - var oldPath: Path? - var pointsChanged = false -} diff --git a/Sources/SwiftCrossUI/Views/Shapes/StyledShape.swift b/Sources/SwiftCrossUI/Views/Shapes/StyledShape.swift deleted file mode 100644 index 1a09c59fc0..0000000000 --- a/Sources/SwiftCrossUI/Views/Shapes/StyledShape.swift +++ /dev/null @@ -1,103 +0,0 @@ -/// A shape that has style information attached to it, including color and stroke style. -public protocol StyledShape: Shape { - var strokeColor: Color? { get } - var fillColor: Color? { get } - var strokeStyle: StrokeStyle? { get } -} - -struct StyledShapeImpl: Sendable { - var base: Base - var strokeColor: Color? - var fillColor: Color? - var strokeStyle: StrokeStyle? - - init( - base: Base, - strokeColor: Color? = nil, - fillColor: Color? = nil, - strokeStyle: StrokeStyle? = nil - ) { - self.base = base - - if let styledBase = base as? any StyledShape { - self.strokeColor = strokeColor ?? styledBase.strokeColor - self.fillColor = fillColor ?? styledBase.fillColor - self.strokeStyle = strokeStyle ?? styledBase.strokeStyle - } else { - self.strokeColor = strokeColor - self.fillColor = fillColor - self.strokeStyle = strokeStyle - } - } -} - -extension StyledShapeImpl: StyledShape { - func path(in bounds: Path.Rect) -> Path { - return base.path(in: bounds) - } - - func size(fitting proposal: SIMD2) -> ViewSize { - return base.size(fitting: proposal) - } -} - -extension Shape { - public func fill(_ color: Color) -> some StyledShape { - StyledShapeImpl(base: self, fillColor: color) - } - - public func stroke(_ color: Color, style: StrokeStyle? = nil) -> some StyledShape { - StyledShapeImpl(base: self, strokeColor: color, strokeStyle: style) - } -} - -extension StyledShape { - @MainActor - public func update( - _ widget: Backend.Widget, - children: any ViewGraphNodeChildren, - proposedSize: SIMD2, - environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - // TODO: Don't duplicate this between Shape and StyledShape - let storage = children as! ShapeStorage - let size = size(fitting: proposedSize) - - let bounds = Path.Rect( - x: 0.0, - y: 0.0, - width: Double(size.size.x), - height: Double(size.size.y) - ) - let path = path(in: bounds) - - storage.pointsChanged = - storage.pointsChanged || storage.oldPath?.actions != path.actions - storage.oldPath = path - - let backendPath = storage.backendPath as! Backend.Path - if !dryRun { - backend.updatePath( - backendPath, - path, - bounds: bounds, - pointsChanged: storage.pointsChanged, - environment: environment - ) - storage.pointsChanged = false - - backend.setSize(of: widget, to: size.size) - backend.renderPath( - backendPath, - container: widget, - strokeColor: strokeColor ?? .clear, - fillColor: fillColor ?? .clear, - overrideStrokeStyle: strokeStyle - ) - } - - return ViewUpdateResult.leafView(size: size) - } -} diff --git a/Sources/SwiftCrossUI/Views/Slider.swift b/Sources/SwiftCrossUI/Views/Slider.swift deleted file mode 100644 index 1745400aa5..0000000000 --- a/Sources/SwiftCrossUI/Views/Slider.swift +++ /dev/null @@ -1,139 +0,0 @@ -/// A value convertible to and from a ``Double``.` -public protocol DoubleConvertible { - /// Creates a value from a ``Double``.` - init(_ value: Double) - - /// Converts the value to a ``Double``.` - var doubleRepresentation: Double { get } -} - -/// A value represented by a ``BinaryFloatingPoint``. -struct FloatingPointValue: DoubleConvertible { - var value: Value - - init(_ value: Value) { - self.value = value - } - - init(_ value: Double) { - self.value = Value(value) - } - - var doubleRepresentation: Double { - return Double(value) - } -} - -/// A value represented by a ``BinaryInteger``. -struct IntegerValue: DoubleConvertible { - var value: Value - - init(_ value: Value) { - self.value = value - } - - init(_ value: Double) { - self.value = Value(value) - } - - var doubleRepresentation: Double { - return Double(value) - } -} - -/// A control for selecting a value from a bounded range of numerical values. -public struct Slider: ElementaryView, View { - /// A binding to the current value. - private var value: Binding? - /// The slider's minimum value. - private var minimum: Double - /// The slider's maximum value. - private var maximum: Double - /// The number of decimal places used when displaying the value. - private var decimalPlaces: Int - - /// Creates a slider to select a value between a minimum and maximum value. - public init(_ value: Binding? = nil, minimum: T, maximum: T) { - if let value = value { - self.value = Binding( - get: { - return Double(value.wrappedValue) - }, - set: { newValue in - value.wrappedValue = T(newValue.rounded()) - } - ) - } - self.minimum = Double(minimum) - self.maximum = Double(maximum) - decimalPlaces = 0 - } - - /// Creates a slider to select a value between a minimum and maximum value. - public init(_ value: Binding? = nil, minimum: T, maximum: T) { - if let value = value { - self.value = Binding( - get: { - return Double(value.wrappedValue) - }, - set: { newValue in - value.wrappedValue = T(newValue) - } - ) - } - self.minimum = Double(minimum) - self.maximum = Double(maximum) - decimalPlaces = 2 - } - - public func asWidget(backend: Backend) -> Backend.Widget { - return backend.createSlider() - } - - public func update( - _ widget: Backend.Widget, - proposedSize: SIMD2, - environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - if !dryRun { - backend.updateSlider( - widget, - minimum: minimum, - maximum: maximum, - decimalPlaces: decimalPlaces, - environment: environment - ) { newValue in - if let value { - value.wrappedValue = newValue - } - } - - if let value = value?.wrappedValue { - backend.setValue(ofSlider: widget, to: value) - } - } - - // TODO: Don't rely on naturalSize for minimum size so that we can get Slider sizes without - // relying on the widget. - let naturalSize = backend.naturalSize(of: widget) - let size = SIMD2(proposedSize.x, naturalSize.y) - - if !dryRun { - backend.setSize(of: widget, to: size) - } - - // TODO: Allow backends to specify their own ideal slider widths. - return ViewUpdateResult.leafView( - size: ViewSize( - size: size, - idealSize: SIMD2(100, naturalSize.y), - minimumWidth: naturalSize.x, - minimumHeight: naturalSize.y, - maximumWidth: nil, - maximumHeight: Double(naturalSize.y) - ) - ) - } -} diff --git a/Sources/SwiftCrossUI/Views/Spacer.swift b/Sources/SwiftCrossUI/Views/Spacer.swift index 913b3b6fa4..f0a22ce01a 100644 --- a/Sources/SwiftCrossUI/Views/Spacer.swift +++ b/Sources/SwiftCrossUI/Views/Spacer.swift @@ -1,6 +1,9 @@ /// A flexible space that expands along the major axis of its containing /// stack layout, or on both axes if not contained in a stack. public struct Spacer: ElementaryView, View { + /// The ideal length of a spacer. + static let idealLength: Double = 8 + /// The minimum length this spacer can be shrunk to, along the axis of /// expansion. package var minLength: Int? @@ -11,53 +14,32 @@ public struct Spacer: ElementaryView, View { self.minLength = minLength } - public func asWidget( - backend: Backend - ) -> Backend.Widget { + func asWidget(backend: Backend) -> Backend.Widget { return backend.createContainer() } - public func update( + func computeLayout( _ widget: Backend.Widget, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - let minLength = minLength ?? 0 + backend: Backend + ) -> ViewLayoutResult { + var size = ViewSize.zero + let proposedLength = proposedSize[component: environment.layoutOrientation] + size[component: environment.layoutOrientation] = max( + Double(minLength ?? 0), + proposedLength ?? Self.idealLength + ) - let size: SIMD2 - let minimumWidth: Int - let minimumHeight: Int - let maximumWidth: Double? - let maximumHeight: Double? - switch environment.layoutOrientation { - case .horizontal: - size = SIMD2(max(minLength, proposedSize.x), 0) - minimumWidth = minLength - minimumHeight = 0 - maximumWidth = nil - maximumHeight = 0 - case .vertical: - size = SIMD2(0, max(minLength, proposedSize.y)) - minimumWidth = 0 - minimumHeight = minLength - maximumWidth = 0 - maximumHeight = nil - } + return ViewLayoutResult.leafView(size: size) + } - if !dryRun { - backend.setSize(of: widget, to: size) - } - return ViewUpdateResult.leafView( - size: ViewSize( - size: size, - idealSize: SIMD2(minimumWidth, minimumHeight), - minimumWidth: minimumWidth, - minimumHeight: minimumHeight, - maximumWidth: maximumWidth, - maximumHeight: maximumHeight - ) - ) + func commit( + _ widget: Backend.Widget, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + // Spacers are invisible so we don't have to update anything. } } diff --git a/Sources/SwiftCrossUI/Views/SplitView.swift b/Sources/SwiftCrossUI/Views/SplitView.swift deleted file mode 100644 index 86a0e28844..0000000000 --- a/Sources/SwiftCrossUI/Views/SplitView.swift +++ /dev/null @@ -1,166 +0,0 @@ -import Foundation - -struct SplitView: TypeSafeView, View { - typealias Children = SplitViewChildren, Detail> - - var body: TupleView2, Detail> - - /// Creates a two column split view. - init(@ViewBuilder sidebar: () -> Sidebar, @ViewBuilder detail: () -> Detail) { - body = TupleView2( - EnvironmentModifier(sidebar()) { $0.with(\.listStyle, .sidebar) }, - detail() - ) - } - - func children( - backend: Backend, - snapshots: [ViewGraphSnapshotter.NodeSnapshot]?, - environment: EnvironmentValues - ) -> Children { - SplitViewChildren( - wrapping: body.children( - backend: backend, - snapshots: snapshots, - environment: environment - ), - backend: backend - ) - } - - func asWidget( - _ children: Children, - backend: Backend - ) -> Backend.Widget { - return backend.createSplitView( - leadingChild: children.leadingPaneContainer.into(), - trailingChild: children.trailingPaneContainer.into() - ) - } - - func update( - _ widget: Backend.Widget, - children: Children, - proposedSize: SIMD2, - environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - let leadingWidth = backend.sidebarWidth(ofSplitView: widget) - if !dryRun { - backend.setResizeHandler(ofSplitView: widget) { - environment.onResize(.empty) - } - } - - // Update pane children - let leadingResult = children.leadingChild.update( - with: body.view0, - proposedSize: SIMD2( - leadingWidth, - proposedSize.y - ), - environment: environment, - dryRun: dryRun - ) - let trailingResult = children.trailingChild.update( - with: body.view1, - proposedSize: SIMD2( - proposedSize.x - max(leadingWidth, leadingResult.size.minimumWidth), - proposedSize.y - ), - environment: environment, - dryRun: dryRun - ) - - // Update split view size and sidebar width bounds - let leadingContentSize = leadingResult.size - let trailingContentSize = trailingResult.size - let size = SIMD2( - max(proposedSize.x, leadingContentSize.size.x + trailingContentSize.size.x), - max(proposedSize.y, max(leadingContentSize.size.y, trailingContentSize.size.y)) - ) - if !dryRun { - backend.setSize(of: widget, to: size) - backend.setSidebarWidthBounds( - ofSplitView: widget, - minimum: leadingContentSize.minimumWidth, - maximum: max( - leadingContentSize.minimumWidth, - proposedSize.x - trailingContentSize.minimumWidth - ) - ) - - // Center pane children - backend.setPosition( - ofChildAt: 0, - in: children.leadingPaneContainer.into(), - to: SIMD2( - leadingWidth - leadingContentSize.size.x, - proposedSize.y - leadingContentSize.size.y - ) / 2 - ) - backend.setPosition( - ofChildAt: 0, - in: children.trailingPaneContainer.into(), - to: SIMD2( - proposedSize.x - leadingWidth - trailingContentSize.size.x, - proposedSize.y - trailingContentSize.size.y - ) / 2 - ) - } - - return ViewUpdateResult( - size: ViewSize( - size: size, - idealSize: leadingContentSize.idealSize &+ trailingContentSize.idealSize, - minimumWidth: leadingContentSize.minimumWidth + trailingContentSize.minimumWidth, - minimumHeight: max( - leadingContentSize.minimumHeight, trailingContentSize.minimumHeight), - maximumWidth: nil, - maximumHeight: nil - ), - childResults: [leadingResult, trailingResult] - ) - } -} - -class SplitViewChildren: ViewGraphNodeChildren { - var paneChildren: TupleView2.Children - var leadingPaneContainer: AnyWidget - var trailingPaneContainer: AnyWidget - - init( - wrapping children: TupleView2.Children, - backend: Backend - ) { - self.paneChildren = children - - let leadingPaneContainer = backend.createContainer() - backend.addChild(paneChildren.child0.widget.into(), to: leadingPaneContainer) - let trailingPaneContainer = backend.createContainer() - backend.addChild(paneChildren.child1.widget.into(), to: trailingPaneContainer) - - self.leadingPaneContainer = AnyWidget(leadingPaneContainer) - self.trailingPaneContainer = AnyWidget(trailingPaneContainer) - } - - var erasedNodes: [ErasedViewGraphNode] { - paneChildren.erasedNodes - } - - var widgets: [AnyWidget] { - [ - leadingPaneContainer, - trailingPaneContainer, - ] - } - - var leadingChild: AnyViewGraphNode { - paneChildren.child0 - } - - var trailingChild: AnyViewGraphNode { - paneChildren.child1 - } -} diff --git a/Sources/SwiftCrossUI/Views/Table.swift b/Sources/SwiftCrossUI/Views/Table.swift deleted file mode 100644 index 30669177eb..0000000000 --- a/Sources/SwiftCrossUI/Views/Table.swift +++ /dev/null @@ -1,207 +0,0 @@ -/// A container that presents rows of data arranged in columns. -public struct Table>: TypeSafeView, View { - typealias Children = TableViewChildren - - public var body = EmptyView() - - /// The row data to display. - private var rows: [RowValue] - /// The columns to display (which each compute their cell values when given - /// ``Table/Row`` instances). - private var columns: RowContent - - /// Creates a table that computes its cell values based on a collection of rows. - public init(_ rows: [RowValue], @TableRowBuilder _ columns: () -> RowContent) { - self.rows = rows - self.columns = columns() - } - - func children( - backend: Backend, - snapshots: [ViewGraphSnapshotter.NodeSnapshot]?, - environment: EnvironmentValues - ) -> Children { - // TODO: Table snapshotting - TableViewChildren() - } - - func asWidget( - _ children: Children, - backend: Backend - ) -> Backend.Widget { - return backend.createTable() - } - - func update( - _ widget: Backend.Widget, - children: Children, - proposedSize: SIMD2, - environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - let size = proposedSize - var cellResults: [ViewUpdateResult] = [] - let rowContent = rows.map(columns.content(for:)).map(RowView.init(_:)) - - for (node, content) in zip(children.rowNodes, rowContent) { - // Updating a RowView simply updates the view stored within its node, so the proposedSize - // is irrelevant. We can just set it to `.zero`. - _ = node.update( - with: content, - proposedSize: .zero, - environment: environment, - dryRun: dryRun - ) - } - - let columnLabels = columns.labels - let columnCount = columnLabels.count - let remainder = rowContent.count - children.rowNodes.count - if remainder < 0 { - children.rowNodes.removeLast(-remainder) - children.cellContainerWidgets.removeLast(-remainder * columnCount) - } else if remainder > 0 { - for row in rowContent[children.rowNodes.count...] { - let rowNode = AnyViewGraphNode( - for: row, - backend: backend, - environment: environment - ) - children.rowNodes.append(rowNode) - for cellWidget in rowNode.getChildren().widgets(for: backend) { - let cellContainer = backend.createContainer() - backend.addChild(cellWidget, to: cellContainer) - children.cellContainerWidgets.append(AnyWidget(cellContainer)) - } - } - } - - if !dryRun { - backend.setRowCount(ofTable: widget, to: rows.count) - backend.setColumnLabels(ofTable: widget, to: columnLabels, environment: environment) - } - - let columnWidth = proposedSize.x / columnCount - var rowHeights: [Int] = [] - for (rowIndex, (rowNode, content)) in zip(children.rowNodes, rowContent).enumerated() { - let rowCells = content.layoutableChildren( - backend: backend, - children: rowNode.getChildren() - ) - - var cellHeights: [Int] = [] - for rowCell in rowCells { - let cellResult = rowCell.update( - proposedSize: SIMD2(columnWidth, backend.defaultTableRowContentHeight), - environment: environment, - dryRun: dryRun - ) - cellResults.append(cellResult) - cellHeights.append(cellResult.size.size.y) - } - - let rowHeight = - max(cellHeights.max() ?? 0, backend.defaultTableRowContentHeight) - + backend.defaultTableCellVerticalPadding * 2 - rowHeights.append(rowHeight) - - for (columnIndex, cellHeight) in zip(0..: ViewGraphNodeChildren { - var rowNodes: [AnyViewGraphNode>] = [] - var cellContainerWidgets: [AnyWidget] = [] - - /// Not used, just a protocol requirement. - var widgets: [AnyWidget] { - rowNodes.map(\.widget) - } - - var erasedNodes: [ErasedViewGraphNode] { - rowNodes.map(ErasedViewGraphNode.init(wrapping:)) - } - - init() { - rowNodes = [] - cellContainerWidgets = [] - } -} - -/// An empty view that simply manages a row's children. Not intended to be rendered directly. -struct RowView: View { - var body: Content - - init(_ content: Content) { - self.body = content - } - - func layoutableChildren( - backend: Backend, - children: any ViewGraphNodeChildren - ) -> [LayoutSystem.LayoutableChild] { - body.layoutableChildren(backend: backend, children: children) - } - - func children( - backend: Backend, - snapshots: [ViewGraphSnapshotter.NodeSnapshot]?, - environment: EnvironmentValues - ) -> any ViewGraphNodeChildren { - body.children(backend: backend, snapshots: snapshots, environment: environment) - } - - func asWidget( - _ children: any ViewGraphNodeChildren, - backend: Backend - ) -> Backend.Widget { - return backend.createContainer() - } - - func update( - _ widget: Backend.Widget, - children: any ViewGraphNodeChildren, - proposedSize: SIMD2, - environment: EnvironmentValues, - backend: Backend - ) -> ViewUpdateResult { - return ViewUpdateResult.leafView(size: .empty) - } -} diff --git a/Sources/SwiftCrossUI/Views/TableColumn.swift b/Sources/SwiftCrossUI/Views/TableColumn.swift deleted file mode 100644 index 4f8ea085b8..0000000000 --- a/Sources/SwiftCrossUI/Views/TableColumn.swift +++ /dev/null @@ -1,25 +0,0 @@ -/// A labelled column with a view for each row in a table. -public struct TableColumn { - /// The label displayed at the top of the column (also known as the column title). - public var label: String - /// The content displayed for this column of each row of the table. - public var content: (RowValue) -> Content -} - -extension TableColumn { - /// Creates a column. - public init(_ label: String, @ViewBuilder content: @escaping (RowValue) -> Content) { - self.label = label - self.content = content - } -} - -extension TableColumn where Content == Text { - /// Creates a column with that displays a string property and has a text label. - public init(_ label: String, value keyPath: KeyPath) { - self.label = label - self.content = { row in - Text(row[keyPath: keyPath]) - } - } -} diff --git a/Sources/SwiftCrossUI/Views/TableRowContent.swift b/Sources/SwiftCrossUI/Views/TableRowContent.swift deleted file mode 100644 index f76429dcff..0000000000 --- a/Sources/SwiftCrossUI/Views/TableRowContent.swift +++ /dev/null @@ -1,1121 +0,0 @@ -// This file was generated using gyb. Do not edit it directly. Edit -// TableRowContent.swift.gyb instead. - -public protocol TableRowContent { - associatedtype RowValue - associatedtype RowContent: View - - var labels: [String] { get } - - func content(for row: RowValue) -> RowContent -} - -public struct EmptyTableRowContent: TableRowContent { - public typealias RowContent = EmptyView - - public var labels: [String] { - [] - } - - public init() {} - - public func content(for row: RowValue) -> EmptyView { - EmptyView() - } -} - -public struct TupleTableRowContent1: TableRowContent { - public typealias RowContent = TupleView1 - - public var column0: TableColumn - - public var labels: [String] { - [column0.label] - } - - public init(_ column0: TableColumn) { - self.column0 = column0 - } - - public func content(for row: RowValue) -> RowContent { - TupleView1(column0.content(row)) - } -} - -public struct TupleTableRowContent2: TableRowContent { - public typealias RowContent = TupleView2 - - public var column0: TableColumn - public var column1: TableColumn - - public var labels: [String] { - [column0.label, column1.label] - } - - public init( - _ column0: TableColumn, _ column1: TableColumn - ) { - self.column0 = column0 - self.column1 = column1 - } - - public func content(for row: RowValue) -> RowContent { - TupleView2(column0.content(row), column1.content(row)) - } -} - -public struct TupleTableRowContent3: - TableRowContent -{ - public typealias RowContent = TupleView3 - - public var column0: TableColumn - public var column1: TableColumn - public var column2: TableColumn - - public var labels: [String] { - [column0.label, column1.label, column2.label] - } - - public init( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn - ) { - self.column0 = column0 - self.column1 = column1 - self.column2 = column2 - } - - public func content(for row: RowValue) -> RowContent { - TupleView3(column0.content(row), column1.content(row), column2.content(row)) - } -} - -public struct TupleTableRowContent4< - RowValue, Content0: View, Content1: View, Content2: View, Content3: View ->: TableRowContent { - public typealias RowContent = TupleView4 - - public var column0: TableColumn - public var column1: TableColumn - public var column2: TableColumn - public var column3: TableColumn - - public var labels: [String] { - [column0.label, column1.label, column2.label, column3.label] - } - - public init( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn - ) { - self.column0 = column0 - self.column1 = column1 - self.column2 = column2 - self.column3 = column3 - } - - public func content(for row: RowValue) -> RowContent { - TupleView4( - column0.content(row), column1.content(row), column2.content(row), column3.content(row)) - } -} - -public struct TupleTableRowContent5< - RowValue, Content0: View, Content1: View, Content2: View, Content3: View, Content4: View ->: TableRowContent { - public typealias RowContent = TupleView5 - - public var column0: TableColumn - public var column1: TableColumn - public var column2: TableColumn - public var column3: TableColumn - public var column4: TableColumn - - public var labels: [String] { - [column0.label, column1.label, column2.label, column3.label, column4.label] - } - - public init( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn - ) { - self.column0 = column0 - self.column1 = column1 - self.column2 = column2 - self.column3 = column3 - self.column4 = column4 - } - - public func content(for row: RowValue) -> RowContent { - TupleView5( - column0.content(row), column1.content(row), column2.content(row), column3.content(row), - column4.content(row)) - } -} - -public struct TupleTableRowContent6< - RowValue, Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View ->: TableRowContent { - public typealias RowContent = TupleView6< - Content0, Content1, Content2, Content3, Content4, Content5 - > - - public var column0: TableColumn - public var column1: TableColumn - public var column2: TableColumn - public var column3: TableColumn - public var column4: TableColumn - public var column5: TableColumn - - public var labels: [String] { - [column0.label, column1.label, column2.label, column3.label, column4.label, column5.label] - } - - public init( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn, _ column5: TableColumn - ) { - self.column0 = column0 - self.column1 = column1 - self.column2 = column2 - self.column3 = column3 - self.column4 = column4 - self.column5 = column5 - } - - public func content(for row: RowValue) -> RowContent { - TupleView6( - column0.content(row), column1.content(row), column2.content(row), column3.content(row), - column4.content(row), column5.content(row)) - } -} - -public struct TupleTableRowContent7< - RowValue, Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View, Content6: View ->: TableRowContent { - public typealias RowContent = TupleView7< - Content0, Content1, Content2, Content3, Content4, Content5, Content6 - > - - public var column0: TableColumn - public var column1: TableColumn - public var column2: TableColumn - public var column3: TableColumn - public var column4: TableColumn - public var column5: TableColumn - public var column6: TableColumn - - public var labels: [String] { - [ - column0.label, column1.label, column2.label, column3.label, column4.label, - column5.label, column6.label, - ] - } - - public init( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn, _ column5: TableColumn, - _ column6: TableColumn - ) { - self.column0 = column0 - self.column1 = column1 - self.column2 = column2 - self.column3 = column3 - self.column4 = column4 - self.column5 = column5 - self.column6 = column6 - } - - public func content(for row: RowValue) -> RowContent { - TupleView7( - column0.content(row), column1.content(row), column2.content(row), column3.content(row), - column4.content(row), column5.content(row), column6.content(row)) - } -} - -public struct TupleTableRowContent8< - RowValue, Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View, Content6: View, Content7: View ->: TableRowContent { - public typealias RowContent = TupleView8< - Content0, Content1, Content2, Content3, Content4, Content5, Content6, Content7 - > - - public var column0: TableColumn - public var column1: TableColumn - public var column2: TableColumn - public var column3: TableColumn - public var column4: TableColumn - public var column5: TableColumn - public var column6: TableColumn - public var column7: TableColumn - - public var labels: [String] { - [ - column0.label, column1.label, column2.label, column3.label, column4.label, - column5.label, column6.label, column7.label, - ] - } - - public init( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn, _ column5: TableColumn, - _ column6: TableColumn, _ column7: TableColumn - ) { - self.column0 = column0 - self.column1 = column1 - self.column2 = column2 - self.column3 = column3 - self.column4 = column4 - self.column5 = column5 - self.column6 = column6 - self.column7 = column7 - } - - public func content(for row: RowValue) -> RowContent { - TupleView8( - column0.content(row), column1.content(row), column2.content(row), column3.content(row), - column4.content(row), column5.content(row), column6.content(row), column7.content(row)) - } -} - -public struct TupleTableRowContent9< - RowValue, Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View, Content6: View, Content7: View, Content8: View ->: TableRowContent { - public typealias RowContent = TupleView9< - Content0, Content1, Content2, Content3, Content4, Content5, Content6, Content7, Content8 - > - - public var column0: TableColumn - public var column1: TableColumn - public var column2: TableColumn - public var column3: TableColumn - public var column4: TableColumn - public var column5: TableColumn - public var column6: TableColumn - public var column7: TableColumn - public var column8: TableColumn - - public var labels: [String] { - [ - column0.label, column1.label, column2.label, column3.label, column4.label, - column5.label, column6.label, column7.label, column8.label, - ] - } - - public init( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn, _ column5: TableColumn, - _ column6: TableColumn, _ column7: TableColumn, - _ column8: TableColumn - ) { - self.column0 = column0 - self.column1 = column1 - self.column2 = column2 - self.column3 = column3 - self.column4 = column4 - self.column5 = column5 - self.column6 = column6 - self.column7 = column7 - self.column8 = column8 - } - - public func content(for row: RowValue) -> RowContent { - TupleView9( - column0.content(row), column1.content(row), column2.content(row), column3.content(row), - column4.content(row), column5.content(row), column6.content(row), column7.content(row), - column8.content(row)) - } -} - -public struct TupleTableRowContent10< - RowValue, Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View, Content6: View, Content7: View, Content8: View, Content9: View ->: TableRowContent { - public typealias RowContent = TupleView10< - Content0, Content1, Content2, Content3, Content4, Content5, Content6, Content7, Content8, - Content9 - > - - public var column0: TableColumn - public var column1: TableColumn - public var column2: TableColumn - public var column3: TableColumn - public var column4: TableColumn - public var column5: TableColumn - public var column6: TableColumn - public var column7: TableColumn - public var column8: TableColumn - public var column9: TableColumn - - public var labels: [String] { - [ - column0.label, column1.label, column2.label, column3.label, column4.label, - column5.label, column6.label, column7.label, column8.label, column9.label, - ] - } - - public init( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn, _ column5: TableColumn, - _ column6: TableColumn, _ column7: TableColumn, - _ column8: TableColumn, _ column9: TableColumn - ) { - self.column0 = column0 - self.column1 = column1 - self.column2 = column2 - self.column3 = column3 - self.column4 = column4 - self.column5 = column5 - self.column6 = column6 - self.column7 = column7 - self.column8 = column8 - self.column9 = column9 - } - - public func content(for row: RowValue) -> RowContent { - TupleView10( - column0.content(row), column1.content(row), column2.content(row), column3.content(row), - column4.content(row), column5.content(row), column6.content(row), column7.content(row), - column8.content(row), column9.content(row)) - } -} - -public struct TupleTableRowContent11< - RowValue, Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View, Content6: View, Content7: View, Content8: View, Content9: View, Content10: View ->: TableRowContent { - public typealias RowContent = TupleView11< - Content0, Content1, Content2, Content3, Content4, Content5, Content6, Content7, Content8, - Content9, Content10 - > - - public var column0: TableColumn - public var column1: TableColumn - public var column2: TableColumn - public var column3: TableColumn - public var column4: TableColumn - public var column5: TableColumn - public var column6: TableColumn - public var column7: TableColumn - public var column8: TableColumn - public var column9: TableColumn - public var column10: TableColumn - - public var labels: [String] { - [ - column0.label, column1.label, column2.label, column3.label, column4.label, - column5.label, column6.label, column7.label, column8.label, column9.label, - column10.label, - ] - } - - public init( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn, _ column5: TableColumn, - _ column6: TableColumn, _ column7: TableColumn, - _ column8: TableColumn, _ column9: TableColumn, - _ column10: TableColumn - ) { - self.column0 = column0 - self.column1 = column1 - self.column2 = column2 - self.column3 = column3 - self.column4 = column4 - self.column5 = column5 - self.column6 = column6 - self.column7 = column7 - self.column8 = column8 - self.column9 = column9 - self.column10 = column10 - } - - public func content(for row: RowValue) -> RowContent { - TupleView11( - column0.content(row), column1.content(row), column2.content(row), column3.content(row), - column4.content(row), column5.content(row), column6.content(row), column7.content(row), - column8.content(row), column9.content(row), column10.content(row)) - } -} - -public struct TupleTableRowContent12< - RowValue, Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View, Content6: View, Content7: View, Content8: View, Content9: View, Content10: View, - Content11: View ->: TableRowContent { - public typealias RowContent = TupleView12< - Content0, Content1, Content2, Content3, Content4, Content5, Content6, Content7, Content8, - Content9, Content10, Content11 - > - - public var column0: TableColumn - public var column1: TableColumn - public var column2: TableColumn - public var column3: TableColumn - public var column4: TableColumn - public var column5: TableColumn - public var column6: TableColumn - public var column7: TableColumn - public var column8: TableColumn - public var column9: TableColumn - public var column10: TableColumn - public var column11: TableColumn - - public var labels: [String] { - [ - column0.label, column1.label, column2.label, column3.label, column4.label, - column5.label, column6.label, column7.label, column8.label, column9.label, - column10.label, column11.label, - ] - } - - public init( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn, _ column5: TableColumn, - _ column6: TableColumn, _ column7: TableColumn, - _ column8: TableColumn, _ column9: TableColumn, - _ column10: TableColumn, _ column11: TableColumn - ) { - self.column0 = column0 - self.column1 = column1 - self.column2 = column2 - self.column3 = column3 - self.column4 = column4 - self.column5 = column5 - self.column6 = column6 - self.column7 = column7 - self.column8 = column8 - self.column9 = column9 - self.column10 = column10 - self.column11 = column11 - } - - public func content(for row: RowValue) -> RowContent { - TupleView12( - column0.content(row), column1.content(row), column2.content(row), column3.content(row), - column4.content(row), column5.content(row), column6.content(row), column7.content(row), - column8.content(row), column9.content(row), column10.content(row), column11.content(row) - ) - } -} - -public struct TupleTableRowContent13< - RowValue, Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View, Content6: View, Content7: View, Content8: View, Content9: View, Content10: View, - Content11: View, Content12: View ->: TableRowContent { - public typealias RowContent = TupleView13< - Content0, Content1, Content2, Content3, Content4, Content5, Content6, Content7, Content8, - Content9, Content10, Content11, Content12 - > - - public var column0: TableColumn - public var column1: TableColumn - public var column2: TableColumn - public var column3: TableColumn - public var column4: TableColumn - public var column5: TableColumn - public var column6: TableColumn - public var column7: TableColumn - public var column8: TableColumn - public var column9: TableColumn - public var column10: TableColumn - public var column11: TableColumn - public var column12: TableColumn - - public var labels: [String] { - [ - column0.label, column1.label, column2.label, column3.label, column4.label, - column5.label, column6.label, column7.label, column8.label, column9.label, - column10.label, column11.label, column12.label, - ] - } - - public init( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn, _ column5: TableColumn, - _ column6: TableColumn, _ column7: TableColumn, - _ column8: TableColumn, _ column9: TableColumn, - _ column10: TableColumn, _ column11: TableColumn, - _ column12: TableColumn - ) { - self.column0 = column0 - self.column1 = column1 - self.column2 = column2 - self.column3 = column3 - self.column4 = column4 - self.column5 = column5 - self.column6 = column6 - self.column7 = column7 - self.column8 = column8 - self.column9 = column9 - self.column10 = column10 - self.column11 = column11 - self.column12 = column12 - } - - public func content(for row: RowValue) -> RowContent { - TupleView13( - column0.content(row), column1.content(row), column2.content(row), column3.content(row), - column4.content(row), column5.content(row), column6.content(row), column7.content(row), - column8.content(row), column9.content(row), column10.content(row), - column11.content(row), column12.content(row)) - } -} - -public struct TupleTableRowContent14< - RowValue, Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View, Content6: View, Content7: View, Content8: View, Content9: View, Content10: View, - Content11: View, Content12: View, Content13: View ->: TableRowContent { - public typealias RowContent = TupleView14< - Content0, Content1, Content2, Content3, Content4, Content5, Content6, Content7, Content8, - Content9, Content10, Content11, Content12, Content13 - > - - public var column0: TableColumn - public var column1: TableColumn - public var column2: TableColumn - public var column3: TableColumn - public var column4: TableColumn - public var column5: TableColumn - public var column6: TableColumn - public var column7: TableColumn - public var column8: TableColumn - public var column9: TableColumn - public var column10: TableColumn - public var column11: TableColumn - public var column12: TableColumn - public var column13: TableColumn - - public var labels: [String] { - [ - column0.label, column1.label, column2.label, column3.label, column4.label, - column5.label, column6.label, column7.label, column8.label, column9.label, - column10.label, column11.label, column12.label, column13.label, - ] - } - - public init( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn, _ column5: TableColumn, - _ column6: TableColumn, _ column7: TableColumn, - _ column8: TableColumn, _ column9: TableColumn, - _ column10: TableColumn, _ column11: TableColumn, - _ column12: TableColumn, _ column13: TableColumn - ) { - self.column0 = column0 - self.column1 = column1 - self.column2 = column2 - self.column3 = column3 - self.column4 = column4 - self.column5 = column5 - self.column6 = column6 - self.column7 = column7 - self.column8 = column8 - self.column9 = column9 - self.column10 = column10 - self.column11 = column11 - self.column12 = column12 - self.column13 = column13 - } - - public func content(for row: RowValue) -> RowContent { - TupleView14( - column0.content(row), column1.content(row), column2.content(row), column3.content(row), - column4.content(row), column5.content(row), column6.content(row), column7.content(row), - column8.content(row), column9.content(row), column10.content(row), - column11.content(row), column12.content(row), column13.content(row)) - } -} - -public struct TupleTableRowContent15< - RowValue, Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View, Content6: View, Content7: View, Content8: View, Content9: View, Content10: View, - Content11: View, Content12: View, Content13: View, Content14: View ->: TableRowContent { - public typealias RowContent = TupleView15< - Content0, Content1, Content2, Content3, Content4, Content5, Content6, Content7, Content8, - Content9, Content10, Content11, Content12, Content13, Content14 - > - - public var column0: TableColumn - public var column1: TableColumn - public var column2: TableColumn - public var column3: TableColumn - public var column4: TableColumn - public var column5: TableColumn - public var column6: TableColumn - public var column7: TableColumn - public var column8: TableColumn - public var column9: TableColumn - public var column10: TableColumn - public var column11: TableColumn - public var column12: TableColumn - public var column13: TableColumn - public var column14: TableColumn - - public var labels: [String] { - [ - column0.label, column1.label, column2.label, column3.label, column4.label, - column5.label, column6.label, column7.label, column8.label, column9.label, - column10.label, column11.label, column12.label, column13.label, column14.label, - ] - } - - public init( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn, _ column5: TableColumn, - _ column6: TableColumn, _ column7: TableColumn, - _ column8: TableColumn, _ column9: TableColumn, - _ column10: TableColumn, _ column11: TableColumn, - _ column12: TableColumn, _ column13: TableColumn, - _ column14: TableColumn - ) { - self.column0 = column0 - self.column1 = column1 - self.column2 = column2 - self.column3 = column3 - self.column4 = column4 - self.column5 = column5 - self.column6 = column6 - self.column7 = column7 - self.column8 = column8 - self.column9 = column9 - self.column10 = column10 - self.column11 = column11 - self.column12 = column12 - self.column13 = column13 - self.column14 = column14 - } - - public func content(for row: RowValue) -> RowContent { - TupleView15( - column0.content(row), column1.content(row), column2.content(row), column3.content(row), - column4.content(row), column5.content(row), column6.content(row), column7.content(row), - column8.content(row), column9.content(row), column10.content(row), - column11.content(row), column12.content(row), column13.content(row), - column14.content(row)) - } -} - -public struct TupleTableRowContent16< - RowValue, Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View, Content6: View, Content7: View, Content8: View, Content9: View, Content10: View, - Content11: View, Content12: View, Content13: View, Content14: View, Content15: View ->: TableRowContent { - public typealias RowContent = TupleView16< - Content0, Content1, Content2, Content3, Content4, Content5, Content6, Content7, Content8, - Content9, Content10, Content11, Content12, Content13, Content14, Content15 - > - - public var column0: TableColumn - public var column1: TableColumn - public var column2: TableColumn - public var column3: TableColumn - public var column4: TableColumn - public var column5: TableColumn - public var column6: TableColumn - public var column7: TableColumn - public var column8: TableColumn - public var column9: TableColumn - public var column10: TableColumn - public var column11: TableColumn - public var column12: TableColumn - public var column13: TableColumn - public var column14: TableColumn - public var column15: TableColumn - - public var labels: [String] { - [ - column0.label, column1.label, column2.label, column3.label, column4.label, - column5.label, column6.label, column7.label, column8.label, column9.label, - column10.label, column11.label, column12.label, column13.label, column14.label, - column15.label, - ] - } - - public init( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn, _ column5: TableColumn, - _ column6: TableColumn, _ column7: TableColumn, - _ column8: TableColumn, _ column9: TableColumn, - _ column10: TableColumn, _ column11: TableColumn, - _ column12: TableColumn, _ column13: TableColumn, - _ column14: TableColumn, _ column15: TableColumn - ) { - self.column0 = column0 - self.column1 = column1 - self.column2 = column2 - self.column3 = column3 - self.column4 = column4 - self.column5 = column5 - self.column6 = column6 - self.column7 = column7 - self.column8 = column8 - self.column9 = column9 - self.column10 = column10 - self.column11 = column11 - self.column12 = column12 - self.column13 = column13 - self.column14 = column14 - self.column15 = column15 - } - - public func content(for row: RowValue) -> RowContent { - TupleView16( - column0.content(row), column1.content(row), column2.content(row), column3.content(row), - column4.content(row), column5.content(row), column6.content(row), column7.content(row), - column8.content(row), column9.content(row), column10.content(row), - column11.content(row), column12.content(row), column13.content(row), - column14.content(row), column15.content(row)) - } -} - -public struct TupleTableRowContent17< - RowValue, Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View, Content6: View, Content7: View, Content8: View, Content9: View, Content10: View, - Content11: View, Content12: View, Content13: View, Content14: View, Content15: View, - Content16: View ->: TableRowContent { - public typealias RowContent = TupleView17< - Content0, Content1, Content2, Content3, Content4, Content5, Content6, Content7, Content8, - Content9, Content10, Content11, Content12, Content13, Content14, Content15, Content16 - > - - public var column0: TableColumn - public var column1: TableColumn - public var column2: TableColumn - public var column3: TableColumn - public var column4: TableColumn - public var column5: TableColumn - public var column6: TableColumn - public var column7: TableColumn - public var column8: TableColumn - public var column9: TableColumn - public var column10: TableColumn - public var column11: TableColumn - public var column12: TableColumn - public var column13: TableColumn - public var column14: TableColumn - public var column15: TableColumn - public var column16: TableColumn - - public var labels: [String] { - [ - column0.label, column1.label, column2.label, column3.label, column4.label, - column5.label, column6.label, column7.label, column8.label, column9.label, - column10.label, column11.label, column12.label, column13.label, column14.label, - column15.label, column16.label, - ] - } - - public init( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn, _ column5: TableColumn, - _ column6: TableColumn, _ column7: TableColumn, - _ column8: TableColumn, _ column9: TableColumn, - _ column10: TableColumn, _ column11: TableColumn, - _ column12: TableColumn, _ column13: TableColumn, - _ column14: TableColumn, _ column15: TableColumn, - _ column16: TableColumn - ) { - self.column0 = column0 - self.column1 = column1 - self.column2 = column2 - self.column3 = column3 - self.column4 = column4 - self.column5 = column5 - self.column6 = column6 - self.column7 = column7 - self.column8 = column8 - self.column9 = column9 - self.column10 = column10 - self.column11 = column11 - self.column12 = column12 - self.column13 = column13 - self.column14 = column14 - self.column15 = column15 - self.column16 = column16 - } - - public func content(for row: RowValue) -> RowContent { - TupleView17( - column0.content(row), column1.content(row), column2.content(row), column3.content(row), - column4.content(row), column5.content(row), column6.content(row), column7.content(row), - column8.content(row), column9.content(row), column10.content(row), - column11.content(row), column12.content(row), column13.content(row), - column14.content(row), column15.content(row), column16.content(row)) - } -} - -public struct TupleTableRowContent18< - RowValue, Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View, Content6: View, Content7: View, Content8: View, Content9: View, Content10: View, - Content11: View, Content12: View, Content13: View, Content14: View, Content15: View, - Content16: View, Content17: View ->: TableRowContent { - public typealias RowContent = TupleView18< - Content0, Content1, Content2, Content3, Content4, Content5, Content6, Content7, Content8, - Content9, Content10, Content11, Content12, Content13, Content14, Content15, Content16, - Content17 - > - - public var column0: TableColumn - public var column1: TableColumn - public var column2: TableColumn - public var column3: TableColumn - public var column4: TableColumn - public var column5: TableColumn - public var column6: TableColumn - public var column7: TableColumn - public var column8: TableColumn - public var column9: TableColumn - public var column10: TableColumn - public var column11: TableColumn - public var column12: TableColumn - public var column13: TableColumn - public var column14: TableColumn - public var column15: TableColumn - public var column16: TableColumn - public var column17: TableColumn - - public var labels: [String] { - [ - column0.label, column1.label, column2.label, column3.label, column4.label, - column5.label, column6.label, column7.label, column8.label, column9.label, - column10.label, column11.label, column12.label, column13.label, column14.label, - column15.label, column16.label, column17.label, - ] - } - - public init( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn, _ column5: TableColumn, - _ column6: TableColumn, _ column7: TableColumn, - _ column8: TableColumn, _ column9: TableColumn, - _ column10: TableColumn, _ column11: TableColumn, - _ column12: TableColumn, _ column13: TableColumn, - _ column14: TableColumn, _ column15: TableColumn, - _ column16: TableColumn, _ column17: TableColumn - ) { - self.column0 = column0 - self.column1 = column1 - self.column2 = column2 - self.column3 = column3 - self.column4 = column4 - self.column5 = column5 - self.column6 = column6 - self.column7 = column7 - self.column8 = column8 - self.column9 = column9 - self.column10 = column10 - self.column11 = column11 - self.column12 = column12 - self.column13 = column13 - self.column14 = column14 - self.column15 = column15 - self.column16 = column16 - self.column17 = column17 - } - - public func content(for row: RowValue) -> RowContent { - TupleView18( - column0.content(row), column1.content(row), column2.content(row), column3.content(row), - column4.content(row), column5.content(row), column6.content(row), column7.content(row), - column8.content(row), column9.content(row), column10.content(row), - column11.content(row), column12.content(row), column13.content(row), - column14.content(row), column15.content(row), column16.content(row), - column17.content(row)) - } -} - -public struct TupleTableRowContent19< - RowValue, Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View, Content6: View, Content7: View, Content8: View, Content9: View, Content10: View, - Content11: View, Content12: View, Content13: View, Content14: View, Content15: View, - Content16: View, Content17: View, Content18: View ->: TableRowContent { - public typealias RowContent = TupleView19< - Content0, Content1, Content2, Content3, Content4, Content5, Content6, Content7, Content8, - Content9, Content10, Content11, Content12, Content13, Content14, Content15, Content16, - Content17, Content18 - > - - public var column0: TableColumn - public var column1: TableColumn - public var column2: TableColumn - public var column3: TableColumn - public var column4: TableColumn - public var column5: TableColumn - public var column6: TableColumn - public var column7: TableColumn - public var column8: TableColumn - public var column9: TableColumn - public var column10: TableColumn - public var column11: TableColumn - public var column12: TableColumn - public var column13: TableColumn - public var column14: TableColumn - public var column15: TableColumn - public var column16: TableColumn - public var column17: TableColumn - public var column18: TableColumn - - public var labels: [String] { - [ - column0.label, column1.label, column2.label, column3.label, column4.label, - column5.label, column6.label, column7.label, column8.label, column9.label, - column10.label, column11.label, column12.label, column13.label, column14.label, - column15.label, column16.label, column17.label, column18.label, - ] - } - - public init( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn, _ column5: TableColumn, - _ column6: TableColumn, _ column7: TableColumn, - _ column8: TableColumn, _ column9: TableColumn, - _ column10: TableColumn, _ column11: TableColumn, - _ column12: TableColumn, _ column13: TableColumn, - _ column14: TableColumn, _ column15: TableColumn, - _ column16: TableColumn, _ column17: TableColumn, - _ column18: TableColumn - ) { - self.column0 = column0 - self.column1 = column1 - self.column2 = column2 - self.column3 = column3 - self.column4 = column4 - self.column5 = column5 - self.column6 = column6 - self.column7 = column7 - self.column8 = column8 - self.column9 = column9 - self.column10 = column10 - self.column11 = column11 - self.column12 = column12 - self.column13 = column13 - self.column14 = column14 - self.column15 = column15 - self.column16 = column16 - self.column17 = column17 - self.column18 = column18 - } - - public func content(for row: RowValue) -> RowContent { - TupleView19( - column0.content(row), column1.content(row), column2.content(row), column3.content(row), - column4.content(row), column5.content(row), column6.content(row), column7.content(row), - column8.content(row), column9.content(row), column10.content(row), - column11.content(row), column12.content(row), column13.content(row), - column14.content(row), column15.content(row), column16.content(row), - column17.content(row), column18.content(row)) - } -} - -public struct TupleTableRowContent20< - RowValue, Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View, Content6: View, Content7: View, Content8: View, Content9: View, Content10: View, - Content11: View, Content12: View, Content13: View, Content14: View, Content15: View, - Content16: View, Content17: View, Content18: View, Content19: View ->: TableRowContent { - public typealias RowContent = TupleView20< - Content0, Content1, Content2, Content3, Content4, Content5, Content6, Content7, Content8, - Content9, Content10, Content11, Content12, Content13, Content14, Content15, Content16, - Content17, Content18, Content19 - > - - public var column0: TableColumn - public var column1: TableColumn - public var column2: TableColumn - public var column3: TableColumn - public var column4: TableColumn - public var column5: TableColumn - public var column6: TableColumn - public var column7: TableColumn - public var column8: TableColumn - public var column9: TableColumn - public var column10: TableColumn - public var column11: TableColumn - public var column12: TableColumn - public var column13: TableColumn - public var column14: TableColumn - public var column15: TableColumn - public var column16: TableColumn - public var column17: TableColumn - public var column18: TableColumn - public var column19: TableColumn - - public var labels: [String] { - [ - column0.label, column1.label, column2.label, column3.label, column4.label, - column5.label, column6.label, column7.label, column8.label, column9.label, - column10.label, column11.label, column12.label, column13.label, column14.label, - column15.label, column16.label, column17.label, column18.label, column19.label, - ] - } - - public init( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn, _ column5: TableColumn, - _ column6: TableColumn, _ column7: TableColumn, - _ column8: TableColumn, _ column9: TableColumn, - _ column10: TableColumn, _ column11: TableColumn, - _ column12: TableColumn, _ column13: TableColumn, - _ column14: TableColumn, _ column15: TableColumn, - _ column16: TableColumn, _ column17: TableColumn, - _ column18: TableColumn, _ column19: TableColumn - ) { - self.column0 = column0 - self.column1 = column1 - self.column2 = column2 - self.column3 = column3 - self.column4 = column4 - self.column5 = column5 - self.column6 = column6 - self.column7 = column7 - self.column8 = column8 - self.column9 = column9 - self.column10 = column10 - self.column11 = column11 - self.column12 = column12 - self.column13 = column13 - self.column14 = column14 - self.column15 = column15 - self.column16 = column16 - self.column17 = column17 - self.column18 = column18 - self.column19 = column19 - } - - public func content(for row: RowValue) -> RowContent { - TupleView20( - column0.content(row), column1.content(row), column2.content(row), column3.content(row), - column4.content(row), column5.content(row), column6.content(row), column7.content(row), - column8.content(row), column9.content(row), column10.content(row), - column11.content(row), column12.content(row), column13.content(row), - column14.content(row), column15.content(row), column16.content(row), - column17.content(row), column18.content(row), column19.content(row)) - } -} diff --git a/Sources/SwiftCrossUI/Views/TableRowContent.swift.gyb b/Sources/SwiftCrossUI/Views/TableRowContent.swift.gyb deleted file mode 100644 index aed9494119..0000000000 --- a/Sources/SwiftCrossUI/Views/TableRowContent.swift.gyb +++ /dev/null @@ -1,53 +0,0 @@ -// This file was generated using gyb. Do not edit it directly. Edit -// TableRowContent.swift.gyb instead. -%{ -maximum_column_count = 20 -}% - -public protocol TableRowContent { - associatedtype RowValue - associatedtype RowContent: View - - var labels: [String] { get } - - func content(for row: RowValue) -> RowContent -} - -public struct EmptyTableRowContent: TableRowContent { - public typealias RowContent = EmptyView - - public var labels: [String] { - [] - } - - public init() {} - - public func content(for row: RowValue) -> EmptyView { - EmptyView() - } -} - -%for i in range(1, maximum_column_count + 1): -public struct TupleTableRowContent${i}: TableRowContent { - public typealias RowContent = TupleView${i}<${", ".join("Content%d" % j for j in range(i))}> - - %for j in range(i): - public var column${j}: TableColumn - %end - - public var labels: [String] { - [${", ".join("column%d.label" % j for j in range(i))}] - } - - public init(${", ".join("_ column%d: TableColumn" % (j, j) for j in range(i))}) { - %for j in range(i): - self.column${j} = column${j} - %end - } - - public func content(for row: RowValue) -> RowContent { - TupleView${i}(${", ".join("column%d.content(row)" % j for j in range(i))}) - } -} - -%end diff --git a/Sources/SwiftCrossUI/Views/Text.swift b/Sources/SwiftCrossUI/Views/Text.swift index ade3737162..779899a7ca 100644 --- a/Sources/SwiftCrossUI/Views/Text.swift +++ b/Sources/SwiftCrossUI/Views/Text.swift @@ -19,60 +19,45 @@ extension Text: ElementaryView { return backend.createTextView() } - public func update( + public func computeLayout( _ widget: Backend.Widget, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - // TODO: Avoid this + backend: Backend + ) -> ViewLayoutResult { + // TODO: Avoid this. Move it to commit once we figure out a solution for Gtk. // Even in dry runs we must update the underlying text view widget // because GtkBackend currently relies on querying the widget for text // properties and such (via Pango). backend.updateTextView(widget, content: string, environment: environment) - let size = backend.size( - of: string, - whenDisplayedIn: widget, - proposedFrame: proposedSize, - environment: environment - ) - if !dryRun { - backend.setSize(of: widget, to: size) + let proposedFrame: SIMD2? + if let width = proposedSize.width { + proposedFrame = SIMD2( + LayoutSystem.roundSize(width), + // Backends don't care about our height proposal here at the moment. + proposedSize.height.map(LayoutSystem.roundSize) ?? 1 + ) + } else { + proposedFrame = nil } - let idealSize = backend.size( + let size = backend.size( of: string, whenDisplayedIn: widget, - proposedFrame: nil, + proposedFrame: proposedFrame, environment: environment ) - let minimumWidth = backend.size( - of: string, - whenDisplayedIn: widget, - proposedFrame: SIMD2(1, proposedSize.y), - environment: environment - ).x - let minimumHeight = backend.size( - of: string, - whenDisplayedIn: widget, - proposedFrame: SIMD2(proposedSize.x, 1), - environment: environment - ).y + return ViewLayoutResult.leafView(size: ViewSize(size)) + } - return ViewUpdateResult.leafView( - size: ViewSize( - size: size, - idealSize: idealSize, - idealWidthForProposedHeight: idealSize.x, - idealHeightForProposedWidth: size.y, - minimumWidth: minimumWidth == 1 ? 0 : minimumWidth, - minimumHeight: minimumHeight, - maximumWidth: Double(idealSize.x), - maximumHeight: Double(size.y) - ) - ) + public func commit( + _ widget: Backend.Widget, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + backend.setSize(of: widget, to: layout.size.vector) } } diff --git a/Sources/SwiftCrossUI/Views/TextEditor.swift b/Sources/SwiftCrossUI/Views/TextEditor.swift deleted file mode 100644 index 7070d6075d..0000000000 --- a/Sources/SwiftCrossUI/Views/TextEditor.swift +++ /dev/null @@ -1,60 +0,0 @@ -/// A control for editing multiline text. -public struct TextEditor: ElementaryView { - @Binding var text: String - - public init(text: Binding) { - _text = text - } - - func asWidget(backend: Backend) -> Backend.Widget { - backend.createTextEditor() - } - - func update( - _ widget: Backend.Widget, - proposedSize: SIMD2, - environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - // Avoid evaluating the binding multiple times - let content = text - - if !dryRun { - backend.updateTextEditor(widget, environment: environment) { newValue in - self.text = newValue - } - if content != backend.getContent(ofTextEditor: widget) { - backend.setContent(ofTextEditor: widget, to: content) - } - } - - let idealHeight = backend.size( - of: content, - whenDisplayedIn: widget, - proposedFrame: SIMD2(proposedSize.x, 1), - environment: environment - ).y - let size = SIMD2( - proposedSize.x, - max(proposedSize.y, idealHeight) - ) - - if !dryRun { - backend.setSize(of: widget, to: size) - } - - return ViewUpdateResult.leafView( - size: ViewSize( - size: size, - idealSize: SIMD2(10, 10), - idealWidthForProposedHeight: 10, - idealHeightForProposedWidth: idealHeight, - minimumWidth: 0, - minimumHeight: idealHeight, - maximumWidth: nil, - maximumHeight: nil - ) - ) - } -} diff --git a/Sources/SwiftCrossUI/Views/TextField.swift b/Sources/SwiftCrossUI/Views/TextField.swift deleted file mode 100644 index 8086f18fc4..0000000000 --- a/Sources/SwiftCrossUI/Views/TextField.swift +++ /dev/null @@ -1,73 +0,0 @@ -/// A control that displays an editable text interface. -public struct TextField: ElementaryView, View { - /// The label to show when the field is empty. - private var placeholder: String - /// The field's content. - private var value: Binding? - - /// Creates an editable text field with a given placeholder. - public init(_ placeholder: String = "", text: Binding) { - self.placeholder = placeholder - self.value = text - } - - /// Creates an editable text field with a given placeholder. - @available( - *, deprecated, - message: "Use TextField(_:text:) instead", - renamed: "TextField.init(_:text:)" - ) - public init(_ placeholder: String = "", _ value: Binding? = nil) { - self.placeholder = placeholder - var dummy = "" - self.value = value ?? Binding(get: { dummy }, set: { dummy = $0 }) - } - - public func asWidget(backend: Backend) -> Backend.Widget { - return backend.createTextField() - } - - public func update( - _ widget: Backend.Widget, - proposedSize: SIMD2, - environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - if !dryRun { - backend.updateTextField( - widget, - placeholder: placeholder, - environment: environment, - onChange: { newValue in - self.value?.wrappedValue = newValue - }, - onSubmit: environment.onSubmit ?? {} - ) - if let value = value?.wrappedValue, value != backend.getContent(ofTextField: widget) { - backend.setContent(ofTextField: widget, to: value) - } - } - - let naturalHeight = backend.naturalSize(of: widget).y - let size = SIMD2( - proposedSize.x, - naturalHeight - ) - if !dryRun { - backend.setSize(of: widget, to: size) - } - - // TODO: Allow backends to set their own ideal text field width - return ViewUpdateResult.leafView( - size: ViewSize( - size: size, - idealSize: SIMD2(100, naturalHeight), - minimumWidth: 0, - minimumHeight: naturalHeight, - maximumWidth: nil, - maximumHeight: Double(naturalHeight) - ) - ) - } -} diff --git a/Sources/SwiftCrossUI/Views/Toggle.swift b/Sources/SwiftCrossUI/Views/Toggle.swift deleted file mode 100644 index dc787811ea..0000000000 --- a/Sources/SwiftCrossUI/Views/Toggle.swift +++ /dev/null @@ -1,58 +0,0 @@ -/// A control for toggling between two values (usually representing on and off). -public struct Toggle: View { - @Environment(\.backend) var backend - @Environment(\.toggleStyle) var toggleStyle - - /// The label to be shown on or beside the toggle. - var label: String - /// Whether the toggle is active or not. - var active: Binding - - /// Creates a toggle that displays a custom label. - public init(_ label: String, active: Binding) { - self.label = label - self.active = active - } - - public var body: some View { - switch toggleStyle.style { - case .switch: - HStack { - Text(label) - - if backend.requiresToggleSwitchSpacer { - Spacer() - } - - ToggleSwitch(active: active) - } - case .button: - ToggleButton(label, active: active) - case .checkbox: - HStack { - Text(label) - - Checkbox(active: active) - } - } - } -} - -/// A style of toggle. -public struct ToggleStyle: Sendable { - package var style: Style - - /// A toggle switch. - public static let `switch` = Self(style: .switch) - /// A toggle button. Generally looks like a regular button when off and an - /// accented button when on. - public static let button = Self(style: .button) - /// A checkbox. - public static let checkbox = Self(style: .checkbox) - - package enum Style { - case `switch` - case button - case checkbox - } -} diff --git a/Sources/SwiftCrossUI/Views/ToggleButton.swift b/Sources/SwiftCrossUI/Views/ToggleButton.swift deleted file mode 100644 index b5488c4da8..0000000000 --- a/Sources/SwiftCrossUI/Views/ToggleButton.swift +++ /dev/null @@ -1,34 +0,0 @@ -/// A button style control that is either on or off. -struct ToggleButton: ElementaryView, View { - /// The label to show on the toggle button. - private var label: String - /// Whether the button is active or not. - private var active: Binding - - /// Creates a toggle button that displays a custom label. - public init(_ label: String, active: Binding) { - self.label = label - self.active = active - } - - public func asWidget(backend: Backend) -> Backend.Widget { - return backend.createToggle() - } - - public func update( - _ widget: Backend.Widget, - proposedSize: SIMD2, - environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - // TODO: Implement toggle button sizing within SwiftCrossUI so that we can properly implement `dryRun`. - backend.setState(ofToggle: widget, to: active.wrappedValue) - backend.updateToggle(widget, label: label, environment: environment) { newActiveState in - active.wrappedValue = newActiveState - } - return ViewUpdateResult.leafView( - size: ViewSize(fixedSize: backend.naturalSize(of: widget)) - ) - } -} diff --git a/Sources/SwiftCrossUI/Views/ToggleSwitch.swift b/Sources/SwiftCrossUI/Views/ToggleSwitch.swift deleted file mode 100644 index d59d58b908..0000000000 --- a/Sources/SwiftCrossUI/Views/ToggleSwitch.swift +++ /dev/null @@ -1,32 +0,0 @@ -/// A light switch style control that is either on or off. -struct ToggleSwitch: ElementaryView, View { - /// Whether the switch is active or not. - private var active: Binding - - /// Creates a switch. - public init(active: Binding) { - self.active = active - } - - public func asWidget(backend: Backend) -> Backend.Widget { - return backend.createSwitch() - } - - public func update( - _ widget: Backend.Widget, - proposedSize: SIMD2, - environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - if !dryRun { - backend.updateSwitch(widget, environment: environment) { newActiveState in - active.wrappedValue = newActiveState - } - backend.setState(ofSwitch: widget, to: active.wrappedValue) - } - return ViewUpdateResult.leafView( - size: ViewSize(fixedSize: backend.naturalSize(of: widget)) - ) - } -} diff --git a/Sources/SwiftCrossUI/Views/TupleView.swift b/Sources/SwiftCrossUI/Views/TupleView.swift index edd1d644fd..0c24ef041b 100644 --- a/Sources/SwiftCrossUI/Views/TupleView.swift +++ b/Sources/SwiftCrossUI/Views/TupleView.swift @@ -7,14 +7,16 @@ private func layoutableChild( view: V ) -> LayoutSystem.LayoutableChild { LayoutSystem.LayoutableChild( - update: { proposedSize, environment, dryRun in - node.update( + computeLayout: { proposedSize, environment in + node.computeLayout( with: view, proposedSize: proposedSize, - environment: environment, - dryRun: dryRun + environment: environment ) }, + commit: { + node.commit() + }, tag: "\(type(of: view))" ) } @@ -34,26 +36,43 @@ extension TupleView { } @MainActor - func update( + func computeLayout( _ widget: Backend.Widget, children: Children, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { + backend: Backend + ) -> ViewLayoutResult { let group = Group(content: self) - return group.update( + return group.computeLayout( widget, children: children, proposedSize: proposedSize, environment: environment, - backend: backend, - dryRun: dryRun + backend: backend + ) + } + + @MainActor + func commit( + _ widget: Backend.Widget, + children: Children, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + let group = Group(content: self) + group.commit( + widget, + children: children, + layout: layout, + environment: environment, + backend: backend ) } } + /// A view with exactly 1 children. Autogenerated as an alternative to Swift's not yet /// production ready variadic generics. /// @@ -92,7 +111,7 @@ extension TupleView1: TupleView { children: Children ) -> [LayoutSystem.LayoutableChild] { [ - layoutableChild(node: children.child0, view: view0) + layoutableChild(node: children.child0, view: view0), ] } } @@ -303,9 +322,7 @@ extension TupleView5: TupleView { /// production ready variadic generics. /// /// Has the same behaviour as ``Group`` when rendered directly. -public struct TupleView6< - View0: View, View1: View, View2: View, View3: View, View4: View, View5: View -> { +public struct TupleView6 { public var view0: View0 public var view1: View1 public var view2: View2 @@ -316,10 +333,7 @@ public struct TupleView6< public var body = EmptyView() /// Wraps 6 child views in a single container view. - public init( - _ view0: View0, _ view1: View1, _ view2: View2, _ view3: View3, _ view4: View4, - _ view5: View5 - ) { + public init(_ view0: View0, _ view1: View1, _ view2: View2, _ view3: View3, _ view4: View4, _ view5: View5) { self.view0 = view0 self.view1 = view1 self.view2 = view2 @@ -366,9 +380,7 @@ extension TupleView6: TupleView { /// production ready variadic generics. /// /// Has the same behaviour as ``Group`` when rendered directly. -public struct TupleView7< - View0: View, View1: View, View2: View, View3: View, View4: View, View5: View, View6: View -> { +public struct TupleView7 { public var view0: View0 public var view1: View1 public var view2: View2 @@ -380,10 +392,7 @@ public struct TupleView7< public var body = EmptyView() /// Wraps 7 child views in a single container view. - public init( - _ view0: View0, _ view1: View1, _ view2: View2, _ view3: View3, _ view4: View4, - _ view5: View5, _ view6: View6 - ) { + public init(_ view0: View0, _ view1: View1, _ view2: View2, _ view3: View3, _ view4: View4, _ view5: View5, _ view6: View6) { self.view0 = view0 self.view1 = view1 self.view2 = view2 @@ -432,10 +441,7 @@ extension TupleView7: TupleView { /// production ready variadic generics. /// /// Has the same behaviour as ``Group`` when rendered directly. -public struct TupleView8< - View0: View, View1: View, View2: View, View3: View, View4: View, View5: View, View6: View, - View7: View -> { +public struct TupleView8 { public var view0: View0 public var view1: View1 public var view2: View2 @@ -448,10 +454,7 @@ public struct TupleView8< public var body = EmptyView() /// Wraps 8 child views in a single container view. - public init( - _ view0: View0, _ view1: View1, _ view2: View2, _ view3: View3, _ view4: View4, - _ view5: View5, _ view6: View6, _ view7: View7 - ) { + public init(_ view0: View0, _ view1: View1, _ view2: View2, _ view3: View3, _ view4: View4, _ view5: View5, _ view6: View6, _ view7: View7) { self.view0 = view0 self.view1 = view1 self.view2 = view2 @@ -502,10 +505,7 @@ extension TupleView8: TupleView { /// production ready variadic generics. /// /// Has the same behaviour as ``Group`` when rendered directly. -public struct TupleView9< - View0: View, View1: View, View2: View, View3: View, View4: View, View5: View, View6: View, - View7: View, View8: View -> { +public struct TupleView9 { public var view0: View0 public var view1: View1 public var view2: View2 @@ -519,10 +519,7 @@ public struct TupleView9< public var body = EmptyView() /// Wraps 9 child views in a single container view. - public init( - _ view0: View0, _ view1: View1, _ view2: View2, _ view3: View3, _ view4: View4, - _ view5: View5, _ view6: View6, _ view7: View7, _ view8: View8 - ) { + public init(_ view0: View0, _ view1: View1, _ view2: View2, _ view3: View3, _ view4: View4, _ view5: View5, _ view6: View6, _ view7: View7, _ view8: View8) { self.view0 = view0 self.view1 = view1 self.view2 = view2 @@ -540,9 +537,7 @@ extension TupleView9: View { } extension TupleView9: TupleView { - typealias Children = TupleViewChildren9< - View0, View1, View2, View3, View4, View5, View6, View7, View8 - > + typealias Children = TupleViewChildren9 func children( backend: Backend, @@ -577,10 +572,7 @@ extension TupleView9: TupleView { /// production ready variadic generics. /// /// Has the same behaviour as ``Group`` when rendered directly. -public struct TupleView10< - View0: View, View1: View, View2: View, View3: View, View4: View, View5: View, View6: View, - View7: View, View8: View, View9: View -> { +public struct TupleView10 { public var view0: View0 public var view1: View1 public var view2: View2 @@ -595,10 +587,7 @@ public struct TupleView10< public var body = EmptyView() /// Wraps 10 child views in a single container view. - public init( - _ view0: View0, _ view1: View1, _ view2: View2, _ view3: View3, _ view4: View4, - _ view5: View5, _ view6: View6, _ view7: View7, _ view8: View8, _ view9: View9 - ) { + public init(_ view0: View0, _ view1: View1, _ view2: View2, _ view3: View3, _ view4: View4, _ view5: View5, _ view6: View6, _ view7: View7, _ view8: View8, _ view9: View9) { self.view0 = view0 self.view1 = view1 self.view2 = view2 @@ -617,9 +606,7 @@ extension TupleView10: View { } extension TupleView10: TupleView { - typealias Children = TupleViewChildren10< - View0, View1, View2, View3, View4, View5, View6, View7, View8, View9 - > + typealias Children = TupleViewChildren10 func children( backend: Backend, @@ -655,10 +642,7 @@ extension TupleView10: TupleView { /// production ready variadic generics. /// /// Has the same behaviour as ``Group`` when rendered directly. -public struct TupleView11< - View0: View, View1: View, View2: View, View3: View, View4: View, View5: View, View6: View, - View7: View, View8: View, View9: View, View10: View -> { +public struct TupleView11 { public var view0: View0 public var view1: View1 public var view2: View2 @@ -674,11 +658,7 @@ public struct TupleView11< public var body = EmptyView() /// Wraps 11 child views in a single container view. - public init( - _ view0: View0, _ view1: View1, _ view2: View2, _ view3: View3, _ view4: View4, - _ view5: View5, _ view6: View6, _ view7: View7, _ view8: View8, _ view9: View9, - _ view10: View10 - ) { + public init(_ view0: View0, _ view1: View1, _ view2: View2, _ view3: View3, _ view4: View4, _ view5: View5, _ view6: View6, _ view7: View7, _ view8: View8, _ view9: View9, _ view10: View10) { self.view0 = view0 self.view1 = view1 self.view2 = view2 @@ -698,9 +678,7 @@ extension TupleView11: View { } extension TupleView11: TupleView { - typealias Children = TupleViewChildren11< - View0, View1, View2, View3, View4, View5, View6, View7, View8, View9, View10 - > + typealias Children = TupleViewChildren11 func children( backend: Backend, @@ -737,10 +715,7 @@ extension TupleView11: TupleView { /// production ready variadic generics. /// /// Has the same behaviour as ``Group`` when rendered directly. -public struct TupleView12< - View0: View, View1: View, View2: View, View3: View, View4: View, View5: View, View6: View, - View7: View, View8: View, View9: View, View10: View, View11: View -> { +public struct TupleView12 { public var view0: View0 public var view1: View1 public var view2: View2 @@ -757,11 +732,7 @@ public struct TupleView12< public var body = EmptyView() /// Wraps 12 child views in a single container view. - public init( - _ view0: View0, _ view1: View1, _ view2: View2, _ view3: View3, _ view4: View4, - _ view5: View5, _ view6: View6, _ view7: View7, _ view8: View8, _ view9: View9, - _ view10: View10, _ view11: View11 - ) { + public init(_ view0: View0, _ view1: View1, _ view2: View2, _ view3: View3, _ view4: View4, _ view5: View5, _ view6: View6, _ view7: View7, _ view8: View8, _ view9: View9, _ view10: View10, _ view11: View11) { self.view0 = view0 self.view1 = view1 self.view2 = view2 @@ -782,9 +753,7 @@ extension TupleView12: View { } extension TupleView12: TupleView { - typealias Children = TupleViewChildren12< - View0, View1, View2, View3, View4, View5, View6, View7, View8, View9, View10, View11 - > + typealias Children = TupleViewChildren12 func children( backend: Backend, @@ -822,10 +791,7 @@ extension TupleView12: TupleView { /// production ready variadic generics. /// /// Has the same behaviour as ``Group`` when rendered directly. -public struct TupleView13< - View0: View, View1: View, View2: View, View3: View, View4: View, View5: View, View6: View, - View7: View, View8: View, View9: View, View10: View, View11: View, View12: View -> { +public struct TupleView13 { public var view0: View0 public var view1: View1 public var view2: View2 @@ -843,11 +809,7 @@ public struct TupleView13< public var body = EmptyView() /// Wraps 13 child views in a single container view. - public init( - _ view0: View0, _ view1: View1, _ view2: View2, _ view3: View3, _ view4: View4, - _ view5: View5, _ view6: View6, _ view7: View7, _ view8: View8, _ view9: View9, - _ view10: View10, _ view11: View11, _ view12: View12 - ) { + public init(_ view0: View0, _ view1: View1, _ view2: View2, _ view3: View3, _ view4: View4, _ view5: View5, _ view6: View6, _ view7: View7, _ view8: View8, _ view9: View9, _ view10: View10, _ view11: View11, _ view12: View12) { self.view0 = view0 self.view1 = view1 self.view2 = view2 @@ -869,9 +831,7 @@ extension TupleView13: View { } extension TupleView13: TupleView { - typealias Children = TupleViewChildren13< - View0, View1, View2, View3, View4, View5, View6, View7, View8, View9, View10, View11, View12 - > + typealias Children = TupleViewChildren13 func children( backend: Backend, @@ -879,8 +839,7 @@ extension TupleView13: TupleView { environment: EnvironmentValues ) -> Children { return Children( - view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11, - view12, + view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11, view12, backend: backend, snapshots: snapshots, environment: environment ) } @@ -911,10 +870,7 @@ extension TupleView13: TupleView { /// production ready variadic generics. /// /// Has the same behaviour as ``Group`` when rendered directly. -public struct TupleView14< - View0: View, View1: View, View2: View, View3: View, View4: View, View5: View, View6: View, - View7: View, View8: View, View9: View, View10: View, View11: View, View12: View, View13: View -> { +public struct TupleView14 { public var view0: View0 public var view1: View1 public var view2: View2 @@ -933,11 +889,7 @@ public struct TupleView14< public var body = EmptyView() /// Wraps 14 child views in a single container view. - public init( - _ view0: View0, _ view1: View1, _ view2: View2, _ view3: View3, _ view4: View4, - _ view5: View5, _ view6: View6, _ view7: View7, _ view8: View8, _ view9: View9, - _ view10: View10, _ view11: View11, _ view12: View12, _ view13: View13 - ) { + public init(_ view0: View0, _ view1: View1, _ view2: View2, _ view3: View3, _ view4: View4, _ view5: View5, _ view6: View6, _ view7: View7, _ view8: View8, _ view9: View9, _ view10: View10, _ view11: View11, _ view12: View12, _ view13: View13) { self.view0 = view0 self.view1 = view1 self.view2 = view2 @@ -960,10 +912,7 @@ extension TupleView14: View { } extension TupleView14: TupleView { - typealias Children = TupleViewChildren14< - View0, View1, View2, View3, View4, View5, View6, View7, View8, View9, View10, View11, - View12, View13 - > + typealias Children = TupleViewChildren14 func children( backend: Backend, @@ -971,8 +920,7 @@ extension TupleView14: TupleView { environment: EnvironmentValues ) -> Children { return Children( - view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11, - view12, view13, + view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11, view12, view13, backend: backend, snapshots: snapshots, environment: environment ) } @@ -1004,11 +952,7 @@ extension TupleView14: TupleView { /// production ready variadic generics. /// /// Has the same behaviour as ``Group`` when rendered directly. -public struct TupleView15< - View0: View, View1: View, View2: View, View3: View, View4: View, View5: View, View6: View, - View7: View, View8: View, View9: View, View10: View, View11: View, View12: View, View13: View, - View14: View -> { +public struct TupleView15 { public var view0: View0 public var view1: View1 public var view2: View2 @@ -1028,11 +972,7 @@ public struct TupleView15< public var body = EmptyView() /// Wraps 15 child views in a single container view. - public init( - _ view0: View0, _ view1: View1, _ view2: View2, _ view3: View3, _ view4: View4, - _ view5: View5, _ view6: View6, _ view7: View7, _ view8: View8, _ view9: View9, - _ view10: View10, _ view11: View11, _ view12: View12, _ view13: View13, _ view14: View14 - ) { + public init(_ view0: View0, _ view1: View1, _ view2: View2, _ view3: View3, _ view4: View4, _ view5: View5, _ view6: View6, _ view7: View7, _ view8: View8, _ view9: View9, _ view10: View10, _ view11: View11, _ view12: View12, _ view13: View13, _ view14: View14) { self.view0 = view0 self.view1 = view1 self.view2 = view2 @@ -1056,10 +996,7 @@ extension TupleView15: View { } extension TupleView15: TupleView { - typealias Children = TupleViewChildren15< - View0, View1, View2, View3, View4, View5, View6, View7, View8, View9, View10, View11, - View12, View13, View14 - > + typealias Children = TupleViewChildren15 func children( backend: Backend, @@ -1067,8 +1004,7 @@ extension TupleView15: TupleView { environment: EnvironmentValues ) -> Children { return Children( - view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11, - view12, view13, view14, + view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11, view12, view13, view14, backend: backend, snapshots: snapshots, environment: environment ) } @@ -1101,11 +1037,7 @@ extension TupleView15: TupleView { /// production ready variadic generics. /// /// Has the same behaviour as ``Group`` when rendered directly. -public struct TupleView16< - View0: View, View1: View, View2: View, View3: View, View4: View, View5: View, View6: View, - View7: View, View8: View, View9: View, View10: View, View11: View, View12: View, View13: View, - View14: View, View15: View -> { +public struct TupleView16 { public var view0: View0 public var view1: View1 public var view2: View2 @@ -1126,12 +1058,7 @@ public struct TupleView16< public var body = EmptyView() /// Wraps 16 child views in a single container view. - public init( - _ view0: View0, _ view1: View1, _ view2: View2, _ view3: View3, _ view4: View4, - _ view5: View5, _ view6: View6, _ view7: View7, _ view8: View8, _ view9: View9, - _ view10: View10, _ view11: View11, _ view12: View12, _ view13: View13, _ view14: View14, - _ view15: View15 - ) { + public init(_ view0: View0, _ view1: View1, _ view2: View2, _ view3: View3, _ view4: View4, _ view5: View5, _ view6: View6, _ view7: View7, _ view8: View8, _ view9: View9, _ view10: View10, _ view11: View11, _ view12: View12, _ view13: View13, _ view14: View14, _ view15: View15) { self.view0 = view0 self.view1 = view1 self.view2 = view2 @@ -1156,10 +1083,7 @@ extension TupleView16: View { } extension TupleView16: TupleView { - typealias Children = TupleViewChildren16< - View0, View1, View2, View3, View4, View5, View6, View7, View8, View9, View10, View11, - View12, View13, View14, View15 - > + typealias Children = TupleViewChildren16 func children( backend: Backend, @@ -1167,8 +1091,7 @@ extension TupleView16: TupleView { environment: EnvironmentValues ) -> Children { return Children( - view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11, - view12, view13, view14, view15, + view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11, view12, view13, view14, view15, backend: backend, snapshots: snapshots, environment: environment ) } @@ -1202,11 +1125,7 @@ extension TupleView16: TupleView { /// production ready variadic generics. /// /// Has the same behaviour as ``Group`` when rendered directly. -public struct TupleView17< - View0: View, View1: View, View2: View, View3: View, View4: View, View5: View, View6: View, - View7: View, View8: View, View9: View, View10: View, View11: View, View12: View, View13: View, - View14: View, View15: View, View16: View -> { +public struct TupleView17 { public var view0: View0 public var view1: View1 public var view2: View2 @@ -1228,12 +1147,7 @@ public struct TupleView17< public var body = EmptyView() /// Wraps 17 child views in a single container view. - public init( - _ view0: View0, _ view1: View1, _ view2: View2, _ view3: View3, _ view4: View4, - _ view5: View5, _ view6: View6, _ view7: View7, _ view8: View8, _ view9: View9, - _ view10: View10, _ view11: View11, _ view12: View12, _ view13: View13, _ view14: View14, - _ view15: View15, _ view16: View16 - ) { + public init(_ view0: View0, _ view1: View1, _ view2: View2, _ view3: View3, _ view4: View4, _ view5: View5, _ view6: View6, _ view7: View7, _ view8: View8, _ view9: View9, _ view10: View10, _ view11: View11, _ view12: View12, _ view13: View13, _ view14: View14, _ view15: View15, _ view16: View16) { self.view0 = view0 self.view1 = view1 self.view2 = view2 @@ -1259,10 +1173,7 @@ extension TupleView17: View { } extension TupleView17: TupleView { - typealias Children = TupleViewChildren17< - View0, View1, View2, View3, View4, View5, View6, View7, View8, View9, View10, View11, - View12, View13, View14, View15, View16 - > + typealias Children = TupleViewChildren17 func children( backend: Backend, @@ -1270,8 +1181,7 @@ extension TupleView17: TupleView { environment: EnvironmentValues ) -> Children { return Children( - view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11, - view12, view13, view14, view15, view16, + view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11, view12, view13, view14, view15, view16, backend: backend, snapshots: snapshots, environment: environment ) } @@ -1306,11 +1216,7 @@ extension TupleView17: TupleView { /// production ready variadic generics. /// /// Has the same behaviour as ``Group`` when rendered directly. -public struct TupleView18< - View0: View, View1: View, View2: View, View3: View, View4: View, View5: View, View6: View, - View7: View, View8: View, View9: View, View10: View, View11: View, View12: View, View13: View, - View14: View, View15: View, View16: View, View17: View -> { +public struct TupleView18 { public var view0: View0 public var view1: View1 public var view2: View2 @@ -1333,12 +1239,7 @@ public struct TupleView18< public var body = EmptyView() /// Wraps 18 child views in a single container view. - public init( - _ view0: View0, _ view1: View1, _ view2: View2, _ view3: View3, _ view4: View4, - _ view5: View5, _ view6: View6, _ view7: View7, _ view8: View8, _ view9: View9, - _ view10: View10, _ view11: View11, _ view12: View12, _ view13: View13, _ view14: View14, - _ view15: View15, _ view16: View16, _ view17: View17 - ) { + public init(_ view0: View0, _ view1: View1, _ view2: View2, _ view3: View3, _ view4: View4, _ view5: View5, _ view6: View6, _ view7: View7, _ view8: View8, _ view9: View9, _ view10: View10, _ view11: View11, _ view12: View12, _ view13: View13, _ view14: View14, _ view15: View15, _ view16: View16, _ view17: View17) { self.view0 = view0 self.view1 = view1 self.view2 = view2 @@ -1365,10 +1266,7 @@ extension TupleView18: View { } extension TupleView18: TupleView { - typealias Children = TupleViewChildren18< - View0, View1, View2, View3, View4, View5, View6, View7, View8, View9, View10, View11, - View12, View13, View14, View15, View16, View17 - > + typealias Children = TupleViewChildren18 func children( backend: Backend, @@ -1376,8 +1274,7 @@ extension TupleView18: TupleView { environment: EnvironmentValues ) -> Children { return Children( - view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11, - view12, view13, view14, view15, view16, view17, + view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11, view12, view13, view14, view15, view16, view17, backend: backend, snapshots: snapshots, environment: environment ) } @@ -1413,11 +1310,7 @@ extension TupleView18: TupleView { /// production ready variadic generics. /// /// Has the same behaviour as ``Group`` when rendered directly. -public struct TupleView19< - View0: View, View1: View, View2: View, View3: View, View4: View, View5: View, View6: View, - View7: View, View8: View, View9: View, View10: View, View11: View, View12: View, View13: View, - View14: View, View15: View, View16: View, View17: View, View18: View -> { +public struct TupleView19 { public var view0: View0 public var view1: View1 public var view2: View2 @@ -1441,12 +1334,7 @@ public struct TupleView19< public var body = EmptyView() /// Wraps 19 child views in a single container view. - public init( - _ view0: View0, _ view1: View1, _ view2: View2, _ view3: View3, _ view4: View4, - _ view5: View5, _ view6: View6, _ view7: View7, _ view8: View8, _ view9: View9, - _ view10: View10, _ view11: View11, _ view12: View12, _ view13: View13, _ view14: View14, - _ view15: View15, _ view16: View16, _ view17: View17, _ view18: View18 - ) { + public init(_ view0: View0, _ view1: View1, _ view2: View2, _ view3: View3, _ view4: View4, _ view5: View5, _ view6: View6, _ view7: View7, _ view8: View8, _ view9: View9, _ view10: View10, _ view11: View11, _ view12: View12, _ view13: View13, _ view14: View14, _ view15: View15, _ view16: View16, _ view17: View17, _ view18: View18) { self.view0 = view0 self.view1 = view1 self.view2 = view2 @@ -1474,10 +1362,7 @@ extension TupleView19: View { } extension TupleView19: TupleView { - typealias Children = TupleViewChildren19< - View0, View1, View2, View3, View4, View5, View6, View7, View8, View9, View10, View11, - View12, View13, View14, View15, View16, View17, View18 - > + typealias Children = TupleViewChildren19 func children( backend: Backend, @@ -1485,8 +1370,7 @@ extension TupleView19: TupleView { environment: EnvironmentValues ) -> Children { return Children( - view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11, - view12, view13, view14, view15, view16, view17, view18, + view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11, view12, view13, view14, view15, view16, view17, view18, backend: backend, snapshots: snapshots, environment: environment ) } @@ -1523,11 +1407,7 @@ extension TupleView19: TupleView { /// production ready variadic generics. /// /// Has the same behaviour as ``Group`` when rendered directly. -public struct TupleView20< - View0: View, View1: View, View2: View, View3: View, View4: View, View5: View, View6: View, - View7: View, View8: View, View9: View, View10: View, View11: View, View12: View, View13: View, - View14: View, View15: View, View16: View, View17: View, View18: View, View19: View -> { +public struct TupleView20 { public var view0: View0 public var view1: View1 public var view2: View2 @@ -1552,12 +1432,7 @@ public struct TupleView20< public var body = EmptyView() /// Wraps 20 child views in a single container view. - public init( - _ view0: View0, _ view1: View1, _ view2: View2, _ view3: View3, _ view4: View4, - _ view5: View5, _ view6: View6, _ view7: View7, _ view8: View8, _ view9: View9, - _ view10: View10, _ view11: View11, _ view12: View12, _ view13: View13, _ view14: View14, - _ view15: View15, _ view16: View16, _ view17: View17, _ view18: View18, _ view19: View19 - ) { + public init(_ view0: View0, _ view1: View1, _ view2: View2, _ view3: View3, _ view4: View4, _ view5: View5, _ view6: View6, _ view7: View7, _ view8: View8, _ view9: View9, _ view10: View10, _ view11: View11, _ view12: View12, _ view13: View13, _ view14: View14, _ view15: View15, _ view16: View16, _ view17: View17, _ view18: View18, _ view19: View19) { self.view0 = view0 self.view1 = view1 self.view2 = view2 @@ -1586,10 +1461,7 @@ extension TupleView20: View { } extension TupleView20: TupleView { - typealias Children = TupleViewChildren20< - View0, View1, View2, View3, View4, View5, View6, View7, View8, View9, View10, View11, - View12, View13, View14, View15, View16, View17, View18, View19 - > + typealias Children = TupleViewChildren20 func children( backend: Backend, @@ -1597,8 +1469,7 @@ extension TupleView20: TupleView { environment: EnvironmentValues ) -> Children { return Children( - view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11, - view12, view13, view14, view15, view16, view17, view18, view19, + view0, view1, view2, view3, view4, view5, view6, view7, view8, view9, view10, view11, view12, view13, view14, view15, view16, view17, view18, view19, backend: backend, snapshots: snapshots, environment: environment ) } diff --git a/Sources/SwiftCrossUI/Views/TupleView.swift.gyb b/Sources/SwiftCrossUI/Views/TupleView.swift.gyb index 38396af12d..68bf66cec5 100644 --- a/Sources/SwiftCrossUI/Views/TupleView.swift.gyb +++ b/Sources/SwiftCrossUI/Views/TupleView.swift.gyb @@ -10,14 +10,16 @@ private func layoutableChild( view: V ) -> LayoutSystem.LayoutableChild { LayoutSystem.LayoutableChild( - update: { proposedSize, environment, dryRun in - node.update( + computeLayout: { proposedSize, environment in + node.computeLayout( with: view, proposedSize: proposedSize, - environment: environment, - dryRun: dryRun + environment: environment ) }, + commit: { + node.commit() + }, tag: "\(type(of: view))" ) } @@ -37,22 +39,38 @@ extension TupleView { } @MainActor - func update( + func computeLayout( _ widget: Backend.Widget, children: Children, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { + backend: Backend + ) -> ViewLayoutResult { let group = Group(content: self) - return group.update( + return group.computeLayout( widget, children: children, proposedSize: proposedSize, environment: environment, - backend: backend, - dryRun: dryRun + backend: backend + ) + } + + @MainActor + func commit( + _ widget: Backend.Widget, + children: Children, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + let group = Group(content: self) + group.commit( + widget, + children: children, + layout: layout, + environment: environment, + backend: backend ) } } diff --git a/Sources/SwiftCrossUI/Views/TupleViewChildren.swift b/Sources/SwiftCrossUI/Views/TupleViewChildren.swift index acf995547a..55f5de387d 100644 --- a/Sources/SwiftCrossUI/Views/TupleViewChildren.swift +++ b/Sources/SwiftCrossUI/Views/TupleViewChildren.swift @@ -1,6 +1,17 @@ // This file was generated using gyb. Do not edit it directly. Edit // TupleViewChildren.swift.gyb instead. +struct StackLayoutCache { + var lastFlexibilityOrdering: [Int]? + var lastHiddenChildren: [Bool] = [] + var redistributeSpaceOnCommit = false +} + +protocol TupleViewChildren: ViewGraphNodeChildren { + @MainActor + var stackLayoutCache: StackLayoutCache { get nonmutating set } +} + /// A helper function to shorten node initialisations to a single line. This /// helps compress the generated code a bit and minimise the number of additions /// and deletions caused by updating the generator. @@ -19,19 +30,22 @@ private func node( ) } + /// A fixed-length strongly-typed collection of 1 child nodes. A counterpart to /// ``TupleView1``. -public struct TupleViewChildren1: ViewGraphNodeChildren { +public class TupleViewChildren1: TupleViewChildren { public var widgets: [AnyWidget] { return [child0.widget] } public var erasedNodes: [ErasedViewGraphNode] { return [ - ErasedViewGraphNode(wrapping: child0) + ErasedViewGraphNode(wrapping: child0), ] } + var stackLayoutCache = StackLayoutCache() + /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. public var child0: AnyViewGraphNode @@ -52,7 +66,7 @@ public struct TupleViewChildren1: ViewGraphNodeChildren { /// A fixed-length strongly-typed collection of 2 child nodes. A counterpart to /// ``TupleView2``. -public struct TupleViewChildren2: ViewGraphNodeChildren { +public class TupleViewChildren2: TupleViewChildren { public var widgets: [AnyWidget] { return [child0.widget, child1.widget] } @@ -64,6 +78,8 @@ public struct TupleViewChildren2: ViewGraphNodeChild ] } + var stackLayoutCache = StackLayoutCache() + /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. public var child0: AnyViewGraphNode /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. @@ -77,7 +93,7 @@ public struct TupleViewChildren2: ViewGraphNodeChild environment: EnvironmentValues ) { let viewTypeNames = [ - ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), + ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self) ] let snapshots = ViewGraphSnapshotter.match(snapshots ?? [], to: viewTypeNames) self.child0 = node(for: child0, backend, snapshots[0], environment) @@ -87,7 +103,7 @@ public struct TupleViewChildren2: ViewGraphNodeChild /// A fixed-length strongly-typed collection of 3 child nodes. A counterpart to /// ``TupleView3``. -public struct TupleViewChildren3: ViewGraphNodeChildren { +public class TupleViewChildren3: TupleViewChildren { public var widgets: [AnyWidget] { return [child0.widget, child1.widget, child2.widget] } @@ -100,6 +116,8 @@ public struct TupleViewChildren3: View ] } + var stackLayoutCache = StackLayoutCache() + /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. public var child0: AnyViewGraphNode /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. @@ -115,8 +133,7 @@ public struct TupleViewChildren3: View environment: EnvironmentValues ) { let viewTypeNames = [ - ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), - ViewGraphSnapshotter.name(of: Child2.self), + ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), ViewGraphSnapshotter.name(of: Child2.self) ] let snapshots = ViewGraphSnapshotter.match(snapshots ?? [], to: viewTypeNames) self.child0 = node(for: child0, backend, snapshots[0], environment) @@ -127,9 +144,7 @@ public struct TupleViewChildren3: View /// A fixed-length strongly-typed collection of 4 child nodes. A counterpart to /// ``TupleView4``. -public struct TupleViewChildren4: - ViewGraphNodeChildren -{ +public class TupleViewChildren4: TupleViewChildren { public var widgets: [AnyWidget] { return [child0.widget, child1.widget, child2.widget, child3.widget] } @@ -143,6 +158,8 @@ public struct TupleViewChildren4 /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. @@ -160,8 +177,7 @@ public struct TupleViewChildren4: ViewGraphNodeChildren { +public class TupleViewChildren5: TupleViewChildren { public var widgets: [AnyWidget] { return [child0.widget, child1.widget, child2.widget, child3.widget, child4.widget] } @@ -190,6 +204,8 @@ public struct TupleViewChildren5< ] } + var stackLayoutCache = StackLayoutCache() + /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. public var child0: AnyViewGraphNode /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. @@ -209,9 +225,7 @@ public struct TupleViewChildren5< environment: EnvironmentValues ) { let viewTypeNames = [ - ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), - ViewGraphSnapshotter.name(of: Child2.self), ViewGraphSnapshotter.name(of: Child3.self), - ViewGraphSnapshotter.name(of: Child4.self), + ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), ViewGraphSnapshotter.name(of: Child2.self), ViewGraphSnapshotter.name(of: Child3.self), ViewGraphSnapshotter.name(of: Child4.self) ] let snapshots = ViewGraphSnapshotter.match(snapshots ?? [], to: viewTypeNames) self.child0 = node(for: child0, backend, snapshots[0], environment) @@ -224,14 +238,9 @@ public struct TupleViewChildren5< /// A fixed-length strongly-typed collection of 6 child nodes. A counterpart to /// ``TupleView6``. -public struct TupleViewChildren6< - Child0: View, Child1: View, Child2: View, Child3: View, Child4: View, Child5: View ->: ViewGraphNodeChildren { +public class TupleViewChildren6: TupleViewChildren { public var widgets: [AnyWidget] { - return [ - child0.widget, child1.widget, child2.widget, child3.widget, child4.widget, - child5.widget, - ] + return [child0.widget, child1.widget, child2.widget, child3.widget, child4.widget, child5.widget] } public var erasedNodes: [ErasedViewGraphNode] { @@ -245,6 +254,8 @@ public struct TupleViewChildren6< ] } + var stackLayoutCache = StackLayoutCache() + /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. public var child0: AnyViewGraphNode /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. @@ -260,16 +271,13 @@ public struct TupleViewChildren6< /// Creates the nodes for 6 child views. public init( - _ child0: Child0, _ child1: Child1, _ child2: Child2, _ child3: Child3, _ child4: Child4, - _ child5: Child5, + _ child0: Child0, _ child1: Child1, _ child2: Child2, _ child3: Child3, _ child4: Child4, _ child5: Child5, backend: Backend, snapshots: [ViewGraphSnapshotter.NodeSnapshot]?, environment: EnvironmentValues ) { let viewTypeNames = [ - ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), - ViewGraphSnapshotter.name(of: Child2.self), ViewGraphSnapshotter.name(of: Child3.self), - ViewGraphSnapshotter.name(of: Child4.self), ViewGraphSnapshotter.name(of: Child5.self), + ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), ViewGraphSnapshotter.name(of: Child2.self), ViewGraphSnapshotter.name(of: Child3.self), ViewGraphSnapshotter.name(of: Child4.self), ViewGraphSnapshotter.name(of: Child5.self) ] let snapshots = ViewGraphSnapshotter.match(snapshots ?? [], to: viewTypeNames) self.child0 = node(for: child0, backend, snapshots[0], environment) @@ -283,14 +291,9 @@ public struct TupleViewChildren6< /// A fixed-length strongly-typed collection of 7 child nodes. A counterpart to /// ``TupleView7``. -public struct TupleViewChildren7< - Child0: View, Child1: View, Child2: View, Child3: View, Child4: View, Child5: View, Child6: View ->: ViewGraphNodeChildren { +public class TupleViewChildren7: TupleViewChildren { public var widgets: [AnyWidget] { - return [ - child0.widget, child1.widget, child2.widget, child3.widget, child4.widget, - child5.widget, child6.widget, - ] + return [child0.widget, child1.widget, child2.widget, child3.widget, child4.widget, child5.widget, child6.widget] } public var erasedNodes: [ErasedViewGraphNode] { @@ -305,6 +308,8 @@ public struct TupleViewChildren7< ] } + var stackLayoutCache = StackLayoutCache() + /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. public var child0: AnyViewGraphNode /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. @@ -322,17 +327,13 @@ public struct TupleViewChildren7< /// Creates the nodes for 7 child views. public init( - _ child0: Child0, _ child1: Child1, _ child2: Child2, _ child3: Child3, _ child4: Child4, - _ child5: Child5, _ child6: Child6, + _ child0: Child0, _ child1: Child1, _ child2: Child2, _ child3: Child3, _ child4: Child4, _ child5: Child5, _ child6: Child6, backend: Backend, snapshots: [ViewGraphSnapshotter.NodeSnapshot]?, environment: EnvironmentValues ) { let viewTypeNames = [ - ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), - ViewGraphSnapshotter.name(of: Child2.self), ViewGraphSnapshotter.name(of: Child3.self), - ViewGraphSnapshotter.name(of: Child4.self), ViewGraphSnapshotter.name(of: Child5.self), - ViewGraphSnapshotter.name(of: Child6.self), + ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), ViewGraphSnapshotter.name(of: Child2.self), ViewGraphSnapshotter.name(of: Child3.self), ViewGraphSnapshotter.name(of: Child4.self), ViewGraphSnapshotter.name(of: Child5.self), ViewGraphSnapshotter.name(of: Child6.self) ] let snapshots = ViewGraphSnapshotter.match(snapshots ?? [], to: viewTypeNames) self.child0 = node(for: child0, backend, snapshots[0], environment) @@ -347,15 +348,9 @@ public struct TupleViewChildren7< /// A fixed-length strongly-typed collection of 8 child nodes. A counterpart to /// ``TupleView8``. -public struct TupleViewChildren8< - Child0: View, Child1: View, Child2: View, Child3: View, Child4: View, Child5: View, - Child6: View, Child7: View ->: ViewGraphNodeChildren { +public class TupleViewChildren8: TupleViewChildren { public var widgets: [AnyWidget] { - return [ - child0.widget, child1.widget, child2.widget, child3.widget, child4.widget, - child5.widget, child6.widget, child7.widget, - ] + return [child0.widget, child1.widget, child2.widget, child3.widget, child4.widget, child5.widget, child6.widget, child7.widget] } public var erasedNodes: [ErasedViewGraphNode] { @@ -371,6 +366,8 @@ public struct TupleViewChildren8< ] } + var stackLayoutCache = StackLayoutCache() + /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. public var child0: AnyViewGraphNode /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. @@ -390,17 +387,13 @@ public struct TupleViewChildren8< /// Creates the nodes for 8 child views. public init( - _ child0: Child0, _ child1: Child1, _ child2: Child2, _ child3: Child3, _ child4: Child4, - _ child5: Child5, _ child6: Child6, _ child7: Child7, + _ child0: Child0, _ child1: Child1, _ child2: Child2, _ child3: Child3, _ child4: Child4, _ child5: Child5, _ child6: Child6, _ child7: Child7, backend: Backend, snapshots: [ViewGraphSnapshotter.NodeSnapshot]?, environment: EnvironmentValues ) { let viewTypeNames = [ - ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), - ViewGraphSnapshotter.name(of: Child2.self), ViewGraphSnapshotter.name(of: Child3.self), - ViewGraphSnapshotter.name(of: Child4.self), ViewGraphSnapshotter.name(of: Child5.self), - ViewGraphSnapshotter.name(of: Child6.self), ViewGraphSnapshotter.name(of: Child7.self), + ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), ViewGraphSnapshotter.name(of: Child2.self), ViewGraphSnapshotter.name(of: Child3.self), ViewGraphSnapshotter.name(of: Child4.self), ViewGraphSnapshotter.name(of: Child5.self), ViewGraphSnapshotter.name(of: Child6.self), ViewGraphSnapshotter.name(of: Child7.self) ] let snapshots = ViewGraphSnapshotter.match(snapshots ?? [], to: viewTypeNames) self.child0 = node(for: child0, backend, snapshots[0], environment) @@ -416,15 +409,9 @@ public struct TupleViewChildren8< /// A fixed-length strongly-typed collection of 9 child nodes. A counterpart to /// ``TupleView9``. -public struct TupleViewChildren9< - Child0: View, Child1: View, Child2: View, Child3: View, Child4: View, Child5: View, - Child6: View, Child7: View, Child8: View ->: ViewGraphNodeChildren { +public class TupleViewChildren9: TupleViewChildren { public var widgets: [AnyWidget] { - return [ - child0.widget, child1.widget, child2.widget, child3.widget, child4.widget, - child5.widget, child6.widget, child7.widget, child8.widget, - ] + return [child0.widget, child1.widget, child2.widget, child3.widget, child4.widget, child5.widget, child6.widget, child7.widget, child8.widget] } public var erasedNodes: [ErasedViewGraphNode] { @@ -441,6 +428,8 @@ public struct TupleViewChildren9< ] } + var stackLayoutCache = StackLayoutCache() + /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. public var child0: AnyViewGraphNode /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. @@ -462,18 +451,13 @@ public struct TupleViewChildren9< /// Creates the nodes for 9 child views. public init( - _ child0: Child0, _ child1: Child1, _ child2: Child2, _ child3: Child3, _ child4: Child4, - _ child5: Child5, _ child6: Child6, _ child7: Child7, _ child8: Child8, + _ child0: Child0, _ child1: Child1, _ child2: Child2, _ child3: Child3, _ child4: Child4, _ child5: Child5, _ child6: Child6, _ child7: Child7, _ child8: Child8, backend: Backend, snapshots: [ViewGraphSnapshotter.NodeSnapshot]?, environment: EnvironmentValues ) { let viewTypeNames = [ - ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), - ViewGraphSnapshotter.name(of: Child2.self), ViewGraphSnapshotter.name(of: Child3.self), - ViewGraphSnapshotter.name(of: Child4.self), ViewGraphSnapshotter.name(of: Child5.self), - ViewGraphSnapshotter.name(of: Child6.self), ViewGraphSnapshotter.name(of: Child7.self), - ViewGraphSnapshotter.name(of: Child8.self), + ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), ViewGraphSnapshotter.name(of: Child2.self), ViewGraphSnapshotter.name(of: Child3.self), ViewGraphSnapshotter.name(of: Child4.self), ViewGraphSnapshotter.name(of: Child5.self), ViewGraphSnapshotter.name(of: Child6.self), ViewGraphSnapshotter.name(of: Child7.self), ViewGraphSnapshotter.name(of: Child8.self) ] let snapshots = ViewGraphSnapshotter.match(snapshots ?? [], to: viewTypeNames) self.child0 = node(for: child0, backend, snapshots[0], environment) @@ -490,15 +474,9 @@ public struct TupleViewChildren9< /// A fixed-length strongly-typed collection of 10 child nodes. A counterpart to /// ``TupleView10``. -public struct TupleViewChildren10< - Child0: View, Child1: View, Child2: View, Child3: View, Child4: View, Child5: View, - Child6: View, Child7: View, Child8: View, Child9: View ->: ViewGraphNodeChildren { +public class TupleViewChildren10: TupleViewChildren { public var widgets: [AnyWidget] { - return [ - child0.widget, child1.widget, child2.widget, child3.widget, child4.widget, - child5.widget, child6.widget, child7.widget, child8.widget, child9.widget, - ] + return [child0.widget, child1.widget, child2.widget, child3.widget, child4.widget, child5.widget, child6.widget, child7.widget, child8.widget, child9.widget] } public var erasedNodes: [ErasedViewGraphNode] { @@ -516,6 +494,8 @@ public struct TupleViewChildren10< ] } + var stackLayoutCache = StackLayoutCache() + /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. public var child0: AnyViewGraphNode /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. @@ -539,18 +519,13 @@ public struct TupleViewChildren10< /// Creates the nodes for 10 child views. public init( - _ child0: Child0, _ child1: Child1, _ child2: Child2, _ child3: Child3, _ child4: Child4, - _ child5: Child5, _ child6: Child6, _ child7: Child7, _ child8: Child8, _ child9: Child9, + _ child0: Child0, _ child1: Child1, _ child2: Child2, _ child3: Child3, _ child4: Child4, _ child5: Child5, _ child6: Child6, _ child7: Child7, _ child8: Child8, _ child9: Child9, backend: Backend, snapshots: [ViewGraphSnapshotter.NodeSnapshot]?, environment: EnvironmentValues ) { let viewTypeNames = [ - ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), - ViewGraphSnapshotter.name(of: Child2.self), ViewGraphSnapshotter.name(of: Child3.self), - ViewGraphSnapshotter.name(of: Child4.self), ViewGraphSnapshotter.name(of: Child5.self), - ViewGraphSnapshotter.name(of: Child6.self), ViewGraphSnapshotter.name(of: Child7.self), - ViewGraphSnapshotter.name(of: Child8.self), ViewGraphSnapshotter.name(of: Child9.self), + ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), ViewGraphSnapshotter.name(of: Child2.self), ViewGraphSnapshotter.name(of: Child3.self), ViewGraphSnapshotter.name(of: Child4.self), ViewGraphSnapshotter.name(of: Child5.self), ViewGraphSnapshotter.name(of: Child6.self), ViewGraphSnapshotter.name(of: Child7.self), ViewGraphSnapshotter.name(of: Child8.self), ViewGraphSnapshotter.name(of: Child9.self) ] let snapshots = ViewGraphSnapshotter.match(snapshots ?? [], to: viewTypeNames) self.child0 = node(for: child0, backend, snapshots[0], environment) @@ -568,16 +543,9 @@ public struct TupleViewChildren10< /// A fixed-length strongly-typed collection of 11 child nodes. A counterpart to /// ``TupleView11``. -public struct TupleViewChildren11< - Child0: View, Child1: View, Child2: View, Child3: View, Child4: View, Child5: View, - Child6: View, Child7: View, Child8: View, Child9: View, Child10: View ->: ViewGraphNodeChildren { +public class TupleViewChildren11: TupleViewChildren { public var widgets: [AnyWidget] { - return [ - child0.widget, child1.widget, child2.widget, child3.widget, child4.widget, - child5.widget, child6.widget, child7.widget, child8.widget, child9.widget, - child10.widget, - ] + return [child0.widget, child1.widget, child2.widget, child3.widget, child4.widget, child5.widget, child6.widget, child7.widget, child8.widget, child9.widget, child10.widget] } public var erasedNodes: [ErasedViewGraphNode] { @@ -596,6 +564,8 @@ public struct TupleViewChildren11< ] } + var stackLayoutCache = StackLayoutCache() + /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. public var child0: AnyViewGraphNode /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. @@ -621,20 +591,13 @@ public struct TupleViewChildren11< /// Creates the nodes for 11 child views. public init( - _ child0: Child0, _ child1: Child1, _ child2: Child2, _ child3: Child3, _ child4: Child4, - _ child5: Child5, _ child6: Child6, _ child7: Child7, _ child8: Child8, _ child9: Child9, - _ child10: Child10, + _ child0: Child0, _ child1: Child1, _ child2: Child2, _ child3: Child3, _ child4: Child4, _ child5: Child5, _ child6: Child6, _ child7: Child7, _ child8: Child8, _ child9: Child9, _ child10: Child10, backend: Backend, snapshots: [ViewGraphSnapshotter.NodeSnapshot]?, environment: EnvironmentValues ) { let viewTypeNames = [ - ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), - ViewGraphSnapshotter.name(of: Child2.self), ViewGraphSnapshotter.name(of: Child3.self), - ViewGraphSnapshotter.name(of: Child4.self), ViewGraphSnapshotter.name(of: Child5.self), - ViewGraphSnapshotter.name(of: Child6.self), ViewGraphSnapshotter.name(of: Child7.self), - ViewGraphSnapshotter.name(of: Child8.self), ViewGraphSnapshotter.name(of: Child9.self), - ViewGraphSnapshotter.name(of: Child10.self), + ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), ViewGraphSnapshotter.name(of: Child2.self), ViewGraphSnapshotter.name(of: Child3.self), ViewGraphSnapshotter.name(of: Child4.self), ViewGraphSnapshotter.name(of: Child5.self), ViewGraphSnapshotter.name(of: Child6.self), ViewGraphSnapshotter.name(of: Child7.self), ViewGraphSnapshotter.name(of: Child8.self), ViewGraphSnapshotter.name(of: Child9.self), ViewGraphSnapshotter.name(of: Child10.self) ] let snapshots = ViewGraphSnapshotter.match(snapshots ?? [], to: viewTypeNames) self.child0 = node(for: child0, backend, snapshots[0], environment) @@ -653,16 +616,9 @@ public struct TupleViewChildren11< /// A fixed-length strongly-typed collection of 12 child nodes. A counterpart to /// ``TupleView12``. -public struct TupleViewChildren12< - Child0: View, Child1: View, Child2: View, Child3: View, Child4: View, Child5: View, - Child6: View, Child7: View, Child8: View, Child9: View, Child10: View, Child11: View ->: ViewGraphNodeChildren { +public class TupleViewChildren12: TupleViewChildren { public var widgets: [AnyWidget] { - return [ - child0.widget, child1.widget, child2.widget, child3.widget, child4.widget, - child5.widget, child6.widget, child7.widget, child8.widget, child9.widget, - child10.widget, child11.widget, - ] + return [child0.widget, child1.widget, child2.widget, child3.widget, child4.widget, child5.widget, child6.widget, child7.widget, child8.widget, child9.widget, child10.widget, child11.widget] } public var erasedNodes: [ErasedViewGraphNode] { @@ -682,6 +638,8 @@ public struct TupleViewChildren12< ] } + var stackLayoutCache = StackLayoutCache() + /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. public var child0: AnyViewGraphNode /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. @@ -709,21 +667,13 @@ public struct TupleViewChildren12< /// Creates the nodes for 12 child views. public init( - _ child0: Child0, _ child1: Child1, _ child2: Child2, _ child3: Child3, _ child4: Child4, - _ child5: Child5, _ child6: Child6, _ child7: Child7, _ child8: Child8, _ child9: Child9, - _ child10: Child10, _ child11: Child11, + _ child0: Child0, _ child1: Child1, _ child2: Child2, _ child3: Child3, _ child4: Child4, _ child5: Child5, _ child6: Child6, _ child7: Child7, _ child8: Child8, _ child9: Child9, _ child10: Child10, _ child11: Child11, backend: Backend, snapshots: [ViewGraphSnapshotter.NodeSnapshot]?, environment: EnvironmentValues ) { let viewTypeNames = [ - ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), - ViewGraphSnapshotter.name(of: Child2.self), ViewGraphSnapshotter.name(of: Child3.self), - ViewGraphSnapshotter.name(of: Child4.self), ViewGraphSnapshotter.name(of: Child5.self), - ViewGraphSnapshotter.name(of: Child6.self), ViewGraphSnapshotter.name(of: Child7.self), - ViewGraphSnapshotter.name(of: Child8.self), ViewGraphSnapshotter.name(of: Child9.self), - ViewGraphSnapshotter.name(of: Child10.self), - ViewGraphSnapshotter.name(of: Child11.self), + ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), ViewGraphSnapshotter.name(of: Child2.self), ViewGraphSnapshotter.name(of: Child3.self), ViewGraphSnapshotter.name(of: Child4.self), ViewGraphSnapshotter.name(of: Child5.self), ViewGraphSnapshotter.name(of: Child6.self), ViewGraphSnapshotter.name(of: Child7.self), ViewGraphSnapshotter.name(of: Child8.self), ViewGraphSnapshotter.name(of: Child9.self), ViewGraphSnapshotter.name(of: Child10.self), ViewGraphSnapshotter.name(of: Child11.self) ] let snapshots = ViewGraphSnapshotter.match(snapshots ?? [], to: viewTypeNames) self.child0 = node(for: child0, backend, snapshots[0], environment) @@ -743,17 +693,9 @@ public struct TupleViewChildren12< /// A fixed-length strongly-typed collection of 13 child nodes. A counterpart to /// ``TupleView13``. -public struct TupleViewChildren13< - Child0: View, Child1: View, Child2: View, Child3: View, Child4: View, Child5: View, - Child6: View, Child7: View, Child8: View, Child9: View, Child10: View, Child11: View, - Child12: View ->: ViewGraphNodeChildren { +public class TupleViewChildren13: TupleViewChildren { public var widgets: [AnyWidget] { - return [ - child0.widget, child1.widget, child2.widget, child3.widget, child4.widget, - child5.widget, child6.widget, child7.widget, child8.widget, child9.widget, - child10.widget, child11.widget, child12.widget, - ] + return [child0.widget, child1.widget, child2.widget, child3.widget, child4.widget, child5.widget, child6.widget, child7.widget, child8.widget, child9.widget, child10.widget, child11.widget, child12.widget] } public var erasedNodes: [ErasedViewGraphNode] { @@ -774,6 +716,8 @@ public struct TupleViewChildren13< ] } + var stackLayoutCache = StackLayoutCache() + /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. public var child0: AnyViewGraphNode /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. @@ -803,22 +747,13 @@ public struct TupleViewChildren13< /// Creates the nodes for 13 child views. public init( - _ child0: Child0, _ child1: Child1, _ child2: Child2, _ child3: Child3, _ child4: Child4, - _ child5: Child5, _ child6: Child6, _ child7: Child7, _ child8: Child8, _ child9: Child9, - _ child10: Child10, _ child11: Child11, _ child12: Child12, + _ child0: Child0, _ child1: Child1, _ child2: Child2, _ child3: Child3, _ child4: Child4, _ child5: Child5, _ child6: Child6, _ child7: Child7, _ child8: Child8, _ child9: Child9, _ child10: Child10, _ child11: Child11, _ child12: Child12, backend: Backend, snapshots: [ViewGraphSnapshotter.NodeSnapshot]?, environment: EnvironmentValues ) { let viewTypeNames = [ - ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), - ViewGraphSnapshotter.name(of: Child2.self), ViewGraphSnapshotter.name(of: Child3.self), - ViewGraphSnapshotter.name(of: Child4.self), ViewGraphSnapshotter.name(of: Child5.self), - ViewGraphSnapshotter.name(of: Child6.self), ViewGraphSnapshotter.name(of: Child7.self), - ViewGraphSnapshotter.name(of: Child8.self), ViewGraphSnapshotter.name(of: Child9.self), - ViewGraphSnapshotter.name(of: Child10.self), - ViewGraphSnapshotter.name(of: Child11.self), - ViewGraphSnapshotter.name(of: Child12.self), + ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), ViewGraphSnapshotter.name(of: Child2.self), ViewGraphSnapshotter.name(of: Child3.self), ViewGraphSnapshotter.name(of: Child4.self), ViewGraphSnapshotter.name(of: Child5.self), ViewGraphSnapshotter.name(of: Child6.self), ViewGraphSnapshotter.name(of: Child7.self), ViewGraphSnapshotter.name(of: Child8.self), ViewGraphSnapshotter.name(of: Child9.self), ViewGraphSnapshotter.name(of: Child10.self), ViewGraphSnapshotter.name(of: Child11.self), ViewGraphSnapshotter.name(of: Child12.self) ] let snapshots = ViewGraphSnapshotter.match(snapshots ?? [], to: viewTypeNames) self.child0 = node(for: child0, backend, snapshots[0], environment) @@ -839,17 +774,9 @@ public struct TupleViewChildren13< /// A fixed-length strongly-typed collection of 14 child nodes. A counterpart to /// ``TupleView14``. -public struct TupleViewChildren14< - Child0: View, Child1: View, Child2: View, Child3: View, Child4: View, Child5: View, - Child6: View, Child7: View, Child8: View, Child9: View, Child10: View, Child11: View, - Child12: View, Child13: View ->: ViewGraphNodeChildren { +public class TupleViewChildren14: TupleViewChildren { public var widgets: [AnyWidget] { - return [ - child0.widget, child1.widget, child2.widget, child3.widget, child4.widget, - child5.widget, child6.widget, child7.widget, child8.widget, child9.widget, - child10.widget, child11.widget, child12.widget, child13.widget, - ] + return [child0.widget, child1.widget, child2.widget, child3.widget, child4.widget, child5.widget, child6.widget, child7.widget, child8.widget, child9.widget, child10.widget, child11.widget, child12.widget, child13.widget] } public var erasedNodes: [ErasedViewGraphNode] { @@ -871,6 +798,8 @@ public struct TupleViewChildren14< ] } + var stackLayoutCache = StackLayoutCache() + /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. public var child0: AnyViewGraphNode /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. @@ -902,23 +831,13 @@ public struct TupleViewChildren14< /// Creates the nodes for 14 child views. public init( - _ child0: Child0, _ child1: Child1, _ child2: Child2, _ child3: Child3, _ child4: Child4, - _ child5: Child5, _ child6: Child6, _ child7: Child7, _ child8: Child8, _ child9: Child9, - _ child10: Child10, _ child11: Child11, _ child12: Child12, _ child13: Child13, + _ child0: Child0, _ child1: Child1, _ child2: Child2, _ child3: Child3, _ child4: Child4, _ child5: Child5, _ child6: Child6, _ child7: Child7, _ child8: Child8, _ child9: Child9, _ child10: Child10, _ child11: Child11, _ child12: Child12, _ child13: Child13, backend: Backend, snapshots: [ViewGraphSnapshotter.NodeSnapshot]?, environment: EnvironmentValues ) { let viewTypeNames = [ - ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), - ViewGraphSnapshotter.name(of: Child2.self), ViewGraphSnapshotter.name(of: Child3.self), - ViewGraphSnapshotter.name(of: Child4.self), ViewGraphSnapshotter.name(of: Child5.self), - ViewGraphSnapshotter.name(of: Child6.self), ViewGraphSnapshotter.name(of: Child7.self), - ViewGraphSnapshotter.name(of: Child8.self), ViewGraphSnapshotter.name(of: Child9.self), - ViewGraphSnapshotter.name(of: Child10.self), - ViewGraphSnapshotter.name(of: Child11.self), - ViewGraphSnapshotter.name(of: Child12.self), - ViewGraphSnapshotter.name(of: Child13.self), + ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), ViewGraphSnapshotter.name(of: Child2.self), ViewGraphSnapshotter.name(of: Child3.self), ViewGraphSnapshotter.name(of: Child4.self), ViewGraphSnapshotter.name(of: Child5.self), ViewGraphSnapshotter.name(of: Child6.self), ViewGraphSnapshotter.name(of: Child7.self), ViewGraphSnapshotter.name(of: Child8.self), ViewGraphSnapshotter.name(of: Child9.self), ViewGraphSnapshotter.name(of: Child10.self), ViewGraphSnapshotter.name(of: Child11.self), ViewGraphSnapshotter.name(of: Child12.self), ViewGraphSnapshotter.name(of: Child13.self) ] let snapshots = ViewGraphSnapshotter.match(snapshots ?? [], to: viewTypeNames) self.child0 = node(for: child0, backend, snapshots[0], environment) @@ -940,17 +859,9 @@ public struct TupleViewChildren14< /// A fixed-length strongly-typed collection of 15 child nodes. A counterpart to /// ``TupleView15``. -public struct TupleViewChildren15< - Child0: View, Child1: View, Child2: View, Child3: View, Child4: View, Child5: View, - Child6: View, Child7: View, Child8: View, Child9: View, Child10: View, Child11: View, - Child12: View, Child13: View, Child14: View ->: ViewGraphNodeChildren { +public class TupleViewChildren15: TupleViewChildren { public var widgets: [AnyWidget] { - return [ - child0.widget, child1.widget, child2.widget, child3.widget, child4.widget, - child5.widget, child6.widget, child7.widget, child8.widget, child9.widget, - child10.widget, child11.widget, child12.widget, child13.widget, child14.widget, - ] + return [child0.widget, child1.widget, child2.widget, child3.widget, child4.widget, child5.widget, child6.widget, child7.widget, child8.widget, child9.widget, child10.widget, child11.widget, child12.widget, child13.widget, child14.widget] } public var erasedNodes: [ErasedViewGraphNode] { @@ -973,6 +884,8 @@ public struct TupleViewChildren15< ] } + var stackLayoutCache = StackLayoutCache() + /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. public var child0: AnyViewGraphNode /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. @@ -1006,25 +919,13 @@ public struct TupleViewChildren15< /// Creates the nodes for 15 child views. public init( - _ child0: Child0, _ child1: Child1, _ child2: Child2, _ child3: Child3, _ child4: Child4, - _ child5: Child5, _ child6: Child6, _ child7: Child7, _ child8: Child8, _ child9: Child9, - _ child10: Child10, _ child11: Child11, _ child12: Child12, _ child13: Child13, - _ child14: Child14, + _ child0: Child0, _ child1: Child1, _ child2: Child2, _ child3: Child3, _ child4: Child4, _ child5: Child5, _ child6: Child6, _ child7: Child7, _ child8: Child8, _ child9: Child9, _ child10: Child10, _ child11: Child11, _ child12: Child12, _ child13: Child13, _ child14: Child14, backend: Backend, snapshots: [ViewGraphSnapshotter.NodeSnapshot]?, environment: EnvironmentValues ) { let viewTypeNames = [ - ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), - ViewGraphSnapshotter.name(of: Child2.self), ViewGraphSnapshotter.name(of: Child3.self), - ViewGraphSnapshotter.name(of: Child4.self), ViewGraphSnapshotter.name(of: Child5.self), - ViewGraphSnapshotter.name(of: Child6.self), ViewGraphSnapshotter.name(of: Child7.self), - ViewGraphSnapshotter.name(of: Child8.self), ViewGraphSnapshotter.name(of: Child9.self), - ViewGraphSnapshotter.name(of: Child10.self), - ViewGraphSnapshotter.name(of: Child11.self), - ViewGraphSnapshotter.name(of: Child12.self), - ViewGraphSnapshotter.name(of: Child13.self), - ViewGraphSnapshotter.name(of: Child14.self), + ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), ViewGraphSnapshotter.name(of: Child2.self), ViewGraphSnapshotter.name(of: Child3.self), ViewGraphSnapshotter.name(of: Child4.self), ViewGraphSnapshotter.name(of: Child5.self), ViewGraphSnapshotter.name(of: Child6.self), ViewGraphSnapshotter.name(of: Child7.self), ViewGraphSnapshotter.name(of: Child8.self), ViewGraphSnapshotter.name(of: Child9.self), ViewGraphSnapshotter.name(of: Child10.self), ViewGraphSnapshotter.name(of: Child11.self), ViewGraphSnapshotter.name(of: Child12.self), ViewGraphSnapshotter.name(of: Child13.self), ViewGraphSnapshotter.name(of: Child14.self) ] let snapshots = ViewGraphSnapshotter.match(snapshots ?? [], to: viewTypeNames) self.child0 = node(for: child0, backend, snapshots[0], environment) @@ -1047,18 +948,9 @@ public struct TupleViewChildren15< /// A fixed-length strongly-typed collection of 16 child nodes. A counterpart to /// ``TupleView16``. -public struct TupleViewChildren16< - Child0: View, Child1: View, Child2: View, Child3: View, Child4: View, Child5: View, - Child6: View, Child7: View, Child8: View, Child9: View, Child10: View, Child11: View, - Child12: View, Child13: View, Child14: View, Child15: View ->: ViewGraphNodeChildren { +public class TupleViewChildren16: TupleViewChildren { public var widgets: [AnyWidget] { - return [ - child0.widget, child1.widget, child2.widget, child3.widget, child4.widget, - child5.widget, child6.widget, child7.widget, child8.widget, child9.widget, - child10.widget, child11.widget, child12.widget, child13.widget, child14.widget, - child15.widget, - ] + return [child0.widget, child1.widget, child2.widget, child3.widget, child4.widget, child5.widget, child6.widget, child7.widget, child8.widget, child9.widget, child10.widget, child11.widget, child12.widget, child13.widget, child14.widget, child15.widget] } public var erasedNodes: [ErasedViewGraphNode] { @@ -1082,6 +974,8 @@ public struct TupleViewChildren16< ] } + var stackLayoutCache = StackLayoutCache() + /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. public var child0: AnyViewGraphNode /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. @@ -1117,26 +1011,13 @@ public struct TupleViewChildren16< /// Creates the nodes for 16 child views. public init( - _ child0: Child0, _ child1: Child1, _ child2: Child2, _ child3: Child3, _ child4: Child4, - _ child5: Child5, _ child6: Child6, _ child7: Child7, _ child8: Child8, _ child9: Child9, - _ child10: Child10, _ child11: Child11, _ child12: Child12, _ child13: Child13, - _ child14: Child14, _ child15: Child15, + _ child0: Child0, _ child1: Child1, _ child2: Child2, _ child3: Child3, _ child4: Child4, _ child5: Child5, _ child6: Child6, _ child7: Child7, _ child8: Child8, _ child9: Child9, _ child10: Child10, _ child11: Child11, _ child12: Child12, _ child13: Child13, _ child14: Child14, _ child15: Child15, backend: Backend, snapshots: [ViewGraphSnapshotter.NodeSnapshot]?, environment: EnvironmentValues ) { let viewTypeNames = [ - ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), - ViewGraphSnapshotter.name(of: Child2.self), ViewGraphSnapshotter.name(of: Child3.self), - ViewGraphSnapshotter.name(of: Child4.self), ViewGraphSnapshotter.name(of: Child5.self), - ViewGraphSnapshotter.name(of: Child6.self), ViewGraphSnapshotter.name(of: Child7.self), - ViewGraphSnapshotter.name(of: Child8.self), ViewGraphSnapshotter.name(of: Child9.self), - ViewGraphSnapshotter.name(of: Child10.self), - ViewGraphSnapshotter.name(of: Child11.self), - ViewGraphSnapshotter.name(of: Child12.self), - ViewGraphSnapshotter.name(of: Child13.self), - ViewGraphSnapshotter.name(of: Child14.self), - ViewGraphSnapshotter.name(of: Child15.self), + ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), ViewGraphSnapshotter.name(of: Child2.self), ViewGraphSnapshotter.name(of: Child3.self), ViewGraphSnapshotter.name(of: Child4.self), ViewGraphSnapshotter.name(of: Child5.self), ViewGraphSnapshotter.name(of: Child6.self), ViewGraphSnapshotter.name(of: Child7.self), ViewGraphSnapshotter.name(of: Child8.self), ViewGraphSnapshotter.name(of: Child9.self), ViewGraphSnapshotter.name(of: Child10.self), ViewGraphSnapshotter.name(of: Child11.self), ViewGraphSnapshotter.name(of: Child12.self), ViewGraphSnapshotter.name(of: Child13.self), ViewGraphSnapshotter.name(of: Child14.self), ViewGraphSnapshotter.name(of: Child15.self) ] let snapshots = ViewGraphSnapshotter.match(snapshots ?? [], to: viewTypeNames) self.child0 = node(for: child0, backend, snapshots[0], environment) @@ -1160,18 +1041,9 @@ public struct TupleViewChildren16< /// A fixed-length strongly-typed collection of 17 child nodes. A counterpart to /// ``TupleView17``. -public struct TupleViewChildren17< - Child0: View, Child1: View, Child2: View, Child3: View, Child4: View, Child5: View, - Child6: View, Child7: View, Child8: View, Child9: View, Child10: View, Child11: View, - Child12: View, Child13: View, Child14: View, Child15: View, Child16: View ->: ViewGraphNodeChildren { +public class TupleViewChildren17: TupleViewChildren { public var widgets: [AnyWidget] { - return [ - child0.widget, child1.widget, child2.widget, child3.widget, child4.widget, - child5.widget, child6.widget, child7.widget, child8.widget, child9.widget, - child10.widget, child11.widget, child12.widget, child13.widget, child14.widget, - child15.widget, child16.widget, - ] + return [child0.widget, child1.widget, child2.widget, child3.widget, child4.widget, child5.widget, child6.widget, child7.widget, child8.widget, child9.widget, child10.widget, child11.widget, child12.widget, child13.widget, child14.widget, child15.widget, child16.widget] } public var erasedNodes: [ErasedViewGraphNode] { @@ -1196,6 +1068,8 @@ public struct TupleViewChildren17< ] } + var stackLayoutCache = StackLayoutCache() + /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. public var child0: AnyViewGraphNode /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. @@ -1233,27 +1107,13 @@ public struct TupleViewChildren17< /// Creates the nodes for 17 child views. public init( - _ child0: Child0, _ child1: Child1, _ child2: Child2, _ child3: Child3, _ child4: Child4, - _ child5: Child5, _ child6: Child6, _ child7: Child7, _ child8: Child8, _ child9: Child9, - _ child10: Child10, _ child11: Child11, _ child12: Child12, _ child13: Child13, - _ child14: Child14, _ child15: Child15, _ child16: Child16, + _ child0: Child0, _ child1: Child1, _ child2: Child2, _ child3: Child3, _ child4: Child4, _ child5: Child5, _ child6: Child6, _ child7: Child7, _ child8: Child8, _ child9: Child9, _ child10: Child10, _ child11: Child11, _ child12: Child12, _ child13: Child13, _ child14: Child14, _ child15: Child15, _ child16: Child16, backend: Backend, snapshots: [ViewGraphSnapshotter.NodeSnapshot]?, environment: EnvironmentValues ) { let viewTypeNames = [ - ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), - ViewGraphSnapshotter.name(of: Child2.self), ViewGraphSnapshotter.name(of: Child3.self), - ViewGraphSnapshotter.name(of: Child4.self), ViewGraphSnapshotter.name(of: Child5.self), - ViewGraphSnapshotter.name(of: Child6.self), ViewGraphSnapshotter.name(of: Child7.self), - ViewGraphSnapshotter.name(of: Child8.self), ViewGraphSnapshotter.name(of: Child9.self), - ViewGraphSnapshotter.name(of: Child10.self), - ViewGraphSnapshotter.name(of: Child11.self), - ViewGraphSnapshotter.name(of: Child12.self), - ViewGraphSnapshotter.name(of: Child13.self), - ViewGraphSnapshotter.name(of: Child14.self), - ViewGraphSnapshotter.name(of: Child15.self), - ViewGraphSnapshotter.name(of: Child16.self), + ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), ViewGraphSnapshotter.name(of: Child2.self), ViewGraphSnapshotter.name(of: Child3.self), ViewGraphSnapshotter.name(of: Child4.self), ViewGraphSnapshotter.name(of: Child5.self), ViewGraphSnapshotter.name(of: Child6.self), ViewGraphSnapshotter.name(of: Child7.self), ViewGraphSnapshotter.name(of: Child8.self), ViewGraphSnapshotter.name(of: Child9.self), ViewGraphSnapshotter.name(of: Child10.self), ViewGraphSnapshotter.name(of: Child11.self), ViewGraphSnapshotter.name(of: Child12.self), ViewGraphSnapshotter.name(of: Child13.self), ViewGraphSnapshotter.name(of: Child14.self), ViewGraphSnapshotter.name(of: Child15.self), ViewGraphSnapshotter.name(of: Child16.self) ] let snapshots = ViewGraphSnapshotter.match(snapshots ?? [], to: viewTypeNames) self.child0 = node(for: child0, backend, snapshots[0], environment) @@ -1278,18 +1138,9 @@ public struct TupleViewChildren17< /// A fixed-length strongly-typed collection of 18 child nodes. A counterpart to /// ``TupleView18``. -public struct TupleViewChildren18< - Child0: View, Child1: View, Child2: View, Child3: View, Child4: View, Child5: View, - Child6: View, Child7: View, Child8: View, Child9: View, Child10: View, Child11: View, - Child12: View, Child13: View, Child14: View, Child15: View, Child16: View, Child17: View ->: ViewGraphNodeChildren { +public class TupleViewChildren18: TupleViewChildren { public var widgets: [AnyWidget] { - return [ - child0.widget, child1.widget, child2.widget, child3.widget, child4.widget, - child5.widget, child6.widget, child7.widget, child8.widget, child9.widget, - child10.widget, child11.widget, child12.widget, child13.widget, child14.widget, - child15.widget, child16.widget, child17.widget, - ] + return [child0.widget, child1.widget, child2.widget, child3.widget, child4.widget, child5.widget, child6.widget, child7.widget, child8.widget, child9.widget, child10.widget, child11.widget, child12.widget, child13.widget, child14.widget, child15.widget, child16.widget, child17.widget] } public var erasedNodes: [ErasedViewGraphNode] { @@ -1315,6 +1166,8 @@ public struct TupleViewChildren18< ] } + var stackLayoutCache = StackLayoutCache() + /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. public var child0: AnyViewGraphNode /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. @@ -1354,28 +1207,13 @@ public struct TupleViewChildren18< /// Creates the nodes for 18 child views. public init( - _ child0: Child0, _ child1: Child1, _ child2: Child2, _ child3: Child3, _ child4: Child4, - _ child5: Child5, _ child6: Child6, _ child7: Child7, _ child8: Child8, _ child9: Child9, - _ child10: Child10, _ child11: Child11, _ child12: Child12, _ child13: Child13, - _ child14: Child14, _ child15: Child15, _ child16: Child16, _ child17: Child17, + _ child0: Child0, _ child1: Child1, _ child2: Child2, _ child3: Child3, _ child4: Child4, _ child5: Child5, _ child6: Child6, _ child7: Child7, _ child8: Child8, _ child9: Child9, _ child10: Child10, _ child11: Child11, _ child12: Child12, _ child13: Child13, _ child14: Child14, _ child15: Child15, _ child16: Child16, _ child17: Child17, backend: Backend, snapshots: [ViewGraphSnapshotter.NodeSnapshot]?, environment: EnvironmentValues ) { let viewTypeNames = [ - ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), - ViewGraphSnapshotter.name(of: Child2.self), ViewGraphSnapshotter.name(of: Child3.self), - ViewGraphSnapshotter.name(of: Child4.self), ViewGraphSnapshotter.name(of: Child5.self), - ViewGraphSnapshotter.name(of: Child6.self), ViewGraphSnapshotter.name(of: Child7.self), - ViewGraphSnapshotter.name(of: Child8.self), ViewGraphSnapshotter.name(of: Child9.self), - ViewGraphSnapshotter.name(of: Child10.self), - ViewGraphSnapshotter.name(of: Child11.self), - ViewGraphSnapshotter.name(of: Child12.self), - ViewGraphSnapshotter.name(of: Child13.self), - ViewGraphSnapshotter.name(of: Child14.self), - ViewGraphSnapshotter.name(of: Child15.self), - ViewGraphSnapshotter.name(of: Child16.self), - ViewGraphSnapshotter.name(of: Child17.self), + ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), ViewGraphSnapshotter.name(of: Child2.self), ViewGraphSnapshotter.name(of: Child3.self), ViewGraphSnapshotter.name(of: Child4.self), ViewGraphSnapshotter.name(of: Child5.self), ViewGraphSnapshotter.name(of: Child6.self), ViewGraphSnapshotter.name(of: Child7.self), ViewGraphSnapshotter.name(of: Child8.self), ViewGraphSnapshotter.name(of: Child9.self), ViewGraphSnapshotter.name(of: Child10.self), ViewGraphSnapshotter.name(of: Child11.self), ViewGraphSnapshotter.name(of: Child12.self), ViewGraphSnapshotter.name(of: Child13.self), ViewGraphSnapshotter.name(of: Child14.self), ViewGraphSnapshotter.name(of: Child15.self), ViewGraphSnapshotter.name(of: Child16.self), ViewGraphSnapshotter.name(of: Child17.self) ] let snapshots = ViewGraphSnapshotter.match(snapshots ?? [], to: viewTypeNames) self.child0 = node(for: child0, backend, snapshots[0], environment) @@ -1401,19 +1239,9 @@ public struct TupleViewChildren18< /// A fixed-length strongly-typed collection of 19 child nodes. A counterpart to /// ``TupleView19``. -public struct TupleViewChildren19< - Child0: View, Child1: View, Child2: View, Child3: View, Child4: View, Child5: View, - Child6: View, Child7: View, Child8: View, Child9: View, Child10: View, Child11: View, - Child12: View, Child13: View, Child14: View, Child15: View, Child16: View, Child17: View, - Child18: View ->: ViewGraphNodeChildren { +public class TupleViewChildren19: TupleViewChildren { public var widgets: [AnyWidget] { - return [ - child0.widget, child1.widget, child2.widget, child3.widget, child4.widget, - child5.widget, child6.widget, child7.widget, child8.widget, child9.widget, - child10.widget, child11.widget, child12.widget, child13.widget, child14.widget, - child15.widget, child16.widget, child17.widget, child18.widget, - ] + return [child0.widget, child1.widget, child2.widget, child3.widget, child4.widget, child5.widget, child6.widget, child7.widget, child8.widget, child9.widget, child10.widget, child11.widget, child12.widget, child13.widget, child14.widget, child15.widget, child16.widget, child17.widget, child18.widget] } public var erasedNodes: [ErasedViewGraphNode] { @@ -1440,6 +1268,8 @@ public struct TupleViewChildren19< ] } + var stackLayoutCache = StackLayoutCache() + /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. public var child0: AnyViewGraphNode /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. @@ -1481,30 +1311,13 @@ public struct TupleViewChildren19< /// Creates the nodes for 19 child views. public init( - _ child0: Child0, _ child1: Child1, _ child2: Child2, _ child3: Child3, _ child4: Child4, - _ child5: Child5, _ child6: Child6, _ child7: Child7, _ child8: Child8, _ child9: Child9, - _ child10: Child10, _ child11: Child11, _ child12: Child12, _ child13: Child13, - _ child14: Child14, _ child15: Child15, _ child16: Child16, _ child17: Child17, - _ child18: Child18, + _ child0: Child0, _ child1: Child1, _ child2: Child2, _ child3: Child3, _ child4: Child4, _ child5: Child5, _ child6: Child6, _ child7: Child7, _ child8: Child8, _ child9: Child9, _ child10: Child10, _ child11: Child11, _ child12: Child12, _ child13: Child13, _ child14: Child14, _ child15: Child15, _ child16: Child16, _ child17: Child17, _ child18: Child18, backend: Backend, snapshots: [ViewGraphSnapshotter.NodeSnapshot]?, environment: EnvironmentValues ) { let viewTypeNames = [ - ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), - ViewGraphSnapshotter.name(of: Child2.self), ViewGraphSnapshotter.name(of: Child3.self), - ViewGraphSnapshotter.name(of: Child4.self), ViewGraphSnapshotter.name(of: Child5.self), - ViewGraphSnapshotter.name(of: Child6.self), ViewGraphSnapshotter.name(of: Child7.self), - ViewGraphSnapshotter.name(of: Child8.self), ViewGraphSnapshotter.name(of: Child9.self), - ViewGraphSnapshotter.name(of: Child10.self), - ViewGraphSnapshotter.name(of: Child11.self), - ViewGraphSnapshotter.name(of: Child12.self), - ViewGraphSnapshotter.name(of: Child13.self), - ViewGraphSnapshotter.name(of: Child14.self), - ViewGraphSnapshotter.name(of: Child15.self), - ViewGraphSnapshotter.name(of: Child16.self), - ViewGraphSnapshotter.name(of: Child17.self), - ViewGraphSnapshotter.name(of: Child18.self), + ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), ViewGraphSnapshotter.name(of: Child2.self), ViewGraphSnapshotter.name(of: Child3.self), ViewGraphSnapshotter.name(of: Child4.self), ViewGraphSnapshotter.name(of: Child5.self), ViewGraphSnapshotter.name(of: Child6.self), ViewGraphSnapshotter.name(of: Child7.self), ViewGraphSnapshotter.name(of: Child8.self), ViewGraphSnapshotter.name(of: Child9.self), ViewGraphSnapshotter.name(of: Child10.self), ViewGraphSnapshotter.name(of: Child11.self), ViewGraphSnapshotter.name(of: Child12.self), ViewGraphSnapshotter.name(of: Child13.self), ViewGraphSnapshotter.name(of: Child14.self), ViewGraphSnapshotter.name(of: Child15.self), ViewGraphSnapshotter.name(of: Child16.self), ViewGraphSnapshotter.name(of: Child17.self), ViewGraphSnapshotter.name(of: Child18.self) ] let snapshots = ViewGraphSnapshotter.match(snapshots ?? [], to: viewTypeNames) self.child0 = node(for: child0, backend, snapshots[0], environment) @@ -1531,19 +1344,9 @@ public struct TupleViewChildren19< /// A fixed-length strongly-typed collection of 20 child nodes. A counterpart to /// ``TupleView20``. -public struct TupleViewChildren20< - Child0: View, Child1: View, Child2: View, Child3: View, Child4: View, Child5: View, - Child6: View, Child7: View, Child8: View, Child9: View, Child10: View, Child11: View, - Child12: View, Child13: View, Child14: View, Child15: View, Child16: View, Child17: View, - Child18: View, Child19: View ->: ViewGraphNodeChildren { +public class TupleViewChildren20: TupleViewChildren { public var widgets: [AnyWidget] { - return [ - child0.widget, child1.widget, child2.widget, child3.widget, child4.widget, - child5.widget, child6.widget, child7.widget, child8.widget, child9.widget, - child10.widget, child11.widget, child12.widget, child13.widget, child14.widget, - child15.widget, child16.widget, child17.widget, child18.widget, child19.widget, - ] + return [child0.widget, child1.widget, child2.widget, child3.widget, child4.widget, child5.widget, child6.widget, child7.widget, child8.widget, child9.widget, child10.widget, child11.widget, child12.widget, child13.widget, child14.widget, child15.widget, child16.widget, child17.widget, child18.widget, child19.widget] } public var erasedNodes: [ErasedViewGraphNode] { @@ -1571,6 +1374,8 @@ public struct TupleViewChildren20< ] } + var stackLayoutCache = StackLayoutCache() + /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. public var child0: AnyViewGraphNode /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. @@ -1614,31 +1419,13 @@ public struct TupleViewChildren20< /// Creates the nodes for 20 child views. public init( - _ child0: Child0, _ child1: Child1, _ child2: Child2, _ child3: Child3, _ child4: Child4, - _ child5: Child5, _ child6: Child6, _ child7: Child7, _ child8: Child8, _ child9: Child9, - _ child10: Child10, _ child11: Child11, _ child12: Child12, _ child13: Child13, - _ child14: Child14, _ child15: Child15, _ child16: Child16, _ child17: Child17, - _ child18: Child18, _ child19: Child19, + _ child0: Child0, _ child1: Child1, _ child2: Child2, _ child3: Child3, _ child4: Child4, _ child5: Child5, _ child6: Child6, _ child7: Child7, _ child8: Child8, _ child9: Child9, _ child10: Child10, _ child11: Child11, _ child12: Child12, _ child13: Child13, _ child14: Child14, _ child15: Child15, _ child16: Child16, _ child17: Child17, _ child18: Child18, _ child19: Child19, backend: Backend, snapshots: [ViewGraphSnapshotter.NodeSnapshot]?, environment: EnvironmentValues ) { let viewTypeNames = [ - ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), - ViewGraphSnapshotter.name(of: Child2.self), ViewGraphSnapshotter.name(of: Child3.self), - ViewGraphSnapshotter.name(of: Child4.self), ViewGraphSnapshotter.name(of: Child5.self), - ViewGraphSnapshotter.name(of: Child6.self), ViewGraphSnapshotter.name(of: Child7.self), - ViewGraphSnapshotter.name(of: Child8.self), ViewGraphSnapshotter.name(of: Child9.self), - ViewGraphSnapshotter.name(of: Child10.self), - ViewGraphSnapshotter.name(of: Child11.self), - ViewGraphSnapshotter.name(of: Child12.self), - ViewGraphSnapshotter.name(of: Child13.self), - ViewGraphSnapshotter.name(of: Child14.self), - ViewGraphSnapshotter.name(of: Child15.self), - ViewGraphSnapshotter.name(of: Child16.self), - ViewGraphSnapshotter.name(of: Child17.self), - ViewGraphSnapshotter.name(of: Child18.self), - ViewGraphSnapshotter.name(of: Child19.self), + ViewGraphSnapshotter.name(of: Child0.self), ViewGraphSnapshotter.name(of: Child1.self), ViewGraphSnapshotter.name(of: Child2.self), ViewGraphSnapshotter.name(of: Child3.self), ViewGraphSnapshotter.name(of: Child4.self), ViewGraphSnapshotter.name(of: Child5.self), ViewGraphSnapshotter.name(of: Child6.self), ViewGraphSnapshotter.name(of: Child7.self), ViewGraphSnapshotter.name(of: Child8.self), ViewGraphSnapshotter.name(of: Child9.self), ViewGraphSnapshotter.name(of: Child10.self), ViewGraphSnapshotter.name(of: Child11.self), ViewGraphSnapshotter.name(of: Child12.self), ViewGraphSnapshotter.name(of: Child13.self), ViewGraphSnapshotter.name(of: Child14.self), ViewGraphSnapshotter.name(of: Child15.self), ViewGraphSnapshotter.name(of: Child16.self), ViewGraphSnapshotter.name(of: Child17.self), ViewGraphSnapshotter.name(of: Child18.self), ViewGraphSnapshotter.name(of: Child19.self) ] let snapshots = ViewGraphSnapshotter.match(snapshots ?? [], to: viewTypeNames) self.child0 = node(for: child0, backend, snapshots[0], environment) diff --git a/Sources/SwiftCrossUI/Views/TupleViewChildren.swift.gyb b/Sources/SwiftCrossUI/Views/TupleViewChildren.swift.gyb index db3e4309f0..b494c5c266 100644 --- a/Sources/SwiftCrossUI/Views/TupleViewChildren.swift.gyb +++ b/Sources/SwiftCrossUI/Views/TupleViewChildren.swift.gyb @@ -4,6 +4,17 @@ maximum_child_count = 20 }% +struct StackLayoutCache { + var lastFlexibilityOrdering: [Int]? + var lastHiddenChildren: [Bool] = [] + var redistributeSpaceOnCommit = false +} + +protocol TupleViewChildren: ViewGraphNodeChildren { + @MainActor + var stackLayoutCache: StackLayoutCache { get nonmutating set } +} + /// A helper function to shorten node initialisations to a single line. This /// helps compress the generated code a bit and minimise the number of additions /// and deletions caused by updating the generator. @@ -34,7 +45,7 @@ variadic_type_parameters = ", ".join(children) /// A fixed-length strongly-typed collection of ${i + 1} child nodes. A counterpart to /// ``TupleView${i + 1}``. -public struct TupleViewChildren${i + 1}<${struct_type_parameters}>: ViewGraphNodeChildren { +public class TupleViewChildren${i + 1}<${struct_type_parameters}>: TupleViewChildren { public var widgets: [AnyWidget] { return [${", ".join("%s.widget" % child.lower() for child in children)}] } @@ -47,6 +58,8 @@ public struct TupleViewChildren${i + 1}<${struct_type_parameters}>: ViewGraphNod ] } + var stackLayoutCache = StackLayoutCache() + % for child in children: /// ``AnyViewGraphNode`` is used instead of ``ViewGraphNode`` because otherwise the backend leaks into views. public var ${child.lower()}: AnyViewGraphNode<${child}> diff --git a/Sources/SwiftCrossUI/Views/TypeSafeView.swift b/Sources/SwiftCrossUI/Views/TypeSafeView.swift index ef99bd4b19..fb218a76c1 100644 --- a/Sources/SwiftCrossUI/Views/TypeSafeView.swift +++ b/Sources/SwiftCrossUI/Views/TypeSafeView.swift @@ -22,14 +22,21 @@ protocol TypeSafeView: View { backend: Backend ) -> Backend.Widget - func update( + func computeLayout( _ widget: Backend.Widget, children: Children, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult + backend: Backend + ) -> ViewLayoutResult + + func commit( + _ widget: Backend.Widget, + children: Children, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) } extension TypeSafeView { @@ -69,21 +76,35 @@ extension TypeSafeView { return asWidget(children as! Children, backend: backend) } - public func update( + public func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - update( + backend: Backend + ) -> ViewLayoutResult { + computeLayout( widget, children: children as! Children, proposedSize: proposedSize, environment: environment, - backend: backend, - dryRun: dryRun + backend: backend + ) + } + + public func commit( + _ widget: Backend.Widget, + children: any ViewGraphNodeChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + commit( + widget, + children: children as! Children, + layout: layout, + environment: environment, + backend: backend ) } } diff --git a/Sources/SwiftCrossUI/Views/VStack.swift b/Sources/SwiftCrossUI/Views/VStack.swift index b3b9973fd6..f3917a2a4b 100644 --- a/Sources/SwiftCrossUI/Views/VStack.swift +++ b/Sources/SwiftCrossUI/Views/VStack.swift @@ -39,25 +39,56 @@ public struct VStack: View { return vStack } - public func update( + public func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - return LayoutSystem.updateStackLayout( + backend: Backend + ) -> ViewLayoutResult { + if !(children is TupleViewChildren) { + // TODO: Make layout caching a ViewGraphNode feature so that we can handle + // these edge cases without a second thought. Would also make introducing + // a port of SwiftUI's Layout protocol much easier. + print("warning: VStack will not function correctly non-TupleView Content") + } + var cache = (children as? TupleViewChildren)?.stackLayoutCache ?? StackLayoutCache() + let result = LayoutSystem.computeStackLayout( container: widget, children: layoutableChildren(backend: backend, children: children), + cache: &cache, proposedSize: proposedSize, environment: environment .with(\.layoutOrientation, .vertical) .with(\.layoutAlignment, alignment.asStackAlignment) .with(\.layoutSpacing, spacing), - backend: backend, - dryRun: dryRun + backend: backend + ) + (children as? TupleViewChildren)?.stackLayoutCache = cache + return result + } + + public func commit( + _ widget: Backend.Widget, + children: any ViewGraphNodeChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + var cache = (children as? TupleViewChildren)?.stackLayoutCache ?? StackLayoutCache() + LayoutSystem.commitStackLayout( + container: widget, + children: layoutableChildren(backend: backend, children: children), + cache: &cache, + layout: layout, + environment: + environment + .with(\.layoutOrientation, .vertical) + .with(\.layoutAlignment, alignment.asStackAlignment) + .with(\.layoutSpacing, spacing), + backend: backend ) + (children as? TupleViewChildren)?.stackLayoutCache = cache } } diff --git a/Sources/SwiftCrossUI/Views/View.swift b/Sources/SwiftCrossUI/Views/View.swift index abeca203fe..562f025cfb 100644 --- a/Sources/SwiftCrossUI/Views/View.swift +++ b/Sources/SwiftCrossUI/Views/View.swift @@ -42,24 +42,27 @@ public protocol View { backend: Backend ) -> Backend.Widget - /// Updates the view's widget after a state change occurs (although the - /// change isn't guaranteed to have affected this particular view). - /// `proposedSize` is the size suggested by the parent container, but child - /// views always get the final call on their own size. - /// - /// Always called once immediately after creating the view's widget with. - /// This helps reduce code duplication between `asWidget` and `update`. - /// - Parameter dryRun: If `true`, avoids updating the UI and only computes - /// sizing. - /// - Returns: The view's new size. - func update( + /// Computes this view's layout after a state change or a change in + /// available space. `proposedSize` is the size suggested by the parent + /// container, but child views always get the final call on their own size. + /// - Returns: The view's layout size. + func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult + backend: Backend + ) -> ViewLayoutResult + + /// Commits the last computed layout to the underlying widget hierarchy. + /// `layout` is guaranteed to be the last value returned by ``computeLayout``. + func commit( + _ widget: Backend.Widget, + children: any ViewGraphNodeChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) } extension View { @@ -119,42 +122,71 @@ extension View { return vStack.asWidget(children, backend: backend) } - public func update( + public func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - defaultUpdate( + backend: Backend + ) -> ViewLayoutResult { + defaultComputeLayout( widget, children: children, proposedSize: proposedSize, environment: environment, - backend: backend, - dryRun: dryRun + backend: backend ) } - /// The default `View.update` implementation. Haters may see this as a + /// The default `View.computeLayout` implementation. Haters may see this as a /// composition lover re-implementing inheritance; I see it as innovation. - public func defaultUpdate( + public func defaultComputeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { + backend: Backend + ) -> ViewLayoutResult { let vStack = VStack(content: body) - return vStack.update( + return vStack.computeLayout( widget, children: children, proposedSize: proposedSize, environment: environment, - backend: backend, - dryRun: dryRun + backend: backend + ) + } + + public func commit( + _ widget: Backend.Widget, + children: any ViewGraphNodeChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + defaultCommit( + widget, + children: children, + layout: layout, + environment: environment, + backend: backend + ) + } + + public func defaultCommit( + _ widget: Backend.Widget, + children: any ViewGraphNodeChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + let vStack = VStack(content: body) + return vStack.commit( + widget, + children: children, + layout: layout, + environment: environment, + backend: backend ) } } diff --git a/Sources/SwiftCrossUI/Views/WebView.swift b/Sources/SwiftCrossUI/Views/WebView.swift deleted file mode 100644 index 0b9e1dae4e..0000000000 --- a/Sources/SwiftCrossUI/Views/WebView.swift +++ /dev/null @@ -1,47 +0,0 @@ -import Foundation - -@available(tvOS, unavailable) -public struct WebView: ElementaryView { - @State var currentURL: URL? - @Binding var url: URL - - public init(_ url: Binding) { - _url = url - } - - func asWidget(backend: Backend) -> Backend.Widget { - backend.createWebView() - } - - func update( - _ widget: Backend.Widget, - proposedSize: SIMD2, - environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - if !dryRun { - if url != currentURL { - backend.navigateWebView(widget, to: url) - currentURL = url - } - backend.updateWebView(widget, environment: environment) { destination in - currentURL = destination - url = destination - } - backend.setSize(of: widget, to: proposedSize) - } - - return ViewUpdateResult( - size: ViewSize( - size: proposedSize, - idealSize: SIMD2(10, 10), - minimumWidth: 0, - minimumHeight: 0, - maximumWidth: nil, - maximumHeight: nil - ), - childResults: [] - ) - } -} diff --git a/Sources/SwiftCrossUI/Views/ZStack.swift b/Sources/SwiftCrossUI/Views/ZStack.swift index 5cd5657c51..87884604bc 100644 --- a/Sources/SwiftCrossUI/Views/ZStack.swift +++ b/Sources/SwiftCrossUI/Views/ZStack.swift @@ -28,51 +28,50 @@ public struct ZStack: View { return zStack } - public func update( + public func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - var childResults: [ViewUpdateResult] = [] - for child in layoutableChildren(backend: backend, children: children) { - let childResult = child.update( - proposedSize: proposedSize, - environment: environment, - dryRun: dryRun - ) - childResults.append(childResult) - } + backend: Backend + ) -> ViewLayoutResult { + let childResults = layoutableChildren(backend: backend, children: children) + .map { child in + child.computeLayout( + proposedSize: proposedSize, + environment: environment + ) + } - let childSizes = childResults.map(\.size) let size = ViewSize( - size: SIMD2( - childSizes.map(\.size.x).max() ?? 0, - childSizes.map(\.size.y).max() ?? 0 - ), - idealSize: SIMD2( - childSizes.map(\.idealSize.x).max() ?? 0, - childSizes.map(\.idealSize.y).max() ?? 0 - ), - minimumWidth: childSizes.map(\.minimumWidth).max() ?? 0, - minimumHeight: childSizes.map(\.minimumHeight).max() ?? 0, - maximumWidth: childSizes.map(\.maximumWidth).max() ?? 0, - maximumHeight: childSizes.map(\.maximumHeight).max() ?? 0 + childResults.map(\.size.width).max() ?? 0, + childResults.map(\.size.height).max() ?? 0 ) - if !dryRun { - for (i, childSize) in childSizes.enumerated() { - let position = alignment.position( - ofChild: childSize.size, - in: size.size - ) - backend.setPosition(ofChildAt: i, in: widget, to: position) + return ViewLayoutResult(size: size, childResults: childResults) + } + + public func commit( + _ widget: Backend.Widget, + children: any ViewGraphNodeChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + let size = layout.size + let children = layoutableChildren(backend: backend, children: children) + .map { child in + child.commit() } - backend.setSize(of: widget, to: size.size) + + for (i, child) in children.enumerated() { + let position = alignment.position( + ofChild: child.size.vector, + in: size.vector + ) + backend.setPosition(ofChildAt: i, in: widget, to: position) } - return ViewUpdateResult(size: size, childResults: childResults) + backend.setSize(of: widget, to: size.vector) } } diff --git a/Sources/UIKitBackend/UIViewRepresentable.swift b/Sources/UIKitBackend/UIViewRepresentable.swift index 5b20929cd9..110fcd05b5 100644 --- a/Sources/UIKitBackend/UIViewRepresentable.swift +++ b/Sources/UIKitBackend/UIViewRepresentable.swift @@ -135,13 +135,12 @@ where Self: UIViewRepresentable { } } - public func update( + public func computeLayout( _ widget: Backend.Widget, children _: any ViewGraphNodeChildren, proposedSize: SIMD2, environment: EnvironmentValues, - backend _: Backend, - dryRun: Bool + backend _: Backend ) -> ViewUpdateResult { let representingWidget = widget as! ViewRepresentingWidget representingWidget.update(with: environment) @@ -153,13 +152,19 @@ where Self: UIViewRepresentable { context: representingWidget.context! ) - if !dryRun { - representingWidget.width = size.size.x - representingWidget.height = size.size.y - } - return ViewUpdateResult.leafView(size: size) } + + public func commit( + _ widget: Backend.Widget, + children: any ViewGraphNodeChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + representingWidget.width = layout.size.size.x + representingWidget.height = layout.size.size.y + } } extension UIViewRepresentable