From ed24b0fa0f6e88baeedf9f0907a8a207a1e3669f Mon Sep 17 00:00:00 2001 From: stackotter Date: Fri, 30 May 2025 00:41:29 +1000 Subject: [PATCH 01/15] Split View.update into View.computeLayout and View.commit --- .../LayoutPerformanceBenchmark.swift | 4 +- .../WindowingExample/WindowingApp.swift | 2 + Sources/AppKitBackend/AppKitBackend.swift | 4 +- .../AppKitBackend/NSViewRepresentable.swift | 23 +- Sources/Gtk/Generated/Entry.swift | 166 ++++++------- Sources/Gtk/Generated/FilterChange.swift | 2 - Sources/Gtk/Generated/GLArea.swift | 7 - Sources/Gtk/Generated/Image.swift | 25 +- Sources/Gtk/Generated/Picture.swift | 4 +- Sources/Gtk/Generated/Popover.swift | 13 +- Sources/Gtk3/Generated/Entry.swift | 223 +++++++++--------- .../SwiftCrossUI/Layout/LayoutSystem.swift | 181 +++++++------- .../Layout/ViewUpdateResult.swift | 6 +- .../SwiftCrossUI/Scenes/WindowGroupNode.swift | 14 +- Sources/SwiftCrossUI/Values/Color.swift | 23 +- .../ViewGraph/AnyViewGraphNode.swift | 34 +-- .../ViewGraph/ErasedViewGraphNode.swift | 24 +- .../SwiftCrossUI/ViewGraph/ViewGraph.swift | 26 +- .../ViewGraph/ViewGraphNode.swift | 128 +++++----- Sources/SwiftCrossUI/Views/AnyView.swift | 37 +-- Sources/SwiftCrossUI/Views/Button.swift | 24 +- Sources/SwiftCrossUI/Views/Checkbox.swift | 28 ++- Sources/SwiftCrossUI/Views/EitherView.swift | 51 ++-- .../SwiftCrossUI/Views/ElementaryView.swift | 41 +++- Sources/SwiftCrossUI/Views/EmptyView.swift | 17 +- Sources/SwiftCrossUI/Views/ForEach.swift | 95 +++++--- .../SwiftCrossUI/Views/GeometryReader.swift | 31 ++- Sources/SwiftCrossUI/Views/Group.swift | 26 +- Sources/SwiftCrossUI/Views/HStack.swift | 32 ++- .../Views/HotReloadableView.swift | 43 ++-- Sources/SwiftCrossUI/Views/Image.swift | 47 ++-- Sources/SwiftCrossUI/Views/List.swift | 76 +++--- Sources/SwiftCrossUI/Views/Menu.swift | 37 +-- .../Views/Modifiers/AlertModifier.swift | 29 ++- .../Views/Modifiers/EnvironmentModifier.swift | 28 ++- .../Modifiers/Handlers/OnChangeModifier.swift | 13 +- .../Modifiers/Handlers/OnHoverModifier.swift | 35 +-- .../Handlers/OnTapGestureModifier.swift | 37 +-- .../Layout/AspectRatioModifier.swift | 51 ++-- .../Modifiers/Layout/BackgroundModifier.swift | 49 ++-- .../Modifiers/Layout/FixedSizeModifier.swift | 44 ++-- .../Modifiers/Layout/FrameModifier.swift | 81 ++++--- .../Modifiers/Layout/OverlayModifier.swift | 49 ++-- .../Modifiers/Layout/PaddingModifier.swift | 43 ++-- .../Lifecycle/OnDisappearModifier.swift | 28 ++- .../Modifiers/Lifecycle/TaskModifier.swift | 17 +- .../Views/Modifiers/PreferenceModifier.swift | 12 +- .../Views/Modifiers/SheetModifier.swift | 39 ++- .../Style/CornerRadiusModifier.swift | 35 ++- Sources/SwiftCrossUI/Views/OptionalView.swift | 51 ++-- Sources/SwiftCrossUI/Views/Picker.swift | 29 ++- Sources/SwiftCrossUI/Views/ProgressView.swift | 40 ++-- Sources/SwiftCrossUI/Views/ScrollView.swift | 129 +++++----- Sources/SwiftCrossUI/Views/Shapes/Shape.swift | 62 ++--- .../Views/Shapes/StyledShape.swift | 62 ++--- Sources/SwiftCrossUI/Views/Slider.swift | 62 ++--- Sources/SwiftCrossUI/Views/Spacer.swift | 25 +- Sources/SwiftCrossUI/Views/SplitView.swift | 97 ++++---- Sources/SwiftCrossUI/Views/Table.swift | 152 +++++++----- Sources/SwiftCrossUI/Views/Text.swift | 23 +- Sources/SwiftCrossUI/Views/TextEditor.swift | 41 ++-- Sources/SwiftCrossUI/Views/TextField.swift | 51 ++-- Sources/SwiftCrossUI/Views/ToggleButton.swift | 21 +- Sources/SwiftCrossUI/Views/ToggleSwitch.swift | 29 ++- Sources/SwiftCrossUI/Views/TupleView.swift | 40 +++- .../SwiftCrossUI/Views/TupleView.swift.gyb | 40 +++- Sources/SwiftCrossUI/Views/TypeSafeView.swift | 43 +++- Sources/SwiftCrossUI/Views/VStack.swift | 32 ++- Sources/SwiftCrossUI/Views/View.swift | 88 ++++--- Sources/SwiftCrossUI/Views/WebView.swift | 38 +-- Sources/SwiftCrossUI/Views/ZStack.swift | 54 +++-- .../UIViewControllerRepresentable.swift | 4 +- .../UIKitBackend/UIViewRepresentable.swift | 24 +- .../SwiftCrossUITests/SwiftCrossUITests.swift | 5 + 74 files changed, 1882 insertions(+), 1444 deletions(-) diff --git a/Benchmarks/LayoutPerformanceBenchmark/LayoutPerformanceBenchmark.swift b/Benchmarks/LayoutPerformanceBenchmark/LayoutPerformanceBenchmark.swift index 539f533013e..d19d5ae0ba4 100644 --- a/Benchmarks/LayoutPerformanceBenchmark/LayoutPerformanceBenchmark.swift +++ b/Benchmarks/LayoutPerformanceBenchmark/LayoutPerformanceBenchmark.swift @@ -35,8 +35,8 @@ 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) + _ = node.computeLayout(proposedSize: size, environment: environment) + _ = node.commit() } #if BENCHMARK_VIZ diff --git a/Examples/Sources/WindowingExample/WindowingApp.swift b/Examples/Sources/WindowingExample/WindowingApp.swift index 40491519239..29056b06053 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 32adf784ebc..aa5becc71ac 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() diff --git a/Sources/AppKitBackend/NSViewRepresentable.swift b/Sources/AppKitBackend/NSViewRepresentable.swift index 115b323a8a3..31238dce7b8 100644 --- a/Sources/AppKitBackend/NSViewRepresentable.swift +++ b/Sources/AppKitBackend/NSViewRepresentable.swift @@ -139,15 +139,14 @@ extension View where Self: NSViewRepresentable { } } - public func update( + public func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, proposedSize: SIMD2, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - guard let backend = backend as? AppKitBackend else { + backend: Backend + ) -> ViewLayoutResult { + guard backend is AppKitBackend else { fatalError("NSViewRepresentable updated by \(Backend.self)") } @@ -160,11 +159,17 @@ extension View where Self: NSViewRepresentable { context: representingWidget.context! ) - if !dryRun { - backend.setSize(of: representingWidget, to: size.size) - } + return ViewLayoutResult.leafView(size: size) + } - return ViewUpdateResult.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) } } diff --git a/Sources/Gtk/Generated/Entry.swift b/Sources/Gtk/Generated/Entry.swift index 26b74f887b1..97cc334be7f 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 ef229a31489..39c9f528a09 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 5247e75482a..3d286c22047 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 be138278de5..455f4f97724 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 641cd3ecf31..df026896e33 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 0a68d9b281c..03aee0c25ba 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 a7982980d92..c76a025c13d 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/Layout/LayoutSystem.swift b/Sources/SwiftCrossUI/Layout/LayoutSystem.swift index 4618efff1ef..bd0de8df004 100644 --- a/Sources/SwiftCrossUI/Layout/LayoutSystem.swift +++ b/Sources/SwiftCrossUI/Layout/LayoutSystem.swift @@ -45,29 +45,36 @@ public enum LayoutSystem { } public struct LayoutableChild { - private var update: + private var computeLayout: @MainActor ( _ proposedSize: SIMD2, - _ environment: EnvironmentValues, - _ dryRun: Bool - ) -> ViewUpdateResult + _ environment: EnvironmentValues + ) -> ViewLayoutResult + private var _commit: @MainActor () -> ViewLayoutResult var tag: String? public init( - update: @escaping @MainActor (SIMD2, EnvironmentValues, Bool) -> ViewUpdateResult, + computeLayout: @escaping @MainActor (SIMD2, 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( + public func computeLayout( proposedSize: SIMD2, environment: EnvironmentValues, dryRun: Bool = false - ) -> ViewUpdateResult { - update(proposedSize, environment, dryRun) + ) -> ViewLayoutResult { + computeLayout(proposedSize, environment) + } + + @MainActor + public func commit() -> ViewLayoutResult { + _commit() } } @@ -77,61 +84,43 @@ 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( + public static func computeStackLayout( container: Backend.Widget, children: [LayoutableChild], proposedSize: SIMD2, environment: EnvironmentValues, backend: Backend, - dryRun: Bool, inheritStackLayoutParticipation: Bool = false - ) -> ViewUpdateResult { + ) -> ViewLayoutResult { let spacing = environment.layoutSpacing - let alignment = environment.layoutAlignment let orientation = environment.layoutOrientation - var renderedChildren: [ViewUpdateResult] = Array( - repeating: ViewUpdateResult.leafView(size: .empty), + var renderedChildren: [ViewLayoutResult] = Array( + repeating: ViewLayoutResult.leafView(size: .empty), 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. + // 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) - for (i, child) in children.enumerated() { - let result = child.update( + let flexibilities = children.enumerated().map { i, child in + let result = child.computeLayout( proposedSize: proposedSize, - environment: environment, - dryRun: true + environment: environment ) isHidden[i] = !result.participatesInStackLayouts - } - - // 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/ - 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) + result.size.maximumWidth - Double(result.size.minimumWidth) case .vertical: - size.maximumHeight - Double(size.minimumHeight) + result.size.maximumHeight - Double(result.size.minimumHeight) } } + let visibleChildrenCount = isHidden.filter { hidden in + !hidden + }.count + let totalSpacing = max(visibleChildrenCount - 1, 0) * spacing let sortedChildren = zip(children.enumerated(), flexibilities) .sorted { first, second in first.1 <= second.1 @@ -146,10 +135,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( @@ -179,13 +167,12 @@ public enum LayoutSystem { proposedWidth = Double(proposedSize.x) } - let childResult = child.update( + let childResult = child.computeLayout( proposedSize: SIMD2( Int(proposedWidth.rounded(.towardZero)), Int(proposedHeight.rounded(.towardZero)) ), - environment: environment, - dryRun: dryRun + environment: environment ) renderedChildren[index] = childResult @@ -249,53 +236,13 @@ public enum LayoutSystem { + totalSpacing } - if !dryRun { - backend.setSize(of: container, to: size) - - 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 - } - - // 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) - } - - backend.setPosition(ofChildAt: index, in: container, to: SIMD2(x, y)) - - switch orientation { - case .horizontal: - x += childSize.size.size.x + spacing - case .vertical: - y += childSize.size.size.y + spacing - } - } - } - // 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 } - return ViewUpdateResult( + return ViewLayoutResult( size: ViewSize( size: size, idealSize: idealSize, @@ -310,4 +257,58 @@ public enum LayoutSystem { childResults: renderedChildren ) } + + @MainActor + public static func commitStackLayout( + container: Backend.Widget, + children: [LayoutableChild], + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + let size = layout.size.size + backend.setSize(of: container, to: size) + + let renderedChildren = children.map { $0.commit() } + + let alignment = environment.layoutAlignment + let spacing = environment.layoutSpacing + let orientation = environment.layoutOrientation + + var x = 0 + var y = 0 + 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 (orientation, alignment) { + case (.vertical, .leading): + x = 0 + case (.horizontal, .leading): + y = 0 + case (.vertical, .center): + x = (size.x - child.size.size.x) / 2 + case (.horizontal, .center): + y = (size.y - child.size.size.y) / 2 + case (.vertical, .trailing): + x = (size.x - child.size.size.x) + case (.horizontal, .trailing): + y = (size.y - child.size.size.y) + } + + backend.setPosition(ofChildAt: index, in: container, to: SIMD2(x, y)) + + switch orientation { + case .horizontal: + x += child.size.size.x + spacing + case .vertical: + y += child.size.size.y + spacing + } + } + } } diff --git a/Sources/SwiftCrossUI/Layout/ViewUpdateResult.swift b/Sources/SwiftCrossUI/Layout/ViewUpdateResult.swift index bd907bc7281..246bbf4fce7 100644 --- a/Sources/SwiftCrossUI/Layout/ViewUpdateResult.swift +++ b/Sources/SwiftCrossUI/Layout/ViewUpdateResult.swift @@ -1,4 +1,4 @@ -public struct ViewUpdateResult { +public struct ViewLayoutResult { public var size: ViewSize public var preferences: PreferenceValues @@ -12,7 +12,7 @@ public struct ViewUpdateResult { public init( size: ViewSize, - childResults: [ViewUpdateResult], + childResults: [ViewLayoutResult], preferencesOverlay: PreferenceValues? = nil ) { self.size = size @@ -24,7 +24,7 @@ public struct ViewUpdateResult { } public static func leafView(size: ViewSize) -> Self { - ViewUpdateResult(size: size, preferences: .default) + ViewLayoutResult(size: size, preferences: .default) } public var participatesInStackLayouts: Bool { diff --git a/Sources/SwiftCrossUI/Scenes/WindowGroupNode.swift b/Sources/SwiftCrossUI/Scenes/WindowGroupNode.swift index 717906b4845..f36efa8765b 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,7 +136,7 @@ public final class WindowGroupNode: SceneGraphNode { } .with(\.window, window) - let dryRunResult: ViewUpdateResult? + let dryRunResult: ViewLayoutResult? if !windowSizeIsFinal { // Perform a dry-run update of the root view to check if the window // needs to change size. @@ -168,6 +168,16 @@ public final class WindowGroupNode: SceneGraphNode { ) } } else { + // We don't use this result, but we do need to do a dry run to + // respect the assumptions of ViewGraph (each non dry run must + // follow a dry run). + _ = viewGraph.update( + with: newScene?.body, + proposedSize: proposedWindowSize, + environment: environment, + dryRun: true + ) + dryRunResult = nil } diff --git a/Sources/SwiftCrossUI/Values/Color.swift b/Sources/SwiftCrossUI/Values/Color.swift index aa9c18f6d02..dcd95dc80f7 100644 --- a/Sources/SwiftCrossUI/Values/Color.swift +++ b/Sources/SwiftCrossUI/Values/Color.swift @@ -69,18 +69,13 @@ extension Color: ElementaryView { backend.createColorableRectangle() } - func update( + func computeLayout( _ widget: Backend.Widget, proposedSize: SIMD2, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - if !dryRun { - backend.setSize(of: widget, to: proposedSize) - backend.setColor(ofColorableRectangle: widget, to: self) - } - return ViewUpdateResult.leafView( + backend: Backend + ) -> ViewLayoutResult { + ViewLayoutResult.leafView( size: ViewSize( size: proposedSize, idealSize: SIMD2(10, 10), @@ -91,4 +86,14 @@ extension Color: ElementaryView { ) ) } + + func commit( + _ widget: Backend.Widget, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + backend.setSize(of: widget, to: layout.size.size) + backend.setColor(ofColorableRectangle: widget, to: self) + } } diff --git a/Sources/SwiftCrossUI/ViewGraph/AnyViewGraphNode.swift b/Sources/SwiftCrossUI/ViewGraph/AnyViewGraphNode.swift index 2edc517100a..9fa4b09bccd 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 + _ 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) + 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 23bba12d409..b84853a9d1b 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) + _ 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: .empty)) } - 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 b868d9ab7e9..a14bf62ef1a 100644 --- a/Sources/SwiftCrossUI/ViewGraph/ViewGraph.swift +++ b/Sources/SwiftCrossUI/ViewGraph/ViewGraph.swift @@ -22,7 +22,7 @@ public class ViewGraph { /// change as opposed to a window resizing event). private var windowSize: SIMD2 /// 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 @@ -41,7 +41,7 @@ public class ViewGraph { self.view = view windowSize = .zero parentEnvironment = environment - currentRootViewResult = ViewUpdateResult.leafView(size: .empty) + currentRootViewResult = ViewLayoutResult.leafView(size: .empty) setIncomingURLHandler = backend.setIncomingURLHandler(to:) } @@ -53,15 +53,23 @@ public class ViewGraph { proposedSize: SIMD2, environment: EnvironmentValues, dryRun: Bool - ) -> ViewUpdateResult { + ) -> ViewLayoutResult { parentEnvironment = environment windowSize = proposedSize - let result = rootNode.update( - with: newView ?? view, - proposedSize: proposedSize, - environment: parentEnvironment, - dryRun: dryRun - ) + + // TODO: Refactor view graph node to be computeLayout+commit based + // instead of update based. + let result: ViewLayoutResult + if dryRun { + result = rootNode.computeLayout( + with: newView ?? view, + proposedSize: proposedSize, + environment: parentEnvironment + ) + } else { + result = rootNode.commit() + } + self.currentRootViewResult = result if isFirstUpdate, !dryRun { setIncomingURLHandler { url in diff --git a/Sources/SwiftCrossUI/ViewGraph/ViewGraphNode.swift b/Sources/SwiftCrossUI/ViewGraph/ViewGraphNode.swift index b80b8486993..1f698b32f00 100644 --- a/Sources/SwiftCrossUI/ViewGraph/ViewGraphNode.swift +++ b/Sources/SwiftCrossUI/ViewGraph/ViewGraphNode.swift @@ -37,10 +37,10 @@ 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: [SIMD2: 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 @@ -70,7 +70,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 +136,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 +158,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 { + 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,7 +176,8 @@ 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 } @@ -204,33 +187,33 @@ public class ViewGraphNode: Sendable { // 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 let currentLayout, !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) + if ((Double(lastProposedSize.x) >= currentLayout.size.maximumWidth + && Double(proposedSize.x) >= currentLayout.size.maximumWidth) || proposedSize.x == lastProposedSize.x) - && ((Double(lastProposedSize.y) >= currentResult.size.maximumHeight - && Double(proposedSize.y) >= currentResult.size.maximumHeight) + && ((Double(lastProposedSize.y) >= currentLayout.size.maximumHeight + && Double(proposedSize.y) >= currentLayout.size.maximumHeight) || proposedSize.y == lastProposedSize.y) { - return currentResult + return currentLayout } // 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 + currentLayout.size.maximumWidth, + currentLayout.size.maximumHeight ) let minimumSize = SIMD2( - Double(currentResult.size.minimumWidth), - Double(currentResult.size.minimumHeight) + Double(currentLayout.size.minimumWidth), + Double(currentLayout.size.minimumHeight) ) if maximumSize == minimumSize { - return currentResult + return currentLayout } } @@ -253,30 +236,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: .empty) + } + + 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 index 7004374fad1..c80ada76b99 100644 --- a/Sources/SwiftCrossUI/Views/AnyView.swift +++ b/Sources/SwiftCrossUI/Views/AnyView.swift @@ -52,19 +52,17 @@ public struct AnyView: TypeSafeView { /// 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( + func computeLayout( _ widget: Backend.Widget, children: AnyViewChildren, proposedSize: SIMD2, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - var (viewTypesMatched, result) = children.node.updateWithNewView( + backend: Backend + ) -> ViewLayoutResult { + var (viewTypesMatched, result) = children.node.computeLayoutWithNewView( child, proposedSize, - environment, - dryRun + environment ) // If the new view's type doesn't match the old view's type then we need to create a new @@ -79,29 +77,34 @@ public struct AnyView: TypeSafeView { // 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( + let (_, newResult) = children.node.computeLayoutWithNewView( child, proposedSize, - environment, - dryRun + environment ) 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 { + return result + } + + func commit( + _ widget: Backend.Widget, + children: AnyViewChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + if 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) - } + _ = children.node.commit() - return result + backend.setSize(of: widget, to: layout.size.size) } } diff --git a/Sources/SwiftCrossUI/Views/Button.swift b/Sources/SwiftCrossUI/Views/Button.swift index 739d1ba407d..2145d94cd32 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, 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(fixedSize: 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.size) } } diff --git a/Sources/SwiftCrossUI/Views/Checkbox.swift b/Sources/SwiftCrossUI/Views/Checkbox.swift index 7c80d354f71..7ddd8a1766c 100644 --- a/Sources/SwiftCrossUI/Views/Checkbox.swift +++ b/Sources/SwiftCrossUI/Views/Checkbox.swift @@ -12,22 +12,26 @@ struct Checkbox: ElementaryView, View { return backend.createCheckbox() } - public func update( + func computeLayout( _ 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( + backend: Backend + ) -> ViewLayoutResult { + return ViewLayoutResult.leafView( size: ViewSize(fixedSize: backend.naturalSize(of: widget)) ) } + + func commit( + _ widget: Backend.Widget, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + backend.updateCheckbox(widget, environment: environment) { newActiveState in + active.wrappedValue = newActiveState + } + backend.setState(ofCheckbox: widget, to: active.wrappedValue) + } } diff --git a/Sources/SwiftCrossUI/Views/EitherView.swift b/Sources/SwiftCrossUI/Views/EitherView.swift index fac32056ff0..916b4d88727 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, 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.size) } } diff --git a/Sources/SwiftCrossUI/Views/ElementaryView.swift b/Sources/SwiftCrossUI/Views/ElementaryView.swift index df835d0e016..cf55fd78fad 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, 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, 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 b541a72366f..44ff860c8d2 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, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - ViewUpdateResult.leafView(size: .empty) + backend: Backend + ) -> ViewLayoutResult { + ViewLayoutResult.leafView(size: .empty) } + + 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 81ad975c1a5..29572a95eae 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, 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,49 @@ extension ForEach: TypeSafeView, View where Child: View { children.nodes.removeLast(unused) } - return LayoutSystem.updateStackLayout( + return LayoutSystem.computeStackLayout( container: widget, children: layoutableChildren, 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 + ) + }, + layout: layout, + environment: environment, + backend: backend ) } } diff --git a/Sources/SwiftCrossUI/Views/GeometryReader.swift b/Sources/SwiftCrossUI/Views/GeometryReader.swift index 79ff46d3d1d..7f49e525872 100644 --- a/Sources/SwiftCrossUI/Views/GeometryReader.swift +++ b/Sources/SwiftCrossUI/Views/GeometryReader.swift @@ -52,14 +52,13 @@ public struct GeometryReader: TypeSafeView, View { return backend.createContainer() } - func update( + func computeLayout( _ widget: Backend.Widget, children: GeometryReaderChildren, proposedSize: SIMD2, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { + backend: Backend + ) -> ViewLayoutResult { let view = content(GeometryProxy(size: proposedSize)) let environment = environment.with(\.layoutAlignment, .leading) @@ -85,19 +84,13 @@ public struct GeometryReader: TypeSafeView, View { // 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( + let contentResult = contentNode.computeLayout( with: view, proposedSize: proposedSize, - environment: environment, - dryRun: dryRun + environment: environment ) - if !dryRun { - backend.setPosition(ofChildAt: 0, in: widget, to: .zero) - backend.setSize(of: widget, to: proposedSize) - } - - return ViewUpdateResult( + return ViewLayoutResult( size: ViewSize( size: proposedSize, idealSize: SIMD2(10, 10), @@ -109,6 +102,18 @@ public struct GeometryReader: TypeSafeView, View { childResults: [contentResult] ) } + + func commit( + _ widget: Backend.Widget, + children: GeometryReaderChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + _ = children.node?.commit() + backend.setPosition(ofChildAt: 0, in: widget, to: .zero) + backend.setSize(of: widget, to: layout.size.size) + } } class GeometryReaderChildren: ViewGraphNodeChildren { diff --git a/Sources/SwiftCrossUI/Views/Group.swift b/Sources/SwiftCrossUI/Views/Group.swift index 7d967f91e1d..41a2786b50f 100644 --- a/Sources/SwiftCrossUI/Views/Group.swift +++ b/Sources/SwiftCrossUI/Views/Group.swift @@ -23,22 +23,36 @@ public struct Group: View { return container } - public func update( + public func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, proposedSize: SIMD2, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - LayoutSystem.updateStackLayout( + backend: Backend + ) -> ViewLayoutResult { + LayoutSystem.computeStackLayout( container: widget, children: layoutableChildren(backend: backend, children: children), proposedSize: proposedSize, environment: environment, backend: backend, - dryRun: dryRun, inheritStackLayoutParticipation: true ) } + + public func commit( + _ widget: Backend.Widget, + children: any ViewGraphNodeChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + LayoutSystem.commitStackLayout( + container: widget, + children: layoutableChildren(backend: backend, children: children), + layout: layout, + environment: environment, + backend: backend + ) + } } diff --git a/Sources/SwiftCrossUI/Views/HStack.swift b/Sources/SwiftCrossUI/Views/HStack.swift index 097df08c0cd..85756c9919a 100644 --- a/Sources/SwiftCrossUI/Views/HStack.swift +++ b/Sources/SwiftCrossUI/Views/HStack.swift @@ -29,15 +29,14 @@ public struct HStack: View { return vStack } - public func update( + public func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, proposedSize: SIMD2, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - return LayoutSystem.updateStackLayout( + backend: Backend + ) -> ViewLayoutResult { + return LayoutSystem.computeStackLayout( container: widget, children: layoutableChildren(backend: backend, children: children), proposedSize: proposedSize, @@ -46,8 +45,27 @@ public struct HStack: View { .with(\.layoutOrientation, .horizontal) .with(\.layoutAlignment, alignment.asStackAlignment) .with(\.layoutSpacing, spacing), - backend: backend, - dryRun: dryRun + backend: backend + ) + } + + public func commit( + _ widget: Backend.Widget, + children: any ViewGraphNodeChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + LayoutSystem.commitStackLayout( + container: widget, + children: layoutableChildren(backend: backend, children: children), + layout: layout, + environment: + environment + .with(\.layoutOrientation, .horizontal) + .with(\.layoutAlignment, alignment.asStackAlignment) + .with(\.layoutSpacing, spacing), + backend: backend ) } } diff --git a/Sources/SwiftCrossUI/Views/HotReloadableView.swift b/Sources/SwiftCrossUI/Views/HotReloadableView.swift index 5c005057480..44d46dc527a 100644 --- a/Sources/SwiftCrossUI/Views/HotReloadableView.swift +++ b/Sources/SwiftCrossUI/Views/HotReloadableView.swift @@ -51,19 +51,17 @@ public struct HotReloadableView: TypeSafeView { /// 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( + func computeLayout( _ widget: Backend.Widget, children: HotReloadableViewChildren, proposedSize: SIMD2, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - var (viewTypeMatched, result) = children.node.updateWithNewView( + backend: Backend + ) -> ViewLayoutResult { + var (viewTypeMatched, result) = children.node.computeLayoutWithNewView( child, proposedSize, - environment, - dryRun + environment ) if !viewTypeMatched { @@ -78,28 +76,35 @@ public struct HotReloadableView: TypeSafeView { // We can assume that the view types match since we just recreated the view // on the line above. - let (_, newResult) = children.node.updateWithNewView( + let (_, newResult) = children.node.computeLayoutWithNewView( child, proposedSize, - environment, - dryRun + environment ) 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 - } + return result + } - backend.setSize(of: widget, to: result.size.size) + func commit( + _ widget: Backend.Widget, + children: HotReloadableViewChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + 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 } - return result + _ = children.node.commit() + + backend.setSize(of: widget, to: layout.size.size) } } diff --git a/Sources/SwiftCrossUI/Views/Image.swift b/Sources/SwiftCrossUI/Views/Image.swift index 2f3be3e6f82..de02903564c 100644 --- a/Sources/SwiftCrossUI/Views/Image.swift +++ b/Sources/SwiftCrossUI/Views/Image.swift @@ -66,14 +66,13 @@ extension Image: TypeSafeView { children.container.into() } - func update( + func computeLayout( _ widget: Backend.Widget, children: _ImageChildren, proposedSize: SIMD2, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { + backend: Backend + ) -> ViewLayoutResult { let image: ImageFormats.Image? if source != children.cachedImageSource { switch source { @@ -117,21 +116,32 @@ extension Image: TypeSafeView { size = ViewSize(fixedSize: idealSize) } - let hasResized = children.cachedImageDisplaySize != size.size - if !dryRun - && (children.imageChanged - || hasResized - || (backend.requiresImageUpdateOnScaleFactorChange - && children.lastScaleFactor != environment.windowScaleFactor)) + return ViewLayoutResult.leafView(size: size) + } + + func commit( + _ widget: Backend.Widget, + children: _ImageChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + let size = layout.size.size + let hasResized = children.cachedImageDisplaySize != size + children.cachedImageDisplaySize = layout.size.size + if children.imageChanged + || hasResized + || (backend.requiresImageUpdateOnScaleFactorChange + && children.lastScaleFactor != environment.windowScaleFactor) { - if let image { + if let image = children.cachedImage { backend.updateImageView( children.imageWidget.into(), rgbaData: image.bytes, width: image.width, height: image.height, - targetWidth: size.size.x, - targetHeight: size.size.y, + targetWidth: size.x, + targetHeight: size.y, dataHasChanged: children.imageChanged, environment: environment ) @@ -147,15 +157,8 @@ extension Image: TypeSafeView { 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) + backend.setSize(of: children.container.into(), to: size) + backend.setSize(of: children.imageWidget.into(), to: size) } } diff --git a/Sources/SwiftCrossUI/Views/List.swift b/Sources/SwiftCrossUI/Views/List.swift index 4283e5249ed..6a988d48626 100644 --- a/Sources/SwiftCrossUI/Views/List.swift +++ b/Sources/SwiftCrossUI/Views/List.swift @@ -98,14 +98,13 @@ public struct List: TypeSafeView, View backend.createSelectableListView() } - func update( + func computeLayout( _ widget: Backend.Widget, children: Children, proposedSize: SIMD2, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { + backend: Backend + ) -> ViewLayoutResult { // 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) @@ -138,18 +137,17 @@ public struct List: TypeSafeView, View children.nodes.removeLast(children.nodes.count - rowCount) } - var childResults: [ViewUpdateResult] = [] + var childResults: [ViewLayoutResult] = [] for (rowView, node) in zip(rowViews, children.nodes) { - let preferredSize = node.update( + let preferredSize = node.computeLayout( with: rowView, proposedSize: SIMD2( max(proposedSize.x, minimumRowSize.x) - baseRowPadding.axisTotals.x, max(proposedSize.y, minimumRowSize.y) - baseRowPadding.axisTotals.y ), - environment: environment, - dryRun: true + environment: environment ).size - let childResult = node.update( + let childResult = node.computeLayout( with: nil, proposedSize: SIMD2( max(proposedSize.x, minimumRowSize.x) - horizontalBasePadding, @@ -158,8 +156,7 @@ public struct List: TypeSafeView, View minimumRowSize.y - baseRowPadding.axisTotals.y ) ), - environment: environment, - dryRun: dryRun + environment: environment ) childResults.append(childResult) } @@ -177,28 +174,7 @@ public struct List: TypeSafeView, View }.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( + return ViewLayoutResult( size: ViewSize( size: size, idealSize: SIMD2( @@ -215,6 +191,40 @@ public struct List: TypeSafeView, View childResults: childResults ) } + + func commit( + _ widget: Backend.Widget, + children: Children, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + let baseRowPadding = backend.baseItemPadding(ofSelectableListView: widget) + let verticalBasePadding = baseRowPadding.axisTotals.y + + let childResults = children.nodes.map { $0.commit() } + 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: layout.size.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) + } } class ListViewChildren: ViewGraphNodeChildren { diff --git a/Sources/SwiftCrossUI/Views/Menu.swift b/Sources/SwiftCrossUI/Views/Menu.swift index 7773db5215c..1b87b9d967b 100644 --- a/Sources/SwiftCrossUI/Views/Menu.swift +++ b/Sources/SwiftCrossUI/Views/Menu.swift @@ -64,19 +64,28 @@ extension Menu: TypeSafeView { [] } - func update( + func computeLayout( _ widget: Backend.Widget, children: MenuStorage, proposedSize: SIMD2, 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(fixedSize: size)) + } + func commit( + _ widget: Backend.Widget, + children: MenuStorage, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + let size = layout.size.size let content = resolve().content switch backend.menuImplementationStyle { case .dynamicPopover: @@ -102,14 +111,12 @@ extension Menu: TypeSafeView { } ) - if !dryRun { - backend.setSize(of: widget, to: size) - children.updateMenuIfShown( - content: content, - environment: environment, - backend: backend - ) - } + backend.setSize(of: widget, to: size) + children.updateMenuIfShown( + content: content, + environment: environment, + backend: backend + ) case .menuButton: let menu = children.menu as? Backend.Menu ?? backend.createPopoverMenu() children.menu = menu @@ -120,12 +127,8 @@ extension Menu: TypeSafeView { ) backend.updateButton(widget, label: label, menu: menu, environment: environment) - if !dryRun { - backend.setSize(of: widget, to: size) - } + 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 index 3f3b9e5dfa9..79851ef4d29 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/AlertModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/AlertModifier.swift @@ -58,24 +58,35 @@ struct AlertModifierView: TypeSafeView { ) } - func asWidget(_ children: Children, backend: Backend) -> Backend.Widget { + func asWidget( + _ children: Children, + backend: Backend + ) -> Backend.Widget { children.childNode.widget.into() } - func update( + func computeLayout( _ widget: Backend.Widget, children: Children, proposedSize: SIMD2, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - let childResult = children.childNode.update( + backend: Backend + ) -> ViewLayoutResult { + children.childNode.computeLayout( with: child, proposedSize: proposedSize, - environment: environment, - dryRun: dryRun + environment: environment ) + } + + func commit( + _ widget: Backend.Widget, + children: AlertModifierViewChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + _ = children.childNode.commit() if isPresented.wrappedValue && children.alert == nil { let alert = backend.createAlert() @@ -101,8 +112,6 @@ struct AlertModifierView: TypeSafeView { ) children.alert = nil } - - return childResult } } diff --git a/Sources/SwiftCrossUI/Views/Modifiers/EnvironmentModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/EnvironmentModifier.swift index 801354c2130..20f2cc5d8b5 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, 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 index 9c226532d36..aced06c2ee9 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnChangeModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnChangeModifier.swift @@ -24,14 +24,14 @@ struct OnChangeModifier: View { var action: () -> Void var initial: Bool - func update( + // TODO: Should this go in computeLayout or commit? + func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, proposedSize: SIMD2, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { + backend: Backend + ) -> ViewLayoutResult { if let previousValue = previousValue, value != previousValue { action() } else if initial && previousValue == nil { @@ -42,13 +42,12 @@ struct OnChangeModifier: View { previousValue = value } - return defaultUpdate( + return defaultComputeLayout( widget, children: children, proposedSize: proposedSize, environment: environment, - backend: backend, - dryRun: dryRun + backend: backend ) } } diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnHoverModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnHoverModifier.swift index 0d1d000f848..91093235407 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnHoverModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnHoverModifier.swift @@ -32,28 +32,33 @@ struct OnHoverModifier: TypeSafeView { backend.createHoverTarget(wrapping: children.child0.widget.into()) } - func update( + func computeLayout( _ widget: Backend.Widget, children: Children, proposedSize: SIMD2, 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: Children, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + let size = children.child0.commit().size.size + backend.setSize(of: widget, to: size) + backend.updateHoverTarget( + widget, environment: environment, - dryRun: dryRun + action: action ) - 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/OnTapGestureModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnTapGestureModifier.swift index b8688eabbd4..a43c8bc922e 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, 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.size + backend.setSize(of: widget, to: size) + 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 index 2fc864a04ef..f44a6f94453 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/Layout/AspectRatioModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/Layout/AspectRatioModifier.swift @@ -63,23 +63,21 @@ struct AspectRatioView: TypeSafeView { return container } - func update( + func computeLayout( _ widget: Backend.Widget, children: TupleViewChildren1, proposedSize: SIMD2, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { + backend: Backend + ) -> ViewLayoutResult { let evaluatedAspectRatio: Double if let aspectRatio { evaluatedAspectRatio = aspectRatio == 0 ? 1 : aspectRatio } else { - let childResult = children.child0.update( + let childResult = children.child0.computeLayout( with: body.view0, proposedSize: proposedSize, - environment: environment, - dryRun: true + environment: environment ) evaluatedAspectRatio = childResult.size.idealAspectRatio } @@ -90,11 +88,10 @@ struct AspectRatioView: TypeSafeView { contentMode: contentMode ) - let childResult = children.child0.update( + let childResult = children.child0.computeLayout( with: nil, proposedSize: proposedFrameSize, - environment: environment, - dryRun: dryRun + environment: environment ) let frameSize = LayoutSystem.frameSize( @@ -103,19 +100,7 @@ struct AspectRatioView: TypeSafeView { 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( + return ViewLayoutResult( size: ViewSize( size: frameSize, idealSize: LayoutSystem.frameSize( @@ -145,4 +130,24 @@ struct AspectRatioView: TypeSafeView { childResults: [childResult] ) } + + func commit( + _ widget: Backend.Widget, + children: TupleViewChildren1, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + // 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 childResult = children.child0.commit() + print(childResult.size.size) + let childPosition = Alignment.center.position( + ofChild: childResult.size.size, + in: layout.size.size + ) + backend.setPosition(ofChildAt: 0, in: widget, to: childPosition) + backend.setSize(of: widget, to: layout.size.size) + } } diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Layout/BackgroundModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Layout/BackgroundModifier.swift index 8b34d55c80f..3c135a1ce6f 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/Layout/BackgroundModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/Layout/BackgroundModifier.swift @@ -34,26 +34,23 @@ struct BackgroundModifier: TypeSafeView { body.asWidget(children, backend: backend) } - func update( + func computeLayout( _ widget: Backend.Widget, children: TupleView2.Children, proposedSize: SIMD2, 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 + environment: environment ) let backgroundSize = backgroundResult.size @@ -62,17 +59,7 @@ struct BackgroundModifier: TypeSafeView { max(backgroundSize.size.y, foregroundSize.size.y) ) - 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) - - backend.setSize(of: widget, to: frameSize) - } - - return ViewUpdateResult( + return ViewLayoutResult( size: ViewSize( size: frameSize, idealSize: SIMD2( @@ -95,4 +82,24 @@ struct BackgroundModifier: TypeSafeView { childResults: [backgroundResult, foregroundResult] ) } + + public func commit( + _ widget: Backend.Widget, + children: TupleView2.Children, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + let frameSize = layout.size.size + let backgroundSize = children.child0.commit().size + let foregroundSize = children.child1.commit().size + + 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) + + backend.setSize(of: widget, to: frameSize) + } } diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Layout/FixedSizeModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Layout/FixedSizeModifier.swift index fa6300781c3..85c7363ef47 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/Layout/FixedSizeModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/Layout/FixedSizeModifier.swift @@ -37,19 +37,17 @@ struct FixedSizeModifier: TypeSafeView { return container } - func update( + func computeLayout( _ widget: Backend.Widget, children: TupleViewChildren1, proposedSize: SIMD2, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - let probingChildResult = children.child0.update( + backend: Backend + ) -> ViewLayoutResult { + let probingChildResult = children.child0.computeLayout( with: body.view0, proposedSize: proposedSize, - environment: environment, - dryRun: true + environment: environment ) var frameSize = probingChildResult.size.size @@ -61,23 +59,13 @@ struct FixedSizeModifier: TypeSafeView { frameSize.y = probingChildResult.size.idealHeightForProposedWidth } - let childResult = children.child0.update( + let childResult = children.child0.computeLayout( with: body.view0, proposedSize: frameSize, - environment: environment, - dryRun: dryRun + environment: environment ) - 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( + return ViewLayoutResult( size: ViewSize( size: frameSize, idealSize: childResult.size.idealSize, @@ -91,4 +79,20 @@ struct FixedSizeModifier: TypeSafeView { 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.size, + in: layout.size.size + ) + backend.setPosition(ofChildAt: 0, in: widget, to: childPosition) + backend.setSize(of: widget, to: layout.size.size) + } } diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Layout/FrameModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Layout/FrameModifier.swift index eebfc350c81..953f85aac85 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/Layout/FrameModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/Layout/FrameModifier.swift @@ -72,24 +72,22 @@ struct StrictFrameView: TypeSafeView { return container } - func update( + func computeLayout( _ widget: Backend.Widget, children: TupleViewChildren1, proposedSize: SIMD2, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { + backend: Backend + ) -> ViewLayoutResult { let proposedSize = SIMD2( width ?? proposedSize.x, height ?? proposedSize.y ) - let childResult = children.child0.update( + let childResult = children.child0.computeLayout( with: body.view0, proposedSize: proposedSize, - environment: environment, - dryRun: dryRun + environment: environment ) let childSize = childResult.size @@ -97,14 +95,6 @@ struct StrictFrameView: TypeSafeView { width ?? childSize.size.x, height ?? childSize.size.y ) - 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 @@ -132,7 +122,7 @@ struct StrictFrameView: TypeSafeView { idealHeightForProposedWidth = idealHeight } - return ViewUpdateResult( + return ViewLayoutResult( size: ViewSize( size: frameSize, idealSize: SIMD2( @@ -149,6 +139,24 @@ struct StrictFrameView: TypeSafeView { childResults: [childResult] ) } + + func commit( + _ widget: Backend.Widget, + children: TupleViewChildren1, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + let frameSize = layout.size.size + let childSize = children.child0.commit().size + + let childPosition = alignment.position( + ofChild: childSize.size, + in: frameSize + ) + backend.setSize(of: widget, to: frameSize) + backend.setPosition(ofChildAt: 0, in: widget, to: childPosition) + } } /// The implementation for the ``View/frame(width:height:)`` view modifier. @@ -202,14 +210,13 @@ struct FlexibleFrameView: TypeSafeView { return container } - func update( + func computeLayout( _ widget: Backend.Widget, children: TupleViewChildren1, proposedSize: SIMD2, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { + backend: Backend + ) -> ViewLayoutResult { var proposedFrameSize = proposedSize if let minWidth { proposedFrameSize.x = max(proposedFrameSize.x, minWidth) @@ -228,11 +235,10 @@ struct FlexibleFrameView: TypeSafeView { ) } - 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 @@ -297,18 +303,27 @@ struct FlexibleFrameView: TypeSafeView { frameSize.idealSize.y = idealHeight } - 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.size + let childSize = children.child0.commit().size + + let childPosition = alignment.position( + ofChild: childSize.size, + in: frameSize + ) + backend.setSize(of: widget, to: frameSize) + backend.setPosition(ofChildAt: 0, in: widget, to: childPosition) + } } diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Layout/OverlayModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Layout/OverlayModifier.swift index 34edb7438d5..70d62db0ed7 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/Layout/OverlayModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/Layout/OverlayModifier.swift @@ -38,26 +38,23 @@ struct OverlayModifier: TypeSafeView { body.asWidget(children, backend: backend) } - func update( + func computeLayout( _ widget: Backend.Widget, children: TupleView2.Children, proposedSize: SIMD2, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - let contentResult = children.child0.update( + backend: Backend + ) -> ViewLayoutResult { + let contentResult = children.child0.computeLayout( with: body.view0, proposedSize: proposedSize, - environment: environment, - dryRun: dryRun + environment: environment ) let contentSize = contentResult.size - let overlayResult = children.child1.update( + let overlayResult = children.child1.computeLayout( with: body.view1, proposedSize: contentSize.size, - environment: environment, - dryRun: dryRun + environment: environment ) let overlaySize = overlayResult.size @@ -66,17 +63,7 @@ struct OverlayModifier: TypeSafeView { 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( + return ViewLayoutResult( size: ViewSize( size: frameSize, idealSize: contentSize.idealSize, @@ -88,4 +75,24 @@ struct OverlayModifier: TypeSafeView { childResults: [contentResult, overlayResult] ) } + + func commit( + _ widget: Backend.Widget, + children: TupleView2.Children, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + let frameSize = layout.size.size + let contentSize = children.child0.commit().size + let overlaySize = children.child1.commit().size + + 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) + } } diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Layout/PaddingModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Layout/PaddingModifier.swift index c5441093ac0..01d8f6ca8d4 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/Layout/PaddingModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/Layout/PaddingModifier.swift @@ -97,24 +97,24 @@ struct PaddingModifierView: TypeSafeView { return container } - func update( + func computeLayout( _ container: Backend.Widget, children: TupleViewChildren1, proposedSize: SIMD2, 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 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 + environment: environment ) let childSize = childResult.size @@ -124,17 +124,15 @@ struct PaddingModifierView: TypeSafeView { 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( + let idealWidth = childSize.idealWidthForProposedHeight + paddingSize.x + let idealHeight = childSize.idealHeightForProposedWidth + paddingSize.y + return ViewLayoutResult( size: ViewSize( size: size, idealSize: childSize.idealSize &+ paddingSize, - idealWidthForProposedHeight: childSize.idealWidthForProposedHeight + paddingSize.x, - idealHeightForProposedWidth: childSize.idealHeightForProposedWidth + paddingSize.y, + idealWidthForProposedHeight: idealWidth, + idealHeightForProposedWidth: idealHeight, minimumWidth: childSize.minimumWidth + paddingSize.x, minimumHeight: childSize.minimumHeight + paddingSize.y, maximumWidth: childSize.maximumWidth + Double(paddingSize.x), @@ -143,4 +141,21 @@ struct PaddingModifierView: TypeSafeView { childResults: [childResult] ) } + + func commit( + _ container: Backend.Widget, + children: TupleViewChildren1, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + _ = children.child0.commit() + + let size = layout.size.size + backend.setSize(of: container, to: size) + + 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/OnDisappearModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Lifecycle/OnDisappearModifier.swift index 6c9ce558c93..cf70e083e62 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/Lifecycle/OnDisappearModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/Lifecycle/OnDisappearModifier.swift @@ -46,21 +46,35 @@ struct OnDisappearModifier: TypeSafeView { defaultAsWidget(children.wrappedChildren, backend: backend) } - func update( + func computeLayout( _ widget: Backend.Widget, children: OnDisappearModifierChildren, proposedSize: SIMD2, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - defaultUpdate( + backend: Backend + ) -> ViewLayoutResult { + defaultComputeLayout( widget, children: children.wrappedChildren, proposedSize: proposedSize, environment: environment, - backend: backend, - dryRun: dryRun + backend: backend + ) + } + + func commit( + _ widget: Backend.Widget, + children: OnDisappearModifierChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + defaultCommit( + widget, + children: children.wrappedChildren, + layout: layout, + environment: environment, + backend: backend ) } } diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Lifecycle/TaskModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Lifecycle/TaskModifier.swift index 62726cf32a5..d0cd8e797c4 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/Lifecycle/TaskModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/Lifecycle/TaskModifier.swift @@ -47,16 +47,13 @@ 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() + 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 index 90ec013d161..3157f7f85ce 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/PreferenceModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/PreferenceModifier.swift @@ -23,21 +23,19 @@ struct PreferenceModifier: View { self.modification = modification } - func update( + func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, proposedSize: SIMD2, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - var result = defaultUpdate( + backend: Backend + ) -> ViewLayoutResult { + var result = defaultComputeLayout( widget, children: children, proposedSize: proposedSize, environment: environment, - backend: backend, - dryRun: dryRun + backend: backend ) result.preferences = modification(result.preferences, environment) return result diff --git a/Sources/SwiftCrossUI/Views/Modifiers/SheetModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/SheetModifier.swift index 00d87b182cd..0631e07443f 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/SheetModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/SheetModifier.swift @@ -64,20 +64,28 @@ struct SheetModifier: TypeSafeView { children.childNode.widget.into() } - func update( + func computeLayout( _ widget: Backend.Widget, children: Children, proposedSize: SIMD2, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - let childResult = children.childNode.update( + backend: Backend + ) -> ViewLayoutResult { + children.childNode.computeLayout( with: body.view0, proposedSize: proposedSize, - environment: environment, - dryRun: dryRun + environment: environment ) + } + + func commit( + _ widget: Backend.Widget, + children: Children, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + _ = children.childNode.commit() if isPresented.wrappedValue && children.sheet == nil { let sheetViewGraphNode = ViewGraphNode( @@ -101,12 +109,12 @@ struct SheetModifier: TypeSafeView { .with(\.dismiss, dismissAction) .with(\.sheet, sheet) - let result = children.sheetContentNode!.update( + _ = children.sheetContentNode!.computeLayout( with: sheetContent(), - proposedSize: SIMD2(x: 10_000, y: 0), - environment: sheetEnvironment, - dryRun: false + proposedSize: SIMD2(10_000, 0), + environment: sheetEnvironment ) + let result = children.sheetContentNode!.commit() let window = environment.window! as! Backend.Window let preferences = result.preferences @@ -147,15 +155,6 @@ struct SheetModifier: TypeSafeView { 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) { diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Style/CornerRadiusModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Style/CornerRadiusModifier.swift index 1dd3b3ff0e4..5fa3c43e498 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, 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/OptionalView.swift b/Sources/SwiftCrossUI/Views/OptionalView.swift index 97394a0be18..7b9590b22ba 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, 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: .hidden) } 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.size) } } diff --git a/Sources/SwiftCrossUI/Views/Picker.swift b/Sources/SwiftCrossUI/Views/Picker.swift index 89efa8424d0..9cfa8a4ea60 100644 --- a/Sources/SwiftCrossUI/Views/Picker.swift +++ b/Sources/SwiftCrossUI/Views/Picker.swift @@ -18,17 +18,18 @@ public struct Picker: ElementaryView, View { self.value = value } - public func asWidget(backend: Backend) -> Backend.Widget { + func asWidget(backend: Backend) -> Backend.Widget { return backend.createPicker() } - public func update( + func computeLayout( _ widget: Backend.Widget, proposedSize: SIMD2, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { + backend: Backend + ) -> ViewLayoutResult { + // TODO: Implement picker sizing within SwiftCrossUI so that we can + // properly separate committing logic out into `commit`. backend.updatePicker( widget, options: options.map { "\($0)" }, @@ -48,11 +49,7 @@ public struct Picker: ElementaryView, View { // 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( + return ViewLayoutResult.leafView( size: ViewSize( size: proposedSize, idealSize: SIMD2(10, 10), @@ -63,10 +60,18 @@ public struct Picker: ElementaryView, View { ) ) } else { - // TODO: Implement picker sizing within SwiftCrossUI so that we can properly implement `dryRun`. - return ViewUpdateResult.leafView( + return ViewLayoutResult.leafView( size: ViewSize(fixedSize: size) ) } } + + func commit( + _ widget: Backend.Widget, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + backend.setSize(of: widget, to: layout.size.size) + } } diff --git a/Sources/SwiftCrossUI/Views/ProgressView.swift b/Sources/SwiftCrossUI/Views/ProgressView.swift index 1653410ba60..c0a44d608a4 100644 --- a/Sources/SwiftCrossUI/Views/ProgressView.swift +++ b/Sources/SwiftCrossUI/Views/ProgressView.swift @@ -107,17 +107,23 @@ struct ProgressSpinnerView: ElementaryView { backend.createProgressSpinner() } - func update( + func computeLayout( _ widget: Backend.Widget, proposedSize: SIMD2, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - ViewUpdateResult.leafView( + backend: Backend + ) -> ViewLayoutResult { + ViewLayoutResult.leafView( size: ViewSize(fixedSize: backend.naturalSize(of: widget)) ) } + + func commit( + _ widget: Backend.Widget, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) {} } struct ProgressBarView: ElementaryView { @@ -131,25 +137,19 @@ struct ProgressBarView: ElementaryView { backend.createProgressBar() } - func update( + func computeLayout( _ widget: Backend.Widget, proposedSize: SIMD2, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { + backend: Backend + ) -> ViewLayoutResult { 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( + return ViewLayoutResult.leafView( size: ViewSize( size: size, idealSize: SIMD2(100, height), @@ -160,4 +160,14 @@ struct ProgressBarView: ElementaryView { ) ) } + + func commit( + _ widget: Backend.Widget, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + backend.updateProgressBar(widget, progressFraction: value, environment: environment) + backend.setSize(of: widget, to: layout.size.size) + } } diff --git a/Sources/SwiftCrossUI/Views/ScrollView.swift b/Sources/SwiftCrossUI/Views/ScrollView.swift index b3d7f1bc51f..c8178b893bb 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,20 +43,18 @@ public struct ScrollView: TypeSafeView, View { return backend.createScrollContainer(for: children.innerContainer.into()) } - func update( + func computeLayout( _ widget: Backend.Widget, children: ScrollViewChildren, proposedSize: SIMD2, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { + backend: Backend + ) -> ViewLayoutResult { // Probe how big the child would like to be - let childResult = children.child.update( + let childResult = children.child.computeLayout( with: body, proposedSize: proposedSize, - environment: environment, - dryRun: true + environment: environment ) let contentSize = childResult.size @@ -64,6 +64,8 @@ public struct ScrollView: TypeSafeView, View { axes.contains(.horizontal) && contentSize.idealWidthForProposedHeight > proposedSize.x let hasVerticalScrollBar = axes.contains(.vertical) && contentSize.idealHeightForProposedWidth > proposedSize.y + children.hasHorizontalScrollBar = hasHorizontalScrollBar + children.hasVerticalScrollBar = hasVerticalScrollBar let verticalScrollBarWidth = hasVerticalScrollBar ? scrollBarWidth : 0 let horizontalScrollBarHeight = hasHorizontalScrollBar ? scrollBarWidth : 0 @@ -98,57 +100,27 @@ public struct ScrollView: TypeSafeView, View { scrollViewHeight ) - 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) - ) + // 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) + ) - 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 - ) - backend.updateScrollContainer(widget, environment: environment) - } else { - finalResult = childResult - } + let finalChildResult = children.child.computeLayout( + with: body, + proposedSize: proposedContentSize, + environment: environment + ) - return ViewUpdateResult( + return ViewLayoutResult( size: ViewSize( size: scrollViewSize, idealSize: contentSize.idealSize, @@ -157,15 +129,58 @@ public struct ScrollView: TypeSafeView, View { maximumWidth: nil, maximumHeight: nil ), - childResults: [finalResult] + childResults: [finalChildResult] ) } + + func commit( + _ widget: Backend.Widget, + children: ScrollViewChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + let scrollViewSize = layout.size.size + let finalContentSize = children.child.commit().size + + let verticalScrollBarWidth = + children.hasVerticalScrollBar + ? backend.scrollBarWidth : 0 + let horizontalScrollBarHeight = + children.hasHorizontalScrollBar + ? backend.scrollBarWidth : 0 + 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: children.hasVerticalScrollBar, + hasHorizontalScrollBar: children.hasHorizontalScrollBar + ) + backend.updateScrollContainer(widget, environment: environment) + } } 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/Shape.swift b/Sources/SwiftCrossUI/Views/Shapes/Shape.swift index e266588cbeb..cb90753e4c0 100644 --- a/Sources/SwiftCrossUI/Views/Shapes/Shape.swift +++ b/Sources/SwiftCrossUI/Views/Shapes/Shape.swift @@ -82,51 +82,54 @@ extension Shape { } @MainActor - public func update( + public func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, proposedSize: SIMD2, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - let storage = children as! ShapeStorage + backend: Backend + ) -> ViewLayoutResult { let size = size(fitting: proposedSize) + return ViewLayoutResult.leafView(size: size) + } + @MainActor + public func commit( + _ widget: Backend.Widget, + children: any ViewGraphNodeChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { let bounds = Path.Rect( x: 0.0, y: 0.0, - width: Double(size.size.x), - height: Double(size.size.y) + width: Double(layout.size.size.x), + height: Double(layout.size.size.y) ) let path = path(in: bounds) - storage.pointsChanged = - storage.pointsChanged || storage.oldPath?.actions != path.actions + let storage = children as! ShapeStorage + let 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 - ) - } + backend.updatePath( + backendPath, + path, + bounds: bounds, + pointsChanged: pointsChanged, + environment: environment + ) - return ViewUpdateResult.leafView(size: size) + backend.setSize(of: widget, to: layout.size.size) + backend.renderPath( + backendPath, + container: widget, + strokeColor: .clear, + fillColor: environment.suggestedForegroundColor, + overrideStrokeStyle: nil + ) } } @@ -135,5 +138,4 @@ final class ShapeStorage: ViewGraphNodeChildren { 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 index 1a09c59fc0c..7850cbff995 100644 --- a/Sources/SwiftCrossUI/Views/Shapes/StyledShape.swift +++ b/Sources/SwiftCrossUI/Views/Shapes/StyledShape.swift @@ -53,51 +53,53 @@ extension Shape { extension StyledShape { @MainActor - public func update( + public func computeLayout( _ 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 + backend: Backend + ) -> ViewLayoutResult { let size = size(fitting: proposedSize) + return ViewLayoutResult.leafView(size: size) + } + @MainActor + public func commit( + _ widget: Backend.Widget, + children: any ViewGraphNodeChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { let bounds = Path.Rect( x: 0.0, y: 0.0, - width: Double(size.size.x), - height: Double(size.size.y) + width: Double(layout.size.size.x), + height: Double(layout.size.size.y) ) let path = path(in: bounds) - storage.pointsChanged = - storage.pointsChanged || storage.oldPath?.actions != path.actions + let storage = children as! ShapeStorage + let 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 - ) - } + backend.updatePath( + backendPath, + path, + bounds: bounds, + pointsChanged: pointsChanged, + environment: environment + ) - return ViewUpdateResult.leafView(size: size) + backend.setSize(of: widget, to: layout.size.size) + backend.renderPath( + backendPath, + container: widget, + strokeColor: strokeColor ?? .clear, + fillColor: fillColor ?? .clear, + overrideStrokeStyle: strokeStyle + ) } } diff --git a/Sources/SwiftCrossUI/Views/Slider.swift b/Sources/SwiftCrossUI/Views/Slider.swift index 1745400aa5d..f06ee294ad9 100644 --- a/Sources/SwiftCrossUI/Views/Slider.swift +++ b/Sources/SwiftCrossUI/Views/Slider.swift @@ -86,46 +86,23 @@ public struct Slider: ElementaryView, View { decimalPlaces = 2 } - public func asWidget(backend: Backend) -> Backend.Widget { + func asWidget(backend: Backend) -> Backend.Widget { return backend.createSlider() } - public func update( + func computeLayout( _ 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. + backend: Backend + ) -> ViewLayoutResult { + // 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( + return ViewLayoutResult.leafView( size: ViewSize( size: size, idealSize: SIMD2(100, naturalSize.y), @@ -136,4 +113,29 @@ public struct Slider: ElementaryView, View { ) ) } + + func commit( + _ widget: Backend.Widget, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + 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) + } + + backend.setSize(of: widget, to: layout.size.size) + } } diff --git a/Sources/SwiftCrossUI/Views/Spacer.swift b/Sources/SwiftCrossUI/Views/Spacer.swift index 913b3b6fa4c..91765a2ff0b 100644 --- a/Sources/SwiftCrossUI/Views/Spacer.swift +++ b/Sources/SwiftCrossUI/Views/Spacer.swift @@ -11,19 +11,16 @@ 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, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { + backend: Backend + ) -> ViewLayoutResult { let minLength = minLength ?? 0 let size: SIMD2 @@ -46,10 +43,7 @@ public struct Spacer: ElementaryView, View { maximumHeight = nil } - if !dryRun { - backend.setSize(of: widget, to: size) - } - return ViewUpdateResult.leafView( + return ViewLayoutResult.leafView( size: ViewSize( size: size, idealSize: SIMD2(minimumWidth, minimumHeight), @@ -60,4 +54,13 @@ public struct Spacer: ElementaryView, View { ) ) } + + 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 index 86a0e28844a..a0d70cbadb9 100644 --- a/Sources/SwiftCrossUI/Views/SplitView.swift +++ b/Sources/SwiftCrossUI/Views/SplitView.swift @@ -38,39 +38,31 @@ struct SplitView: TypeSafeView, View { ) } - func update( + func computeLayout( _ widget: Backend.Widget, children: Children, proposedSize: SIMD2, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { + backend: Backend + ) -> ViewLayoutResult { let leadingWidth = backend.sidebarWidth(ofSplitView: widget) - if !dryRun { - backend.setResizeHandler(ofSplitView: widget) { - environment.onResize(.empty) - } - } // Update pane children - let leadingResult = children.leadingChild.update( + let leadingResult = children.leadingChild.computeLayout( with: body.view0, proposedSize: SIMD2( leadingWidth, proposedSize.y ), - environment: environment, - dryRun: dryRun + environment: environment ) - let trailingResult = children.trailingChild.update( + let trailingResult = children.trailingChild.computeLayout( with: body.view1, proposedSize: SIMD2( proposedSize.x - max(leadingWidth, leadingResult.size.minimumWidth), proposedSize.y ), - environment: environment, - dryRun: dryRun + environment: environment ) // Update split view size and sidebar width bounds @@ -80,37 +72,8 @@ struct SplitView: TypeSafeView, View { 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( + return ViewLayoutResult( size: ViewSize( size: size, idealSize: leadingContentSize.idealSize &+ trailingContentSize.idealSize, @@ -123,6 +86,50 @@ struct SplitView: TypeSafeView, View { childResults: [leadingResult, trailingResult] ) } + + func commit( + _ widget: Backend.Widget, + children: Children, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + backend.setResizeHandler(ofSplitView: widget) { + environment.onResize(.empty) + } + + let leadingWidth = backend.sidebarWidth(ofSplitView: widget) + let leadingResult = children.leadingChild.commit() + let trailingResult = children.trailingChild.commit() + + backend.setSize(of: widget, to: layout.size.size) + backend.setSidebarWidthBounds( + ofSplitView: widget, + minimum: leadingResult.size.minimumWidth, + maximum: max( + leadingResult.size.minimumWidth, + layout.size.size.x - trailingResult.size.minimumWidth + ) + ) + + // Center pane children + backend.setPosition( + ofChildAt: 0, + in: children.leadingPaneContainer.into(), + to: SIMD2( + leadingWidth - leadingResult.size.size.x, + layout.size.size.y - leadingResult.size.size.y + ) / 2 + ) + backend.setPosition( + ofChildAt: 0, + in: children.trailingPaneContainer.into(), + to: SIMD2( + layout.size.size.x - leadingWidth - trailingResult.size.size.x, + layout.size.size.y - trailingResult.size.size.y + ) / 2 + ) + } } class SplitViewChildren: ViewGraphNodeChildren { diff --git a/Sources/SwiftCrossUI/Views/Table.swift b/Sources/SwiftCrossUI/Views/Table.swift index 30669177eb6..b18b7281be6 100644 --- a/Sources/SwiftCrossUI/Views/Table.swift +++ b/Sources/SwiftCrossUI/Views/Table.swift @@ -32,37 +32,26 @@ public struct Table>: TypeSafeVi return backend.createTable() } - func update( + func computeLayout( _ widget: Backend.Widget, children: Children, proposedSize: SIMD2, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { + backend: Backend + ) -> ViewLayoutResult { 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 - ) - } - + var cellResults: [ViewLayoutResult] = [] + children.rowContent = rows.map(columns.content(for:)).map(RowView.init(_:)) let columnLabels = columns.labels let columnCount = columnLabels.count - let remainder = rowContent.count - children.rowNodes.count + + // Create and destroy row nodes + let remainder = children.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...] { + for row in children.rowContent[children.rowNodes.count...] { let rowNode = AnyViewGraphNode( for: row, backend: backend, @@ -77,62 +66,52 @@ public struct Table>: TypeSafeVi } } - if !dryRun { - backend.setRowCount(ofTable: widget, to: rows.count) - backend.setColumnLabels(ofTable: widget, to: columnLabels, environment: environment) + // Update row nodes + let columnWidth = proposedSize.x / columnCount + for (node, content) in zip(children.rowNodes, children.rowContent) { + // TODO: Figure out if this is required + // This doesn't update the row's cells. It just updates the view + // instance stored in the row's ViewGraphNode + _ = node.computeLayout( + with: content, + proposedSize: .zero, + environment: environment + ) } - let columnWidth = proposedSize.x / columnCount + // Compute cell layouts. Really only done during this initial layout + // step to propagate cell preference values. Otherwise we'd do it + // during commit. var rowHeights: [Int] = [] - for (rowIndex, (rowNode, content)) in zip(children.rowNodes, rowContent).enumerated() { + let rows = zip(children.rowNodes, children.rowContent) + for (rowNode, content) in rows { let rowCells = content.layoutableChildren( backend: backend, children: rowNode.getChildren() ) - var cellHeights: [Int] = [] + var rowCellHeights: [Int] = [] for rowCell in rowCells { - let cellResult = rowCell.update( + let cellResult = rowCell.computeLayout( proposedSize: SIMD2(columnWidth, backend.defaultTableRowContentHeight), - environment: environment, - dryRun: dryRun + environment: environment ) cellResults.append(cellResult) - cellHeights.append(cellResult.size.size.y) + rowCellHeights.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..>: TypeSafeVi childResults: cellResults ) } + + func commit( + _ widget: Backend.Widget, + children: TableViewChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + let columnLabels = columns.labels + backend.setRowCount(ofTable: widget, to: rows.count) + backend.setColumnLabels(ofTable: widget, to: columnLabels, environment: environment) + + // TODO: Avoid overhead of converting `cellContainerWidgets` to + // `[AnyWidget]` and back again all the time. + backend.setCells( + ofTable: widget, + to: children.cellContainerWidgets.map { $0.into() }, + withRowHeights: children.rowHeights + ) + + let columnCount = columnLabels.count + for (rowIndex, rowHeight) in children.rowHeights.enumerated() { + let rowCells = children.rowContent[rowIndex].layoutableChildren( + backend: backend, + children: children.rowNodes[rowIndex].getChildren() + ) + + for (columnIndex, cell) in rowCells.enumerated() { + let index = rowIndex * columnCount + columnIndex + let cellSize = cell.commit() + backend.setPosition( + ofChildAt: 0, + in: children.cellContainerWidgets[index].into(), + to: SIMD2( + 0, + (rowHeight - cellSize.size.size.y) / 2 + ) + ) + } + } + + backend.setSize(of: widget, to: layout.size.size) + } } class TableViewChildren: ViewGraphNodeChildren { var rowNodes: [AnyViewGraphNode>] = [] var cellContainerWidgets: [AnyWidget] = [] + var rowHeights: [Int] = [] + var rowContent: [RowView] = [] /// Not used, just a protocol requirement. var widgets: [AnyWidget] { @@ -195,13 +219,21 @@ struct RowView: View { return backend.createContainer() } - func update( + func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, proposedSize: SIMD2, environment: EnvironmentValues, backend: Backend - ) -> ViewUpdateResult { - return ViewUpdateResult.leafView(size: .empty) + ) -> ViewLayoutResult { + return ViewLayoutResult.leafView(size: .empty) } + + func commit( + _ widget: Backend.Widget, + children: any ViewGraphNodeChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) {} } diff --git a/Sources/SwiftCrossUI/Views/Text.swift b/Sources/SwiftCrossUI/Views/Text.swift index ade37371625..906343a4b6a 100644 --- a/Sources/SwiftCrossUI/Views/Text.swift +++ b/Sources/SwiftCrossUI/Views/Text.swift @@ -19,14 +19,13 @@ extension Text: ElementaryView { return backend.createTextView() } - public func update( + public func computeLayout( _ widget: Backend.Widget, proposedSize: SIMD2, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - // TODO: Avoid this + backend: Backend + ) -> ViewLayoutResult { + // TODO: Avoid this. Move it to commit // 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). @@ -38,9 +37,6 @@ extension Text: ElementaryView { proposedFrame: proposedSize, environment: environment ) - if !dryRun { - backend.setSize(of: widget, to: size) - } let idealSize = backend.size( of: string, @@ -62,7 +58,7 @@ extension Text: ElementaryView { environment: environment ).y - return ViewUpdateResult.leafView( + return ViewLayoutResult.leafView( size: ViewSize( size: size, idealSize: idealSize, @@ -75,4 +71,13 @@ extension Text: ElementaryView { ) ) } + + public func commit( + _ widget: Backend.Widget, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + backend.setSize(of: widget, to: layout.size.size) + } } diff --git a/Sources/SwiftCrossUI/Views/TextEditor.swift b/Sources/SwiftCrossUI/Views/TextEditor.swift index 7070d6075df..960094730ce 100644 --- a/Sources/SwiftCrossUI/Views/TextEditor.swift +++ b/Sources/SwiftCrossUI/Views/TextEditor.swift @@ -10,25 +10,15 @@ public struct TextEditor: ElementaryView { backend.createTextEditor() } - func update( + func computeLayout( _ widget: Backend.Widget, proposedSize: SIMD2, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { + backend: Backend + ) -> ViewLayoutResult { // 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, @@ -40,11 +30,7 @@ public struct TextEditor: ElementaryView { max(proposedSize.y, idealHeight) ) - if !dryRun { - backend.setSize(of: widget, to: size) - } - - return ViewUpdateResult.leafView( + return ViewLayoutResult.leafView( size: ViewSize( size: size, idealSize: SIMD2(10, 10), @@ -57,4 +43,23 @@ public struct TextEditor: ElementaryView { ) ) } + + func commit( + _ widget: Backend.Widget, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + // Avoid evaluating the binding multiple times + let content = self.text + + backend.updateTextEditor(widget, environment: environment) { newValue in + self.text = newValue + } + if text != backend.getContent(ofTextEditor: widget) { + backend.setContent(ofTextEditor: widget, to: content) + } + + backend.setSize(of: widget, to: layout.size.size) + } } diff --git a/Sources/SwiftCrossUI/Views/TextField.swift b/Sources/SwiftCrossUI/Views/TextField.swift index 8086f18fc4a..1bb883eda28 100644 --- a/Sources/SwiftCrossUI/Views/TextField.swift +++ b/Sources/SwiftCrossUI/Views/TextField.swift @@ -23,43 +23,24 @@ public struct TextField: ElementaryView, View { self.value = value ?? Binding(get: { dummy }, set: { dummy = $0 }) } - public func asWidget(backend: Backend) -> Backend.Widget { + func asWidget(backend: Backend) -> Backend.Widget { return backend.createTextField() } - public func update( + func computeLayout( _ 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) - } - } - + backend: Backend + ) -> ViewLayoutResult { 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( + return ViewLayoutResult.leafView( size: ViewSize( size: size, idealSize: SIMD2(100, naturalHeight), @@ -70,4 +51,26 @@ public struct TextField: ElementaryView, View { ) ) } + + func commit( + _ widget: Backend.Widget, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + 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) + } + + backend.setSize(of: widget, to: layout.size.size) + } } diff --git a/Sources/SwiftCrossUI/Views/ToggleButton.swift b/Sources/SwiftCrossUI/Views/ToggleButton.swift index b5488c4da8c..8cf2c91a3b7 100644 --- a/Sources/SwiftCrossUI/Views/ToggleButton.swift +++ b/Sources/SwiftCrossUI/Views/ToggleButton.swift @@ -11,24 +11,31 @@ struct ToggleButton: ElementaryView, View { self.active = active } - public func asWidget(backend: Backend) -> Backend.Widget { + func asWidget(backend: Backend) -> Backend.Widget { return backend.createToggle() } - public func update( + func computeLayout( _ 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: Backend + ) -> ViewLayoutResult { + // TODO: Implement toggle button sizing within SwiftCrossUI so that we + // can delay updating the underlying widget until `commit`. backend.setState(ofToggle: widget, to: active.wrappedValue) backend.updateToggle(widget, label: label, environment: environment) { newActiveState in active.wrappedValue = newActiveState } - return ViewUpdateResult.leafView( + return ViewLayoutResult.leafView( size: ViewSize(fixedSize: backend.naturalSize(of: widget)) ) } + + func commit( + _ widget: Backend.Widget, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) {} } diff --git a/Sources/SwiftCrossUI/Views/ToggleSwitch.swift b/Sources/SwiftCrossUI/Views/ToggleSwitch.swift index d59d58b9080..a500d0509bc 100644 --- a/Sources/SwiftCrossUI/Views/ToggleSwitch.swift +++ b/Sources/SwiftCrossUI/Views/ToggleSwitch.swift @@ -8,25 +8,30 @@ struct ToggleSwitch: ElementaryView, View { self.active = active } - public func asWidget(backend: Backend) -> Backend.Widget { + func asWidget(backend: Backend) -> Backend.Widget { return backend.createSwitch() } - public func update( + func computeLayout( _ 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( + backend: Backend + ) -> ViewLayoutResult { + return ViewLayoutResult.leafView( size: ViewSize(fixedSize: backend.naturalSize(of: widget)) ) } + + func commit( + _ widget: Backend.Widget, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + backend.updateSwitch(widget, environment: environment) { newActiveState in + active.wrappedValue = newActiveState + } + backend.setState(ofSwitch: widget, to: active.wrappedValue) + } } diff --git a/Sources/SwiftCrossUI/Views/TupleView.swift b/Sources/SwiftCrossUI/Views/TupleView.swift index edd1d644fd7..0dab664be3a 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,22 +36,38 @@ extension TupleView { } @MainActor - func update( + func computeLayout( _ widget: Backend.Widget, children: Children, proposedSize: SIMD2, 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/TupleView.swift.gyb b/Sources/SwiftCrossUI/Views/TupleView.swift.gyb index 38396af12d9..6c9533bd7ed 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, 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/TypeSafeView.swift b/Sources/SwiftCrossUI/Views/TypeSafeView.swift index ef99bd4b194..3724688e924 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, 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, 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 b3b9973fd64..e621aee0f0f 100644 --- a/Sources/SwiftCrossUI/Views/VStack.swift +++ b/Sources/SwiftCrossUI/Views/VStack.swift @@ -39,15 +39,14 @@ public struct VStack: View { return vStack } - public func update( + public func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, proposedSize: SIMD2, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult { - return LayoutSystem.updateStackLayout( + backend: Backend + ) -> ViewLayoutResult { + return LayoutSystem.computeStackLayout( container: widget, children: layoutableChildren(backend: backend, children: children), proposedSize: proposedSize, @@ -56,8 +55,27 @@ public struct VStack: View { .with(\.layoutOrientation, .vertical) .with(\.layoutAlignment, alignment.asStackAlignment) .with(\.layoutSpacing, spacing), - backend: backend, - dryRun: dryRun + backend: backend + ) + } + + public func commit( + _ widget: Backend.Widget, + children: any ViewGraphNodeChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + LayoutSystem.commitStackLayout( + container: widget, + children: layoutableChildren(backend: backend, children: children), + layout: layout, + environment: + environment + .with(\.layoutOrientation, .vertical) + .with(\.layoutAlignment, alignment.asStackAlignment) + .with(\.layoutSpacing, spacing), + backend: backend ) } } diff --git a/Sources/SwiftCrossUI/Views/View.swift b/Sources/SwiftCrossUI/Views/View.swift index abeca203fe7..e6a4fbfb649 100644 --- a/Sources/SwiftCrossUI/Views/View.swift +++ b/Sources/SwiftCrossUI/Views/View.swift @@ -42,24 +42,25 @@ 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, environment: EnvironmentValues, - backend: Backend, - dryRun: Bool - ) -> ViewUpdateResult + backend: Backend + ) -> ViewLayoutResult + + func commit( + _ widget: Backend.Widget, + children: any ViewGraphNodeChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) } extension View { @@ -119,42 +120,71 @@ extension View { return vStack.asWidget(children, backend: backend) } - public func update( + public func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, proposedSize: SIMD2, 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, 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 index 0b9e1dae4e6..116e990da63 100644 --- a/Sources/SwiftCrossUI/Views/WebView.swift +++ b/Sources/SwiftCrossUI/Views/WebView.swift @@ -13,26 +13,13 @@ public struct WebView: ElementaryView { backend.createWebView() } - func update( + func computeLayout( _ 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( + backend: Backend + ) -> ViewLayoutResult { + return ViewLayoutResult( size: ViewSize( size: proposedSize, idealSize: SIMD2(10, 10), @@ -44,4 +31,21 @@ public struct WebView: ElementaryView { childResults: [] ) } + + func commit( + _ widget: Backend.Widget, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + 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: layout.size.size) + } } diff --git a/Sources/SwiftCrossUI/Views/ZStack.swift b/Sources/SwiftCrossUI/Views/ZStack.swift index 5cd5657c519..3bc815a6db0 100644 --- a/Sources/SwiftCrossUI/Views/ZStack.swift +++ b/Sources/SwiftCrossUI/Views/ZStack.swift @@ -28,23 +28,20 @@ public struct ZStack: View { return zStack } - public func update( + public func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, proposedSize: SIMD2, 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( @@ -62,17 +59,30 @@ public struct ZStack: View { maximumHeight: childSizes.map(\.maximumHeight).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.size, + in: size.size + ) + backend.setPosition(ofChildAt: i, in: widget, to: position) } - return ViewUpdateResult(size: size, childResults: childResults) + backend.setSize(of: widget, to: size.size) } } diff --git a/Sources/UIKitBackend/UIViewControllerRepresentable.swift b/Sources/UIKitBackend/UIViewControllerRepresentable.swift index cfd73e84373..9f0e23c764c 100644 --- a/Sources/UIKitBackend/UIViewControllerRepresentable.swift +++ b/Sources/UIKitBackend/UIViewControllerRepresentable.swift @@ -115,7 +115,7 @@ where Self: UIViewControllerRepresentable { environment: EnvironmentValues, backend _: Backend, dryRun: Bool - ) -> ViewUpdateResult { + ) -> ViewLayoutResult { let representingWidget = widget as! ControllerRepresentingWidget representingWidget.update(with: environment) @@ -131,7 +131,7 @@ where Self: UIViewControllerRepresentable { representingWidget.height = size.size.y } - return ViewUpdateResult.leafView(size: size) + return ViewLayoutResult.leafView(size: size) } } diff --git a/Sources/UIKitBackend/UIViewRepresentable.swift b/Sources/UIKitBackend/UIViewRepresentable.swift index 5b20929cd92..fa094d59b2d 100644 --- a/Sources/UIKitBackend/UIViewRepresentable.swift +++ b/Sources/UIKitBackend/UIViewRepresentable.swift @@ -135,14 +135,13 @@ where Self: UIViewRepresentable { } } - public func update( + public func computeLayout( _ widget: Backend.Widget, children _: any ViewGraphNodeChildren, proposedSize: SIMD2, environment: EnvironmentValues, - backend _: Backend, - dryRun: Bool - ) -> ViewUpdateResult { + backend _: Backend + ) -> ViewLayoutResult { let representingWidget = widget as! ViewRepresentingWidget representingWidget.update(with: environment) @@ -153,12 +152,19 @@ where Self: UIViewRepresentable { context: representingWidget.context! ) - if !dryRun { - representingWidget.width = size.size.x - representingWidget.height = size.size.y - } + return ViewLayoutResult.leafView(size: size) + } - return ViewUpdateResult.leafView(size: size) + public func commit( + _ widget: Backend.Widget, + children: any ViewGraphNodeChildren, + layout: ViewLayoutResult, + environment: EnvironmentValues, + backend: Backend + ) { + let representingWidget = widget as! ViewRepresentingWidget + representingWidget.width = layout.size.size.x + representingWidget.height = layout.size.size.y } } diff --git a/Tests/SwiftCrossUITests/SwiftCrossUITests.swift b/Tests/SwiftCrossUITests/SwiftCrossUITests.swift index e88a827aee3..78ff52fc3ac 100644 --- a/Tests/SwiftCrossUITests/SwiftCrossUITests.swift +++ b/Tests/SwiftCrossUITests/SwiftCrossUITests.swift @@ -91,6 +91,11 @@ struct SwiftCrossUITests { ) backend.setChild(ofWindow: window, to: viewGraph.rootNode.widget.into()) + _ = viewGraph.update( + proposedSize: SIMD2(200, 200), + environment: environment, + dryRun: true + ) let result = viewGraph.update( proposedSize: SIMD2(200, 200), environment: environment, From e4daa213118878e59d7701d8e328861531ace847 Mon Sep 17 00:00:00 2001 From: stackotter Date: Thu, 11 Dec 2025 11:34:44 +1000 Subject: [PATCH 02/15] Implement SwiftUI's layout system (worse behaviour, but better perf) Some of the bad behaviour from SwiftUI's layout system can probably be mitigated, but other undesirable behaviour is probably the best we can do, cause some things are impossible to compute nicely without laying out child views more than once (a no go for performant layout). --- .../LayoutPerformanceBenchmark.swift | 8 +- Sources/AppKitBackend/AppKitBackend.swift | 6 +- .../AppKitBackend/NSViewRepresentable.swift | 32 +- Sources/DummyBackend/DummyBackend.swift | 2 +- .../SwiftCrossUI/Builders/SceneBuilder.swift | 239 +------- .../Builders/TableRowBuilder.swift | 258 ++------- .../SwiftCrossUI/Builders/ViewBuilder.swift | 254 ++------- .../Environment/EnvironmentValues.swift | 12 +- .../SwiftCrossUI/Layout/LayoutSystem.swift | 351 ++++++------ Sources/SwiftCrossUI/Layout/Position.swift | 53 ++ .../Layout/ProposedViewSize.swift | 78 +++ .../Layout/ViewLayoutResult.swift | 53 ++ Sources/SwiftCrossUI/Layout/ViewSize.swift | 159 ++---- .../Layout/ViewUpdateResult.swift | 33 -- Sources/SwiftCrossUI/Scenes/TupleScene.swift | 399 +++---------- .../SwiftCrossUI/Scenes/WindowGroupNode.swift | 149 +---- Sources/SwiftCrossUI/Values/Axis.swift | 22 +- Sources/SwiftCrossUI/Values/Color.swift | 16 +- .../SwiftCrossUI/Values/GeometryProxy.swift | 2 +- Sources/SwiftCrossUI/Values/Orientation.swift | 10 + .../ViewGraph/AnyViewGraphNode.swift | 4 +- .../ViewGraph/ErasedViewGraphNode.swift | 4 +- .../SwiftCrossUI/ViewGraph/ViewGraph.swift | 52 +- .../ViewGraph/ViewGraphNode.swift | 73 +-- Sources/SwiftCrossUI/Views/AnyView.swift | 4 +- Sources/SwiftCrossUI/Views/Button.swift | 6 +- Sources/SwiftCrossUI/Views/Checkbox.swift | 4 +- Sources/SwiftCrossUI/Views/EitherView.swift | 4 +- .../SwiftCrossUI/Views/ElementaryView.swift | 4 +- Sources/SwiftCrossUI/Views/EmptyView.swift | 4 +- Sources/SwiftCrossUI/Views/ForEach.swift | 6 +- .../SwiftCrossUI/Views/GeometryReader.swift | 25 +- Sources/SwiftCrossUI/Views/Group.swift | 14 +- Sources/SwiftCrossUI/Views/HStack.swift | 14 +- .../Views/HotReloadableView.swift | 4 +- Sources/SwiftCrossUI/Views/Image.swift | 25 +- Sources/SwiftCrossUI/Views/List.swift | 70 +-- Sources/SwiftCrossUI/Views/Menu.swift | 13 +- .../Views/Modifiers/AlertModifier.swift | 2 +- .../Views/Modifiers/EnvironmentModifier.swift | 2 +- .../Modifiers/Handlers/OnChangeModifier.swift | 2 +- .../Modifiers/Handlers/OnHoverModifier.swift | 4 +- .../Handlers/OnTapGestureModifier.swift | 6 +- .../Layout/AspectRatioModifier.swift | 150 +++-- .../Modifiers/Layout/BackgroundModifier.swift | 46 +- .../Modifiers/Layout/FixedSizeModifier.swift | 41 +- .../Modifiers/Layout/FrameModifier.swift | 166 ++---- .../Modifiers/Layout/OverlayModifier.swift | 29 +- .../Modifiers/Layout/PaddingModifier.swift | 44 +- .../Lifecycle/OnDisappearModifier.swift | 2 +- .../Views/Modifiers/PreferenceModifier.swift | 2 +- .../Views/Modifiers/SheetModifier.swift | 6 +- .../Style/CornerRadiusModifier.swift | 2 +- Sources/SwiftCrossUI/Views/OptionalView.swift | 6 +- Sources/SwiftCrossUI/Views/Picker.swift | 25 +- Sources/SwiftCrossUI/Views/ProgressView.swift | 31 +- Sources/SwiftCrossUI/Views/ScrollView.swift | 158 +++--- .../SwiftCrossUI/Views/Shapes/Circle.swift | 23 +- Sources/SwiftCrossUI/Views/Shapes/Shape.swift | 32 +- .../Views/Shapes/StyledShape.swift | 10 +- Sources/SwiftCrossUI/Views/Slider.swift | 24 +- Sources/SwiftCrossUI/Views/Spacer.swift | 43 +- Sources/SwiftCrossUI/Views/SplitView.swift | 87 +-- Sources/SwiftCrossUI/Views/Table.swift | 31 +- .../SwiftCrossUI/Views/TableRowContent.swift | 523 +++--------------- Sources/SwiftCrossUI/Views/Text.swift | 52 +- Sources/SwiftCrossUI/Views/TextEditor.swift | 44 +- Sources/SwiftCrossUI/Views/TextField.swift | 24 +- Sources/SwiftCrossUI/Views/ToggleButton.swift | 9 +- Sources/SwiftCrossUI/Views/ToggleSwitch.swift | 7 +- Sources/SwiftCrossUI/Views/TupleView.swift | 253 ++------- .../SwiftCrossUI/Views/TupleView.swift.gyb | 2 +- .../Views/TupleViewChildren.swift | 457 ++++----------- .../Views/TupleViewChildren.swift.gyb | 15 +- Sources/SwiftCrossUI/Views/TypeSafeView.swift | 4 +- Sources/SwiftCrossUI/Views/VStack.swift | 17 +- Sources/SwiftCrossUI/Views/View.swift | 8 +- Sources/SwiftCrossUI/Views/WebView.swift | 20 +- Sources/SwiftCrossUI/Views/ZStack.swift | 23 +- .../UIViewControllerRepresentable.swift | 23 +- .../UIKitBackend/UIViewRepresentable.swift | 32 +- .../SwiftCrossUITests/SwiftCrossUITests.swift | 17 +- 82 files changed, 1678 insertions(+), 3292 deletions(-) create mode 100644 Sources/SwiftCrossUI/Layout/Position.swift create mode 100644 Sources/SwiftCrossUI/Layout/ProposedViewSize.swift create mode 100644 Sources/SwiftCrossUI/Layout/ViewLayoutResult.swift delete mode 100644 Sources/SwiftCrossUI/Layout/ViewUpdateResult.swift diff --git a/Benchmarks/LayoutPerformanceBenchmark/LayoutPerformanceBenchmark.swift b/Benchmarks/LayoutPerformanceBenchmark/LayoutPerformanceBenchmark.swift index d19d5ae0ba4..dc344387180 100644 --- a/Benchmarks/LayoutPerformanceBenchmark/LayoutPerformanceBenchmark.swift +++ b/Benchmarks/LayoutPerformanceBenchmark/LayoutPerformanceBenchmark.swift @@ -34,7 +34,7 @@ struct Benchmarks { } @MainActor - func updateNode(_ node: ViewGraphNode, _ size: SIMD2) { + func updateNode(_ node: ViewGraphNode, _ size: ProposedViewSize) { _ = node.computeLayout(proposedSize: size, environment: environment) _ = node.commit() } @@ -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/Sources/AppKitBackend/AppKitBackend.swift b/Sources/AppKitBackend/AppKitBackend.swift index aa5becc71ac..26eaddc712a 100644 --- a/Sources/AppKitBackend/AppKitBackend.swift +++ b/Sources/AppKitBackend/AppKitBackend.swift @@ -31,14 +31,12 @@ 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. - let result = Int( + return Int( NSScroller.scrollerWidth( for: .regular, scrollerStyle: NSScroller.preferredScrollerStyle ).rounded(.awayFromZero) ) - print(result) - return result } private let appDelegate = NSCustomApplicationDelegate() @@ -530,7 +528,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 31238dce7b8..498518f09ac 100644 --- a/Sources/AppKitBackend/NSViewRepresentable.swift +++ b/Sources/AppKitBackend/NSViewRepresentable.swift @@ -55,7 +55,7 @@ public protocol NSViewRepresentable: View where Content == Never { /// for the maximum width/height if the view has no maximum size (and /// therefore may occupy the entire screen). func determineViewSize( - for proposal: SIMD2, + for proposal: ProposedViewSize, nsView: NSViewType, context: NSViewRepresentableContext ) -> ViewSize @@ -76,34 +76,16 @@ extension NSViewRepresentable { } public func determineViewSize( - for proposal: SIMD2, nsView: NSViewType, + for proposal: ProposedViewSize, + 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 + intrinsicSize.width < 0 ? (proposal.width ?? 10) : sizeThatFits.width, + intrinsicSize.height < 0 ? (proposal.height ?? 10) : sizeThatFits.height ) } } @@ -142,7 +124,7 @@ extension View where Self: NSViewRepresentable { public func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { @@ -169,7 +151,7 @@ extension View where Self: NSViewRepresentable { environment: EnvironmentValues, backend: Backend ) { - backend.setSize(of: widget, to: layout.size.size) + backend.setSize(of: widget, to: layout.size.vector) } } diff --git a/Sources/DummyBackend/DummyBackend.swift b/Sources/DummyBackend/DummyBackend.swift index ccc6cb9f729..2d007aebd05 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/SwiftCrossUI/Builders/SceneBuilder.swift b/Sources/SwiftCrossUI/Builders/SceneBuilder.swift index ee690388e77..c40e8321196 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 index 2db59f9fb2c..5be12a0bdf4 100644 --- a/Sources/SwiftCrossUI/Builders/TableRowBuilder.swift +++ b/Sources/SwiftCrossUI/Builders/TableRowBuilder.swift @@ -33,8 +33,7 @@ public struct TableRowBuilder { public static func buildBlock< Content0: View, Content1: View, Content2: View >( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn + _ column0: TableColumn, _ column1: TableColumn, _ column2: TableColumn ) -> TupleTableRowContent3< RowValue, Content0, Content1, Content2 > { @@ -45,8 +44,7 @@ public struct TableRowBuilder { public static func buildBlock< Content0: View, Content1: View, Content2: View, Content3: View >( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn + _ column0: TableColumn, _ column1: TableColumn, _ column2: TableColumn, _ column3: TableColumn ) -> TupleTableRowContent4< RowValue, Content0, Content1, Content2, Content3 > { @@ -57,9 +55,7 @@ public struct TableRowBuilder { public static func buildBlock< Content0: View, Content1: View, Content2: View, Content3: View, Content4: View >( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn + _ column0: TableColumn, _ column1: TableColumn, _ column2: TableColumn, _ column3: TableColumn, _ column4: TableColumn ) -> TupleTableRowContent5< RowValue, Content0, Content1, Content2, Content3, Content4 > { @@ -68,12 +64,9 @@ public struct TableRowBuilder { ) } public static func buildBlock< - Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View + Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, Content5: View >( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn, _ column5: TableColumn + _ column0: TableColumn, _ column1: TableColumn, _ column2: TableColumn, _ column3: TableColumn, _ column4: TableColumn, _ column5: TableColumn ) -> TupleTableRowContent6< RowValue, Content0, Content1, Content2, Content3, Content4, Content5 > { @@ -82,13 +75,9 @@ public struct TableRowBuilder { ) } public static func buildBlock< - Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View, Content6: View + 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 + _ column0: TableColumn, _ column1: TableColumn, _ column2: TableColumn, _ column3: TableColumn, _ column4: TableColumn, _ column5: TableColumn, _ column6: TableColumn ) -> TupleTableRowContent7< RowValue, Content0, Content1, Content2, Content3, Content4, Content5, Content6 > { @@ -97,13 +86,9 @@ public struct TableRowBuilder { ) } public static func buildBlock< - Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View, Content6: View, Content7: View + 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 + _ 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 > { @@ -112,268 +97,135 @@ public struct TableRowBuilder { ) } public static func buildBlock< - Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, - Content5: View, Content6: View, Content7: View, Content8: View + 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 + _ 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 + 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 + 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 + _ 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 + 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 + 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 + _ 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 + RowValue, Content0, Content1, Content2, Content3, Content4, Content5, Content6, Content7, Content8, Content9, Content10 > { TupleTableRowContent11( - column0, column1, column2, column3, column4, column5, column6, column7, column8, - column9, column10 + 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 + 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 + _ 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 + 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 + 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 + 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 + _ 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 + 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 + 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 + 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 + _ 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 + 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 + 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 + 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 + _ 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 + 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 + 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 + 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 + _ 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 + 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 + 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 + 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 + _ 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 + 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 + 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 + 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 + _ 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 + 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 + 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 + 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 + _ 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 + 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 + 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 + 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 + _ 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 + 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 + 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/ViewBuilder.swift b/Sources/SwiftCrossUI/Builders/ViewBuilder.swift index 2b5252d3673..81d052d982a 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 cafe0dea636..f9f5f1d2bec 100644 --- a/Sources/SwiftCrossUI/Environment/EnvironmentValues.swift +++ b/Sources/SwiftCrossUI/Environment/EnvironmentValues.swift @@ -98,9 +98,18 @@ public struct EnvironmentValues { /// Whether the text should be selectable. Set by ``View/textSelectionEnabled(_:)``. public var isTextSelectionEnabled: Bool - // Backing storage for extensible subscript + /// Backing storage for extensible subscript private var extraValues: [ObjectIdentifier: Any] + /// An internal environment value used to control whether layout caching is + /// enabled or not. This is set to true when computing non-final layouts. E.g. + /// when a stack computes the minimum and maximum sizes of its children, it + /// should enable layout caching because those updates are guaranteed to be + /// non-final. The reason that we can't cache on non-final updates is that + /// the last layout proposal received by each view must be its intended final + /// proposal. + var allowLayoutCaching: Bool + public subscript(_ key: T.Type) -> T.Value { get { extraValues[ObjectIdentifier(T.self), default: T.defaultValue] as! T.Value @@ -217,6 +226,7 @@ public struct EnvironmentValues { isEnabled = true scrollDismissesKeyboardMode = .automatic isTextSelectionEnabled = false + allowLayoutCaching = false } /// Returns a copy of the environment with the specified property set to the diff --git a/Sources/SwiftCrossUI/Layout/LayoutSystem.swift b/Sources/SwiftCrossUI/Layout/LayoutSystem.swift index bd0de8df004..ea9acf0e8da 100644 --- a/Sources/SwiftCrossUI/Layout/LayoutSystem.swift +++ b/Sources/SwiftCrossUI/Layout/LayoutSystem.swift @@ -1,14 +1,36 @@ public enum LayoutSystem { - static func width(forHeight height: Int, aspectRatio: Double) -> Int { - roundSize(Double(height) * aspectRatio) + static func width(forHeight height: Double, aspectRatio: Double) -> Double { + Double(height) * aspectRatio } - static func height(forWidth width: Int, aspectRatio: Double) -> Int { - roundSize(Double(width) / aspectRatio) + static func height(forWidth width: Double, aspectRatio: Double) -> Double { + Double(width) / aspectRatio } - static func roundSize(_ size: Double) -> Int { - Int(size.rounded(.towardZero)) + package static func roundSize(_ size: Double) -> Int { + 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: ViewSize) -> Double { + aspectRatio(of: SIMD2(frame.width, frame.height)) } static func aspectRatio(of frame: SIMD2) -> Double { @@ -23,38 +45,17 @@ public enum LayoutSystem { } } - static func frameSize( - forProposedSize proposedSize: SIMD2, - aspectRatio: Double, - contentMode: ContentMode - ) -> SIMD2 { - let widthForHeight = width(forHeight: proposedSize.y, aspectRatio: aspectRatio) - let heightForWidth = height(forWidth: proposedSize.x, aspectRatio: aspectRatio) - switch contentMode { - case .fill: - return SIMD2( - max(proposedSize.x, widthForHeight), - max(proposedSize.y, heightForWidth) - ) - case .fit: - return SIMD2( - min(proposedSize.x, widthForHeight), - min(proposedSize.y, heightForWidth) - ) - } - } - public struct LayoutableChild { private var computeLayout: @MainActor ( - _ proposedSize: SIMD2, + _ proposedSize: ProposedViewSize, _ environment: EnvironmentValues ) -> ViewLayoutResult private var _commit: @MainActor () -> ViewLayoutResult var tag: String? public init( - computeLayout: @escaping @MainActor (SIMD2, EnvironmentValues) -> ViewLayoutResult, + computeLayout: @escaping @MainActor (ProposedViewSize, EnvironmentValues) -> ViewLayoutResult, commit: @escaping @MainActor () -> ViewLayoutResult, tag: String? = nil ) { @@ -65,7 +66,7 @@ public enum LayoutSystem { @MainActor public func computeLayout( - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, dryRun: Bool = false ) -> ViewLayoutResult { @@ -84,50 +85,109 @@ 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 computeStackLayout( + static func computeStackLayout( container: Backend.Widget, children: [LayoutableChild], - proposedSize: SIMD2, + cache: inout StackLayoutCache, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend, inheritStackLayoutParticipation: Bool = false ) -> ViewLayoutResult { let spacing = environment.layoutSpacing let orientation = environment.layoutOrientation + let perpendicularOrientation = orientation.perpendicular var renderedChildren: [ViewLayoutResult] = Array( - repeating: ViewLayoutResult.leafView(size: .empty), + repeating: ViewLayoutResult.leafView(size: .zero), count: children.count ) + let interesting = children.count == 1000 + if interesting { + print("interesting") + print(" proposedSize: \(proposedSize)") + } + + 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) + } + + let visibleChildrenCount = results.count { result in + result.participatesInStackLayouts + } + + let totalSpacing = Double(max(visibleChildrenCount - 1, 0) * spacing) + var size = ViewSize.zero + size[component: orientation] = resultLength + totalSpacing + 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 + ) + } + + 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 result = child.computeLayout( - proposedSize: proposedSize, - environment: environment + let minimumResult = child.computeLayout( + proposedSize: minimumProposedSize, + environment: environment.with(\.allowLayoutCaching, true) ) - isHidden[i] = !result.participatesInStackLayouts - return switch orientation { - case .horizontal: - result.size.maximumWidth - Double(result.size.minimumWidth) - case .vertical: - result.size.maximumHeight - Double(result.size.minimumHeight) - } + let maximumResult = child.computeLayout( + proposedSize: maximumProposedSize, + environment: environment.with(\.allowLayoutCaching, true) + ) + isHidden[i] = !minimumResult.participatesInStackLayouts + let maximum = maximumResult.size[component: orientation] + let minimum = minimumResult.size[component: orientation] + return maximum - minimum + } + if interesting { + print(" flexibilities: \(flexibilities)") } let visibleChildrenCount = isHidden.filter { hidden in !hidden }.count - let totalSpacing = max(visibleChildrenCount - 1, 0) * spacing + 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. @@ -148,135 +208,109 @@ 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) + + if interesting { + print(" proposedChildSize: \(proposedChildSize)") } let childResult = child.computeLayout( - proposedSize: SIMD2( - Int(proposedWidth.rounded(.towardZero)), - Int(proposedHeight.rounded(.towardZero)) - ), + 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: perpendicularOrientation]).max() ?? 0 + + cache.lastFlexibilityOrdering = sortedChildren.map(\.offset) + cache.lastHiddenChildren = isHidden - // 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 } + // 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 return ViewLayoutResult( - size: ViewSize( - size: size, - idealSize: idealSize, - idealWidthForProposedHeight: idealWidthForProposedHeight, - idealHeightForProposedWidth: idealHeightForProposedWidth, - minimumWidth: minimumWidth, - minimumHeight: minimumHeight, - maximumWidth: maximumWidth, - maximumHeight: maximumHeight, - participateInStackLayoutsWhenEmpty: !shouldGetIgnoredInStackLayouts - ), - childResults: renderedChildren + size: size, + childResults: renderedChildren, + participateInStackLayoutsWhenEmpty: + renderedChildren.contains(where: \.participateInStackLayoutsWhenEmpty), ) } @MainActor - public static func commitStackLayout( + static func commitStackLayout( container: Backend.Widget, children: [LayoutableChild], + cache: inout StackLayoutCache, layout: ViewLayoutResult, environment: EnvironmentValues, backend: Backend ) { - let size = layout.size.size - backend.setSize(of: container, to: size) - - let renderedChildren = children.map { $0.commit() } + 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 - var x = 0 - var y = 0 + 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.count { isHidden in + !isHidden + } + let totalSpacing = Double(max(visibleChildrenCount - 1, 0) * spacing) + var childrenRemaining = visibleChildrenCount + + // 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 + } + } + + let renderedChildren = children.map { $0.commit() } + + 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 @@ -286,29 +320,22 @@ public enum LayoutSystem { } // Compute alignment - switch (orientation, alignment) { - case (.vertical, .leading): - x = 0 - case (.horizontal, .leading): - y = 0 - case (.vertical, .center): - x = (size.x - child.size.size.x) / 2 - case (.horizontal, .center): - y = (size.y - child.size.size.y) / 2 - case (.vertical, .trailing): - x = (size.x - child.size.size.x) - case (.horizontal, .trailing): - y = (size.y - child.size.size.y) + 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: SIMD2(x, y)) + backend.setPosition(ofChildAt: index, in: container, to: position.vector) - switch orientation { - case .horizontal: - x += child.size.size.x + spacing - case .vertical: - y += child.size.size.y + spacing - } + 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 00000000000..1d9682397bb --- /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 00000000000..7eae852f078 --- /dev/null +++ b/Sources/SwiftCrossUI/Layout/ProposedViewSize.swift @@ -0,0 +1,78 @@ +/// 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? + + /// The proposal as a concrete view size if both dimensions are specified. + var concrete: ViewSize? { + if let width, let height { + ViewSize(width, height) + } else { + nil + } + } + + /// 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 00000000000..7dc1e015960 --- /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 cd1a478979b..528f6460b44 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. + package 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 246bbf4fce7..00000000000 --- a/Sources/SwiftCrossUI/Layout/ViewUpdateResult.swift +++ /dev/null @@ -1,33 +0,0 @@ -public struct ViewLayoutResult { - 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: [ViewLayoutResult], - preferencesOverlay: PreferenceValues? = nil - ) { - self.size = size - - preferences = PreferenceValues( - merging: childResults.map(\.preferences) - + [preferencesOverlay].compactMap { $0 } - ) - } - - public static func leafView(size: ViewSize) -> Self { - ViewLayoutResult(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 e5301fc3ba7..7578d95757d 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 f36efa8765b..4096c0d36bd 100644 --- a/Sources/SwiftCrossUI/Scenes/WindowGroupNode.swift +++ b/Sources/SwiftCrossUI/Scenes/WindowGroupNode.swift @@ -136,129 +136,63 @@ public final class WindowGroupNode: SceneGraphNode { } .with(\.window, window) - let dryRunResult: ViewLayoutResult? - 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 + proposedSize: .zero, + environment: environment.with(\.allowLayoutCaching, true) + ).size - let newWindowSize = computeNewWindowSize( - currentProposedSize: proposedWindowSize, - backend: backend, - contentSize: contentResult.size, - environment: environment + 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 { - // We don't use this result, but we do need to do a dry run to - // respect the assumptions of ViewGraph (each non dry run must - // follow a dry run). - _ = viewGraph.update( - with: newScene?.body, - proposedSize: proposedWindowSize, - environment: environment, - dryRun: true - ) - 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 { + let initialContentResult = viewGraph.computeLayout( + with: newScene?.body, + proposedSize: ProposedViewSize(proposedWindowSize), environment: environment ) - - if let newWindowSize { + if initialContentResult.size.vector != proposedWindowSize && !windowSizeIsFinal { return update( scene, - proposedWindowSize: newWindowSize, + proposedWindowSize: initialContentResult.size.vector, backend: backend, environment: environment, windowSizeIsFinal: true ) } + finalContentResult = initialContentResult } - // 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) @@ -273,29 +207,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 16f14a00e62..eaf1a95a0cf 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 dcd95dc80f7..ef67f26d417 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). @@ -71,19 +74,12 @@ extension Color: ElementaryView { func computeLayout( _ widget: Backend.Widget, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { ViewLayoutResult.leafView( - size: ViewSize( - size: proposedSize, - idealSize: SIMD2(10, 10), - minimumWidth: 0, - minimumHeight: 0, - maximumWidth: nil, - maximumHeight: nil - ) + size: proposedSize.replacingUnspecifiedDimensions(by: Self.idealSize) ) } @@ -93,7 +89,7 @@ extension Color: ElementaryView { environment: EnvironmentValues, backend: Backend ) { - backend.setSize(of: widget, to: layout.size.size) + backend.setSize(of: widget, to: layout.size.vector) backend.setColor(ofColorableRectangle: widget, to: self) } } diff --git a/Sources/SwiftCrossUI/Values/GeometryProxy.swift b/Sources/SwiftCrossUI/Values/GeometryProxy.swift index c08bf23f8bd..d9165b16016 100644 --- a/Sources/SwiftCrossUI/Values/GeometryProxy.swift +++ b/Sources/SwiftCrossUI/Values/GeometryProxy.swift @@ -3,5 +3,5 @@ public struct GeometryProxy { /// The size proposed to the view by its parent. In the context of /// ``GeometryReader``, this is the size that the ``GeometryReader`` /// will take on (to prevent feedback loops). - public var size: SIMD2 + public var size: ViewSize } diff --git a/Sources/SwiftCrossUI/Values/Orientation.swift b/Sources/SwiftCrossUI/Values/Orientation.swift index 0139a6d721c..01ac11dac3a 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 9fa4b09bccd..886a140baf8 100644 --- a/Sources/SwiftCrossUI/ViewGraph/AnyViewGraphNode.swift +++ b/Sources/SwiftCrossUI/ViewGraph/AnyViewGraphNode.swift @@ -16,7 +16,7 @@ public class AnyViewGraphNode { private var _computeLayoutWithNewView: ( _ newView: NodeView?, - _ proposedSize: SIMD2, + _ proposedSize: ProposedViewSize, _ environment: EnvironmentValues ) -> ViewLayoutResult /// The node's type-erased commit method. @@ -70,7 +70,7 @@ public class AnyViewGraphNode { /// the given size proposal already has a cached result. public func computeLayout( with newView: NodeView?, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues ) -> ViewLayoutResult { _computeLayoutWithNewView(newView, proposedSize, environment) diff --git a/Sources/SwiftCrossUI/ViewGraph/ErasedViewGraphNode.swift b/Sources/SwiftCrossUI/ViewGraph/ErasedViewGraphNode.swift index b84853a9d1b..0293bd32d92 100644 --- a/Sources/SwiftCrossUI/ViewGraph/ErasedViewGraphNode.swift +++ b/Sources/SwiftCrossUI/ViewGraph/ErasedViewGraphNode.swift @@ -11,7 +11,7 @@ public struct ErasedViewGraphNode { public var computeLayoutWithNewView: ( _ newView: Any?, - _ proposedSize: SIMD2, + _ proposedSize: ProposedViewSize, _ environment: EnvironmentValues ) -> (viewTypeMatched: Bool, size: ViewLayoutResult) /// The underlying view graph node's commit method. @@ -46,7 +46,7 @@ public struct ErasedViewGraphNode { computeLayoutWithNewView = { view, proposedSize, environment in if let view { guard let view = view as? V else { - return (false, ViewLayoutResult.leafView(size: .empty)) + return (false, ViewLayoutResult.leafView(size: .zero)) } let size = node.computeLayout( with: view, diff --git a/Sources/SwiftCrossUI/ViewGraph/ViewGraph.swift b/Sources/SwiftCrossUI/ViewGraph/ViewGraph.swift index a14bf62ef1a..0cdd4a80676 100644 --- a/Sources/SwiftCrossUI/ViewGraph/ViewGraph.swift +++ b/Sources/SwiftCrossUI/ViewGraph/ViewGraph.swift @@ -18,9 +18,11 @@ 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: ViewLayoutResult @@ -39,45 +41,47 @@ public class ViewGraph { rootNode = AnyViewGraphNode(for: view, backend: backend, environment: environment) self.view = view - windowSize = .zero + latestProposal = .zero + committedProposal = .zero parentEnvironment = environment - currentRootViewResult = ViewLayoutResult.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 + proposedSize: ProposedViewSize, + environment: EnvironmentValues ) -> ViewLayoutResult { parentEnvironment = environment - windowSize = proposedSize + latestProposal = proposedSize - // TODO: Refactor view graph node to be computeLayout+commit based - // instead of update based. - let result: ViewLayoutResult - if dryRun { - result = rootNode.computeLayout( - with: newView ?? view, - proposedSize: proposedSize, - environment: parentEnvironment - ) - } else { - result = rootNode.commit() + let result = rootNode.computeLayout( + with: newView ?? view, + proposedSize: proposedSize, + environment: parentEnvironment + ) + self.currentRootViewResult = result + if let newView { + self.view = newView } + return result + } - self.currentRootViewResult = result - if isFirstUpdate, !dryRun { + /// 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 1f698b32f00..483ffa429cf 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! @@ -40,10 +41,10 @@ public class ViewGraphNode: Sendable { 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: ViewLayoutResult] + 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] @@ -164,7 +165,7 @@ public class ViewGraphNode: Sendable { /// state. public func computeLayout( with newView: NodeView? = nil, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues ) -> ViewLayoutResult { // Defensively ensure that all future scene implementations obey this @@ -176,47 +177,22 @@ public class ViewGraphNode: Sendable { "View graph updated without parent window present in environment" ) - if let cachedResult = resultCache[proposedSize] { + if proposedSize == lastProposedSize && !resultCache.isEmpty && (!parentEnvironment.allowLayoutCaching || environment.allowLayoutCaching), let currentLayout { + // If the previous proposal is the same as the current one, and our + // cache hasn't been invalidated, then we can reuse the current layout. + // But only if the previous layout was computed without caching, or the + // current layout is being computed with caching, cause otherwise we could + // end up using a layout computed with caching while computing a layout + // without caching. + return currentLayout + } else if environment.allowLayoutCaching, let cachedResult = resultCache[proposedSize] { + // If this layout pass is a probing pass (not a final pass), then we + // can reuse any layouts that we've computed since the cache was last + // cleared. The cache gets cleared on commit. 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 let currentLayout, !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) >= currentLayout.size.maximumWidth - && Double(proposedSize.x) >= currentLayout.size.maximumWidth) - || proposedSize.x == lastProposedSize.x) - && ((Double(lastProposedSize.y) >= currentLayout.size.maximumHeight - && Double(proposedSize.y) >= currentLayout.size.maximumHeight) - || proposedSize.y == lastProposedSize.y) - { - return currentLayout - } - - // 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( - currentLayout.size.maximumWidth, - currentLayout.size.maximumHeight - ) - let minimumSize = SIMD2( - Double(currentLayout.size.minimumWidth), - Double(currentLayout.size.minimumHeight) - ) - if maximumSize == minimumSize { - return currentLayout - } - } - parentEnvironment = environment lastProposedSize = proposedSize @@ -263,7 +239,14 @@ public class ViewGraphNode: Sendable { guard let currentLayout else { print("warning: layout committed before being computed, ignoring") - return .leafView(size: .empty) + return .leafView(size: .zero) + } + + if parentEnvironment.allowLayoutCaching { + print("warning: Committing layout computed with caching enabled. Results may be invalid. NodeView = \(NodeView.self)") + } + if currentLayout.size.height == .infinity || currentLayout.size.width == .infinity { + print("warning: \(NodeView.self) has infinite height or width on commit, currentLayout.size: \(currentLayout.size), lastProposedSize: \(lastProposedSize)") } view.commit( diff --git a/Sources/SwiftCrossUI/Views/AnyView.swift b/Sources/SwiftCrossUI/Views/AnyView.swift index c80ada76b99..c83d533404b 100644 --- a/Sources/SwiftCrossUI/Views/AnyView.swift +++ b/Sources/SwiftCrossUI/Views/AnyView.swift @@ -55,7 +55,7 @@ public struct AnyView: TypeSafeView { func computeLayout( _ widget: Backend.Widget, children: AnyViewChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { @@ -104,7 +104,7 @@ public struct AnyView: TypeSafeView { _ = children.node.commit() - backend.setSize(of: widget, to: layout.size.size) + backend.setSize(of: widget, to: layout.size.vector) } } diff --git a/Sources/SwiftCrossUI/Views/Button.swift b/Sources/SwiftCrossUI/Views/Button.swift index 2145d94cd32..dc86fdc6945 100644 --- a/Sources/SwiftCrossUI/Views/Button.swift +++ b/Sources/SwiftCrossUI/Views/Button.swift @@ -31,7 +31,7 @@ extension Button: ElementaryView { public func computeLayout( _ widget: Backend.Widget, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { @@ -57,7 +57,7 @@ extension Button: ElementaryView { naturalSize.y ) - return ViewLayoutResult.leafView(size: ViewSize(fixedSize: size)) + return ViewLayoutResult.leafView(size: ViewSize(size)) } public func commit( @@ -66,6 +66,6 @@ extension Button: ElementaryView { environment: EnvironmentValues, backend: Backend ) { - backend.setSize(of: widget, to: layout.size.size) + backend.setSize(of: widget, to: layout.size.vector) } } diff --git a/Sources/SwiftCrossUI/Views/Checkbox.swift b/Sources/SwiftCrossUI/Views/Checkbox.swift index 7ddd8a1766c..1d9d810f9b2 100644 --- a/Sources/SwiftCrossUI/Views/Checkbox.swift +++ b/Sources/SwiftCrossUI/Views/Checkbox.swift @@ -14,12 +14,12 @@ struct Checkbox: ElementaryView, View { func computeLayout( _ widget: Backend.Widget, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { return ViewLayoutResult.leafView( - size: ViewSize(fixedSize: backend.naturalSize(of: widget)) + size: ViewSize(backend.naturalSize(of: widget)) ) } diff --git a/Sources/SwiftCrossUI/Views/EitherView.swift b/Sources/SwiftCrossUI/Views/EitherView.swift index 916b4d88727..685ca682469 100644 --- a/Sources/SwiftCrossUI/Views/EitherView.swift +++ b/Sources/SwiftCrossUI/Views/EitherView.swift @@ -50,7 +50,7 @@ extension EitherView: TypeSafeView { func computeLayout( _ widget: Backend.Widget, children: EitherViewChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { @@ -125,7 +125,7 @@ extension EitherView: TypeSafeView { _ = children.node.erasedNode.commit() - backend.setSize(of: widget, to: layout.size.size) + backend.setSize(of: widget, to: layout.size.vector) } } diff --git a/Sources/SwiftCrossUI/Views/ElementaryView.swift b/Sources/SwiftCrossUI/Views/ElementaryView.swift index cf55fd78fad..3e93b86f22a 100644 --- a/Sources/SwiftCrossUI/Views/ElementaryView.swift +++ b/Sources/SwiftCrossUI/Views/ElementaryView.swift @@ -9,7 +9,7 @@ protocol ElementaryView: View where Content == EmptyView { func computeLayout( _ widget: Backend.Widget, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult @@ -39,7 +39,7 @@ extension ElementaryView { public func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { diff --git a/Sources/SwiftCrossUI/Views/EmptyView.swift b/Sources/SwiftCrossUI/Views/EmptyView.swift index 44ff860c8d2..90cc8815073 100644 --- a/Sources/SwiftCrossUI/Views/EmptyView.swift +++ b/Sources/SwiftCrossUI/Views/EmptyView.swift @@ -39,11 +39,11 @@ public struct EmptyView: View, Sendable { public func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { - ViewLayoutResult.leafView(size: .empty) + ViewLayoutResult.leafView(size: .zero) } public func commit( diff --git a/Sources/SwiftCrossUI/Views/ForEach.swift b/Sources/SwiftCrossUI/Views/ForEach.swift index 29572a95eae..b050be06585 100644 --- a/Sources/SwiftCrossUI/Views/ForEach.swift +++ b/Sources/SwiftCrossUI/Views/ForEach.swift @@ -81,7 +81,7 @@ extension ForEach: TypeSafeView, View where Child: View { func computeLayout( _ widget: Backend.Widget, children: ForEachViewChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { @@ -159,6 +159,7 @@ extension ForEach: TypeSafeView, View where Child: View { return LayoutSystem.computeStackLayout( container: widget, children: layoutableChildren, + cache: &children.stackLayoutCache, proposedSize: proposedSize, environment: environment, backend: backend @@ -196,6 +197,7 @@ extension ForEach: TypeSafeView, View where Child: View { commit: node.commit ) }, + cache: &children.stackLayoutCache, layout: layout, environment: environment, backend: backend @@ -237,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 index 7f49e525872..4a1bd9ea4dd 100644 --- a/Sources/SwiftCrossUI/Views/GeometryReader.swift +++ b/Sources/SwiftCrossUI/Views/GeometryReader.swift @@ -55,11 +55,12 @@ public struct GeometryReader: TypeSafeView, View { func computeLayout( _ widget: Backend.Widget, children: GeometryReaderChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { - let view = content(GeometryProxy(size: proposedSize)) + let size = proposedSize.replacingUnspecifiedDimensions(by: ViewSize(10, 10)) + let view = content(GeometryProxy(size: size)) let environment = environment.with(\.layoutAlignment, .leading) @@ -74,31 +75,17 @@ public struct GeometryReader: TypeSafeView, View { ) 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.computeLayout( with: view, - proposedSize: proposedSize, + proposedSize: ProposedViewSize(size), environment: environment ) return ViewLayoutResult( - size: ViewSize( - size: proposedSize, - idealSize: SIMD2(10, 10), - minimumWidth: 0, - minimumHeight: 0, - maximumWidth: nil, - maximumHeight: nil - ), + size: size, childResults: [contentResult] ) } @@ -112,7 +99,7 @@ public struct GeometryReader: TypeSafeView, View { ) { _ = children.node?.commit() backend.setPosition(ofChildAt: 0, in: widget, to: .zero) - backend.setSize(of: widget, to: layout.size.size) + backend.setSize(of: widget, to: layout.size.vector) } } diff --git a/Sources/SwiftCrossUI/Views/Group.swift b/Sources/SwiftCrossUI/Views/Group.swift index 41a2786b50f..1e92224e476 100644 --- a/Sources/SwiftCrossUI/Views/Group.swift +++ b/Sources/SwiftCrossUI/Views/Group.swift @@ -26,18 +26,25 @@ public struct Group: View { public func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { - LayoutSystem.computeStackLayout( + 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, inheritStackLayoutParticipation: true ) + (children as? TupleViewChildren)?.stackLayoutCache = cache + return result } public func commit( @@ -47,12 +54,15 @@ public struct Group: View { 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 85756c9919a..36fe1f788d4 100644 --- a/Sources/SwiftCrossUI/Views/HStack.swift +++ b/Sources/SwiftCrossUI/Views/HStack.swift @@ -32,13 +32,18 @@ public struct HStack: View { public func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { - return LayoutSystem.computeStackLayout( + 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 @@ -47,6 +52,8 @@ public struct HStack: View { .with(\.layoutSpacing, spacing), backend: backend ) + (children as? TupleViewChildren)?.stackLayoutCache = cache + return result } public func commit( @@ -56,9 +63,11 @@ public struct HStack: View { 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 @@ -67,5 +76,6 @@ public struct HStack: View { .with(\.layoutSpacing, spacing), backend: backend ) + (children as? TupleViewChildren)?.stackLayoutCache = cache } } diff --git a/Sources/SwiftCrossUI/Views/HotReloadableView.swift b/Sources/SwiftCrossUI/Views/HotReloadableView.swift index 44d46dc527a..e610762bec9 100644 --- a/Sources/SwiftCrossUI/Views/HotReloadableView.swift +++ b/Sources/SwiftCrossUI/Views/HotReloadableView.swift @@ -54,7 +54,7 @@ public struct HotReloadableView: TypeSafeView { func computeLayout( _ widget: Backend.Widget, children: HotReloadableViewChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { @@ -104,7 +104,7 @@ public struct HotReloadableView: TypeSafeView { _ = children.node.commit() - backend.setSize(of: widget, to: layout.size.size) + backend.setSize(of: widget, to: layout.size.vector) } } diff --git a/Sources/SwiftCrossUI/Views/Image.swift b/Sources/SwiftCrossUI/Views/Image.swift index de02903564c..b363c2919d4 100644 --- a/Sources/SwiftCrossUI/Views/Image.swift +++ b/Sources/SwiftCrossUI/Views/Image.swift @@ -69,7 +69,7 @@ extension Image: TypeSafeView { func computeLayout( _ widget: Backend.Widget, children: _ImageChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { @@ -101,19 +101,16 @@ extension Image: TypeSafeView { 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 - ) + if let image { + let idealSize = ViewSize(Double(image.width), Double(image.height)) + if isResizable { + size = proposedSize.replacingUnspecifiedDimensions(by: idealSize) + } else { + size = idealSize + } } else { - size = ViewSize(fixedSize: idealSize) + size = .zero } return ViewLayoutResult.leafView(size: size) @@ -126,9 +123,9 @@ extension Image: TypeSafeView { environment: EnvironmentValues, backend: Backend ) { - let size = layout.size.size + let size = layout.size.vector let hasResized = children.cachedImageDisplaySize != size - children.cachedImageDisplaySize = layout.size.size + children.cachedImageDisplaySize = size if children.imageChanged || hasResized || (backend.requiresImageUpdateOnScaleFactorChange diff --git a/Sources/SwiftCrossUI/Views/List.swift b/Sources/SwiftCrossUI/Views/List.swift index 6a988d48626..258edad9cc9 100644 --- a/Sources/SwiftCrossUI/Views/List.swift +++ b/Sources/SwiftCrossUI/Views/List.swift @@ -101,7 +101,7 @@ public struct List: TypeSafeView, View func computeLayout( _ widget: Backend.Widget, children: Children, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { @@ -139,55 +139,41 @@ public struct List: TypeSafeView, View var childResults: [ViewLayoutResult] = [] for (rowView, node) in zip(rowViews, children.nodes) { - let preferredSize = node.computeLayout( - with: rowView, - proposedSize: SIMD2( - max(proposedSize.x, minimumRowSize.x) - baseRowPadding.axisTotals.x, - max(proposedSize.y, minimumRowSize.y) - baseRowPadding.axisTotals.y - ), - environment: environment - ).size + let proposedWidth: Double? + if let width = proposedSize.width { + proposedWidth = max( + Double(minimumRowSize.x), + width - Double(baseRowPadding.axisTotals.x) + ) + } else { + proposedWidth = nil + } + let childResult = node.computeLayout( - with: nil, - proposedSize: SIMD2( - max(proposedSize.x, minimumRowSize.x) - horizontalBasePadding, - max( - preferredSize.idealHeightForProposedWidth, - minimumRowSize.y - baseRowPadding.axisTotals.y - ) + with: rowView, + proposedSize: ProposedViewSize( + proposedWidth, + nil ), environment: environment ) childResults.append(childResult) } - let size = SIMD2( + let height = childResults.map(\.size.height).map { rowHeight in 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, +) + rowHeight + Double(verticalBasePadding), + Double(minimumRowSize.y) + ) + }.reduce(0, +) + let minimumWidth = (childResults.map(\.size.width).max() ?? 0) + Double(horizontalBasePadding) + let size = ViewSize( + max(proposedSize.width ?? minimumWidth, minimumWidth), + height ) return ViewLayoutResult( - 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) - ), + size: size, childResults: childResults ) } @@ -206,12 +192,12 @@ public struct List: TypeSafeView, View backend.setItems( ofSelectableListView: widget, to: children.widgets.map { $0.into() }, - withRowHeights: childResults.map(\.size.size.y).map { height in - height + verticalBasePadding + withRowHeights: childResults.map(\.size.height).map { height in + LayoutSystem.roundSize(height) + verticalBasePadding } ) - backend.setSize(of: widget, to: layout.size.size) + backend.setSize(of: widget, to: layout.size.vector) backend.setSelectionHandler(forSelectableListView: widget) { selectedIndex in selection.wrappedValue = associatedSelectionValue(selectedIndex) } diff --git a/Sources/SwiftCrossUI/Views/Menu.swift b/Sources/SwiftCrossUI/Views/Menu.swift index 1b87b9d967b..cf9a78b80c1 100644 --- a/Sources/SwiftCrossUI/Views/Menu.swift +++ b/Sources/SwiftCrossUI/Views/Menu.swift @@ -67,7 +67,7 @@ extension Menu: TypeSafeView { func computeLayout( _ widget: Backend.Widget, children: MenuStorage, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { @@ -75,7 +75,7 @@ extension Menu: TypeSafeView { // continue updating it even once it's open. var size = backend.naturalSize(of: widget) size.x = buttonWidth ?? size.x - return ViewLayoutResult.leafView(size: ViewSize(fixedSize: size)) + return ViewLayoutResult.leafView(size: ViewSize(size)) } func commit( @@ -85,7 +85,9 @@ extension Menu: TypeSafeView { environment: EnvironmentValues, backend: Backend ) { - let size = layout.size.size + let size = layout.size + backend.setSize(of: widget, to: size.vector) + let content = resolve().content switch backend.menuImplementationStyle { case .dynamicPopover: @@ -103,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 @@ -111,7 +113,6 @@ extension Menu: TypeSafeView { } ) - backend.setSize(of: widget, to: size) children.updateMenuIfShown( content: content, environment: environment, @@ -126,8 +127,6 @@ extension Menu: TypeSafeView { environment: environment ) backend.updateButton(widget, label: label, menu: menu, environment: environment) - - backend.setSize(of: widget, to: size) } } diff --git a/Sources/SwiftCrossUI/Views/Modifiers/AlertModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/AlertModifier.swift index 79851ef4d29..a001365c195 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/AlertModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/AlertModifier.swift @@ -68,7 +68,7 @@ struct AlertModifierView: TypeSafeView { func computeLayout( _ widget: Backend.Widget, children: Children, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { diff --git a/Sources/SwiftCrossUI/Views/Modifiers/EnvironmentModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/EnvironmentModifier.swift index 20f2cc5d8b5..216a2142446 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/EnvironmentModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/EnvironmentModifier.swift @@ -22,7 +22,7 @@ package struct EnvironmentModifier: View { package func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnChangeModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnChangeModifier.swift index aced06c2ee9..fd5cf309888 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnChangeModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnChangeModifier.swift @@ -28,7 +28,7 @@ struct OnChangeModifier: View { func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnHoverModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnHoverModifier.swift index 91093235407..4cd346a44e1 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnHoverModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnHoverModifier.swift @@ -35,7 +35,7 @@ struct OnHoverModifier: TypeSafeView { func computeLayout( _ widget: Backend.Widget, children: Children, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { @@ -53,7 +53,7 @@ struct OnHoverModifier: TypeSafeView { environment: EnvironmentValues, backend: Backend ) { - let size = children.child0.commit().size.size + let size = children.child0.commit().size.vector backend.setSize(of: widget, to: size) backend.updateHoverTarget( widget, diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnTapGestureModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnTapGestureModifier.swift index a43c8bc922e..1feb583a42f 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnTapGestureModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/Handlers/OnTapGestureModifier.swift @@ -64,7 +64,7 @@ struct OnTapGestureModifier: TypeSafeView { func computeLayout( _ widget: Backend.Widget, children: Children, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { @@ -82,8 +82,8 @@ struct OnTapGestureModifier: TypeSafeView { environment: EnvironmentValues, backend: Backend ) { - let size = children.child0.commit().size.size - backend.setSize(of: widget, to: size) + let size = children.child0.commit().size + backend.setSize(of: widget, to: size.vector) backend.updateTapGestureTarget( widget, gesture: gesture, diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Layout/AspectRatioModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Layout/AspectRatioModifier.swift index f44a6f94453..a2da3d68826 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/Layout/AspectRatioModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/Layout/AspectRatioModifier.swift @@ -1,18 +1,10 @@ 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. + /// Modifies size proposals to match the specified aspect ratio, or the view's + /// ideal aspect ratio if unspecified. + /// + /// This modifier doesn't guarantee that the view's size will maintain a + /// constant aspect ratio, as it only changes the size proposed to the + /// wrapped content. /// - 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. @@ -20,8 +12,11 @@ extension View { AspectRatioView(self, aspectRatio: aspectRatio, contentMode: contentMode) } - /// Constrains a view to maintain an aspect ratio matching that of the - /// provided size. + /// Modifies size proposals to match the aspect ratio of the provided size. + /// + /// This modifier doesn't guarantee that the view's size will maintain a + /// constant aspect ratio, as it only changes the size proposed to the + /// wrapped content. /// - 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. @@ -58,77 +53,74 @@ struct AspectRatioView: TypeSafeView { _ children: TupleViewChildren1, backend: Backend ) -> Backend.Widget { - let container = backend.createContainer() - backend.addChild(children.child0.widget.into(), to: container) - return container + children.child0.widget.into() } func computeLayout( _ widget: Backend.Widget, children: TupleViewChildren1, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { - let evaluatedAspectRatio: Double - if let aspectRatio { - evaluatedAspectRatio = aspectRatio == 0 ? 1 : aspectRatio - } else { - let childResult = children.child0.computeLayout( - with: body.view0, - proposedSize: proposedSize, - environment: environment - ) - evaluatedAspectRatio = childResult.size.idealAspectRatio + // We lazily compute the aspect ratio because we don't always need it. + // For example, when both dimensions are specified we pass them unmodified. + func computeAspectRatio() -> Double { + if let aspectRatio { + return aspectRatio == 0 ? 1 : aspectRatio + } else { + let childResult = children.child0.computeLayout( + with: body.view0, + proposedSize: .unspecified, + environment: environment + ) + return LayoutSystem.aspectRatio(of: childResult.size) + } } - let proposedFrameSize = LayoutSystem.frameSize( - forProposedSize: proposedSize, - aspectRatio: evaluatedAspectRatio, - contentMode: contentMode - ) + let proposedFrameSize: ProposedViewSize + switch (proposedSize.width, proposedSize.height) { + case (.some(let width), .some(let height)): + let evaluatedAspectRatio = computeAspectRatio() + let widthForHeight = LayoutSystem.width( + forHeight: height, + aspectRatio: evaluatedAspectRatio + ) + let heightForWidth = LayoutSystem.height( + forWidth: width, + aspectRatio: evaluatedAspectRatio + ) + switch contentMode { + case .fill: + proposedFrameSize = ProposedViewSize( + max(width, widthForHeight), + max(height, heightForWidth) + ) + case .fit: + proposedFrameSize = ProposedViewSize( + min(width, widthForHeight), + min(height, heightForWidth) + ) + } + case (.some(let width), .none): + proposedFrameSize = ProposedViewSize( + width, + LayoutSystem.height(forWidth: width, aspectRatio: computeAspectRatio()) + ) + case (.none, .some(let height)): + proposedFrameSize = ProposedViewSize( + LayoutSystem.width(forHeight: height, aspectRatio: computeAspectRatio()), + height + ) + case (.none, .none): + proposedFrameSize = .unspecified + } - let childResult = children.child0.computeLayout( - with: nil, + return children.child0.computeLayout( + with: body.view0, proposedSize: proposedFrameSize, environment: environment ) - - let frameSize = LayoutSystem.frameSize( - forProposedSize: childResult.size.size, - aspectRatio: evaluatedAspectRatio, - contentMode: contentMode.opposite - ) - - return ViewLayoutResult( - 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] - ) } func commit( @@ -138,16 +130,6 @@ struct AspectRatioView: TypeSafeView { environment: EnvironmentValues, backend: Backend ) { - // 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 childResult = children.child0.commit() - print(childResult.size.size) - let childPosition = Alignment.center.position( - ofChild: childResult.size.size, - in: layout.size.size - ) - backend.setPosition(ofChildAt: 0, in: widget, to: childPosition) - backend.setSize(of: widget, to: layout.size.size) + _ = children.child0.commit() } } diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Layout/BackgroundModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Layout/BackgroundModifier.swift index 3c135a1ce6f..c4088710210 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/Layout/BackgroundModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/Layout/BackgroundModifier.swift @@ -37,7 +37,7 @@ struct BackgroundModifier: TypeSafeView { func computeLayout( _ widget: Backend.Widget, children: TupleView2.Children, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { @@ -49,36 +49,20 @@ struct BackgroundModifier: TypeSafeView { let foregroundSize = foregroundResult.size let backgroundResult = children.child0.computeLayout( with: body.view0, - proposedSize: foregroundSize.size, + 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) ) + // TODO: Investigate the ordering of SwiftUI's preference merging for + // the background modifier. return ViewLayoutResult( - 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) - ), + size: frameSize, childResults: [backgroundResult, foregroundResult] ) } @@ -90,16 +74,22 @@ struct BackgroundModifier: TypeSafeView { environment: EnvironmentValues, backend: Backend ) { - let frameSize = layout.size.size + let frameSize = layout.size let backgroundSize = children.child0.commit().size let foregroundSize = children.child1.commit().size - let backgroundPosition = (frameSize &- backgroundSize.size) / 2 - let foregroundPosition = (frameSize &- foregroundSize.size) / 2 + 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) + 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 85c7363ef47..9000ff2a184 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/Layout/FixedSizeModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/Layout/FixedSizeModifier.swift @@ -40,42 +40,25 @@ struct FixedSizeModifier: TypeSafeView { func computeLayout( _ widget: Backend.Widget, children: TupleViewChildren1, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { - let probingChildResult = children.child0.computeLayout( - with: body.view0, - proposedSize: proposedSize, - 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 + var childProposal = proposedSize + if horizontal { + childProposal.width = nil + } + if vertical { + childProposal.height = nil } - let childResult = children.child0.computeLayout( with: body.view0, - proposedSize: frameSize, + proposedSize: proposedSize, environment: environment ) return ViewLayoutResult( - 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 - ), + size: childResult.size, childResults: [childResult] ) } @@ -89,10 +72,10 @@ struct FixedSizeModifier: TypeSafeView { ) { let childResult = children.child0.commit() let childPosition = Alignment.center.position( - ofChild: childResult.size.size, - in: layout.size.size + ofChild: childResult.size.vector, + in: layout.size.vector ) backend.setPosition(ofChildAt: 0, in: widget, to: childPosition) - backend.setSize(of: widget, to: layout.size.size) + 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 953f85aac85..3c297c28587 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/Layout/FrameModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/Layout/FrameModifier.swift @@ -75,67 +75,30 @@ struct StrictFrameView: TypeSafeView { func computeLayout( _ widget: Backend.Widget, children: TupleViewChildren1, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { - let proposedSize = SIMD2( - width ?? proposedSize.x, - height ?? proposedSize.y - ) + let width = width.map(Double.init) + let height = height.map(Double.init) let childResult = children.child0.computeLayout( with: body.view0, - proposedSize: proposedSize, + 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 ) - 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 - } - - let idealWidthForProposedHeight: Int - let idealHeightForProposedWidth: Int - if width == nil && height == nil { - idealWidthForProposedHeight = childSize.idealWidthForProposedHeight - idealHeightForProposedWidth = childSize.idealHeightForProposedWidth - } else { - idealWidthForProposedHeight = idealWidth - idealHeightForProposedWidth = idealHeight - } - return ViewLayoutResult( - 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 - ), + size: frameSize, childResults: [childResult] ) } @@ -147,14 +110,14 @@ struct StrictFrameView: TypeSafeView { environment: EnvironmentValues, backend: Backend ) { - let frameSize = layout.size.size + let frameSize = layout.size let childSize = children.child0.commit().size let childPosition = alignment.position( - ofChild: childSize.size, - in: frameSize + ofChild: childSize.vector, + in: frameSize.vector ) - backend.setSize(of: widget, to: frameSize) + backend.setSize(of: widget, to: frameSize.vector) backend.setPosition(ofChildAt: 0, in: widget, to: childPosition) } } @@ -213,25 +176,25 @@ struct FlexibleFrameView: TypeSafeView { func computeLayout( _ widget: Backend.Widget, children: TupleViewChildren1, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, 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 ) } @@ -248,59 +211,22 @@ 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 } return ViewLayoutResult( @@ -316,14 +242,14 @@ struct FlexibleFrameView: TypeSafeView { environment: EnvironmentValues, backend: Backend ) { - let frameSize = layout.size.size + let frameSize = layout.size let childSize = children.child0.commit().size let childPosition = alignment.position( - ofChild: childSize.size, - in: frameSize + ofChild: childSize.vector, + in: frameSize.vector ) - backend.setSize(of: widget, to: frameSize) + backend.setSize(of: widget, to: frameSize.vector) backend.setPosition(ofChildAt: 0, in: widget, to: childPosition) } } diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Layout/OverlayModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Layout/OverlayModifier.swift index 70d62db0ed7..51cc165e641 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/Layout/OverlayModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/Layout/OverlayModifier.swift @@ -41,7 +41,7 @@ struct OverlayModifier: TypeSafeView { func computeLayout( _ widget: Backend.Widget, children: TupleView2.Children, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { @@ -53,25 +53,18 @@ struct OverlayModifier: TypeSafeView { let contentSize = contentResult.size let overlayResult = children.child1.computeLayout( with: body.view1, - proposedSize: contentSize.size, + proposedSize: ProposedViewSize(contentSize), environment: environment ) let overlaySize = overlayResult.size - let frameSize = SIMD2( - max(contentSize.size.x, overlaySize.size.x), - max(contentSize.size.y, overlaySize.size.y) + let size = ViewSize( + max(contentSize.width, overlaySize.width), + max(contentSize.height, overlaySize.height) ) return ViewLayoutResult( - 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) - ), + size: size, childResults: [contentResult, overlayResult] ) } @@ -83,12 +76,12 @@ struct OverlayModifier: TypeSafeView { environment: EnvironmentValues, backend: Backend ) { - let frameSize = layout.size.size - let contentSize = children.child0.commit().size - let overlaySize = children.child1.commit().size + let frameSize = layout.size.vector + let contentSize = children.child0.commit().size.vector + let overlaySize = children.child1.commit().size.vector - let contentPosition = (frameSize &- contentSize.size) / 2 - let overlayPosition = (frameSize &- overlaySize.size) / 2 + let contentPosition = Alignment.center.position(ofChild: contentSize, in: frameSize) + let overlayPosition = Alignment.center.position(ofChild: overlaySize, in: frameSize) backend.setPosition(ofChildAt: 0, in: widget, to: contentPosition) backend.setPosition(ofChildAt: 1, in: widget, to: overlayPosition) diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Layout/PaddingModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Layout/PaddingModifier.swift index 01d8f6ca8d4..d2429b6942c 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/Layout/PaddingModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/Layout/PaddingModifier.swift @@ -100,44 +100,36 @@ struct PaddingModifierView: TypeSafeView { func computeLayout( _ container: Backend.Widget, children: TupleViewChildren1, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, 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.computeLayout( with: body.view0, - proposedSize: SIMD2( - max(proposedSize.x - insets.leading - insets.trailing, 0), - max(proposedSize.y - insets.top - insets.bottom, 0) - ), + 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 + var size = childResult.size + size.width += horizontalPadding + size.height += verticalPadding - let idealWidth = childSize.idealWidthForProposedHeight + paddingSize.x - let idealHeight = childSize.idealHeightForProposedWidth + paddingSize.y return ViewLayoutResult( - size: ViewSize( - size: size, - idealSize: childSize.idealSize &+ paddingSize, - idealWidthForProposedHeight: idealWidth, - idealHeightForProposedWidth: idealHeight, - minimumWidth: childSize.minimumWidth + paddingSize.x, - minimumHeight: childSize.minimumHeight + paddingSize.y, - maximumWidth: childSize.maximumWidth + Double(paddingSize.x), - maximumHeight: childSize.maximumHeight + Double(paddingSize.y) - ), + size: size, childResults: [childResult] ) } @@ -151,8 +143,8 @@ struct PaddingModifierView: TypeSafeView { ) { _ = children.child0.commit() - let size = layout.size.size - backend.setSize(of: container, to: size) + 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) diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Lifecycle/OnDisappearModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Lifecycle/OnDisappearModifier.swift index cf70e083e62..0d214cbef15 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/Lifecycle/OnDisappearModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/Lifecycle/OnDisappearModifier.swift @@ -49,7 +49,7 @@ struct OnDisappearModifier: TypeSafeView { func computeLayout( _ widget: Backend.Widget, children: OnDisappearModifierChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { diff --git a/Sources/SwiftCrossUI/Views/Modifiers/PreferenceModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/PreferenceModifier.swift index 3157f7f85ce..c4a641bf3ad 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/PreferenceModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/PreferenceModifier.swift @@ -26,7 +26,7 @@ struct PreferenceModifier: View { func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { diff --git a/Sources/SwiftCrossUI/Views/Modifiers/SheetModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/SheetModifier.swift index 0631e07443f..7f5f1dadd79 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/SheetModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/SheetModifier.swift @@ -67,7 +67,7 @@ struct SheetModifier: TypeSafeView { func computeLayout( _ widget: Backend.Widget, children: Children, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { @@ -111,7 +111,7 @@ struct SheetModifier: TypeSafeView { _ = children.sheetContentNode!.computeLayout( with: sheetContent(), - proposedSize: SIMD2(10_000, 0), + proposedSize: .unspecified, environment: sheetEnvironment ) let result = children.sheetContentNode!.commit() @@ -125,7 +125,7 @@ struct SheetModifier: TypeSafeView { // sheetEnvironment here, because this is meant to be the sheet's // environment, not that of its content. environment: environment, - size: result.size.size, + size: result.size.vector, onDismiss: { handleDismiss(children: children) }, cornerRadius: preferences.presentationCornerRadius, detents: preferences.presentationDetents ?? [], diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Style/CornerRadiusModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Style/CornerRadiusModifier.swift index 5fa3c43e498..98e350eaddd 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/Style/CornerRadiusModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/Style/CornerRadiusModifier.swift @@ -33,7 +33,7 @@ struct CornerRadiusModifier: View { func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { diff --git a/Sources/SwiftCrossUI/Views/OptionalView.swift b/Sources/SwiftCrossUI/Views/OptionalView.swift index 7b9590b22ba..c39dfcbd6b6 100644 --- a/Sources/SwiftCrossUI/Views/OptionalView.swift +++ b/Sources/SwiftCrossUI/Views/OptionalView.swift @@ -42,7 +42,7 @@ extension OptionalView: TypeSafeView { func computeLayout( _ widget: Backend.Widget, children: OptionalViewChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { @@ -73,7 +73,7 @@ extension OptionalView: TypeSafeView { } else { hasToggled = children.node != nil children.node = nil - result = ViewLayoutResult.leafView(size: .hidden) + result = ViewLayoutResult.leafView(size: .zero) } children.hasToggled = children.hasToggled || hasToggled @@ -98,7 +98,7 @@ extension OptionalView: TypeSafeView { _ = children.node?.commit() - backend.setSize(of: widget, to: layout.size.size) + backend.setSize(of: widget, to: layout.size.vector) } } diff --git a/Sources/SwiftCrossUI/Views/Picker.swift b/Sources/SwiftCrossUI/Views/Picker.swift index 9cfa8a4ea60..d7157fccb35 100644 --- a/Sources/SwiftCrossUI/Views/Picker.swift +++ b/Sources/SwiftCrossUI/Views/Picker.swift @@ -24,7 +24,7 @@ public struct Picker: ElementaryView, View { func computeLayout( _ widget: Backend.Widget, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { @@ -47,23 +47,14 @@ public struct Picker: ElementaryView, View { // 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) { - return ViewLayoutResult.leafView( - size: ViewSize( - size: proposedSize, - idealSize: SIMD2(10, 10), - minimumWidth: 0, - minimumHeight: 0, - maximumWidth: nil, - maximumHeight: nil - ) - ) + let naturalSize = backend.naturalSize(of: widget) + let size: ViewSize + if naturalSize == SIMD2(-1, -1) { + size = proposedSize.replacingUnspecifiedDimensions(by: ViewSize(10, 10)) } else { - return ViewLayoutResult.leafView( - size: ViewSize(fixedSize: size) - ) + size = ViewSize(naturalSize) } + return ViewLayoutResult.leafView(size: size) } func commit( @@ -72,6 +63,6 @@ public struct Picker: ElementaryView, View { environment: EnvironmentValues, backend: Backend ) { - backend.setSize(of: widget, to: layout.size.size) + backend.setSize(of: widget, to: layout.size.vector) } } diff --git a/Sources/SwiftCrossUI/Views/ProgressView.swift b/Sources/SwiftCrossUI/Views/ProgressView.swift index c0a44d608a4..abd34ca147c 100644 --- a/Sources/SwiftCrossUI/Views/ProgressView.swift +++ b/Sources/SwiftCrossUI/Views/ProgressView.swift @@ -109,13 +109,12 @@ struct ProgressSpinnerView: ElementaryView { func computeLayout( _ widget: Backend.Widget, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { - ViewLayoutResult.leafView( - size: ViewSize(fixedSize: backend.naturalSize(of: widget)) - ) + let size = ViewSize(backend.naturalSize(of: widget)) + return ViewLayoutResult.leafView(size: size) } func commit( @@ -127,6 +126,9 @@ struct ProgressSpinnerView: ElementaryView { } struct ProgressBarView: ElementaryView { + /// The ideal width of a ProgressBarView. + static let idealWidth: Double = 100 + var value: Double? init(value: Double?) { @@ -139,26 +141,17 @@ struct ProgressBarView: ElementaryView { func computeLayout( _ widget: Backend.Widget, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { let height = backend.naturalSize(of: widget).y - let size = SIMD2( - proposedSize.x, - height + let size = ViewSize( + proposedSize.width ?? Self.idealWidth, + Double(height) ) - return ViewLayoutResult.leafView( - size: ViewSize( - size: size, - idealSize: SIMD2(100, height), - minimumWidth: 0, - minimumHeight: height, - maximumWidth: nil, - maximumHeight: Double(height) - ) - ) + return ViewLayoutResult.leafView(size: size) } func commit( @@ -168,6 +161,6 @@ struct ProgressBarView: ElementaryView { backend: Backend ) { backend.updateProgressBar(widget, progressFraction: value, environment: environment) - backend.setSize(of: widget, to: layout.size.size) + backend.setSize(of: widget, to: layout.size.vector) } } diff --git a/Sources/SwiftCrossUI/Views/ScrollView.swift b/Sources/SwiftCrossUI/Views/ScrollView.swift index c8178b893bb..257f91dee89 100644 --- a/Sources/SwiftCrossUI/Views/ScrollView.swift +++ b/Sources/SwiftCrossUI/Views/ScrollView.swift @@ -46,89 +46,98 @@ public struct ScrollView: TypeSafeView, View { func computeLayout( _ widget: Backend.Widget, children: ScrollViewChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { + // If all scroll axes are unspecified, then our size is exactly that of + // the child view. This includes when we have no scroll axes. + let willEarlyExit = Axis.allCases.allSatisfy({ axis in + !axes.contains(axis) || proposedSize[component: axis] == nil + }) + // Probe how big the child would like to be + 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 + proposedSize: childProposal, + environment: environment.with( + \.allowLayoutCaching, + !willEarlyExit || environment.allowLayoutCaching + ) ) - let contentSize = childResult.size - - let scrollBarWidth = backend.scrollBarWidth - let hasHorizontalScrollBar = - axes.contains(.horizontal) && contentSize.idealWidthForProposedHeight > proposedSize.x - let hasVerticalScrollBar = - axes.contains(.vertical) && contentSize.idealHeightForProposedWidth > proposedSize.y - children.hasHorizontalScrollBar = hasHorizontalScrollBar - children.hasVerticalScrollBar = hasVerticalScrollBar + if willEarlyExit { + return childResult + } - let verticalScrollBarWidth = hasVerticalScrollBar ? scrollBarWidth : 0 - let horizontalScrollBarHeight = hasHorizontalScrollBar ? scrollBarWidth : 0 + let contentSize = childResult.size - let scrollViewWidth: Int - let scrollViewHeight: Int - let minimumWidth: Int - let minimumHeight: Int - if axes.contains(.horizontal) { - scrollViewWidth = max(proposedSize.x, verticalScrollBarWidth) - minimumWidth = verticalScrollBarWidth + // 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 { - scrollViewWidth = min( - contentSize.size.x + verticalScrollBarWidth, - max(proposedSize.x, contentSize.minimumWidth + verticalScrollBarWidth) - ) - minimumWidth = contentSize.minimumWidth + verticalScrollBarWidth + hasHorizontalScrollBar = false } - if axes.contains(.vertical) { - scrollViewHeight = max(proposedSize.y, horizontalScrollBarHeight) - minimumHeight = horizontalScrollBarHeight + + let hasVerticalScrollBar: Bool + if axes.contains(.vertical), let proposedHeight = proposedSize.height { + hasVerticalScrollBar = contentSize.height > proposedHeight } else { - scrollViewHeight = min( - contentSize.size.y + horizontalScrollBarHeight, - max(proposedSize.y, contentSize.minimumHeight + horizontalScrollBarHeight) - ) - minimumHeight = contentSize.minimumHeight + horizontalScrollBarHeight + hasVerticalScrollBar = false } - let scrollViewSize = SIMD2( - scrollViewWidth, - scrollViewHeight - ) + let scrollBarWidth = Double(backend.scrollBarWidth) + let verticalScrollBarWidth = hasVerticalScrollBar ? scrollBarWidth : 0 + let horizontalScrollBarHeight = hasHorizontalScrollBar ? scrollBarWidth : 0 - // 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 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), let proposedHeight = childProposal.height { + finalContentSizeProposal.height = proposedHeight - horizontalScrollBarHeight + } + + // Propose a final size to the child view. let finalChildResult = children.child.computeLayout( - with: body, - proposedSize: proposedContentSize, + with: nil, + proposedSize: finalContentSizeProposal, environment: environment ) + // Compute the outer size. + var outerSize = finalChildResult.size + if axes.contains(.horizontal) { + outerSize.width = min( + finalChildResult.size.width + verticalScrollBarWidth, + proposedSize.width ?? 0 + ) + } else { + outerSize.width += verticalScrollBarWidth + } + + if axes.contains(.vertical) { + outerSize.height = min( + finalChildResult.size.height + horizontalScrollBarHeight, + proposedSize.height ?? 0 + ) + } else { + outerSize.height += horizontalScrollBarHeight + } + return ViewLayoutResult( - size: ViewSize( - size: scrollViewSize, - idealSize: contentSize.idealSize, - minimumWidth: minimumWidth, - minimumHeight: minimumHeight, - maximumWidth: nil, - maximumHeight: nil - ), + size: outerSize, childResults: [finalChildResult] ) } @@ -140,31 +149,12 @@ public struct ScrollView: TypeSafeView, View { environment: EnvironmentValues, backend: Backend ) { - let scrollViewSize = layout.size.size + let scrollViewSize = layout.size let finalContentSize = children.child.commit().size - let verticalScrollBarWidth = - children.hasVerticalScrollBar - ? backend.scrollBarWidth : 0 - let horizontalScrollBarHeight = - children.hasHorizontalScrollBar - ? backend.scrollBarWidth : 0 - 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.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, diff --git a/Sources/SwiftCrossUI/Views/Shapes/Circle.swift b/Sources/SwiftCrossUI/Views/Shapes/Circle.swift index 4eb1cfba9f4..cfa8035afdb 100644 --- a/Sources/SwiftCrossUI/Views/Shapes/Circle.swift +++ b/Sources/SwiftCrossUI/Views/Shapes/Circle.swift @@ -1,4 +1,7 @@ public struct Circle: Shape { + /// The ideal diameter of a Circle. + static let idealDiameter = 10.0 + public nonisolated init() {} public nonisolated func path(in bounds: Path.Rect) -> Path { @@ -6,18 +9,14 @@ public struct Circle: Shape { .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) + public nonisolated func size(fitting proposal: ProposedViewSize) -> ViewSize { + let diameter: Double + if let proposal = proposal.concrete { + diameter = min(proposal.width, proposal.height) + } else { + diameter = proposal.width ?? proposal.height ?? 10 + } - 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 - ) + return ViewSize(diameter, diameter) } } diff --git a/Sources/SwiftCrossUI/Views/Shapes/Shape.swift b/Sources/SwiftCrossUI/Views/Shapes/Shape.swift index cb90753e4c0..c19a56030f7 100644 --- a/Sources/SwiftCrossUI/Views/Shapes/Shape.swift +++ b/Sources/SwiftCrossUI/Views/Shapes/Shape.swift @@ -36,29 +36,17 @@ public protocol Shape: View, Sendable where Content == EmptyView { 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 + /// The default implementation accepts the proposal, replacing unspecified + /// dimensions with `10`. + /// - Returns: The shape's size for the given proposal. + func size(fitting proposal: ProposedViewSize) -> 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 - ) + public func size(fitting proposal: ProposedViewSize) -> ViewSize { + proposal.replacingUnspecifiedDimensions(by: ViewSize(10, 10)) } @MainActor @@ -85,7 +73,7 @@ extension Shape { public func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { @@ -104,8 +92,8 @@ extension Shape { let bounds = Path.Rect( x: 0.0, y: 0.0, - width: Double(layout.size.size.x), - height: Double(layout.size.size.y) + width: layout.size.width, + height: layout.size.height ) let path = path(in: bounds) @@ -122,7 +110,7 @@ extension Shape { environment: environment ) - backend.setSize(of: widget, to: layout.size.size) + backend.setSize(of: widget, to: layout.size.vector) backend.renderPath( backendPath, container: widget, diff --git a/Sources/SwiftCrossUI/Views/Shapes/StyledShape.swift b/Sources/SwiftCrossUI/Views/Shapes/StyledShape.swift index 7850cbff995..7b04c9692ee 100644 --- a/Sources/SwiftCrossUI/Views/Shapes/StyledShape.swift +++ b/Sources/SwiftCrossUI/Views/Shapes/StyledShape.swift @@ -36,7 +36,7 @@ extension StyledShapeImpl: StyledShape { return base.path(in: bounds) } - func size(fitting proposal: SIMD2) -> ViewSize { + func size(fitting proposal: ProposedViewSize) -> ViewSize { return base.size(fitting: proposal) } } @@ -56,7 +56,7 @@ extension StyledShape { public func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { @@ -75,8 +75,8 @@ extension StyledShape { let bounds = Path.Rect( x: 0.0, y: 0.0, - width: Double(layout.size.size.x), - height: Double(layout.size.size.y) + width: layout.size.width, + height: layout.size.height ) let path = path(in: bounds) @@ -93,7 +93,7 @@ extension StyledShape { environment: environment ) - backend.setSize(of: widget, to: layout.size.size) + backend.setSize(of: widget, to: layout.size.vector) backend.renderPath( backendPath, container: widget, diff --git a/Sources/SwiftCrossUI/Views/Slider.swift b/Sources/SwiftCrossUI/Views/Slider.swift index f06ee294ad9..a7bc029c87d 100644 --- a/Sources/SwiftCrossUI/Views/Slider.swift +++ b/Sources/SwiftCrossUI/Views/Slider.swift @@ -43,6 +43,9 @@ struct IntegerValue: DoubleConvertible { /// A control for selecting a value from a bounded range of numerical values. public struct Slider: ElementaryView, View { + /// The ideal width of a Slider. + private static let idealWidth: Double = 100 + /// A binding to the current value. private var value: Binding? /// The slider's minimum value. @@ -92,26 +95,21 @@ public struct Slider: ElementaryView, View { func computeLayout( _ widget: Backend.Widget, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { // 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) - // TODO: Allow backends to specify their own ideal slider widths. - return ViewLayoutResult.leafView( - size: ViewSize( - size: size, - idealSize: SIMD2(100, naturalSize.y), - minimumWidth: naturalSize.x, - minimumHeight: naturalSize.y, - maximumWidth: nil, - maximumHeight: Double(naturalSize.y) - ) + let size = ViewSize( + max(Double(naturalSize.x), proposedSize.width ?? Self.idealWidth), + Double(naturalSize.y) ) + + // TODO: Allow backends to specify their own ideal slider widths. + return ViewLayoutResult.leafView(size: size) } func commit( @@ -136,6 +134,6 @@ public struct Slider: ElementaryView, View { backend.setValue(ofSlider: widget, to: value) } - backend.setSize(of: widget, to: layout.size.size) + backend.setSize(of: widget, to: layout.size.vector) } } diff --git a/Sources/SwiftCrossUI/Views/Spacer.swift b/Sources/SwiftCrossUI/Views/Spacer.swift index 91765a2ff0b..f0a22ce01a1 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? @@ -17,42 +20,18 @@ public struct Spacer: ElementaryView, View { func computeLayout( _ widget: Backend.Widget, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { - let minLength = minLength ?? 0 - - 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: ViewSize( - size: size, - idealSize: SIMD2(minimumWidth, minimumHeight), - minimumWidth: minimumWidth, - minimumHeight: minimumHeight, - maximumWidth: maximumWidth, - maximumHeight: maximumHeight - ) + var size = ViewSize.zero + let proposedLength = proposedSize[component: environment.layoutOrientation] + size[component: environment.layoutOrientation] = max( + Double(minLength ?? 0), + proposedLength ?? Self.idealLength ) + + return ViewLayoutResult.leafView(size: size) } func commit( diff --git a/Sources/SwiftCrossUI/Views/SplitView.swift b/Sources/SwiftCrossUI/Views/SplitView.swift index a0d70cbadb9..86f2d25fd96 100644 --- a/Sources/SwiftCrossUI/Views/SplitView.swift +++ b/Sources/SwiftCrossUI/Views/SplitView.swift @@ -41,26 +41,47 @@ struct SplitView: TypeSafeView, View { func computeLayout( _ widget: Backend.Widget, children: Children, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { - let leadingWidth = backend.sidebarWidth(ofSplitView: widget) + let leadingWidth = Double(backend.sidebarWidth(ofSplitView: widget)) + // TODO: If computeLayout ever becomes a pure requirement of View, then we + // can delay this until commit. + children.minimumLeadingWidth = children.leadingChild.computeLayout( + with: body.view0, + proposedSize: ProposedViewSize( + 0, + proposedSize.height + ), + environment: environment + ).size.width + + children.minimumTrailingWidth = children.trailingChild.computeLayout( + with: body.view1, + proposedSize: ProposedViewSize( + 0, + proposedSize.height + ), + environment: environment + ).size.width + + // TODO: Figure out proper fixedSize behaviour (when width is unspecified) // Update pane children let leadingResult = children.leadingChild.computeLayout( with: body.view0, - proposedSize: SIMD2( - leadingWidth, - proposedSize.y + proposedSize: ProposedViewSize( + proposedSize.width == nil ? nil : leadingWidth, + proposedSize.height ), environment: environment ) let trailingResult = children.trailingChild.computeLayout( with: body.view1, - proposedSize: SIMD2( - proposedSize.x - max(leadingWidth, leadingResult.size.minimumWidth), - proposedSize.y + proposedSize: ProposedViewSize( + proposedSize.width.map { $0 - max(leadingWidth, leadingResult.size.width) }, + proposedSize.height ), environment: environment ) @@ -68,21 +89,20 @@ struct SplitView: TypeSafeView, View { // 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)) + var size = ViewSize( + leadingContentSize.width + trailingContentSize.width, + max(leadingContentSize.height, trailingContentSize.height) ) + if let proposedWidth = proposedSize.width { + size.width = max(size.width, proposedWidth) + } + if let proposedHeight = proposedSize.height { + size.height = max(size.height, proposedHeight) + } + return ViewLayoutResult( - size: ViewSize( - size: size, - idealSize: leadingContentSize.idealSize &+ trailingContentSize.idealSize, - minimumWidth: leadingContentSize.minimumWidth + trailingContentSize.minimumWidth, - minimumHeight: max( - leadingContentSize.minimumHeight, trailingContentSize.minimumHeight), - maximumWidth: nil, - maximumHeight: nil - ), + size: size, childResults: [leadingResult, trailingResult] ) } @@ -95,21 +115,22 @@ struct SplitView: TypeSafeView, View { backend: Backend ) { backend.setResizeHandler(ofSplitView: widget) { - environment.onResize(.empty) + // The parameter to onResize is currently unused + environment.onResize(.zero) } let leadingWidth = backend.sidebarWidth(ofSplitView: widget) let leadingResult = children.leadingChild.commit() let trailingResult = children.trailingChild.commit() - backend.setSize(of: widget, to: layout.size.size) + backend.setSize(of: widget, to: layout.size.vector) backend.setSidebarWidthBounds( ofSplitView: widget, - minimum: leadingResult.size.minimumWidth, - maximum: max( - leadingResult.size.minimumWidth, - layout.size.size.x - trailingResult.size.minimumWidth - ) + minimum: LayoutSystem.roundSize(children.minimumLeadingWidth), + maximum: LayoutSystem.roundSize(max( + children.minimumLeadingWidth, + layout.size.width - children.minimumTrailingWidth + )) ) // Center pane children @@ -117,16 +138,16 @@ struct SplitView: TypeSafeView, View { ofChildAt: 0, in: children.leadingPaneContainer.into(), to: SIMD2( - leadingWidth - leadingResult.size.size.x, - layout.size.size.y - leadingResult.size.size.y + leadingWidth - leadingResult.size.vector.x, + layout.size.vector.y - leadingResult.size.vector.y ) / 2 ) backend.setPosition( ofChildAt: 0, in: children.trailingPaneContainer.into(), to: SIMD2( - layout.size.size.x - leadingWidth - trailingResult.size.size.x, - layout.size.size.y - trailingResult.size.size.y + layout.size.vector.x - leadingWidth - trailingResult.size.vector.x, + layout.size.vector.y - trailingResult.size.vector.y ) / 2 ) } @@ -136,6 +157,8 @@ class SplitViewChildren: ViewGraphNodeChildren { var paneChildren: TupleView2.Children var leadingPaneContainer: AnyWidget var trailingPaneContainer: AnyWidget + var minimumLeadingWidth: Double + var minimumTrailingWidth: Double init( wrapping children: TupleView2.Children, @@ -150,6 +173,8 @@ class SplitViewChildren: ViewGraphNodeChildren { self.leadingPaneContainer = AnyWidget(leadingPaneContainer) self.trailingPaneContainer = AnyWidget(trailingPaneContainer) + self.minimumLeadingWidth = 0 + self.minimumTrailingWidth = 0 } var erasedNodes: [ErasedViewGraphNode] { diff --git a/Sources/SwiftCrossUI/Views/Table.swift b/Sources/SwiftCrossUI/Views/Table.swift index b18b7281be6..82d2f8efec1 100644 --- a/Sources/SwiftCrossUI/Views/Table.swift +++ b/Sources/SwiftCrossUI/Views/Table.swift @@ -35,7 +35,7 @@ public struct Table>: TypeSafeVi func computeLayout( _ widget: Backend.Widget, children: Children, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { @@ -67,7 +67,6 @@ public struct Table>: TypeSafeVi } // Update row nodes - let columnWidth = proposedSize.x / columnCount for (node, content) in zip(children.rowNodes, children.rowContent) { // TODO: Figure out if this is required // This doesn't update the row's cells. It just updates the view @@ -79,6 +78,9 @@ public struct Table>: TypeSafeVi ) } + // TODO: Compute a proper ideal size for tables. Look to SwiftUI to see what it does. + let columnWidth = (proposedSize.width ?? 0) / Double(columnCount) + // Compute cell layouts. Really only done during this initial layout // step to propagate cell preference values. Otherwise we'd do it // during commit. @@ -93,11 +95,14 @@ public struct Table>: TypeSafeVi var rowCellHeights: [Int] = [] for rowCell in rowCells { let cellResult = rowCell.computeLayout( - proposedSize: SIMD2(columnWidth, backend.defaultTableRowContentHeight), + proposedSize: ProposedViewSize( + columnWidth, + Double(backend.defaultTableRowContentHeight) + ), environment: environment ) cellResults.append(cellResult) - rowCellHeights.append(cellResult.size.size.y) + rowCellHeights.append(cellResult.size.vector.y) } let rowHeight = @@ -110,16 +115,8 @@ public struct Table>: TypeSafeVi } children.rowHeights = rowHeights - // TODO: Compute a proper ideal size for tables return ViewLayoutResult( - size: ViewSize( - size: size, - idealSize: .zero, - minimumWidth: 0, - minimumHeight: 0, - maximumWidth: nil, - maximumHeight: nil - ), + size: size.replacingUnspecifiedDimensions(by: .zero), childResults: cellResults ) } @@ -158,13 +155,13 @@ public struct Table>: TypeSafeVi in: children.cellContainerWidgets[index].into(), to: SIMD2( 0, - (rowHeight - cellSize.size.size.y) / 2 + (rowHeight - cellSize.size.vector.y) / 2 ) ) } } - backend.setSize(of: widget, to: layout.size.size) + backend.setSize(of: widget, to: layout.size.vector) } } @@ -222,11 +219,11 @@ struct RowView: View { func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { - return ViewLayoutResult.leafView(size: .empty) + return ViewLayoutResult.leafView(size: .zero) } func commit( diff --git a/Sources/SwiftCrossUI/Views/TableRowContent.swift b/Sources/SwiftCrossUI/Views/TableRowContent.swift index f76429dcffb..91219482cc1 100644 --- a/Sources/SwiftCrossUI/Views/TableRowContent.swift +++ b/Sources/SwiftCrossUI/Views/TableRowContent.swift @@ -52,9 +52,7 @@ public struct TupleTableRowContent2: T [column0.label, column1.label] } - public init( - _ column0: TableColumn, _ column1: TableColumn - ) { + public init(_ column0: TableColumn, _ column1: TableColumn) { self.column0 = column0 self.column1 = column1 } @@ -64,9 +62,7 @@ public struct TupleTableRowContent2: T } } -public struct TupleTableRowContent3: - TableRowContent -{ +public struct TupleTableRowContent3: TableRowContent { public typealias RowContent = TupleView3 public var column0: TableColumn @@ -77,10 +73,7 @@ public struct TupleTableRowContent3, _ column1: TableColumn, - _ column2: TableColumn - ) { + public init(_ column0: TableColumn, _ column1: TableColumn, _ column2: TableColumn) { self.column0 = column0 self.column1 = column1 self.column2 = column2 @@ -91,9 +84,7 @@ public struct TupleTableRowContent3: TableRowContent { +public struct TupleTableRowContent4: TableRowContent { public typealias RowContent = TupleView4 public var column0: TableColumn @@ -105,10 +96,7 @@ public struct TupleTableRowContent4< [column0.label, column1.label, column2.label, column3.label] } - public init( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn - ) { + public init(_ column0: TableColumn, _ column1: TableColumn, _ column2: TableColumn, _ column3: TableColumn) { self.column0 = column0 self.column1 = column1 self.column2 = column2 @@ -116,14 +104,11 @@ public struct TupleTableRowContent4< } public func content(for row: RowValue) -> RowContent { - TupleView4( - column0.content(row), column1.content(row), column2.content(row), column3.content(row)) + 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 struct TupleTableRowContent5: TableRowContent { public typealias RowContent = TupleView5 public var column0: TableColumn @@ -136,11 +121,7 @@ public struct TupleTableRowContent5< [column0.label, column1.label, column2.label, column3.label, column4.label] } - public init( - _ column0: TableColumn, _ column1: TableColumn, - _ column2: TableColumn, _ column3: TableColumn, - _ column4: TableColumn - ) { + public init(_ column0: TableColumn, _ column1: TableColumn, _ column2: TableColumn, _ column3: TableColumn, _ column4: TableColumn) { self.column0 = column0 self.column1 = column1 self.column2 = column2 @@ -149,19 +130,12 @@ public struct TupleTableRowContent5< } public func content(for row: RowValue) -> RowContent { - TupleView5( - column0.content(row), column1.content(row), column2.content(row), column3.content(row), - column4.content(row)) + 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 struct TupleTableRowContent6: TableRowContent { + public typealias RowContent = TupleView6 public var column0: TableColumn public var column1: TableColumn @@ -174,11 +148,7 @@ public struct TupleTableRowContent6< [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 - ) { + public init(_ column0: TableColumn, _ column1: TableColumn, _ column2: TableColumn, _ column3: TableColumn, _ column4: TableColumn, _ column5: TableColumn) { self.column0 = column0 self.column1 = column1 self.column2 = column2 @@ -188,19 +158,12 @@ public struct TupleTableRowContent6< } 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)) + 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 struct TupleTableRowContent7: TableRowContent { + public typealias RowContent = TupleView7 public var column0: TableColumn public var column1: TableColumn @@ -211,18 +174,10 @@ public struct TupleTableRowContent7< public var column6: TableColumn public var labels: [String] { - [ - column0.label, column1.label, column2.label, column3.label, column4.label, - column5.label, column6.label, - ] + [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 - ) { + 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 @@ -233,19 +188,12 @@ public struct TupleTableRowContent7< } 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)) + 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 struct TupleTableRowContent8: TableRowContent { + public typealias RowContent = TupleView8 public var column0: TableColumn public var column1: TableColumn @@ -257,18 +205,10 @@ public struct TupleTableRowContent8< public var column7: TableColumn public var labels: [String] { - [ - column0.label, column1.label, column2.label, column3.label, column4.label, - column5.label, column6.label, column7.label, - ] + [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 - ) { + 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 @@ -280,19 +220,12 @@ public struct TupleTableRowContent8< } 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)) + 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 struct TupleTableRowContent9: TableRowContent { + public typealias RowContent = TupleView9 public var column0: TableColumn public var column1: TableColumn @@ -305,19 +238,10 @@ public struct TupleTableRowContent9< 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, - ] + [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 - ) { + 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 @@ -330,21 +254,12 @@ public struct TupleTableRowContent9< } 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)) + 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 struct TupleTableRowContent10: TableRowContent { + public typealias RowContent = TupleView10 public var column0: TableColumn public var column1: TableColumn @@ -358,19 +273,10 @@ public struct TupleTableRowContent10< 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, - ] + [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 - ) { + 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 @@ -384,21 +290,12 @@ public struct TupleTableRowContent10< } 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)) + 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 struct TupleTableRowContent11: TableRowContent { + public typealias RowContent = TupleView11 public var column0: TableColumn public var column1: TableColumn @@ -413,21 +310,10 @@ public struct TupleTableRowContent11< 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, - ] + [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 - ) { + 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 @@ -442,22 +328,12 @@ public struct TupleTableRowContent11< } 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)) + 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 struct TupleTableRowContent12: TableRowContent { + public typealias RowContent = TupleView12 public var column0: TableColumn public var column1: TableColumn @@ -473,21 +349,10 @@ public struct TupleTableRowContent12< 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, - ] + [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 - ) { + 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 @@ -503,23 +368,12 @@ public struct TupleTableRowContent12< } 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) - ) + 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 struct TupleTableRowContent13: TableRowContent { + public typealias RowContent = TupleView13 public var column0: TableColumn public var column1: TableColumn @@ -536,22 +390,10 @@ public struct TupleTableRowContent13< 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, - ] + [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 - ) { + 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 @@ -568,23 +410,12 @@ public struct TupleTableRowContent13< } 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)) + 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 struct TupleTableRowContent14: TableRowContent { + public typealias RowContent = TupleView14 public var column0: TableColumn public var column1: TableColumn @@ -602,22 +433,10 @@ public struct TupleTableRowContent14< 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, - ] + [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 - ) { + 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 @@ -635,23 +454,12 @@ public struct TupleTableRowContent14< } 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)) + 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 struct TupleTableRowContent15: TableRowContent { + public typealias RowContent = TupleView15 public var column0: TableColumn public var column1: TableColumn @@ -670,23 +478,10 @@ public struct TupleTableRowContent15< 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, - ] + [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 - ) { + 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 @@ -705,24 +500,12 @@ public struct TupleTableRowContent15< } 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)) + 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 struct TupleTableRowContent16: TableRowContent { + public typealias RowContent = TupleView16 public var column0: TableColumn public var column1: TableColumn @@ -742,24 +525,10 @@ public struct TupleTableRowContent16< 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, - ] + [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 - ) { + 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 @@ -779,25 +548,12 @@ public struct TupleTableRowContent16< } 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)) + 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 struct TupleTableRowContent17: TableRowContent { + public typealias RowContent = TupleView17 public var column0: TableColumn public var column1: TableColumn @@ -818,25 +574,10 @@ public struct TupleTableRowContent17< 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, - ] + [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 - ) { + 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 @@ -857,26 +598,12 @@ public struct TupleTableRowContent17< } 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)) + 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 struct TupleTableRowContent18: TableRowContent { + public typealias RowContent = TupleView18 public var column0: TableColumn public var column1: TableColumn @@ -898,25 +625,10 @@ public struct TupleTableRowContent18< 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, - ] + [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 - ) { + 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 @@ -938,27 +650,12 @@ public struct TupleTableRowContent18< } 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)) + 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 struct TupleTableRowContent19: TableRowContent { + public typealias RowContent = TupleView19 public var column0: TableColumn public var column1: TableColumn @@ -981,26 +678,10 @@ public struct TupleTableRowContent19< 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, - ] + [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 - ) { + 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 @@ -1023,27 +704,12 @@ public struct TupleTableRowContent19< } 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)) + 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 struct TupleTableRowContent20: TableRowContent { + public typealias RowContent = TupleView20 public var column0: TableColumn public var column1: TableColumn @@ -1067,26 +733,10 @@ public struct TupleTableRowContent20< 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, - ] + [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 - ) { + 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 @@ -1110,12 +760,7 @@ public struct TupleTableRowContent20< } 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)) + 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/Text.swift b/Sources/SwiftCrossUI/Views/Text.swift index 906343a4b6a..779899a7cac 100644 --- a/Sources/SwiftCrossUI/Views/Text.swift +++ b/Sources/SwiftCrossUI/Views/Text.swift @@ -21,55 +21,35 @@ extension Text: ElementaryView { public func computeLayout( _ widget: Backend.Widget, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { - // TODO: Avoid this. Move it to commit + // 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 - ) + 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: size, - idealSize: idealSize, - idealWidthForProposedHeight: idealSize.x, - idealHeightForProposedWidth: size.y, - minimumWidth: minimumWidth == 1 ? 0 : minimumWidth, - minimumHeight: minimumHeight, - maximumWidth: Double(idealSize.x), - maximumHeight: Double(size.y) - ) - ) + return ViewLayoutResult.leafView(size: ViewSize(size)) } public func commit( @@ -78,6 +58,6 @@ extension Text: ElementaryView { environment: EnvironmentValues, backend: Backend ) { - backend.setSize(of: widget, to: layout.size.size) + backend.setSize(of: widget, to: layout.size.vector) } } diff --git a/Sources/SwiftCrossUI/Views/TextEditor.swift b/Sources/SwiftCrossUI/Views/TextEditor.swift index 960094730ce..c0ac84829f8 100644 --- a/Sources/SwiftCrossUI/Views/TextEditor.swift +++ b/Sources/SwiftCrossUI/Views/TextEditor.swift @@ -12,36 +12,32 @@ public struct TextEditor: ElementaryView { func computeLayout( _ widget: Backend.Widget, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { // Avoid evaluating the binding multiple times let content = text - 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) - ) - - return ViewLayoutResult.leafView( - size: ViewSize( - size: size, - idealSize: SIMD2(10, 10), - idealWidthForProposedHeight: 10, - idealHeightForProposedWidth: idealHeight, - minimumWidth: 0, - minimumHeight: idealHeight, - maximumWidth: nil, - maximumHeight: nil + let size: ViewSize + if proposedSize == .unspecified { + size = ViewSize(10, 10) + } else if let width = proposedSize.width, proposedSize.height == nil { + let idealHeight = backend.size( + of: content, + whenDisplayedIn: widget, + proposedFrame: SIMD2(LayoutSystem.roundSize(width), 1), + environment: environment + ).y + size = ViewSize( + width, + Double(idealHeight) ) - ) + } else { + size = proposedSize.replacingUnspecifiedDimensions(by: ViewSize(10, 10)) + } + + return ViewLayoutResult.leafView(size: size) } func commit( @@ -60,6 +56,6 @@ public struct TextEditor: ElementaryView { backend.setContent(ofTextEditor: widget, to: content) } - backend.setSize(of: widget, to: layout.size.size) + backend.setSize(of: widget, to: layout.size.vector) } } diff --git a/Sources/SwiftCrossUI/Views/TextField.swift b/Sources/SwiftCrossUI/Views/TextField.swift index 1bb883eda28..01117d417ab 100644 --- a/Sources/SwiftCrossUI/Views/TextField.swift +++ b/Sources/SwiftCrossUI/Views/TextField.swift @@ -1,5 +1,8 @@ /// A control that displays an editable text interface. public struct TextField: ElementaryView, View { + /// The ideal width of a TextField. + private static let idealWidth: Double = 100 + /// The label to show when the field is empty. private var placeholder: String /// The field's content. @@ -29,27 +32,18 @@ public struct TextField: ElementaryView, View { func computeLayout( _ widget: Backend.Widget, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { let naturalHeight = backend.naturalSize(of: widget).y - let size = SIMD2( - proposedSize.x, - naturalHeight + let size = ViewSize( + proposedSize.width ?? Self.idealWidth, + Double(naturalHeight) ) // TODO: Allow backends to set their own ideal text field width - return ViewLayoutResult.leafView( - size: ViewSize( - size: size, - idealSize: SIMD2(100, naturalHeight), - minimumWidth: 0, - minimumHeight: naturalHeight, - maximumWidth: nil, - maximumHeight: Double(naturalHeight) - ) - ) + return ViewLayoutResult.leafView(size: size) } func commit( @@ -71,6 +65,6 @@ public struct TextField: ElementaryView, View { backend.setContent(ofTextField: widget, to: value) } - backend.setSize(of: widget, to: layout.size.size) + backend.setSize(of: widget, to: layout.size.vector) } } diff --git a/Sources/SwiftCrossUI/Views/ToggleButton.swift b/Sources/SwiftCrossUI/Views/ToggleButton.swift index 8cf2c91a3b7..c6a1e129591 100644 --- a/Sources/SwiftCrossUI/Views/ToggleButton.swift +++ b/Sources/SwiftCrossUI/Views/ToggleButton.swift @@ -17,18 +17,17 @@ struct ToggleButton: ElementaryView, View { func computeLayout( _ widget: Backend.Widget, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { // TODO: Implement toggle button sizing within SwiftCrossUI so that we // can delay updating the underlying widget until `commit`. - backend.setState(ofToggle: widget, to: active.wrappedValue) backend.updateToggle(widget, label: label, environment: environment) { newActiveState in active.wrappedValue = newActiveState } return ViewLayoutResult.leafView( - size: ViewSize(fixedSize: backend.naturalSize(of: widget)) + size: ViewSize(backend.naturalSize(of: widget)) ) } @@ -37,5 +36,7 @@ struct ToggleButton: ElementaryView, View { layout: ViewLayoutResult, environment: EnvironmentValues, backend: Backend - ) {} + ) { + backend.setState(ofToggle: widget, to: active.wrappedValue) + } } diff --git a/Sources/SwiftCrossUI/Views/ToggleSwitch.swift b/Sources/SwiftCrossUI/Views/ToggleSwitch.swift index a500d0509bc..ccee23d89b9 100644 --- a/Sources/SwiftCrossUI/Views/ToggleSwitch.swift +++ b/Sources/SwiftCrossUI/Views/ToggleSwitch.swift @@ -14,13 +14,12 @@ struct ToggleSwitch: ElementaryView, View { func computeLayout( _ widget: Backend.Widget, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { - return ViewLayoutResult.leafView( - size: ViewSize(fixedSize: backend.naturalSize(of: widget)) - ) + let size = ViewSize(backend.naturalSize(of: widget)) + return ViewLayoutResult.leafView(size: size) } func commit( diff --git a/Sources/SwiftCrossUI/Views/TupleView.swift b/Sources/SwiftCrossUI/Views/TupleView.swift index 0dab664be3a..0c24ef041b2 100644 --- a/Sources/SwiftCrossUI/Views/TupleView.swift +++ b/Sources/SwiftCrossUI/Views/TupleView.swift @@ -39,7 +39,7 @@ extension TupleView { func computeLayout( _ widget: Backend.Widget, children: Children, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { @@ -72,6 +72,7 @@ extension TupleView { } } + /// A view with exactly 1 children. Autogenerated as an alternative to Swift's not yet /// production ready variadic generics. /// @@ -110,7 +111,7 @@ extension TupleView1: TupleView { children: Children ) -> [LayoutSystem.LayoutableChild] { [ - layoutableChild(node: children.child0, view: view0) + layoutableChild(node: children.child0, view: view0), ] } } @@ -321,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 @@ -334,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 @@ -384,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 @@ -398,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 @@ -450,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 @@ -466,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 @@ -520,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 @@ -537,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 @@ -558,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, @@ -595,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 @@ -613,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 @@ -635,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, @@ -673,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 @@ -692,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 @@ -716,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, @@ -755,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 @@ -775,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 @@ -800,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, @@ -840,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 @@ -861,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 @@ -887,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, @@ -897,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 ) } @@ -929,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 @@ -951,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 @@ -978,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, @@ -989,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 ) } @@ -1022,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 @@ -1046,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 @@ -1074,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, @@ -1085,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 ) } @@ -1119,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 @@ -1144,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 @@ -1174,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, @@ -1185,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 ) } @@ -1220,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 @@ -1246,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 @@ -1277,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, @@ -1288,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 ) } @@ -1324,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 @@ -1351,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 @@ -1383,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, @@ -1394,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 ) } @@ -1431,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 @@ -1459,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 @@ -1492,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, @@ -1503,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 ) } @@ -1541,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 @@ -1570,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 @@ -1604,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, @@ -1615,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 6c9533bd7ed..68bf66cec54 100644 --- a/Sources/SwiftCrossUI/Views/TupleView.swift.gyb +++ b/Sources/SwiftCrossUI/Views/TupleView.swift.gyb @@ -42,7 +42,7 @@ extension TupleView { func computeLayout( _ widget: Backend.Widget, children: Children, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { diff --git a/Sources/SwiftCrossUI/Views/TupleViewChildren.swift b/Sources/SwiftCrossUI/Views/TupleViewChildren.swift index acf995547a6..55f5de387d9 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 db3e4309f0b..b494c5c2667 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 3724688e924..fb218a76c1c 100644 --- a/Sources/SwiftCrossUI/Views/TypeSafeView.swift +++ b/Sources/SwiftCrossUI/Views/TypeSafeView.swift @@ -25,7 +25,7 @@ protocol TypeSafeView: View { func computeLayout( _ widget: Backend.Widget, children: Children, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult @@ -79,7 +79,7 @@ extension TypeSafeView { public func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { diff --git a/Sources/SwiftCrossUI/Views/VStack.swift b/Sources/SwiftCrossUI/Views/VStack.swift index e621aee0f0f..f3917a2a4be 100644 --- a/Sources/SwiftCrossUI/Views/VStack.swift +++ b/Sources/SwiftCrossUI/Views/VStack.swift @@ -42,13 +42,21 @@ public struct VStack: View { public func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { - return LayoutSystem.computeStackLayout( + 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 @@ -57,6 +65,8 @@ public struct VStack: View { .with(\.layoutSpacing, spacing), backend: backend ) + (children as? TupleViewChildren)?.stackLayoutCache = cache + return result } public func commit( @@ -66,9 +76,11 @@ public struct VStack: View { 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 @@ -77,5 +89,6 @@ public struct VStack: View { .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 e6a4fbfb649..562f025cfb4 100644 --- a/Sources/SwiftCrossUI/Views/View.swift +++ b/Sources/SwiftCrossUI/Views/View.swift @@ -49,11 +49,13 @@ public protocol View { func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, 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, @@ -123,7 +125,7 @@ extension View { public func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { @@ -141,7 +143,7 @@ extension View { public func defaultComputeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { diff --git a/Sources/SwiftCrossUI/Views/WebView.swift b/Sources/SwiftCrossUI/Views/WebView.swift index 116e990da63..fab062c431c 100644 --- a/Sources/SwiftCrossUI/Views/WebView.swift +++ b/Sources/SwiftCrossUI/Views/WebView.swift @@ -2,6 +2,9 @@ import Foundation @available(tvOS, unavailable) public struct WebView: ElementaryView { + /// The ideal size of a WebView. + private static let idealSize = ViewSize(10, 10) + @State var currentURL: URL? @Binding var url: URL @@ -15,21 +18,12 @@ public struct WebView: ElementaryView { func computeLayout( _ widget: Backend.Widget, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { - return ViewLayoutResult( - size: ViewSize( - size: proposedSize, - idealSize: SIMD2(10, 10), - minimumWidth: 0, - minimumHeight: 0, - maximumWidth: nil, - maximumHeight: nil - ), - childResults: [] - ) + let size = proposedSize.replacingUnspecifiedDimensions(by: Self.idealSize) + return ViewLayoutResult.leafView(size: size) } func commit( @@ -46,6 +40,6 @@ public struct WebView: ElementaryView { currentURL = destination url = destination } - backend.setSize(of: widget, to: layout.size.size) + backend.setSize(of: widget, to: layout.size.vector) } } diff --git a/Sources/SwiftCrossUI/Views/ZStack.swift b/Sources/SwiftCrossUI/Views/ZStack.swift index 3bc815a6db0..87884604bc9 100644 --- a/Sources/SwiftCrossUI/Views/ZStack.swift +++ b/Sources/SwiftCrossUI/Views/ZStack.swift @@ -31,7 +31,7 @@ public struct ZStack: View { public func computeLayout( _ widget: Backend.Widget, children: any ViewGraphNodeChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend: Backend ) -> ViewLayoutResult { @@ -43,20 +43,9 @@ public struct ZStack: View { ) } - 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 ) return ViewLayoutResult(size: size, childResults: childResults) @@ -77,12 +66,12 @@ public struct ZStack: View { for (i, child) in children.enumerated() { let position = alignment.position( - ofChild: child.size.size, - in: size.size + ofChild: child.size.vector, + in: size.vector ) backend.setPosition(ofChildAt: i, in: widget, to: position) } - backend.setSize(of: widget, to: size.size) + backend.setSize(of: widget, to: size.vector) } } diff --git a/Sources/UIKitBackend/UIViewControllerRepresentable.swift b/Sources/UIKitBackend/UIViewControllerRepresentable.swift index 9f0e23c764c..ad436307275 100644 --- a/Sources/UIKitBackend/UIViewControllerRepresentable.swift +++ b/Sources/UIKitBackend/UIViewControllerRepresentable.swift @@ -47,7 +47,8 @@ where Content == Never { /// 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, uiViewController: UIViewControllerType, + for proposal: ProposedViewSize, + uiViewController: UIViewControllerType, context: UIViewControllerRepresentableContext ) -> ViewSize @@ -69,7 +70,8 @@ extension UIViewControllerRepresentable { } public func determineViewSize( - for proposal: SIMD2, uiViewController: UIViewControllerType, + for proposal: ProposedViewSize, + uiViewController: UIViewControllerType, context: UIViewControllerRepresentableContext ) -> ViewSize { defaultViewSize(proposal: proposal, view: uiViewController.view) @@ -111,7 +113,7 @@ where Self: UIViewControllerRepresentable { public func update( _ widget: Backend.Widget, children _: any ViewGraphNodeChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend _: Backend, dryRun: Bool @@ -119,16 +121,15 @@ where Self: UIViewControllerRepresentable { let representingWidget = widget as! ControllerRepresentingWidget representingWidget.update(with: environment) - let size = - representingWidget.representable.determineViewSize( - for: proposedSize, - uiViewController: representingWidget.subcontroller, - context: representingWidget.context! - ) + let size = representingWidget.representable.determineViewSize( + for: proposedSize, + uiViewController: representingWidget.subcontroller, + context: representingWidget.context! + ) if !dryRun { - representingWidget.width = size.size.x - representingWidget.height = size.size.y + representingWidget.width = LayoutSystem.roundSize(size.width) + representingWidget.height = LayoutSystem.roundSize(size.height) } return ViewLayoutResult.leafView(size: size) diff --git a/Sources/UIKitBackend/UIViewRepresentable.swift b/Sources/UIKitBackend/UIViewRepresentable.swift index fa094d59b2d..efc996b5ee5 100644 --- a/Sources/UIKitBackend/UIViewRepresentable.swift +++ b/Sources/UIKitBackend/UIViewRepresentable.swift @@ -64,29 +64,16 @@ where Content == Never { } // Used both here and by UIViewControllerRepresentable -func defaultViewSize(proposal: SIMD2, view: UIView) -> ViewSize { - let intrinsicSize = view.intrinsicContentSize - - let sizeThatFits = view.systemLayoutSizeFitting( - CGSize(width: CGFloat(proposal.x), height: CGFloat(proposal.y))) +func defaultViewSize(proposal: ProposedViewSize, view: UIView) -> ViewSize { + let size = CGSize(width: proposal.width ?? 10, height: proposal.height ?? 10) + let sizeThatFits = view.systemLayoutSizeFitting(size) let minimumSize = view.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) let maximumSize = view.systemLayoutSizeFitting(UIView.layoutFittingExpandedSize) return ViewSize( - size: SIMD2( - Int(sizeThatFits.width.rounded(.up)), - Int(sizeThatFits.height.rounded(.up))), - // 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 : Int(intrinsicSize.width.rounded(.awayFromZero)), - intrinsicSize.height < 0.0 ? 10 : Int(intrinsicSize.height.rounded(.awayFromZero)) - ), - minimumWidth: Int(minimumSize.width.rounded(.towardZero)), - minimumHeight: Int(minimumSize.height.rounded(.towardZero)), - maximumWidth: maximumSize.width, - maximumHeight: maximumSize.height + sizeThatFits.width, + sizeThatFits.height ) } @@ -96,7 +83,8 @@ extension UIViewRepresentable { } public func determineViewSize( - for proposal: SIMD2, uiView: UIViewType, + for proposal: ProposedViewSize, + uiView: UIViewType, context _: UIViewRepresentableContext ) -> ViewSize { defaultViewSize(proposal: proposal, view: uiView) @@ -138,7 +126,7 @@ where Self: UIViewRepresentable { public func computeLayout( _ widget: Backend.Widget, children _: any ViewGraphNodeChildren, - proposedSize: SIMD2, + proposedSize: ProposedViewSize, environment: EnvironmentValues, backend _: Backend ) -> ViewLayoutResult { @@ -163,8 +151,8 @@ where Self: UIViewRepresentable { backend: Backend ) { let representingWidget = widget as! ViewRepresentingWidget - representingWidget.width = layout.size.size.x - representingWidget.height = layout.size.size.y + representingWidget.width = layout.size.vector.x + representingWidget.height = layout.size.vector.y } } diff --git a/Tests/SwiftCrossUITests/SwiftCrossUITests.swift b/Tests/SwiftCrossUITests/SwiftCrossUITests.swift index 78ff52fc3ac..818759e27c1 100644 --- a/Tests/SwiftCrossUITests/SwiftCrossUITests.swift +++ b/Tests/SwiftCrossUITests/SwiftCrossUITests.swift @@ -91,22 +91,13 @@ struct SwiftCrossUITests { ) backend.setChild(ofWindow: window, to: viewGraph.rootNode.widget.into()) - _ = viewGraph.update( - proposedSize: SIMD2(200, 200), - environment: environment, - dryRun: true - ) - let result = viewGraph.update( - proposedSize: SIMD2(200, 200), - environment: environment, - dryRun: false + let result = viewGraph.computeLayout( + proposedSize: ProposedViewSize(200, 200), + environment: environment ) - let view: AppKitBackend.Widget = viewGraph.rootNode.widget.into() - backend.setSize(of: view, to: result.size.size) - backend.setSize(ofWindow: window, to: result.size.size) #expect( - result.size == ViewSize(fixedSize: SIMD2(92, 96)), + result.size == ViewSize(92, 96), "View update result mismatch" ) From 8971331eee6dd6fa88f03f5a03e8c0dd1d421e55 Mon Sep 17 00:00:00 2001 From: stackotter Date: Sat, 13 Dec 2025 10:51:45 +1000 Subject: [PATCH 03/15] Cache dynamic property offsets to avoid Mirror usage (2x faster layout) --- .../LayoutPerformanceBenchmark.swift | 2 +- .../SwiftCrossUI/Layout/LayoutSystem.swift | 17 +- .../SwiftCrossUI/State/DynamicKeyPath.swift | 83 +++++++ .../SwiftCrossUI/State/DynamicProperty.swift | 79 ------- .../State/DynamicPropertyUpdater.swift | 211 ++++++++++++++++++ .../ViewGraph/ViewGraphNode.swift | 19 +- Sources/SwiftCrossUI/_App.swift | 30 +-- 7 files changed, 321 insertions(+), 120 deletions(-) create mode 100644 Sources/SwiftCrossUI/State/DynamicKeyPath.swift create mode 100644 Sources/SwiftCrossUI/State/DynamicPropertyUpdater.swift diff --git a/Benchmarks/LayoutPerformanceBenchmark/LayoutPerformanceBenchmark.swift b/Benchmarks/LayoutPerformanceBenchmark/LayoutPerformanceBenchmark.swift index dc344387180..720b8ec8e73 100644 --- a/Benchmarks/LayoutPerformanceBenchmark/LayoutPerformanceBenchmark.swift +++ b/Benchmarks/LayoutPerformanceBenchmark/LayoutPerformanceBenchmark.swift @@ -54,8 +54,8 @@ struct Benchmarks { } )) #else - let node = makeNode(V()) benchmark(label) { @MainActor in + let node = makeNode(V()) updateNode(node, size) } #endif diff --git a/Sources/SwiftCrossUI/Layout/LayoutSystem.swift b/Sources/SwiftCrossUI/Layout/LayoutSystem.swift index ea9acf0e8da..2fcea230583 100644 --- a/Sources/SwiftCrossUI/Layout/LayoutSystem.swift +++ b/Sources/SwiftCrossUI/Layout/LayoutSystem.swift @@ -103,14 +103,8 @@ public enum LayoutSystem { count: children.count ) - let interesting = children.count == 1000 - if interesting { - print("interesting") - print(" proposedSize: \(proposedSize)") - } - let stackLength = proposedSize[component: orientation] - if stackLength == 0 || stackLength == .infinity || stackLength == nil { + if stackLength == 0 || stackLength == .infinity || stackLength == nil || children.count == 1 { var resultLength: Double = 0 var resultWidth: Double = 0 var results: [ViewLayoutResult] = [] @@ -174,9 +168,6 @@ public enum LayoutSystem { let minimum = minimumResult.size[component: orientation] return maximum - minimum } - if interesting { - print(" flexibilities: \(flexibilities)") - } let visibleChildrenCount = isHidden.filter { hidden in !hidden }.count @@ -217,10 +208,6 @@ public enum LayoutSystem { proposedChildSize[component: orientation] = max(stackLength - spaceUsedAlongStackAxis - totalSpacing, 0) / Double(childrenRemaining) - if interesting { - print(" proposedChildSize: \(proposedChildSize)") - } - let childResult = child.computeLayout( proposedSize: proposedChildSize, environment: environment @@ -255,7 +242,7 @@ public enum LayoutSystem { size: size, childResults: renderedChildren, participateInStackLayoutsWhenEmpty: - renderedChildren.contains(where: \.participateInStackLayoutsWhenEmpty), + renderedChildren.contains(where: \.participateInStackLayoutsWhenEmpty) ) } diff --git a/Sources/SwiftCrossUI/State/DynamicKeyPath.swift b/Sources/SwiftCrossUI/State/DynamicKeyPath.swift new file mode 100644 index 00000000000..77904396f17 --- /dev/null +++ b/Sources/SwiftCrossUI/State/DynamicKeyPath.swift @@ -0,0 +1,83 @@ +#if canImport(Darwin) + import func Darwin.memcmp +#elseif canImport(Glibc) + import func Glibc.memcmp +#elseif canImport(WinSDK) + import func WinSDK.memcmp +#elseif canImport(Android) + import func Android.memcmp +#endif + +/// A type similar to KeyPath, but that can be constructed at run time given +/// an instance of a struct, and the value of the desired property. Construction +/// fails if the property's in-memory representation is not unique within the +/// struct. SwiftCrossUI only uses ``DynamicKeyPath`` in situations where it is +/// highly likely for properties to have unique in-memory representations, such +/// as when properties have internal storage pointers. +struct DynamicKeyPath { + /// The property's offset within instances of ``T``. + var offset: Int + + /// Constructs a key path given an instance of the base type, and the + /// value of the desired property. The initializer will search through + /// the base instance's in-memory representation to find the unique offset + /// that matches the representation of the given property value. If such an + /// offset can't be found or isn't unique, then the initialiser returns `nil`. + init?( + forProperty value: Value, + of base: Base, + label: String? = nil + ) { + let propertyAlignment = MemoryLayout.alignment + let propertySize = MemoryLayout.size + let baseStructSize = MemoryLayout.size + + var index = 0 + var matches: [Int] = [] + while index + propertySize <= baseStructSize { + let isMatch = withUnsafeBytes(of: base) { viewPointer in + withUnsafeBytes(of: value) { valuePointer in + memcmp( + viewPointer.baseAddress!.advanced(by: index), + valuePointer.baseAddress!, + propertySize + ) + } + } == 0 + if isMatch { + matches.append(index) + } + index += propertyAlignment + } + + guard let offset = matches.first else { + print("Warning: No offset found for dynamic property '\(label ?? "")'") + return nil + } + + guard matches.count == 1 else { + print("Warning: Multiple offsets found for dynamic property '\(label ?? "")'") + return nil + } + + self.offset = offset + } + + /// Gets the property's value on the given instance. + func get(_ base: Base) -> Value { + withUnsafeBytes(of: base) { buffer in + buffer.baseAddress!.advanced(by: offset) + .assumingMemoryBound(to: Value.self) + .pointee + } + } + + /// Sets the property's value to a new value on the given instance. + func set(_ base: inout Base, _ newValue: Value) { + withUnsafeMutableBytes(of: &base) { buffer in + buffer.baseAddress!.advanced(by: offset) + .assumingMemoryBound(to: Value.self) + .pointee = newValue + } + } +} diff --git a/Sources/SwiftCrossUI/State/DynamicProperty.swift b/Sources/SwiftCrossUI/State/DynamicProperty.swift index 2ba43c633d9..c5734159363 100644 --- a/Sources/SwiftCrossUI/State/DynamicProperty.swift +++ b/Sources/SwiftCrossUI/State/DynamicProperty.swift @@ -11,82 +11,3 @@ public protocol DynamicProperty { previousValue: Self? ) } - -/// Updates the dynamic properties of a value given a previous instance of the -/// type (if one exists) and the current environment. -func updateDynamicProperties( - of value: T, - previousValue: T?, - environment: EnvironmentValues -) { - let newMirror = Mirror(reflecting: value) - let previousMirror = previousValue.map(Mirror.init(reflecting:)) - if let previousChildren = previousMirror?.children { - let propertySequence = zip(newMirror.children, previousChildren) - for (newProperty, previousProperty) in propertySequence { - guard - let newValue = newProperty.value as? any DynamicProperty, - let previousValue = previousProperty.value as? any DynamicProperty - else { - continue - } - - updateDynamicProperty( - newProperty: newValue, - previousProperty: previousValue, - environment: environment, - enclosingTypeName: "\(T.self)", - propertyName: newProperty.label - ) - } - } else { - for property in newMirror.children { - guard let newValue = property.value as? any DynamicProperty else { - continue - } - - updateDynamicProperty( - newProperty: newValue, - previousProperty: nil, - environment: environment, - enclosingTypeName: "\(T.self)", - propertyName: property.label - ) - } - } -} - -/// Updates a dynamic property. Required to unmask the concrete type of the -/// property. Since the two properties can technically be two different -/// types, Swift correctly wouldn't allow us to assume they're both the -/// same. So we unwrap one and then dynamically check whether the other -/// matches using a type cast. -private func updateDynamicProperty( - newProperty: Property, - previousProperty: (any DynamicProperty)?, - environment: EnvironmentValues, - enclosingTypeName: String, - propertyName: String? -) { - let castedPreviousProperty: Property? - if let previousProperty { - guard let previousProperty = previousProperty as? Property else { - fatalError( - """ - Supposedly unreachable... previous and current types of \ - \(enclosingTypeName).\(propertyName ?? "") \ - don't match. - """ - ) - } - - castedPreviousProperty = previousProperty - } else { - castedPreviousProperty = nil - } - - newProperty.update( - with: environment, - previousValue: castedPreviousProperty - ) -} diff --git a/Sources/SwiftCrossUI/State/DynamicPropertyUpdater.swift b/Sources/SwiftCrossUI/State/DynamicPropertyUpdater.swift new file mode 100644 index 00000000000..8a99ab24e72 --- /dev/null +++ b/Sources/SwiftCrossUI/State/DynamicPropertyUpdater.swift @@ -0,0 +1,211 @@ +/// A cache for dynamic property updaters. The keys are the ObjectIdentifiers of +/// various Base types that we have already computed dynamic property updaters +/// for, and the elements are corresponding cached instances of +/// DynamicPropertyUpdater. +/// +/// From some basic testing, this caching seems to reduce layout times by 5-10% +/// (at the time of implementation). +@MainActor +var updaterCache: [ObjectIdentifier: Any] = [:] + +/// A helper for updating the dynamic properties of a stateful struct (e.g. +/// a View or App conforming struct). Dynamic properties are those that conform +/// to ``DynamicProperty``, e.g. properties annotated with `@State`. +/// +/// At initialisation the updater will attempt to determine the byte offset of +/// each stateful property in the struct. This is guaranteed to succeed if every +/// dynamic property in the provided struct instance contains internal mutable +/// storage, because the storage pointers will provide unique byte sequences. +/// Otherwise, offset discovery will fail when two dynamic properties share the +/// same pattern in memory. When offset discovery fails the updater will fall +/// back to using Mirrors each time `update` gets called, which can be 1500x +/// times slower when the view has 0 state properties, and 9x slower when the +/// view has 4 properties, with the factor slowly dropping as the number of +/// properties increases. +struct DynamicPropertyUpdater { + typealias PropertyUpdater = ( + _ old: Base?, + _ new: Base, + _ environment: EnvironmentValues + ) -> Void + + /// The updaters for each of Base's dynamic properties. If `nil`, then we + /// failed to compute + let propertyUpdaters: [PropertyUpdater]? + + /// Creates a new dynamic property updater which can efficiently update + /// all dynamic properties on any value of type Base without creating + /// any mirrors after the initial creation of the updater. Pass in a + /// `mirror` of base if you already have one to save us creating another one. + @MainActor + init(for base: Base, mirror: Mirror? = nil) { + // Unlikely shortcut, but worthwhile when we can. + if MemoryLayout.size == 0 { + self.propertyUpdaters = [] + return + } + + if + let cachedUpdater = updaterCache[ObjectIdentifier(Base.self)], + let cachedUpdater = cachedUpdater as? Self + { + self = cachedUpdater + return + } + + var propertyUpdaters: [PropertyUpdater] = [] + + let mirror = mirror ?? Mirror(reflecting: base) + for child in mirror.children { + let label = child.label ?? "" + let value = child.value + + guard let value = value as? any DynamicProperty else { + continue + } + + guard let updater = Self.getUpdater(for: value, base: base, label: label) else { + // We have failed to create the required property updaters. Fallback + // to using Mirrors to update all properties. + print( + """ + warning: Failed to produce DynamicPropertyUpdater for \(Base.self), \ + falling back to slower Mirror-based property updating approach. + """ + ) + self.propertyUpdaters = nil + + // We intentionally return without caching the updaters here so + // that we if this failure is a fluke we can recover on a + // subsequent attempt for the same type. It may turn out that in + // practice types that fail are ones that always fail, in which + // case we should update this code to add the current updater to + // the cache. + return + } + + propertyUpdaters.append(updater) + } + + self.propertyUpdaters = propertyUpdaters + + updaterCache[ObjectIdentifier(Base.self)] = self + } + + /// Updates each dynamic property of the given value. + func update(_ value: Base, with environment: EnvironmentValues, previousValue: Base?) { + guard let propertyUpdaters else { + // Fall back to our old dynamic property updating approach which involves a lot of + // Mirror overhead. This should be rare. + Self.updateFallback(of: value, previousValue: previousValue, environment: environment) + return + } + + for updater in propertyUpdaters { + updater(previousValue, value, environment) + } + } + + /// Gets an updater for the property of base with the given value. If multiple + /// properties exist matching the byte pattern of `value`, then `nil` is returned. + /// + /// The returned updater is reusable and doesn't use Mirror. + private static func getUpdater( + for value: T, + base: Base, + label: String + ) -> PropertyUpdater? { + guard let keyPath = DynamicKeyPath(forProperty: value, of: base, label: label) else { + return nil + } + + let updater = { (old: Base?, new: Base, environment: EnvironmentValues) in + let property = keyPath.get(new) + property.update( + with: environment, + previousValue: old.map(keyPath.get) + ) + } + + return updater + } + + /// Updates the dynamic properties of a value given a previous instance of the + /// type (if one exists) and the current environment. + private static func updateFallback( + of value: T, + previousValue: T?, + environment: EnvironmentValues + ) { + let newMirror = Mirror(reflecting: value) + let previousMirror = previousValue.map(Mirror.init(reflecting:)) + if let previousChildren = previousMirror?.children { + let propertySequence = zip(newMirror.children, previousChildren) + for (newProperty, previousProperty) in propertySequence { + guard + let newValue = newProperty.value as? any DynamicProperty, + let previousValue = previousProperty.value as? any DynamicProperty + else { + continue + } + + updateDynamicPropertyFallback( + newProperty: newValue, + previousProperty: previousValue, + environment: environment, + enclosingTypeName: "\(T.self)", + propertyName: newProperty.label + ) + } + } else { + for property in newMirror.children { + guard let newValue = property.value as? any DynamicProperty else { + continue + } + + updateDynamicPropertyFallback( + newProperty: newValue, + previousProperty: nil, + environment: environment, + enclosingTypeName: "\(T.self)", + propertyName: property.label + ) + } + } + } + + /// Updates a dynamic property. Required to unmask the concrete type of the + /// property. Since the two properties can technically be two different + /// types, Swift correctly wouldn't allow us to assume they're both the + /// same. So we unwrap one and then dynamically check whether the other + /// matches using a type cast. + private static func updateDynamicPropertyFallback( + newProperty: Property, + previousProperty: (any DynamicProperty)?, + environment: EnvironmentValues, + enclosingTypeName: String, + propertyName: String? + ) { + let castedPreviousProperty: Property? + if let previousProperty { + guard let previousProperty = previousProperty as? Property else { + fatalError( + """ + Supposedly unreachable... previous and current types of \ + \(enclosingTypeName).\(propertyName ?? "") \ + don't match. + """ + ) + } + + castedPreviousProperty = previousProperty + } else { + castedPreviousProperty = nil + } + + newProperty.update( + with: environment, + previousValue: castedPreviousProperty + ) + } +} diff --git a/Sources/SwiftCrossUI/ViewGraph/ViewGraphNode.swift b/Sources/SwiftCrossUI/ViewGraph/ViewGraphNode.swift index 483ffa429cf..e82a8c329af 100644 --- a/Sources/SwiftCrossUI/ViewGraph/ViewGraphNode.swift +++ b/Sources/SwiftCrossUI/ViewGraph/ViewGraphNode.swift @@ -52,6 +52,9 @@ public class ViewGraphNode: Sendable { /// The environment most recently provided by this node's parent. private var parentEnvironment: EnvironmentValues + /// The dynamic property updater for this view. + private var dynamicPropertyUpdater: DynamicPropertyUpdater + /// Creates a node for a given view while also creating the nodes for its children, creating /// the view's widget, and starting to observe its state for changes. public init( @@ -77,13 +80,12 @@ public class ViewGraphNode: Sendable { parentEnvironment = environment cancellables = [] + let mirror = Mirror(reflecting: view) + dynamicPropertyUpdater = DynamicPropertyUpdater(for: view, mirror: mirror) + let viewEnvironment = updateEnvironment(environment) - updateDynamicProperties( - of: view, - previousValue: nil, - environment: viewEnvironment - ) + dynamicPropertyUpdater.update(view, with: viewEnvironment, previousValue: nil) let children = view.children( backend: backend, @@ -103,7 +105,6 @@ public class ViewGraphNode: Sendable { backend.tag(widget: widget, as: tag) // Update the view and its children when state changes (children are always updated first). - let mirror = Mirror(reflecting: view) for property in mirror.children { if property.label == "state" && property.value is ObservableObject { print( @@ -206,11 +207,7 @@ public class ViewGraphNode: Sendable { let viewEnvironment = updateEnvironment(environment) - updateDynamicProperties( - of: view, - previousValue: previousView, - environment: viewEnvironment - ) + dynamicPropertyUpdater.update(view, with: viewEnvironment, previousValue: previousView) let result = view.computeLayout( widget, diff --git a/Sources/SwiftCrossUI/_App.swift b/Sources/SwiftCrossUI/_App.swift index 4d0a0a36247..4bd5a9e3827 100644 --- a/Sources/SwiftCrossUI/_App.swift +++ b/Sources/SwiftCrossUI/_App.swift @@ -15,6 +15,8 @@ class _App { var cancellables: [Cancellable] /// The root level environment. var environment: EnvironmentValues + /// The dynamic property updater for ``app``. + var dynamicPropertyUpdater: DynamicPropertyUpdater /// Wraps a user's app implementation. init(_ app: AppRoot) { @@ -22,16 +24,14 @@ class _App { self.app = app self.environment = EnvironmentValues(backend: backend) self.cancellables = [] + + dynamicPropertyUpdater = DynamicPropertyUpdater(for: app) } func forceRefresh() { - updateDynamicProperties( - of: self.app, - previousValue: nil, - environment: self.environment - ) + dynamicPropertyUpdater.update(app, with: environment, previousValue: nil) - self.sceneGraphRoot?.update( + sceneGraphRoot?.update( self.app.body, backend: self.backend, environment: environment @@ -46,10 +46,10 @@ class _App { defaultEnvironment: baseEnvironment ) - updateDynamicProperties( - of: self.app, - previousValue: nil, - environment: self.environment + self.dynamicPropertyUpdater.update( + self.app, + with: self.environment, + previousValue: nil ) let mirror = Mirror(reflecting: self.app) @@ -74,10 +74,12 @@ class _App { [weak self] in guard let self = self else { return } - updateDynamicProperties( - of: self.app, - previousValue: nil, - environment: self.environment + // TODO: Do we have to do this on state changes? Can probably get + // away with only doing it when the root environment changes. + self.dynamicPropertyUpdater.update( + self.app, + with: self.environment, + previousValue: nil ) let body = self.app.body From 5479677d61dfad35ffeaa41c93a798fd7687282b Mon Sep 17 00:00:00 2001 From: stackotter Date: Wed, 24 Dec 2025 17:08:51 +1000 Subject: [PATCH 04/15] Fix window resize loop caused by bad window sizing assumption Some backends (such as AppKitBackend) trigger window resize handlers before the underlying window's size gets updated. Our existing code assumed that it should set a window's size if the proposed window size didn't match the underlying window's size, but in this case that led to an infinite update loop. --- Sources/AppKitBackend/AppKitBackend.swift | 2 +- .../SwiftCrossUI/Scenes/WindowGroupNode.swift | 42 ++++++++++++++++--- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/Sources/AppKitBackend/AppKitBackend.swift b/Sources/AppKitBackend/AppKitBackend.swift index 26eaddc712a..90084a58ea0 100644 --- a/Sources/AppKitBackend/AppKitBackend.swift +++ b/Sources/AppKitBackend/AppKitBackend.swift @@ -2253,7 +2253,7 @@ public class NSCustomWindow: NSWindow { } let contentSize = sender.contentRect( - forFrameRect: NSRect(x: 0, y: 0, width: frameSize.width, height: frameSize.height) + forFrameRect: NSRect(x: sender.frame.origin.x, y: sender.frame.origin.y, width: frameSize.width, height: frameSize.height) ) resizeHandler( diff --git a/Sources/SwiftCrossUI/Scenes/WindowGroupNode.swift b/Sources/SwiftCrossUI/Scenes/WindowGroupNode.swift index 4096c0d36bd..fad7cf42f58 100644 --- a/Sources/SwiftCrossUI/Scenes/WindowGroupNode.swift +++ b/Sources/SwiftCrossUI/Scenes/WindowGroupNode.swift @@ -48,9 +48,11 @@ public final class WindowGroupNode: SceneGraphNode { guard let self else { return } + _ = self.update( self.scene, proposedWindowSize: newSize, + needsWindowSizeCommit: false, backend: backend, environment: self.parentEnvironment, windowSizeIsFinal: @@ -62,9 +64,11 @@ public final class WindowGroupNode: SceneGraphNode { guard let self else { return } + _ = self.update( self.scene, proposedWindowSize: backend.size(ofWindow: window), + needsWindowSizeCommit: false, backend: backend, environment: self.parentEnvironment, windowSizeIsFinal: @@ -85,20 +89,46 @@ public final class WindowGroupNode: SceneGraphNode { let isProgramaticallyResizable = backend.isWindowProgrammaticallyResizable(window) + let proposedWindowSize: SIMD2 + let usedDefaultSize: Bool + if isFirstUpdate && isProgramaticallyResizable { + proposedWindowSize = (newScene ?? scene).defaultSize + usedDefaultSize = true + } else { + proposedWindowSize = backend.size(ofWindow: window) + usedDefaultSize = false + } + _ = update( newScene, - proposedWindowSize: isFirstUpdate && isProgramaticallyResizable - ? (newScene ?? scene).defaultSize - : backend.size(ofWindow: window), + proposedWindowSize: proposedWindowSize, + needsWindowSizeCommit: usedDefaultSize, backend: backend, environment: environment, windowSizeIsFinal: !isProgramaticallyResizable ) } + /// Updates the WindowGroupNode. + /// - Parameters: + /// - newScene: The scene's body if recomputed. + /// - proposedWindowSize: The proposed window size. + /// - needsWindowSizeCommit: Whether the proposed window size matches the + /// windows current size (or imminent size in the case of a window + /// resize). We use this parameter instead of comparing to the window's + /// current size to the proposed size, because some backends (such as + /// AppKitBackend) trigger window resize handlers *before* the underlying + /// window gets assigned its new size (allowing us to pre-emptively update the + /// window's content to match the new size). + /// - backend: The backend to use. + /// - environment: The current environment. + /// - windowSizeIsFinal: If true, no further resizes can/will be made. This + /// is true on platforms that don't support programmatic window resizing, + /// and when a window is full screen. public func update( _ newScene: WindowGroup?, proposedWindowSize: SIMD2, + needsWindowSizeCommit: Bool, backend: Backend, environment: EnvironmentValues, windowSizeIsFinal: Bool = false @@ -130,6 +160,7 @@ public final class WindowGroupNode: SceneGraphNode { _ = self.update( self.scene, proposedWindowSize: backend.size(ofWindow: window), + needsWindowSizeCommit: false, backend: backend, environment: environment ) @@ -155,6 +186,7 @@ public final class WindowGroupNode: SceneGraphNode { return update( scene, proposedWindowSize: clampedWindowSize.vector, + needsWindowSizeCommit: true, backend: backend, environment: environment, windowSizeIsFinal: true @@ -179,6 +211,7 @@ public final class WindowGroupNode: SceneGraphNode { return update( scene, proposedWindowSize: initialContentResult.size.vector, + needsWindowSizeCommit: true, backend: backend, environment: environment, windowSizeIsFinal: true @@ -195,8 +228,7 @@ public final class WindowGroupNode: SceneGraphNode { to: (proposedWindowSize &- finalContentResult.size.vector) / 2 ) - let currentWindowSize = backend.size(ofWindow: window) - if currentWindowSize != proposedWindowSize { + if needsWindowSizeCommit { backend.setSize(ofWindow: window, to: proposedWindowSize) } From 073a3d95049a93284edc3847e76fc619bd0696f7 Mon Sep 17 00:00:00 2001 From: stackotter Date: Sun, 28 Dec 2025 23:42:32 +1000 Subject: [PATCH 05/15] Update Text to truncate by default (so that new layout sys acts nice) --- Sources/AppKitBackend/AppKitBackend.swift | 30 +- Sources/DummyBackend/DummyBackend.swift | 170 ++++-- Sources/Gtk/Datatypes/EllipsizeMode.swift | 42 ++ Sources/Gtk/Generated/Button.swift | 2 +- Sources/Gtk/Generated/CheckButton.swift | 2 +- Sources/Gtk/Generated/DrawingArea.swift | 2 +- Sources/Gtk/Generated/DropDown.swift | 2 +- Sources/Gtk/Generated/Entry.swift | 2 +- Sources/Gtk/Generated/EventController.swift | 2 +- .../Gtk/Generated/EventControllerKey.swift | 2 +- .../Gtk/Generated/EventControllerMotion.swift | 2 +- Sources/Gtk/Generated/FileChooserNative.swift | 2 +- Sources/Gtk/Generated/GLArea.swift | 2 +- Sources/Gtk/Generated/Gesture.swift | 2 +- Sources/Gtk/Generated/GestureClick.swift | 2 +- Sources/Gtk/Generated/GestureLongPress.swift | 2 +- Sources/Gtk/Generated/GestureSingle.swift | 2 +- Sources/Gtk/Generated/Image.swift | 2 +- Sources/Gtk/Generated/Label.swift | 15 +- Sources/Gtk/Generated/ListBox.swift | 2 +- Sources/Gtk/Generated/NativeDialog.swift | 2 +- Sources/Gtk/Generated/Picture.swift | 2 +- Sources/Gtk/Generated/Popover.swift | 2 +- Sources/Gtk/Generated/ProgressBar.swift | 13 +- Sources/Gtk/Generated/Range.swift | 2 +- Sources/Gtk/Generated/Scale.swift | 2 +- Sources/Gtk/Generated/Spinner.swift | 2 +- Sources/Gtk/Generated/Switch.swift | 2 +- Sources/Gtk/Utility/Pango.swift | 2 + Sources/Gtk/Widgets/Box.swift | 2 +- Sources/Gtk/Widgets/Paned.swift | 2 +- Sources/Gtk/Widgets/PopoverMenu.swift | 2 +- Sources/Gtk/Widgets/ScrolledWindow.swift | 2 +- Sources/Gtk/Widgets/TextView.swift | 2 +- Sources/Gtk/Widgets/ToggleButton.swift | 2 +- Sources/Gtk/Widgets/Widget.swift | 4 +- Sources/Gtk3/Datatypes/EllipsizeMode.swift | 42 ++ Sources/Gtk3/Generated/Button.swift | 2 +- Sources/Gtk3/Generated/Entry.swift | 2 +- Sources/Gtk3/Generated/EventBox.swift | 2 +- Sources/Gtk3/Generated/EventController.swift | 2 +- .../Gtk3/Generated/FileChooserNative.swift | 2 +- Sources/Gtk3/Generated/GLArea.swift | 2 +- Sources/Gtk3/Generated/Gesture.swift | 2 +- Sources/Gtk3/Generated/GestureLongPress.swift | 2 +- Sources/Gtk3/Generated/GestureSingle.swift | 2 +- Sources/Gtk3/Generated/Image.swift | 11 +- Sources/Gtk3/Generated/Label.swift | 44 +- Sources/Gtk3/Generated/MenuShell.swift | 2 +- Sources/Gtk3/Generated/NativeDialog.swift | 2 +- Sources/Gtk3/Generated/ProgressBar.swift | 12 +- Sources/Gtk3/Generated/Range.swift | 2 +- Sources/Gtk3/Generated/Scale.swift | 2 +- Sources/Gtk3/Generated/Spinner.swift | 2 +- Sources/Gtk3/Generated/Switch.swift | 2 +- Sources/Gtk3/Utility/Pango.swift | 4 + Sources/Gtk3/Widgets/Box.swift | 2 +- Sources/Gtk3/Widgets/ListBox.swift | 2 +- Sources/Gtk3/Widgets/Paned.swift | 2 +- Sources/Gtk3/Widgets/ScrolledWindow.swift | 2 +- Sources/Gtk3/Widgets/TextView.swift | 2 +- Sources/Gtk3/Widgets/ToggleButton.swift | 2 +- Sources/Gtk3/Widgets/Widget.swift | 45 +- Sources/Gtk3Backend/Gtk3Backend.swift | 51 +- Sources/GtkBackend/GtkBackend.swift | 49 +- Sources/GtkCodeGen/GtkCodeGen.swift | 13 +- Sources/SwiftCrossUI/Backend/AppBackend.swift | 24 +- .../SwiftCrossUI/Builders/SceneBuilder.swift | 239 +++++++- .../Builders/TableRowBuilder.swift | 258 +++++++-- .../SwiftCrossUI/Builders/ViewBuilder.swift | 254 +++++++-- .../SwiftCrossUI/Layout/LayoutSystem.swift | 12 +- Sources/SwiftCrossUI/Scenes/TupleScene.swift | 399 ++++++++++--- .../SwiftCrossUI/State/DynamicKeyPath.swift | 19 +- .../State/DynamicPropertyUpdater.swift | 7 +- .../ViewGraph/ViewGraphNode.swift | 13 +- Sources/SwiftCrossUI/Views/List.swift | 3 +- .../Modifiers/PresentationModifiers.swift | 2 +- .../Views/Modifiers/SheetModifier.swift | 4 +- .../Modifiers/TextSelectionModifier.swift | 5 +- Sources/SwiftCrossUI/Views/SplitView.swift | 45 +- .../SwiftCrossUI/Views/TableRowContent.swift | 523 +++++++++++++++--- Sources/SwiftCrossUI/Views/Text.swift | 89 ++- Sources/SwiftCrossUI/Views/TupleView.swift | 251 +++++++-- .../Views/TupleViewChildren.swift | 400 +++++++++++--- .../UIKitBackend/UIKitBackend+Passive.swift | 96 ++-- Sources/UIKitBackend/UIKitBackend+Sheet.swift | 5 +- Sources/WinUIBackend/WinUIBackend.swift | 44 +- 87 files changed, 2642 insertions(+), 697 deletions(-) create mode 100644 Sources/Gtk/Datatypes/EllipsizeMode.swift create mode 100644 Sources/Gtk3/Datatypes/EllipsizeMode.swift diff --git a/Sources/AppKitBackend/AppKitBackend.swift b/Sources/AppKitBackend/AppKitBackend.swift index 90084a58ea0..1d2d2e7487e 100644 --- a/Sources/AppKitBackend/AppKitBackend.swift +++ b/Sources/AppKitBackend/AppKitBackend.swift @@ -523,32 +523,17 @@ public final class AppKitBackend: AppBackend { public func size( of text: String, whenDisplayedIn widget: Widget, - proposedFrame: SIMD2?, + proposedWidth: Int?, + proposedHeight: Int?, environment: EnvironmentValues ) -> 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 suddenly jump when the text - // reaches zero width. - let size = size( - of: text, - whenDisplayedIn: widget, - proposedFrame: SIMD2(1, proposedFrame.y), - environment: environment - ) - return SIMD2( - 0, - size.y - ) - } - let proposedSize = NSSize( - width: (proposedFrame?.x).map(CGFloat.init) ?? 0, - height: .greatestFiniteMagnitude + width: proposedWidth.map(Double.init) ?? .greatestFiniteMagnitude, + height: proposedHeight.map(Double.init) ?? .greatestFiniteMagnitude ) let rect = NSString(string: text).boundingRect( with: proposedSize, - options: [.usesLineFragmentOrigin], + options: [.usesLineFragmentOrigin, .truncatesLastVisibleLine], attributes: Self.attributes(forTextIn: environment) ) return SIMD2( @@ -564,6 +549,7 @@ public final class AppKitBackend: AppBackend { // styles when clicked (yeah that happens...) field.allowsEditingTextAttributes = true field.isSelectable = false + field.cell?.truncatesLastVisibleLine = true return field } @@ -2253,7 +2239,9 @@ public class NSCustomWindow: NSWindow { } let contentSize = sender.contentRect( - forFrameRect: NSRect(x: sender.frame.origin.x, y: sender.frame.origin.y, width: frameSize.width, height: frameSize.height) + forFrameRect: NSRect( + x: sender.frame.origin.x, y: sender.frame.origin.y, width: frameSize.width, + height: frameSize.height) ) resizeHandler( diff --git a/Sources/DummyBackend/DummyBackend.swift b/Sources/DummyBackend/DummyBackend.swift index 2d007aebd05..5ee44962f6a 100644 --- a/Sources/DummyBackend/DummyBackend.swift +++ b/Sources/DummyBackend/DummyBackend.swift @@ -1,5 +1,5 @@ -import SwiftCrossUI import Foundation +import SwiftCrossUI public final class DummyBackend: AppBackend { public class Window { @@ -64,7 +64,7 @@ public final class DummyBackend: AppBackend { public var maximumValue: Double = 100 public var decimalPlaces = 1 public var changeHandler: ((Double) -> Void)? - + override public var naturalSize: SIMD2 { SIMD2(20, 10) } @@ -233,7 +233,8 @@ public final class DummyBackend: AppBackend { window.minimumSize = minimumSize } - public func setResizeHandler(ofWindow window: Window, to action: @escaping (SIMD2) -> Void) { + public func setResizeHandler(ofWindow window: Window, to action: @escaping (SIMD2) -> Void) + { window.resizeHandler = action } @@ -253,11 +254,15 @@ public final class DummyBackend: AppBackend { public func setRootEnvironmentChangeHandler(to action: @escaping () -> Void) {} - public func computeWindowEnvironment(window: Window, rootEnvironment: EnvironmentValues) -> EnvironmentValues { + public func computeWindowEnvironment(window: Window, rootEnvironment: EnvironmentValues) + -> EnvironmentValues + { rootEnvironment } - public func setWindowEnvironmentChangeHandler(of window: Window, to action: @escaping () -> Void) {} + public func setWindowEnvironmentChangeHandler( + of window: Window, to action: @escaping () -> Void + ) {} public func show(widget: Widget) {} @@ -317,7 +322,10 @@ public final class DummyBackend: AppBackend { public func updateScrollContainer(_ scrollView: Widget, environment: EnvironmentValues) {} - public func setScrollBarPresence(ofScrollContainer scrollView: Widget, hasVerticalScrollBar: Bool, hasHorizontalScrollBar: Bool) { + public func setScrollBarPresence( + ofScrollContainer scrollView: Widget, hasVerticalScrollBar: Bool, + hasHorizontalScrollBar: Bool + ) { let scrollContainer = scrollView as! ScrollContainer scrollContainer.hasVerticalScrollBar = hasVerticalScrollBar scrollContainer.hasHorizontalScrollBar = hasHorizontalScrollBar @@ -335,13 +343,17 @@ public final class DummyBackend: AppBackend { .zero } - public func setItems(ofSelectableListView listView: Widget, to items: [Widget], withRowHeights rowHeights: [Int]) { + public func setItems( + ofSelectableListView listView: Widget, to items: [Widget], withRowHeights rowHeights: [Int] + ) { let selectableListView = listView as! SelectableListView selectableListView.items = items selectableListView.rowHeights = rowHeights } - public func setSelectionHandler(forSelectableListView listView: Widget, to action: @escaping (Int) -> Void) { + public func setSelectionHandler( + forSelectableListView listView: Widget, to action: @escaping (Int) -> Void + ) { (listView as! SelectableListView).selectionHandler = action } @@ -364,27 +376,39 @@ public final class DummyBackend: AppBackend { (splitView as! SplitView).sidebarWidth } - public func setSidebarWidthBounds(ofSplitView splitView: Widget, minimum minimumWidth: Int, maximum maximumWidth: Int) { + public func setSidebarWidthBounds( + ofSplitView splitView: Widget, minimum minimumWidth: Int, maximum maximumWidth: Int + ) { let splitView = splitView as! SplitView splitView.minimumSidebarWidth = minimumWidth splitView.maximumSidebarWidth = maximumWidth } - public func size(of text: String, whenDisplayedIn widget: Widget, proposedFrame: SIMD2?, environment: EnvironmentValues) -> SIMD2 { + public func size( + of text: String, + whenDisplayedIn widget: Widget, + proposedWidth: Int?, + proposedHeight: Int?, + environment: EnvironmentValues + ) -> SIMD2 { let resolvedFont = environment.resolvedFont let lineHeight = Int(resolvedFont.lineHeight) let characterHeight = Int(resolvedFont.pointSize) let characterWidth = characterHeight * 2 / 3 - guard let proposedFrame else { + guard let proposedWidth else { return SIMD2( characterWidth * text.count, lineHeight ) } - let charactersPerLine = max(1, proposedFrame.x / characterWidth) - let lineCount = (text.count + charactersPerLine - 1) / charactersPerLine + let charactersPerLine = max(1, proposedWidth / characterWidth) + var lineCount = (text.count + charactersPerLine - 1) / charactersPerLine + if let proposedHeight { + lineCount = min(max(1, proposedHeight / lineHeight), lineCount) + } + return SIMD2( characterWidth * min(charactersPerLine, text.count), lineHeight * lineCount @@ -395,7 +419,8 @@ public final class DummyBackend: AppBackend { TextView() } - public func updateTextView(_ textView: Widget, content: String, environment: EnvironmentValues) { + public func updateTextView(_ textView: Widget, content: String, environment: EnvironmentValues) + { let textView = textView as! TextView textView.content = content textView.color = environment.suggestedForegroundColor @@ -406,7 +431,10 @@ public final class DummyBackend: AppBackend { ImageView() } - public func updateImageView(_ imageView: Widget, rgbaData: [UInt8], width: Int, height: Int, targetWidth: Int, targetHeight: Int, dataHasChanged: Bool, environment: EnvironmentValues) { + public func updateImageView( + _ imageView: Widget, rgbaData: [UInt8], width: Int, height: Int, targetWidth: Int, + targetHeight: Int, dataHasChanged: Bool, environment: EnvironmentValues + ) { let imageView = imageView as! ImageView imageView.rgbaData = rgbaData imageView.pixelWidth = width @@ -421,11 +449,15 @@ public final class DummyBackend: AppBackend { (table as! Table).rowCount = rows } - public func setColumnLabels(ofTable table: Widget, to labels: [String], environment: EnvironmentValues) { + public func setColumnLabels( + ofTable table: Widget, to labels: [String], environment: EnvironmentValues + ) { (table as! Table).columnLabels = labels } - public func setCells(ofTable table: Widget, to cells: [Widget], withRowHeights rowHeights: [Int]) { + public func setCells( + ofTable table: Widget, to cells: [Widget], withRowHeights rowHeights: [Int] + ) { let table = table as! Table table.cells = cells table.rowHeights = rowHeights @@ -435,13 +467,18 @@ public final class DummyBackend: AppBackend { Button() } - public func updateButton(_ button: Widget, label: String, environment: EnvironmentValues, action: @escaping () -> Void) { + public func updateButton( + _ button: Widget, label: String, environment: EnvironmentValues, + action: @escaping () -> Void + ) { let button = button as! Button button.label = label button.action = action } - public func updateButton(_ button: Widget, label: String, menu: Menu, environment: EnvironmentValues) { + public func updateButton( + _ button: Widget, label: String, menu: Menu, environment: EnvironmentValues + ) { let button = button as! Button button.label = label button.menu = menu @@ -452,7 +489,10 @@ public final class DummyBackend: AppBackend { ToggleButton() } - public func updateToggle(_ toggle: Widget, label: String, environment: EnvironmentValues, onChange: @escaping (Bool) -> Void) { + public func updateToggle( + _ toggle: Widget, label: String, environment: EnvironmentValues, + onChange: @escaping (Bool) -> Void + ) { let toggle = toggle as! ToggleButton toggle.label = label toggle.toggleHandler = onChange @@ -467,7 +507,10 @@ public final class DummyBackend: AppBackend { ToggleSwitch() } - public func updateSwitch(_ switchWidget: Widget, environment: SwiftCrossUI.EnvironmentValues, onChange: @escaping (Bool) -> Void) { + public func updateSwitch( + _ switchWidget: Widget, environment: SwiftCrossUI.EnvironmentValues, + onChange: @escaping (Bool) -> Void + ) { (switchWidget as! ToggleSwitch).toggleHandler = onChange } @@ -479,7 +522,10 @@ public final class DummyBackend: AppBackend { Checkbox() } - public func updateCheckbox(_ checkboxWidget: Widget, environment: SwiftCrossUI.EnvironmentValues, onChange: @escaping (Bool) -> Void) { + public func updateCheckbox( + _ checkboxWidget: Widget, environment: SwiftCrossUI.EnvironmentValues, + onChange: @escaping (Bool) -> Void + ) { (checkboxWidget as! Checkbox).toggleHandler = onChange } @@ -491,7 +537,10 @@ public final class DummyBackend: AppBackend { Slider() } - public func updateSlider(_ slider: Widget, minimum: Double, maximum: Double, decimalPlaces: Int, environment: SwiftCrossUI.EnvironmentValues, onChange: @escaping (Double) -> Void) { + public func updateSlider( + _ slider: Widget, minimum: Double, maximum: Double, decimalPlaces: Int, + environment: SwiftCrossUI.EnvironmentValues, onChange: @escaping (Double) -> Void + ) { let slider = slider as! Slider slider.minimumValue = minimum slider.maximumValue = maximum @@ -507,7 +556,10 @@ public final class DummyBackend: AppBackend { TextField() } - public func updateTextField(_ textField: Widget, placeholder: String, environment: SwiftCrossUI.EnvironmentValues, onChange: @escaping (String) -> Void, onSubmit: @escaping () -> Void) { + public func updateTextField( + _ textField: Widget, placeholder: String, environment: SwiftCrossUI.EnvironmentValues, + onChange: @escaping (String) -> Void, onSubmit: @escaping () -> Void + ) { let textField = textField as! TextField textField.placeholder = placeholder textField.font = environment.resolvedFont @@ -524,142 +576,142 @@ public final class DummyBackend: AppBackend { } // public func createTextEditor() -> Widget { - + // } // public func updateTextEditor(_ textEditor: Widget, environment: SwiftCrossUI.EnvironmentValues, onChange: @escaping (String) -> Void) { - + // } // public func setContent(ofTextEditor textEditor: Widget, to content: String) { - + // } // public func getContent(ofTextEditor textEditor: Widget) -> String { - + // } // public func createPicker() -> Widget { - + // } // public func updatePicker(_ picker: Widget, options: [String], environment: SwiftCrossUI.EnvironmentValues, onChange: @escaping (Int?) -> Void) { - + // } // public func setSelectedOption(ofPicker picker: Widget, to selectedOption: Int?) { - + // } // public func createProgressSpinner() -> Widget { - + // } // public func createProgressBar() -> Widget { - + // } // public func updateProgressBar(_ widget: Widget, progressFraction: Double?, environment: SwiftCrossUI.EnvironmentValues) { - + // } // public func createPopoverMenu() -> Menu { - + // } // public func updatePopoverMenu(_ menu: Menu, content: SwiftCrossUI.ResolvedMenu, environment: SwiftCrossUI.EnvironmentValues) { - + // } // public func showPopoverMenu(_ menu: Menu, at position: SIMD2, relativeTo widget: Widget, closeHandler handleClose: @escaping () -> Void) { - + // } // public func createAlert() -> Alert { - + // } // public func updateAlert(_ alert: Alert, title: String, actionLabels: [String], environment: SwiftCrossUI.EnvironmentValues) { - + // } // public func showAlert(_ alert: Alert, window: Window?, responseHandler handleResponse: @escaping (Int) -> Void) { - + // } // public func dismissAlert(_ alert: Alert, window: Window?) { - + // } // public func createSheet(content: Widget) -> Sheet { - + // } // public func updateSheet(_ sheet: Sheet, window: Window, environment: SwiftCrossUI.EnvironmentValues, size: SIMD2, onDismiss: @escaping () -> Void, cornerRadius: Double?, detents: [SwiftCrossUI.PresentationDetent], dragIndicatorVisibility: SwiftCrossUI.Visibility, backgroundColor: SwiftCrossUI.Color?, interactiveDismissDisabled: Bool) { - + // } // public func presentSheet(_ sheet: Sheet, window: Window, parentSheet: Sheet?) { - + // } // public func dismissSheet(_ sheet: Sheet, window: Window, parentSheet: Sheet?) { - + // } // public func size(ofSheet sheet: Sheet) -> SIMD2 { - + // } // public func showOpenDialog(fileDialogOptions: SwiftCrossUI.FileDialogOptions, openDialogOptions: SwiftCrossUI.OpenDialogOptions, window: Window?, resultHandler handleResult: @escaping (SwiftCrossUI.DialogResult<[URL]>) -> Void) { - + // } // public func showSaveDialog(fileDialogOptions: SwiftCrossUI.FileDialogOptions, saveDialogOptions: SwiftCrossUI.SaveDialogOptions, window: Window?, resultHandler handleResult: @escaping (SwiftCrossUI.DialogResult) -> Void) { - + // } // public func createTapGestureTarget(wrapping child: Widget, gesture: SwiftCrossUI.TapGesture) -> Widget { - + // } // public func updateTapGestureTarget(_ tapGestureTarget: Widget, gesture: SwiftCrossUI.TapGesture, environment: SwiftCrossUI.EnvironmentValues, action: @escaping () -> Void) { - + // } // public func createHoverTarget(wrapping child: Widget) -> Widget { - + // } // public func updateHoverTarget(_ hoverTarget: Widget, environment: SwiftCrossUI.EnvironmentValues, action: @escaping (Bool) -> Void) { - + // } // public func createPathWidget() -> Widget { - + // } // public func createPath() -> Path { - + // } // public func updatePath(_ path: Path, _ source: SwiftCrossUI.Path, bounds: SwiftCrossUI.Path.Rect, pointsChanged: Bool, environment: SwiftCrossUI.EnvironmentValues) { - + // } // public func renderPath(_ path: Path, container: Widget, strokeColor: SwiftCrossUI.Color, fillColor: SwiftCrossUI.Color, overrideStrokeStyle: SwiftCrossUI.StrokeStyle?) { - + // } // public func createWebView() -> Widget { - + // } // public func updateWebView(_ webView: Widget, environment: SwiftCrossUI.EnvironmentValues, onNavigate: @escaping (URL) -> Void) { - + // } // public func navigateWebView(_ webView: Widget, to url: URL) { - + // } } diff --git a/Sources/Gtk/Datatypes/EllipsizeMode.swift b/Sources/Gtk/Datatypes/EllipsizeMode.swift new file mode 100644 index 00000000000..a19050d8ee8 --- /dev/null +++ b/Sources/Gtk/Datatypes/EllipsizeMode.swift @@ -0,0 +1,42 @@ +import CGtk + +public enum EllipsizeMode: GValueRepresentableEnum { + public typealias GtkEnum = PangoEllipsizeMode + + case none + case start + case middle + case end + + public static var type: GType { + pango_ellipsize_mode_get_type() + } + + public init(from gtkEnum: PangoEllipsizeMode) { + switch gtkEnum { + case PANGO_ELLIPSIZE_NONE: + self = .none + case PANGO_ELLIPSIZE_START: + self = .start + case PANGO_ELLIPSIZE_MIDDLE: + self = .middle + case PANGO_ELLIPSIZE_END: + self = .end + default: + fatalError("Unsupported PangoEllipsizeMode enum value: \(gtkEnum.rawValue)") + } + } + + public func toGtk() -> PangoEllipsizeMode { + switch self { + case .none: + PANGO_ELLIPSIZE_NONE + case .start: + PANGO_ELLIPSIZE_START + case .middle: + PANGO_ELLIPSIZE_MIDDLE + case .end: + PANGO_ELLIPSIZE_END + } + } +} diff --git a/Sources/Gtk/Generated/Button.swift b/Sources/Gtk/Generated/Button.swift index b932a6ac929..b67ba671279 100644 --- a/Sources/Gtk/Generated/Button.swift +++ b/Sources/Gtk/Generated/Button.swift @@ -76,7 +76,7 @@ open class Button: Widget, Actionable { ) } - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() addSignal(name: "activate") { [weak self] () in diff --git a/Sources/Gtk/Generated/CheckButton.swift b/Sources/Gtk/Generated/CheckButton.swift index 287ea926a3c..1a2c80f5a30 100644 --- a/Sources/Gtk/Generated/CheckButton.swift +++ b/Sources/Gtk/Generated/CheckButton.swift @@ -85,7 +85,7 @@ open class CheckButton: Widget, Actionable { ) } - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() addSignal(name: "activate") { [weak self] () in diff --git a/Sources/Gtk/Generated/DrawingArea.swift b/Sources/Gtk/Generated/DrawingArea.swift index 4fa422c1dcb..7e4b0abe0c1 100644 --- a/Sources/Gtk/Generated/DrawingArea.swift +++ b/Sources/Gtk/Generated/DrawingArea.swift @@ -86,7 +86,7 @@ open class DrawingArea: Widget { ) } - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() let handler0: diff --git a/Sources/Gtk/Generated/DropDown.swift b/Sources/Gtk/Generated/DropDown.swift index 5ff024cbf5b..ffa69800d69 100644 --- a/Sources/Gtk/Generated/DropDown.swift +++ b/Sources/Gtk/Generated/DropDown.swift @@ -58,7 +58,7 @@ open class DropDown: Widget { ) } - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() addSignal(name: "activate") { [weak self] () in diff --git a/Sources/Gtk/Generated/Entry.swift b/Sources/Gtk/Generated/Entry.swift index 97cc334be7f..7f1b0ef2df1 100644 --- a/Sources/Gtk/Generated/Entry.swift +++ b/Sources/Gtk/Generated/Entry.swift @@ -97,7 +97,7 @@ open class Entry: Widget, CellEditable, Editable { ) } - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() addSignal(name: "activate") { [weak self] () in diff --git a/Sources/Gtk/Generated/EventController.swift b/Sources/Gtk/Generated/EventController.swift index c29ce1254ae..77009b50586 100644 --- a/Sources/Gtk/Generated/EventController.swift +++ b/Sources/Gtk/Generated/EventController.swift @@ -14,7 +14,7 @@ import CGtk /// phases of event propagation. open class EventController: GObject { - public override func registerSignals() { + open override func registerSignals() { super.registerSignals() let handler0: diff --git a/Sources/Gtk/Generated/EventControllerKey.swift b/Sources/Gtk/Generated/EventControllerKey.swift index 99ffe0301a7..759ed79694d 100644 --- a/Sources/Gtk/Generated/EventControllerKey.swift +++ b/Sources/Gtk/Generated/EventControllerKey.swift @@ -9,7 +9,7 @@ open class EventControllerKey: EventController { ) } - public override func registerSignals() { + open override func registerSignals() { super.registerSignals() addSignal(name: "im-update") { [weak self] () in diff --git a/Sources/Gtk/Generated/EventControllerMotion.swift b/Sources/Gtk/Generated/EventControllerMotion.swift index 152ca94e59e..2e31c87d055 100644 --- a/Sources/Gtk/Generated/EventControllerMotion.swift +++ b/Sources/Gtk/Generated/EventControllerMotion.swift @@ -16,7 +16,7 @@ open class EventControllerMotion: EventController { ) } - public override func registerSignals() { + open override func registerSignals() { super.registerSignals() let handler0: diff --git a/Sources/Gtk/Generated/FileChooserNative.swift b/Sources/Gtk/Generated/FileChooserNative.swift index ad788b8891d..4e6560b21ab 100644 --- a/Sources/Gtk/Generated/FileChooserNative.swift +++ b/Sources/Gtk/Generated/FileChooserNative.swift @@ -157,7 +157,7 @@ open class FileChooserNative: NativeDialog, FileChooser { ) } - public override func registerSignals() { + open override func registerSignals() { super.registerSignals() let handler0: diff --git a/Sources/Gtk/Generated/GLArea.swift b/Sources/Gtk/Generated/GLArea.swift index 3d286c22047..e22ffbf9afd 100644 --- a/Sources/Gtk/Generated/GLArea.swift +++ b/Sources/Gtk/Generated/GLArea.swift @@ -114,7 +114,7 @@ open class GLArea: Widget { ) } - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() addSignal(name: "create-context") { [weak self] () in diff --git a/Sources/Gtk/Generated/Gesture.swift b/Sources/Gtk/Generated/Gesture.swift index 9b9b702cbf5..7eb196783d7 100644 --- a/Sources/Gtk/Generated/Gesture.swift +++ b/Sources/Gtk/Generated/Gesture.swift @@ -91,7 +91,7 @@ import CGtk /// %GDK_TOUCHPAD_SWIPE and %GDK_TOUCHPAD_PINCH are handled by the `GtkGesture` open class Gesture: EventController { - public override func registerSignals() { + open override func registerSignals() { super.registerSignals() let handler0: diff --git a/Sources/Gtk/Generated/GestureClick.swift b/Sources/Gtk/Generated/GestureClick.swift index f71c8f6c848..be3e7d36cca 100644 --- a/Sources/Gtk/Generated/GestureClick.swift +++ b/Sources/Gtk/Generated/GestureClick.swift @@ -16,7 +16,7 @@ open class GestureClick: GestureSingle { ) } - public override func registerSignals() { + open override func registerSignals() { super.registerSignals() let handler0: diff --git a/Sources/Gtk/Generated/GestureLongPress.swift b/Sources/Gtk/Generated/GestureLongPress.swift index ee0eaaa4dff..2a412ad8012 100644 --- a/Sources/Gtk/Generated/GestureLongPress.swift +++ b/Sources/Gtk/Generated/GestureLongPress.swift @@ -23,7 +23,7 @@ open class GestureLongPress: GestureSingle { ) } - public override func registerSignals() { + open override func registerSignals() { super.registerSignals() addSignal(name: "cancelled") { [weak self] () in diff --git a/Sources/Gtk/Generated/GestureSingle.swift b/Sources/Gtk/Generated/GestureSingle.swift index c131329c8ab..2c9f0a009c5 100644 --- a/Sources/Gtk/Generated/GestureSingle.swift +++ b/Sources/Gtk/Generated/GestureSingle.swift @@ -15,7 +15,7 @@ import CGtk /// [method@Gtk.GestureSingle.get_current_button]. open class GestureSingle: Gesture { - public override func registerSignals() { + open override func registerSignals() { super.registerSignals() let handler0: diff --git a/Sources/Gtk/Generated/Image.swift b/Sources/Gtk/Generated/Image.swift index 455f4f97724..f0325ce114e 100644 --- a/Sources/Gtk/Generated/Image.swift +++ b/Sources/Gtk/Generated/Image.swift @@ -121,7 +121,7 @@ open class Image: Widget { ) } - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() let handler0: diff --git a/Sources/Gtk/Generated/Label.swift b/Sources/Gtk/Generated/Label.swift index f1e759cca08..576c3885974 100644 --- a/Sources/Gtk/Generated/Label.swift +++ b/Sources/Gtk/Generated/Label.swift @@ -238,7 +238,7 @@ open class Label: Widget { ) } - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() addSignal(name: "activate-current-link") { [weak self] () in @@ -507,6 +507,19 @@ open class Label: Widget { } } + /// The preferred place to ellipsize the string, if the label does + /// not have enough room to display the entire string. + /// + /// Note that setting this property to a value other than + /// [enum.Pango.EllipsizeMode.none] has the side-effect that the label requests + /// only enough space to display the ellipsis "...". In particular, this + /// means that ellipsizing labels do not work well in notebook tabs, unless + /// the [property@Gtk.NotebookPage:tab-expand] child property is set to true. + /// + /// Other ways to set a label's width are [method@Gtk.Widget.set_size_request] + /// and [method@Gtk.Label.set_width_chars]. + @GObjectProperty(named: "ellipsize") public var ellipsize: EllipsizeMode + /// The alignment of the lines in the text of the label, relative to each other. /// /// This does *not* affect the alignment of the label within its allocation. diff --git a/Sources/Gtk/Generated/ListBox.swift b/Sources/Gtk/Generated/ListBox.swift index bc5762d6c77..2f9baae1b25 100644 --- a/Sources/Gtk/Generated/ListBox.swift +++ b/Sources/Gtk/Generated/ListBox.swift @@ -72,7 +72,7 @@ open class ListBox: Widget { ) } - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() addSignal(name: "activate-cursor-row") { [weak self] () in diff --git a/Sources/Gtk/Generated/NativeDialog.swift b/Sources/Gtk/Generated/NativeDialog.swift index 1f7e967650a..2e77c0795a3 100644 --- a/Sources/Gtk/Generated/NativeDialog.swift +++ b/Sources/Gtk/Generated/NativeDialog.swift @@ -19,7 +19,7 @@ import CGtk /// object. open class NativeDialog: GObject { - public override func registerSignals() { + open override func registerSignals() { super.registerSignals() let handler0: diff --git a/Sources/Gtk/Generated/Picture.swift b/Sources/Gtk/Generated/Picture.swift index df026896e33..fd211b5a2d6 100644 --- a/Sources/Gtk/Generated/Picture.swift +++ b/Sources/Gtk/Generated/Picture.swift @@ -84,7 +84,7 @@ open class Picture: Widget { ) } - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() let handler0: diff --git a/Sources/Gtk/Generated/Popover.swift b/Sources/Gtk/Generated/Popover.swift index 03aee0c25ba..ab97f6ca02a 100644 --- a/Sources/Gtk/Generated/Popover.swift +++ b/Sources/Gtk/Generated/Popover.swift @@ -79,7 +79,7 @@ open class Popover: Widget, Native, ShortcutManager { ) } - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() addSignal(name: "activate-default") { [weak self] () in diff --git a/Sources/Gtk/Generated/ProgressBar.swift b/Sources/Gtk/Generated/ProgressBar.swift index 65796c43966..6634e8bf4fb 100644 --- a/Sources/Gtk/Generated/ProgressBar.swift +++ b/Sources/Gtk/Generated/ProgressBar.swift @@ -53,7 +53,7 @@ open class ProgressBar: Widget, Orientable { ) } - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() let handler0: @@ -141,6 +141,17 @@ open class ProgressBar: Widget, Orientable { } } + /// The preferred place to ellipsize the string. + /// + /// The text will be ellipsized if the progress bar does not have enough room + /// to display the entire string, specified as a `PangoEllipsizeMode`. + /// + /// Note that setting this property to a value other than + /// %PANGO_ELLIPSIZE_NONE has the side-effect that the progress bar requests + /// only enough space to display the ellipsis ("..."). Another means to set a + /// progress bar's width is [method@Gtk.Widget.set_size_request]. + @GObjectProperty(named: "ellipsize") public var ellipsize: EllipsizeMode + /// The fraction of total work that has been completed. @GObjectProperty(named: "fraction") public var fraction: Double diff --git a/Sources/Gtk/Generated/Range.swift b/Sources/Gtk/Generated/Range.swift index 8bb7e472c14..270bf988883 100644 --- a/Sources/Gtk/Generated/Range.swift +++ b/Sources/Gtk/Generated/Range.swift @@ -16,7 +16,7 @@ import CGtk /// fine-tuning mode. open class Range: Widget, Orientable { - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() let handler0: diff --git a/Sources/Gtk/Generated/Scale.swift b/Sources/Gtk/Generated/Scale.swift index 7d9d13b9649..5451bc69505 100644 --- a/Sources/Gtk/Generated/Scale.swift +++ b/Sources/Gtk/Generated/Scale.swift @@ -118,7 +118,7 @@ open class Scale: Range { ) } - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() let handler0: diff --git a/Sources/Gtk/Generated/Spinner.swift b/Sources/Gtk/Generated/Spinner.swift index 3ba1db393ca..968c619a7d4 100644 --- a/Sources/Gtk/Generated/Spinner.swift +++ b/Sources/Gtk/Generated/Spinner.swift @@ -23,7 +23,7 @@ open class Spinner: Widget { ) } - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() let handler0: diff --git a/Sources/Gtk/Generated/Switch.swift b/Sources/Gtk/Generated/Switch.swift index e4145d8e1e3..d953218c162 100644 --- a/Sources/Gtk/Generated/Switch.swift +++ b/Sources/Gtk/Generated/Switch.swift @@ -45,7 +45,7 @@ open class Switch: Widget, Actionable { ) } - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() addSignal(name: "activate") { [weak self] () in diff --git a/Sources/Gtk/Utility/Pango.swift b/Sources/Gtk/Utility/Pango.swift index 8c83b15d6bf..1597bf524cd 100644 --- a/Sources/Gtk/Utility/Pango.swift +++ b/Sources/Gtk/Utility/Pango.swift @@ -21,12 +21,14 @@ public class Pango { /// Uses the `PANGO_WRAP_WORD_CHAR` text wrapping mode. public func getTextSize( _ text: String, + ellipsize: EllipsizeMode, proposedWidth: Double? = nil, proposedHeight: Double? = nil ) -> (width: Int, height: Int) { let layout = pango_layout_new(pangoContext)! pango_layout_set_text(layout, text, Int32(text.count)) pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR) + pango_layout_set_ellipsize(layout, ellipsize.toGtk()) if let proposedWidth { pango_layout_set_width( diff --git a/Sources/Gtk/Widgets/Box.swift b/Sources/Gtk/Widgets/Box.swift index 2111d4f1a20..75e0b8d1ca8 100644 --- a/Sources/Gtk/Widgets/Box.swift +++ b/Sources/Gtk/Widgets/Box.swift @@ -11,7 +11,7 @@ open class Box: Widget, Orientable { self.init(gtk_box_new(orientation.toGtk(), gint(spacing))) } - override func didMoveToParent() { + open override func didMoveToParent() { for widget in children { widget.didMoveToParent() } diff --git a/Sources/Gtk/Widgets/Paned.swift b/Sources/Gtk/Widgets/Paned.swift index 6f41447cbc1..faa196e2ea7 100644 --- a/Sources/Gtk/Widgets/Paned.swift +++ b/Sources/Gtk/Widgets/Paned.swift @@ -21,7 +21,7 @@ open class Paned: Widget, Orientable { } } - override func didMoveToParent() { + open override func didMoveToParent() { startChild?.didMoveToParent() endChild?.didMoveToParent() diff --git a/Sources/Gtk/Widgets/PopoverMenu.swift b/Sources/Gtk/Widgets/PopoverMenu.swift index a62842c710c..66ceb724f73 100644 --- a/Sources/Gtk/Widgets/PopoverMenu.swift +++ b/Sources/Gtk/Widgets/PopoverMenu.swift @@ -184,7 +184,7 @@ public class PopoverMenu: Popover { } } - override func didMoveToParent() { + open override func didMoveToParent() { removeSignals() super.didMoveToParent() diff --git a/Sources/Gtk/Widgets/ScrolledWindow.swift b/Sources/Gtk/Widgets/ScrolledWindow.swift index f66a7cba296..7c89ae973f5 100644 --- a/Sources/Gtk/Widgets/ScrolledWindow.swift +++ b/Sources/Gtk/Widgets/ScrolledWindow.swift @@ -7,7 +7,7 @@ public class ScrolledWindow: Widget { self.init(gtk_scrolled_window_new()) } - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() } diff --git a/Sources/Gtk/Widgets/TextView.swift b/Sources/Gtk/Widgets/TextView.swift index 80bc00b53c9..7fb353ba2b5 100644 --- a/Sources/Gtk/Widgets/TextView.swift +++ b/Sources/Gtk/Widgets/TextView.swift @@ -90,7 +90,7 @@ open class TextView: Widget, Scrollable { open var buffer: TextBuffer - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() addSignal(name: "backspace") { [weak self] () in diff --git a/Sources/Gtk/Widgets/ToggleButton.swift b/Sources/Gtk/Widgets/ToggleButton.swift index ea732f4a2cf..c168c908b65 100644 --- a/Sources/Gtk/Widgets/ToggleButton.swift +++ b/Sources/Gtk/Widgets/ToggleButton.swift @@ -19,7 +19,7 @@ public class ToggleButton: Button { ) } - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() addSignal(name: "toggled") { [weak self] in diff --git a/Sources/Gtk/Widgets/Widget.swift b/Sources/Gtk/Widgets/Widget.swift index 1ad5a93ee52..946707145e2 100644 --- a/Sources/Gtk/Widgets/Widget.swift +++ b/Sources/Gtk/Widgets/Widget.swift @@ -61,7 +61,7 @@ open class Widget: GObject { gtk_widget_set_visible(widgetPointer, false.toGBoolean()) } - public func setSizeRequest(width: Int, height: Int) { + open func setSizeRequest(width: Int, height: Int) { gtk_widget_set_size_request(widgetPointer, Int32(width), Int32(height)) } @@ -98,6 +98,8 @@ open class Widget: GObject { @GObjectProperty(named: "name") public var name: String? + @GObjectProperty(named: "overflow") public var overflow: Overflow + @GObjectProperty(named: "sensitive") public var sensitive: Bool @GObjectProperty(named: "opacity") public var opacity: Double diff --git a/Sources/Gtk3/Datatypes/EllipsizeMode.swift b/Sources/Gtk3/Datatypes/EllipsizeMode.swift new file mode 100644 index 00000000000..f171825122f --- /dev/null +++ b/Sources/Gtk3/Datatypes/EllipsizeMode.swift @@ -0,0 +1,42 @@ +import CGtk3 + +public enum EllipsizeMode: GValueRepresentableEnum { + public typealias GtkEnum = PangoEllipsizeMode + + case none + case start + case middle + case end + + public static var type: GType { + pango_ellipsize_mode_get_type() + } + + public init(from gtkEnum: PangoEllipsizeMode) { + switch gtkEnum { + case PANGO_ELLIPSIZE_NONE: + self = .none + case PANGO_ELLIPSIZE_START: + self = .start + case PANGO_ELLIPSIZE_MIDDLE: + self = .middle + case PANGO_ELLIPSIZE_END: + self = .end + default: + fatalError("Unsupported PangoEllipsizeMode enum value: \(gtkEnum.rawValue)") + } + } + + public func toGtk() -> PangoEllipsizeMode { + switch self { + case .none: + PANGO_ELLIPSIZE_NONE + case .start: + PANGO_ELLIPSIZE_START + case .middle: + PANGO_ELLIPSIZE_MIDDLE + case .end: + PANGO_ELLIPSIZE_END + } + } +} diff --git a/Sources/Gtk3/Generated/Button.swift b/Sources/Gtk3/Generated/Button.swift index 59a967469c1..671e3315511 100644 --- a/Sources/Gtk3/Generated/Button.swift +++ b/Sources/Gtk3/Generated/Button.swift @@ -65,7 +65,7 @@ open class Button: Bin, Activatable { ) } - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() addSignal(name: "activate") { [weak self] () in diff --git a/Sources/Gtk3/Generated/Entry.swift b/Sources/Gtk3/Generated/Entry.swift index c76a025c13d..4faa7a2cb6b 100644 --- a/Sources/Gtk3/Generated/Entry.swift +++ b/Sources/Gtk3/Generated/Entry.swift @@ -87,7 +87,7 @@ open class Entry: Widget, CellEditable, Editable { ) } - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() addSignal(name: "activate") { [weak self] () in diff --git a/Sources/Gtk3/Generated/EventBox.swift b/Sources/Gtk3/Generated/EventBox.swift index a8a6766047b..2a38117b020 100644 --- a/Sources/Gtk3/Generated/EventBox.swift +++ b/Sources/Gtk3/Generated/EventBox.swift @@ -11,7 +11,7 @@ open class EventBox: Bin { ) } - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() let handler0: diff --git a/Sources/Gtk3/Generated/EventController.swift b/Sources/Gtk3/Generated/EventController.swift index f5897da2abb..748ae26332d 100644 --- a/Sources/Gtk3/Generated/EventController.swift +++ b/Sources/Gtk3/Generated/EventController.swift @@ -5,7 +5,7 @@ import CGtk3 /// actions as a consequence of those. open class EventController: GObject { - public override func registerSignals() { + open override func registerSignals() { super.registerSignals() let handler0: diff --git a/Sources/Gtk3/Generated/FileChooserNative.swift b/Sources/Gtk3/Generated/FileChooserNative.swift index afdf0862ca5..014ff736a04 100644 --- a/Sources/Gtk3/Generated/FileChooserNative.swift +++ b/Sources/Gtk3/Generated/FileChooserNative.swift @@ -167,7 +167,7 @@ open class FileChooserNative: NativeDialog, FileChooser { ) } - public override func registerSignals() { + open override func registerSignals() { super.registerSignals() addSignal(name: "confirm-overwrite") { [weak self] () in diff --git a/Sources/Gtk3/Generated/GLArea.swift b/Sources/Gtk3/Generated/GLArea.swift index 0dec451dc67..7378d9f0378 100644 --- a/Sources/Gtk3/Generated/GLArea.swift +++ b/Sources/Gtk3/Generated/GLArea.swift @@ -107,7 +107,7 @@ open class GLArea: Widget { ) } - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() addSignal(name: "create-context") { [weak self] () in diff --git a/Sources/Gtk3/Generated/Gesture.swift b/Sources/Gtk3/Generated/Gesture.swift index fc44d23709c..991de668c8f 100644 --- a/Sources/Gtk3/Generated/Gesture.swift +++ b/Sources/Gtk3/Generated/Gesture.swift @@ -91,7 +91,7 @@ import CGtk3 /// %GDK_TOUCHPAD_SWIPE and %GDK_TOUCHPAD_PINCH are handled by the #GtkGesture open class Gesture: EventController { - public override func registerSignals() { + open override func registerSignals() { super.registerSignals() let handler0: diff --git a/Sources/Gtk3/Generated/GestureLongPress.swift b/Sources/Gtk3/Generated/GestureLongPress.swift index 2a975cae634..38c84a63db7 100644 --- a/Sources/Gtk3/Generated/GestureLongPress.swift +++ b/Sources/Gtk3/Generated/GestureLongPress.swift @@ -15,7 +15,7 @@ open class GestureLongPress: GestureSingle { ) } - public override func registerSignals() { + open override func registerSignals() { super.registerSignals() addSignal(name: "cancelled") { [weak self] () in diff --git a/Sources/Gtk3/Generated/GestureSingle.swift b/Sources/Gtk3/Generated/GestureSingle.swift index f51f30dc779..09dc1cb6eba 100644 --- a/Sources/Gtk3/Generated/GestureSingle.swift +++ b/Sources/Gtk3/Generated/GestureSingle.swift @@ -14,7 +14,7 @@ import CGtk3 /// currently pressed can be known through gtk_gesture_single_get_current_button(). open class GestureSingle: Gesture { - public override func registerSignals() { + open override func registerSignals() { super.registerSignals() let handler0: diff --git a/Sources/Gtk3/Generated/Image.swift b/Sources/Gtk3/Generated/Image.swift index f9570e99592..201930f3c60 100644 --- a/Sources/Gtk3/Generated/Image.swift +++ b/Sources/Gtk3/Generated/Image.swift @@ -156,7 +156,7 @@ open class Image: Misc { ) } - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() let handler0: @@ -316,6 +316,15 @@ open class Image: Misc { } } + /// The name of the icon in the icon theme. If the icon theme is + /// changed, the image will be updated automatically. + @GObjectProperty(named: "icon-name") public var iconName: String + + /// The "pixel-size" property can be used to specify a fixed size + /// overriding the #GtkImage:icon-size property for images of type + /// %GTK_IMAGE_ICON_NAME. + @GObjectProperty(named: "pixel-size") public var pixelSize: Int + @GObjectProperty(named: "stock") public var stock: String @GObjectProperty(named: "storage-type") public var storageType: ImageType diff --git a/Sources/Gtk3/Generated/Label.swift b/Sources/Gtk3/Generated/Label.swift index fbb6155cd52..1f2f556b7cd 100644 --- a/Sources/Gtk3/Generated/Label.swift +++ b/Sources/Gtk3/Generated/Label.swift @@ -200,7 +200,7 @@ open class Label: Misc { ) } - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() addSignal(name: "activate-current-link") { [weak self] () in @@ -493,6 +493,25 @@ open class Label: Misc { } } + /// The angle that the baseline of the label makes with the horizontal, + /// in degrees, measured counterclockwise. An angle of 90 reads from + /// from bottom to top, an angle of 270, from top to bottom. Ignored + /// if the label is selectable. + @GObjectProperty(named: "angle") public var angle: Double + + /// The preferred place to ellipsize the string, if the label does + /// not have enough room to display the entire string, specified as a + /// #PangoEllipsizeMode. + /// + /// Note that setting this property to a value other than + /// %PANGO_ELLIPSIZE_NONE has the side-effect that the label requests + /// only enough space to display the ellipsis "...". In particular, this + /// means that ellipsizing labels do not work well in notebook tabs, unless + /// the #GtkNotebook tab-expand child property is set to %TRUE. Other ways + /// to set a label's width are gtk_widget_set_size_request() and + /// gtk_label_set_width_chars(). + @GObjectProperty(named: "ellipsize") public var ellipsize: EllipsizeMode + @GObjectProperty(named: "justify") public var justify: Justification /// The contents of the label. @@ -508,14 +527,37 @@ open class Label: Misc { /// to display them. @GObjectProperty(named: "label") public var label: String + /// The desired maximum width of the label, in characters. If this property + /// is set to -1, the width will be calculated automatically. + /// + /// See the section on [text layout][label-text-layout] + /// for details of how #GtkLabel:width-chars and #GtkLabel:max-width-chars + /// determine the width of ellipsized and wrapped labels. + @GObjectProperty(named: "max-width-chars") public var maxWidthChars: Int + @GObjectProperty(named: "mnemonic-keyval") public var mnemonicKeyval: UInt @GObjectProperty(named: "selectable") public var selectable: Bool + /// Whether the label is in single line mode. In single line mode, + /// the height of the label does not depend on the actual text, it + /// is always set to ascent + descent of the font. This can be an + /// advantage in situations where resizing the label because of text + /// changes would be distracting, e.g. in a statusbar. + @GObjectProperty(named: "single-line-mode") public var singleLineMode: Bool + @GObjectProperty(named: "use-markup") public var useMarkup: Bool @GObjectProperty(named: "use-underline") public var useUnderline: Bool + /// The desired width of the label, in characters. If this property is set to + /// -1, the width will be calculated automatically. + /// + /// See the section on [text layout][label-text-layout] + /// for details of how #GtkLabel:width-chars and #GtkLabel:max-width-chars + /// determine the width of ellipsized and wrapped labels. + @GObjectProperty(named: "width-chars") public var widthChars: Int + /// A [keybinding signal][GtkBindingSignal] /// which gets emitted when the user activates a link in the label. /// diff --git a/Sources/Gtk3/Generated/MenuShell.swift b/Sources/Gtk3/Generated/MenuShell.swift index 1fb3f71e87d..26c8b535a57 100644 --- a/Sources/Gtk3/Generated/MenuShell.swift +++ b/Sources/Gtk3/Generated/MenuShell.swift @@ -29,7 +29,7 @@ import CGtk3 /// grab and receive all key presses. open class MenuShell: Container { - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() let handler0: diff --git a/Sources/Gtk3/Generated/NativeDialog.swift b/Sources/Gtk3/Generated/NativeDialog.swift index df63a37b594..752473ee9e9 100644 --- a/Sources/Gtk3/Generated/NativeDialog.swift +++ b/Sources/Gtk3/Generated/NativeDialog.swift @@ -17,7 +17,7 @@ import CGtk3 /// similar to gtk_dialog_run(). open class NativeDialog: GObject { - public override func registerSignals() { + open override func registerSignals() { super.registerSignals() let handler0: diff --git a/Sources/Gtk3/Generated/ProgressBar.swift b/Sources/Gtk3/Generated/ProgressBar.swift index 9b3eb446a44..3f3c6948928 100644 --- a/Sources/Gtk3/Generated/ProgressBar.swift +++ b/Sources/Gtk3/Generated/ProgressBar.swift @@ -47,7 +47,7 @@ open class ProgressBar: Widget, Orientable { ) } - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() let handler0: @@ -135,6 +135,16 @@ open class ProgressBar: Widget, Orientable { } } + /// The preferred place to ellipsize the string, if the progress bar does + /// not have enough room to display the entire string, specified as a + /// #PangoEllipsizeMode. + /// + /// Note that setting this property to a value other than + /// %PANGO_ELLIPSIZE_NONE has the side-effect that the progress bar requests + /// only enough space to display the ellipsis ("..."). Another means to set a + /// progress bar's width is gtk_widget_set_size_request(). + @GObjectProperty(named: "ellipsize") public var ellipsize: EllipsizeMode + @GObjectProperty(named: "fraction") public var fraction: Double @GObjectProperty(named: "inverted") public var inverted: Bool diff --git a/Sources/Gtk3/Generated/Range.swift b/Sources/Gtk3/Generated/Range.swift index b93bddbbe79..cf558bb830b 100644 --- a/Sources/Gtk3/Generated/Range.swift +++ b/Sources/Gtk3/Generated/Range.swift @@ -9,7 +9,7 @@ import CGtk3 /// “fill level” on range widgets. See gtk_range_set_fill_level(). open class Range: Widget, Orientable { - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() let handler0: diff --git a/Sources/Gtk3/Generated/Scale.swift b/Sources/Gtk3/Generated/Scale.swift index b6970ca14b1..e1f3fcfe623 100644 --- a/Sources/Gtk3/Generated/Scale.swift +++ b/Sources/Gtk3/Generated/Scale.swift @@ -99,7 +99,7 @@ open class Scale: Range { ) } - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() let handler0: diff --git a/Sources/Gtk3/Generated/Spinner.swift b/Sources/Gtk3/Generated/Spinner.swift index c65f02682d5..48c6878ea90 100644 --- a/Sources/Gtk3/Generated/Spinner.swift +++ b/Sources/Gtk3/Generated/Spinner.swift @@ -19,7 +19,7 @@ open class Spinner: Widget { ) } - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() let handler0: diff --git a/Sources/Gtk3/Generated/Switch.swift b/Sources/Gtk3/Generated/Switch.swift index 3ce717d9350..d93fa5d4a7a 100644 --- a/Sources/Gtk3/Generated/Switch.swift +++ b/Sources/Gtk3/Generated/Switch.swift @@ -24,7 +24,7 @@ open class Switch: Widget, Activatable { ) } - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() addSignal(name: "activate") { [weak self] () in diff --git a/Sources/Gtk3/Utility/Pango.swift b/Sources/Gtk3/Utility/Pango.swift index 8a46cfd47ca..2bd0d79255b 100644 --- a/Sources/Gtk3/Utility/Pango.swift +++ b/Sources/Gtk3/Utility/Pango.swift @@ -17,14 +17,18 @@ public class Pango { /// acts as a suggested width. The text will attempt to take up less than or equal to the proposed /// width but if the text wrapping strategy doesn't allow the text to become as small as required /// than it may take up more the proposed width. + /// + /// Uses the `PANGO_WRAP_WORD_CHAR` text wrapping mode. public func getTextSize( _ text: String, + ellipsize: EllipsizeMode, proposedWidth: Double? = nil, proposedHeight: Double? = nil ) -> (width: Int, height: Int) { let layout = pango_layout_new(pangoContext)! pango_layout_set_text(layout, text, Int32(text.count)) pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR) + pango_layout_set_ellipsize(layout, ellipsize.toGtk()) if let proposedWidth { pango_layout_set_width( diff --git a/Sources/Gtk3/Widgets/Box.swift b/Sources/Gtk3/Widgets/Box.swift index f51ac656bf7..d6c21e3f95f 100644 --- a/Sources/Gtk3/Widgets/Box.swift +++ b/Sources/Gtk3/Widgets/Box.swift @@ -14,7 +14,7 @@ open class Box: Widget, Orientable { self.init(gtk_box_new(orientation.toGtk(), gint(spacing))) } - override func didMoveToParent() { + open override func didMoveToParent() { for widget in children { widget.didMoveToParent() } diff --git a/Sources/Gtk3/Widgets/ListBox.swift b/Sources/Gtk3/Widgets/ListBox.swift index e93cc94180f..0ec82a89c83 100644 --- a/Sources/Gtk3/Widgets/ListBox.swift +++ b/Sources/Gtk3/Widgets/ListBox.swift @@ -46,7 +46,7 @@ open class ListBox: Container { ) } - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() addSignal(name: "activate-cursor-row") { [weak self] () in diff --git a/Sources/Gtk3/Widgets/Paned.swift b/Sources/Gtk3/Widgets/Paned.swift index 9c1d2660344..d9bd29ad5d0 100644 --- a/Sources/Gtk3/Widgets/Paned.swift +++ b/Sources/Gtk3/Widgets/Paned.swift @@ -29,7 +29,7 @@ open class Paned: Container, Orientable { } } - override func didMoveToParent() { + open override func didMoveToParent() { startChild?.didMoveToParent() endChild?.didMoveToParent() diff --git a/Sources/Gtk3/Widgets/ScrolledWindow.swift b/Sources/Gtk3/Widgets/ScrolledWindow.swift index 96d2ad0fc20..a244c53dac3 100644 --- a/Sources/Gtk3/Widgets/ScrolledWindow.swift +++ b/Sources/Gtk3/Widgets/ScrolledWindow.swift @@ -5,7 +5,7 @@ public class ScrolledWindow: Bin { self.init(gtk_scrolled_window_new(nil, nil)) } - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() } diff --git a/Sources/Gtk3/Widgets/TextView.swift b/Sources/Gtk3/Widgets/TextView.swift index 33e7a1dff66..1cff0a9e1d6 100644 --- a/Sources/Gtk3/Widgets/TextView.swift +++ b/Sources/Gtk3/Widgets/TextView.swift @@ -46,7 +46,7 @@ open class TextView: Container, Scrollable { open var buffer: TextBuffer - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() addSignal(name: "backspace") { [weak self] () in diff --git a/Sources/Gtk3/Widgets/ToggleButton.swift b/Sources/Gtk3/Widgets/ToggleButton.swift index adc1a753340..ed69749d6df 100644 --- a/Sources/Gtk3/Widgets/ToggleButton.swift +++ b/Sources/Gtk3/Widgets/ToggleButton.swift @@ -13,7 +13,7 @@ open class ToggleButton: Button { self.init(gtk_toggle_button_new_with_mnemonic(label)) } - override func didMoveToParent() { + open override func didMoveToParent() { super.didMoveToParent() addSignal(name: "toggled") { [weak self] in diff --git a/Sources/Gtk3/Widgets/Widget.swift b/Sources/Gtk3/Widgets/Widget.swift index fadce434880..881e9bbf6df 100644 --- a/Sources/Gtk3/Widgets/Widget.swift +++ b/Sources/Gtk3/Widgets/Widget.swift @@ -23,7 +23,7 @@ open class Widget: GObject { } } - func didMoveToParent() { + open func didMoveToParent() { // The Gtk3 docs claim that this handler should take GdkEventButton as a // value, but that leads to crashes on Rocky Linux. These crashes are // fixed by instead taking the event as a pointer. I've confirmed that @@ -54,8 +54,10 @@ open class Widget: GObject { UnsafeMutableRawPointer, OpaquePointer, UnsafeMutableRawPointer - ) -> Void = { _, cairo, data in + ) -> Bool = { _, cairo, data in SignalBox1.run(data, cairo) + // Propagate event to next handler + return false } addSignal( @@ -65,6 +67,39 @@ open class Widget: GObject { guard let self = self else { return } self.doDraw?(cairo) } + + let handler3: + @convention(c) ( + UnsafeMutableRawPointer, + OpaquePointer, + UnsafeMutableRawPointer + ) -> Void = { _, screen, data in + SignalBox1.run(data, screen) + } + + addSignal( + name: "screen-changed", + handler: gCallback(handler3) + ) { [weak self] (previousScreen: OpaquePointer) in + guard let self = self else { return } + self.screenChanged?() + } + + let handler4: + @convention(c) ( + UnsafeMutableRawPointer, + UnsafeMutableRawPointer + ) -> Void = { _, data in + SignalBox1.run(data, ()) + } + + addSignal( + name: "style-updated", + handler: gCallback(handler4) + ) { [weak self] (_: Void) in + guard let self = self else { return } + self.styleUpdated?() + } } open func didMoveFromParent() {} @@ -110,7 +145,7 @@ open class Widget: GObject { gtk_widget_show(widgetPointer) } - public func setSizeRequest(width: Int, height: Int) { + open func setSizeRequest(width: Int, height: Int) { gtk_widget_set_size_request(widgetPointer, Int32(width), Int32(height)) } @@ -143,6 +178,10 @@ open class Widget: GObject { public var doDraw: ((_ cairo: OpaquePointer) -> Void)? + public var screenChanged: (() -> Void)? + + public var styleUpdated: (() -> Void)? + @GObjectProperty(named: "name") public var name: String? @GObjectProperty(named: "sensitive") public var sensitive: Bool diff --git a/Sources/Gtk3Backend/Gtk3Backend.swift b/Sources/Gtk3Backend/Gtk3Backend.swift index a8e412896b9..d64d0234949 100644 --- a/Sources/Gtk3Backend/Gtk3Backend.swift +++ b/Sources/Gtk3Backend/Gtk3Backend.swift @@ -614,9 +614,12 @@ public final class Gtk3Backend: AppBackend { // MARK: Passive views public func createTextView() -> Widget { - let label = Label(string: "") - label.horizontalAlignment = .start - return label + let textView = CustomLabel(string: "") + textView.horizontalAlignment = .start + textView.wrap = true + textView.lineWrapMode = .wordCharacter + textView.ellipsize = .end + return textView } public func updateTextView( @@ -624,10 +627,8 @@ public final class Gtk3Backend: AppBackend { content: String, environment: EnvironmentValues ) { - let textView = textView as! Label + let textView = textView as! CustomLabel textView.label = content - textView.wrap = true - textView.lineWrapMode = .wordCharacter textView.justify = switch environment.multilineTextAlignment { case .leading: @@ -645,14 +646,16 @@ public final class Gtk3Backend: AppBackend { public func size( of text: String, whenDisplayedIn widget: Widget, - proposedFrame: SIMD2?, + proposedWidth: Int?, + proposedHeight: Int?, environment: EnvironmentValues ) -> SIMD2 { let pango = Pango(for: widget) let (width, height) = pango.getTextSize( text, - proposedWidth: (proposedFrame?.x).map(Double.init), - proposedHeight: nil + ellipsize: (widget as! CustomLabel).ellipsize, + proposedWidth: proposedWidth.map(Double.init), + proposedHeight: proposedHeight.map(Double.init) ) return SIMD2(width, height) } @@ -1517,3 +1520,33 @@ struct Gtk3Error: LocalizedError { "gerror: code=\(code), domain=\(domain), message=\(message)" } } + +/// A custom label subclass that supports ellipsizing multi-line text. Regular +/// `Label`s only display a single line of text when ellipsizing is enabled +/// because they don't pass their size request to their underlying Pango layout. +class CustomLabel: Label { + override func didMoveToParent() { + super.didMoveToParent() + + doDraw = { [weak self] _ in + guard let self else { return } + self.setLayoutHeight(getSizeRequest().height) + } + } + + private func setLayoutHeight(_ height: Int) { + // Override the label's layout height. We do this so that the label grows + // vertically to fill available space even though we have ellipsizing + // enabled (which generally causes labels to limit themselves to a single line). + // + // This code relies on the assumption that the layout won't get recreated + // during rendering. From reading the Gtk 3 source code I believe that's + // unlikely, but note that the docs recommend against mutating + // the layout returned by gtk_label_get_layout. + let layout = gtk_label_get_layout(castedPointer()) + pango_layout_set_height( + layout, + Int32((Double(height) * Double(PANGO_SCALE)).rounded(.towardZero)) + ) + } +} diff --git a/Sources/GtkBackend/GtkBackend.swift b/Sources/GtkBackend/GtkBackend.swift index 49f5fc8d2b3..318b0e7e30b 100644 --- a/Sources/GtkBackend/GtkBackend.swift +++ b/Sources/GtkBackend/GtkBackend.swift @@ -590,9 +590,12 @@ public final class GtkBackend: AppBackend { // MARK: Passive views public func createTextView() -> Widget { - let label = Label(string: "") - label.horizontalAlignment = .start - return label + let textView = CustomLabel(string: "") + textView.horizontalAlignment = .start + textView.wrap = true + textView.lineWrapMode = .wordCharacter + textView.ellipsize = .end + return textView } public func updateTextView( @@ -600,10 +603,8 @@ public final class GtkBackend: AppBackend { content: String, environment: EnvironmentValues ) { - let textView = textView as! Label + let textView = textView as! CustomLabel textView.label = content - textView.wrap = true - textView.lineWrapMode = .wordCharacter textView.justify = switch environment.multilineTextAlignment { case .leading: @@ -621,14 +622,16 @@ public final class GtkBackend: AppBackend { public func size( of text: String, whenDisplayedIn widget: Widget, - proposedFrame: SIMD2?, + proposedWidth: Int?, + proposedHeight: Int?, environment: EnvironmentValues ) -> SIMD2 { let pango = Pango(for: widget) let (width, height) = pango.getTextSize( text, - proposedWidth: (proposedFrame?.x).map(Double.init), - proposedHeight: nil + ellipsize: (widget as! CustomLabel).ellipsize, + proposedWidth: proposedWidth.map(Double.init), + proposedHeight: proposedHeight.map(Double.init) ) return SIMD2(width, height) } @@ -1692,3 +1695,31 @@ extension UnsafeMutablePointer { class CustomListBox: ListBox { var cachedSelection: Int? = nil } + +/// A custom label subclass that supports ellipsizing multi-line text. Regular +/// `Label`s only display a single line of text when ellipsizing is enabled +/// because they don't pass their size request to their underlying Pango layout. +class CustomLabel: Label { + override func setSizeRequest(width: Int, height: Int) { + super.setSizeRequest(width: width, height: height) + + // Override the label's layout height. We do this so that the label grows + // vertically to fill available space even though we have ellipsizing + // enabled (which generally causes labels to limit themselves to a single line). + // + // This code relies on the assumption that the layout won't get recreated + // until after the label gets rendered. The docs recommend against mutating + // the layout returned by gtk_label_get_layout. + // + // Ideally we'd use an Inscription instead, because it has this behavior + // by default, but that's only available from Gtk 4.8, and the predecessor + // CellRendererText isn't a widget. + let layout = gtk_label_get_layout(opaquePointer) + pango_layout_set_height( + layout, + Int32( + (Double(height) * Double(PANGO_SCALE)) + .rounded(.towardZero)) + ) + } +} diff --git a/Sources/GtkCodeGen/GtkCodeGen.swift b/Sources/GtkCodeGen/GtkCodeGen.swift index a1f18b01c29..12187dfd35b 100644 --- a/Sources/GtkCodeGen/GtkCodeGen.swift +++ b/Sources/GtkCodeGen/GtkCodeGen.swift @@ -69,6 +69,7 @@ struct GtkCodeGen { "Gdk.Paintable": "OpaquePointer", "Gdk.Clipboard": "OpaquePointer", "Gdk.ModifierType": "GdkModifierType", + "Pango.EllipsizeMode": "EllipsizeMode", ] static let interfaces: [String] = [ @@ -109,15 +110,16 @@ struct GtkCodeGen { ) throws { let allowListedClasses = [ "Button", "Entry", "Label", "Range", "Scale", "Image", "Switch", "Spinner", - "ProgressBar", "FileChooserNative", "NativeDialog", "GestureClick", "GestureSingle", - "Gesture", "EventController", "GestureLongPress", "GLArea", "DrawingArea", - "CheckButton", + "ProgressBar", "FileChooserNative", "NativeDialog", "GestureClick", + "GestureSingle", "Gesture", "EventController", "GestureLongPress", "GLArea", + "DrawingArea", "CheckButton", ] let gtk3AllowListedClasses = ["MenuShell", "EventBox"] let gtk4AllowListedClasses = [ "Picture", "DropDown", "Popover", "ListBox", "EventControllerMotion", "EventControllerKey", ] + for class_ in gir.namespace.classes { guard allowListedClasses.contains(class_.name) @@ -373,7 +375,7 @@ struct GtkCodeGen { var properties: [DeclSyntax] = [] for (classLike, property) in class_.getAllImplemented(\.properties, namespace: namespace) { guard - property.version == nil || property.version == "3.2", + property.version == nil || property.version == "3.2" || property.version == "2.6", property.name != "child", let decl = generateProperty( property, namespace: namespace, classLike: classLike, forProtocol: false @@ -583,7 +585,7 @@ struct GtkCodeGen { return DeclSyntax( """ - \(raw: isWidget ? "" : "public") override func \(raw: methodName)() { + open override func \(raw: methodName)() { super.\(raw: methodName)() \(raw: exprs.joined(separator: "\n\n")) @@ -666,6 +668,7 @@ struct GtkCodeGen { } if !cTypeReplacements.values.contains(type) + && !typeNameReplacements.values.contains(type) && !namespace.enumerations.contains(where: { $0.name == type }) && type != "OpaquePointer" { diff --git a/Sources/SwiftCrossUI/Backend/AppBackend.swift b/Sources/SwiftCrossUI/Backend/AppBackend.swift index 52dfbab1adb..90b7632b20c 100644 --- a/Sources/SwiftCrossUI/Backend/AppBackend.swift +++ b/Sources/SwiftCrossUI/Backend/AppBackend.swift @@ -334,27 +334,35 @@ public protocol AppBackend: Sendable { // MARK: Passive views /// Gets the size that the given text would have if it were layed out attempting to stay - /// within the proposed frame (most backends only use the proposed width and ignore the - /// proposed height). The size returned by this function will be upheld by the layout + /// within the proposed frame. The given text should be truncated/ellipsized to fit within + /// the proposal if possible. + /// + /// The size returned by this function will be upheld by the layout /// system; child views always get the final say on their own size, parents just choose how - /// the children get layed out. + /// the children get layed out. The text should get truncated to fit within the proposal if + /// possible. + /// + /// SwiftCrossUI will never supply zero as the proposed width or height, because some UI + /// frameworks handle that in special ways. /// /// The target widget is supplied because some backends (such as Gtk) require a /// reference to the target widget to get a text layout context. /// - /// If `proposedFrame` isn't supplied, the text should be layed out on a single line - /// taking up as much width as it needs. - /// /// Used by both ``SwiftCrossUI/Text`` and ``SwiftCrossUI/TextEditor``. func size( of text: String, whenDisplayedIn widget: Widget, - proposedFrame: SIMD2?, + proposedWidth: Int?, + proposedHeight: Int?, environment: EnvironmentValues ) -> SIMD2 /// Creates a non-editable text view with optional text wrapping. Predominantly used - /// by ``Text``.` + /// by ``Text``. + /// + /// The returned widget should truncate and ellipsize its content when given a size + /// which isn't big enough to fit the full content, as per + /// ``size(of:whenDisplayedIn:proposedWidth:proposedHeight:environment)``. func createTextView() -> Widget /// Sets the content and wrapping mode of a non-editable text view. func updateTextView(_ textView: Widget, content: String, environment: EnvironmentValues) diff --git a/Sources/SwiftCrossUI/Builders/SceneBuilder.swift b/Sources/SwiftCrossUI/Builders/SceneBuilder.swift index c40e8321196..ee690388e77 100644 --- a/Sources/SwiftCrossUI/Builders/SceneBuilder.swift +++ b/Sources/SwiftCrossUI/Builders/SceneBuilder.swift @@ -9,79 +9,258 @@ 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: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4) -> TupleScene5 { + 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 + { return TupleScene5(scene0, scene1, scene2, scene3, scene4) } - public static func buildBlock(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5) -> TupleScene6 { + 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 { return TupleScene6(scene0, scene1, scene2, scene3, scene4, scene5) } - public static func buildBlock(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5, _ scene6: Scene6) -> TupleScene7 { + 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 { return TupleScene7(scene0, scene1, scene2, scene3, scene4, scene5, scene6) } - public static func buildBlock(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7) -> TupleScene8 { + 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 { return TupleScene8(scene0, scene1, scene2, scene3, scene4, scene5, scene6, scene7) } - public static func buildBlock(_ scene0: Scene0, _ scene1: Scene1, _ scene2: Scene2, _ scene3: Scene3, _ scene4: Scene4, _ scene5: Scene5, _ scene6: Scene6, _ scene7: Scene7, _ scene8: Scene8) -> TupleScene9 { + 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 { return TupleScene9(scene0, scene1, scene2, scene3, scene4, scene5, scene6, scene7, scene8) } - 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 + >( + _ 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, _ 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 + >( + _ 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, _ 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 + >( + _ 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, _ 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 + >( + _ 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, _ 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 + >( + _ 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, _ 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 + >( + _ 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, _ 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 + >( + _ 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, _ 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 + >( + _ 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, _ 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 + >( + _ 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, _ 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 + >( + _ 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, _ 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) + 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) } } diff --git a/Sources/SwiftCrossUI/Builders/TableRowBuilder.swift b/Sources/SwiftCrossUI/Builders/TableRowBuilder.swift index 5be12a0bdf4..2db59f9fb2c 100644 --- a/Sources/SwiftCrossUI/Builders/TableRowBuilder.swift +++ b/Sources/SwiftCrossUI/Builders/TableRowBuilder.swift @@ -33,7 +33,8 @@ public struct TableRowBuilder { public static func buildBlock< Content0: View, Content1: View, Content2: View >( - _ column0: TableColumn, _ column1: TableColumn, _ column2: TableColumn + _ column0: TableColumn, _ column1: TableColumn, + _ column2: TableColumn ) -> TupleTableRowContent3< RowValue, Content0, Content1, Content2 > { @@ -44,7 +45,8 @@ public struct TableRowBuilder { public static func buildBlock< Content0: View, Content1: View, Content2: View, Content3: View >( - _ column0: TableColumn, _ column1: TableColumn, _ column2: TableColumn, _ column3: TableColumn + _ column0: TableColumn, _ column1: TableColumn, + _ column2: TableColumn, _ column3: TableColumn ) -> TupleTableRowContent4< RowValue, Content0, Content1, Content2, Content3 > { @@ -55,7 +57,9 @@ public struct TableRowBuilder { public static func buildBlock< Content0: View, Content1: View, Content2: View, Content3: View, Content4: View >( - _ column0: TableColumn, _ column1: TableColumn, _ column2: TableColumn, _ column3: TableColumn, _ column4: TableColumn + _ column0: TableColumn, _ column1: TableColumn, + _ column2: TableColumn, _ column3: TableColumn, + _ column4: TableColumn ) -> TupleTableRowContent5< RowValue, Content0, Content1, Content2, Content3, Content4 > { @@ -64,9 +68,12 @@ public struct TableRowBuilder { ) } public static func buildBlock< - Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, Content5: View + Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, + Content5: View >( - _ column0: TableColumn, _ column1: TableColumn, _ column2: TableColumn, _ column3: TableColumn, _ column4: TableColumn, _ column5: TableColumn + _ column0: TableColumn, _ column1: TableColumn, + _ column2: TableColumn, _ column3: TableColumn, + _ column4: TableColumn, _ column5: TableColumn ) -> TupleTableRowContent6< RowValue, Content0, Content1, Content2, Content3, Content4, Content5 > { @@ -75,9 +82,13 @@ public struct TableRowBuilder { ) } public static func buildBlock< - Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, Content5: View, Content6: View + 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 + _ column0: TableColumn, _ column1: TableColumn, + _ column2: TableColumn, _ column3: TableColumn, + _ column4: TableColumn, _ column5: TableColumn, + _ column6: TableColumn ) -> TupleTableRowContent7< RowValue, Content0, Content1, Content2, Content3, Content4, Content5, Content6 > { @@ -86,9 +97,13 @@ public struct TableRowBuilder { ) } public static func buildBlock< - Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, Content5: View, Content6: View, Content7: View + 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 + _ 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 > { @@ -97,135 +112,268 @@ public struct TableRowBuilder { ) } public static func buildBlock< - Content0: View, Content1: View, Content2: View, Content3: View, Content4: View, Content5: View, Content6: View, Content7: View, Content8: View + 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 + _ 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 + 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 + 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 + _ 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 + 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 + 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 + _ 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 + RowValue, Content0, Content1, Content2, Content3, Content4, Content5, Content6, Content7, + Content8, Content9, Content10 > { TupleTableRowContent11( - column0, column1, column2, column3, column4, column5, column6, column7, column8, column9, column10 + 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 + 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 + _ 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 + 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 + 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 + 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 + _ 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 + 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 + 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 + 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 + _ 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 + 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 + 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 + 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 + _ 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 + 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 + 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 + 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 + _ 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 + 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 + 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 + 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 + _ 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 + 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 + 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 + 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 + _ 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 + 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 + 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 + 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 + _ 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 + 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 + 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 + 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 + _ 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 + 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 + 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/ViewBuilder.swift b/Sources/SwiftCrossUI/Builders/ViewBuilder.swift index 81d052d982a..2b5252d3673 100644 --- a/Sources/SwiftCrossUI/Builders/ViewBuilder.swift +++ b/Sources/SwiftCrossUI/Builders/ViewBuilder.swift @@ -13,80 +13,216 @@ public struct ViewBuilder { return TupleView1(view0) } - public static func buildBlock(_ view0: V0, _ view1: V1) -> TupleView2 { + public static func buildBlock(_ view0: V0, _ view1: V1) -> TupleView2< + V0, V1 + > { 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(_ 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 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 buildEither(first component: A) -> EitherView { diff --git a/Sources/SwiftCrossUI/Layout/LayoutSystem.swift b/Sources/SwiftCrossUI/Layout/LayoutSystem.swift index 2fcea230583..60a60edf213 100644 --- a/Sources/SwiftCrossUI/Layout/LayoutSystem.swift +++ b/Sources/SwiftCrossUI/Layout/LayoutSystem.swift @@ -55,7 +55,8 @@ public enum LayoutSystem { var tag: String? public init( - computeLayout: @escaping @MainActor (ProposedViewSize, EnvironmentValues) -> ViewLayoutResult, + computeLayout: @escaping @MainActor (ProposedViewSize, EnvironmentValues) -> + ViewLayoutResult, commit: @escaping @MainActor () -> ViewLayoutResult, tag: String? = nil ) { @@ -104,7 +105,8 @@ public enum LayoutSystem { ) let stackLength = proposedSize[component: orientation] - if stackLength == 0 || stackLength == .infinity || stackLength == nil || children.count == 1 { + if stackLength == 0 || stackLength == .infinity || stackLength == nil || children.count == 1 + { var resultLength: Double = 0 var resultWidth: Double = 0 var results: [ViewLayoutResult] = [] @@ -206,7 +208,8 @@ public enum LayoutSystem { var proposedChildSize = proposedSize proposedChildSize[component: orientation] = - max(stackLength - spaceUsedAlongStackAxis - totalSpacing, 0) / Double(childrenRemaining) + max(stackLength - spaceUsedAlongStackAxis - totalSpacing, 0) + / Double(childrenRemaining) let childResult = child.computeLayout( proposedSize: proposedChildSize, @@ -265,7 +268,8 @@ public enum LayoutSystem { if cache.redistributeSpaceOnCommit { guard let ordering = cache.lastFlexibilityOrdering else { - fatalError("Expected flexibility ordering in order to redistribute space during commit") + fatalError( + "Expected flexibility ordering in order to redistribute space during commit") } var spaceUsedAlongStackAxis: Double = 0 diff --git a/Sources/SwiftCrossUI/Scenes/TupleScene.swift b/Sources/SwiftCrossUI/Scenes/TupleScene.swift index 7578d95757d..e5301fc3ba7 100644 --- a/Sources/SwiftCrossUI/Scenes/TupleScene.swift +++ b/Sources/SwiftCrossUI/Scenes/TupleScene.swift @@ -115,7 +115,9 @@ public struct TupleScene4: SceneGraphNode { +public final class TupleSceneNode4: + SceneGraphNode +{ public typealias NodeScene = TupleScene4 var node0: Scene0.Node @@ -145,7 +147,9 @@ public final class TupleSceneNode4: Scene { +public struct TupleScene5< + Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene +>: Scene { public typealias Node = TupleSceneNode5 var scene0: Scene0 @@ -156,7 +160,9 @@ public struct TupleScene5: SceneGraphNode { +public final class TupleSceneNode5< + Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene +>: SceneGraphNode { public typealias NodeScene = TupleScene5 var node0: Scene0.Node @@ -205,7 +213,9 @@ public final class TupleSceneNode5: Scene { +public struct TupleScene6< + Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene +>: Scene { public typealias Node = TupleSceneNode6 var scene0: Scene0 @@ -217,7 +227,10 @@ public struct TupleScene6: SceneGraphNode { +public final class TupleSceneNode6< + Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene +>: SceneGraphNode { public typealias NodeScene = TupleScene6 var node0: Scene0.Node @@ -271,7 +286,10 @@ public final class TupleSceneNode6: Scene { +public struct TupleScene7< + Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, + Scene6: Scene +>: Scene { public typealias Node = TupleSceneNode7 var scene0: Scene0 @@ -284,7 +302,10 @@ public struct TupleScene7: SceneGraphNode { +public final class TupleSceneNode7< + Scene0: Scene, Scene1: Scene, Scene2: Scene, Scene3: Scene, Scene4: Scene, Scene5: Scene, + Scene6: Scene +>: SceneGraphNode { public typealias NodeScene = TupleScene7 var node0: Scene0.Node @@ -343,8 +367,13 @@ public final class TupleSceneNode7: Scene { - public typealias Node = TupleSceneNode8 +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 + > var scene0: Scene0 var scene1: Scene1 @@ -357,7 +386,10 @@ public struct TupleScene8: SceneGraphNode { - public typealias NodeScene = 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 + > var node0: Scene0.Node var node1: Scene1.Node @@ -421,8 +458,13 @@ public final class TupleSceneNode8: Scene { - public typealias Node = TupleSceneNode9 +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 + > var scene0: Scene0 var scene1: Scene1 @@ -436,7 +478,10 @@ public struct TupleScene9: SceneGraphNode { - public typealias NodeScene = 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 + > var node0: Scene0.Node var node1: Scene1.Node @@ -505,8 +555,13 @@ public final class TupleSceneNode9: Scene { - public typealias Node = TupleSceneNode10 +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 + > var scene0: Scene0 var scene1: Scene1 @@ -521,7 +576,10 @@ public struct TupleScene10: SceneGraphNode { - public typealias NodeScene = 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 + > var node0: Scene0.Node var node1: Scene1.Node @@ -595,8 +658,13 @@ public final class TupleSceneNode10: Scene { - public typealias Node = TupleSceneNode11 +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 + > var scene0: Scene0 var scene1: Scene1 @@ -612,7 +680,11 @@ public struct TupleScene11: SceneGraphNode { - public typealias NodeScene = 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 + > var node0: Scene0.Node var node1: Scene1.Node @@ -691,8 +768,14 @@ public final class TupleSceneNode11: Scene { - public typealias Node = TupleSceneNode12 +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 + > var scene0: Scene0 var scene1: Scene1 @@ -709,7 +792,11 @@ public struct TupleScene12: SceneGraphNode { - public typealias NodeScene = 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 + > var node0: Scene0.Node var node1: Scene1.Node @@ -793,8 +886,15 @@ public final class TupleSceneNode12: Scene { - public typealias Node = TupleSceneNode13 +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 + > var scene0: Scene0 var scene1: Scene1 @@ -812,7 +912,11 @@ public struct TupleScene13: SceneGraphNode { - public typealias NodeScene = 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 + > var node0: Scene0.Node var node1: Scene1.Node @@ -901,8 +1012,15 @@ public final class TupleSceneNode13: Scene { - public typealias Node = TupleSceneNode14 +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 + > var scene0: Scene0 var scene1: Scene1 @@ -921,7 +1039,11 @@ public struct TupleScene14: SceneGraphNode { - public typealias NodeScene = 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 + > var node0: Scene0.Node var node1: Scene1.Node @@ -1015,8 +1144,15 @@ public final class TupleSceneNode14: Scene { - public typealias Node = TupleSceneNode15 +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 + > var scene0: Scene0 var scene1: Scene1 @@ -1036,7 +1172,12 @@ public struct TupleScene15: SceneGraphNode { - public typealias NodeScene = 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 + > var node0: Scene0.Node var node1: Scene1.Node @@ -1135,8 +1283,15 @@ public final class TupleSceneNode15: Scene { - public typealias Node = TupleSceneNode16 +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 + > var scene0: Scene0 var scene1: Scene1 @@ -1157,7 +1312,12 @@ public struct TupleScene16: SceneGraphNode { - public typealias NodeScene = 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 + > var node0: Scene0.Node var node1: Scene1.Node @@ -1261,8 +1428,15 @@ public final class TupleSceneNode16: Scene { - public typealias Node = TupleSceneNode17 +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 + > var scene0: Scene0 var scene1: Scene1 @@ -1284,7 +1458,12 @@ public struct TupleScene17: SceneGraphNode { - public typealias NodeScene = 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 + > var node0: Scene0.Node var node1: Scene1.Node @@ -1393,8 +1579,15 @@ public final class TupleSceneNode17: Scene { - public typealias Node = TupleSceneNode18 +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 + > var scene0: Scene0 var scene1: Scene1 @@ -1417,7 +1610,12 @@ public struct TupleScene18: SceneGraphNode { - public typealias NodeScene = 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 + > var node0: Scene0.Node var node1: Scene1.Node @@ -1531,8 +1736,16 @@ public final class TupleSceneNode18: Scene { - public typealias Node = TupleSceneNode19 +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 + > var scene0: Scene0 var scene1: Scene1 @@ -1556,7 +1769,13 @@ public struct TupleScene19: SceneGraphNode { - public typealias NodeScene = 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 + > var node0: Scene0.Node var node1: Scene1.Node @@ -1675,8 +1902,16 @@ public final class TupleSceneNode19: Scene { - public typealias Node = TupleSceneNode20 +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 + > var scene0: Scene0 var scene1: Scene1 @@ -1701,7 +1936,13 @@ public struct TupleScene20: SceneGraphNode { - public typealias NodeScene = 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 + > var node0: Scene0.Node var node1: Scene1.Node diff --git a/Sources/SwiftCrossUI/State/DynamicKeyPath.swift b/Sources/SwiftCrossUI/State/DynamicKeyPath.swift index 77904396f17..9671ae4ebbd 100644 --- a/Sources/SwiftCrossUI/State/DynamicKeyPath.swift +++ b/Sources/SwiftCrossUI/State/DynamicKeyPath.swift @@ -35,15 +35,16 @@ struct DynamicKeyPath { var index = 0 var matches: [Int] = [] while index + propertySize <= baseStructSize { - let isMatch = withUnsafeBytes(of: base) { viewPointer in - withUnsafeBytes(of: value) { valuePointer in - memcmp( - viewPointer.baseAddress!.advanced(by: index), - valuePointer.baseAddress!, - propertySize - ) - } - } == 0 + let isMatch = + withUnsafeBytes(of: base) { viewPointer in + withUnsafeBytes(of: value) { valuePointer in + memcmp( + viewPointer.baseAddress!.advanced(by: index), + valuePointer.baseAddress!, + propertySize + ) + } + } == 0 if isMatch { matches.append(index) } diff --git a/Sources/SwiftCrossUI/State/DynamicPropertyUpdater.swift b/Sources/SwiftCrossUI/State/DynamicPropertyUpdater.swift index 8a99ab24e72..d1af87fe389 100644 --- a/Sources/SwiftCrossUI/State/DynamicPropertyUpdater.swift +++ b/Sources/SwiftCrossUI/State/DynamicPropertyUpdater.swift @@ -28,9 +28,9 @@ struct DynamicPropertyUpdater { _ new: Base, _ environment: EnvironmentValues ) -> Void - + /// The updaters for each of Base's dynamic properties. If `nil`, then we - /// failed to compute + /// failed to compute let propertyUpdaters: [PropertyUpdater]? /// Creates a new dynamic property updater which can efficiently update @@ -45,8 +45,7 @@ struct DynamicPropertyUpdater { return } - if - let cachedUpdater = updaterCache[ObjectIdentifier(Base.self)], + if let cachedUpdater = updaterCache[ObjectIdentifier(Base.self)], let cachedUpdater = cachedUpdater as? Self { self = cachedUpdater diff --git a/Sources/SwiftCrossUI/ViewGraph/ViewGraphNode.swift b/Sources/SwiftCrossUI/ViewGraph/ViewGraphNode.swift index e82a8c329af..20d8ca2779d 100644 --- a/Sources/SwiftCrossUI/ViewGraph/ViewGraphNode.swift +++ b/Sources/SwiftCrossUI/ViewGraph/ViewGraphNode.swift @@ -178,7 +178,10 @@ public class ViewGraphNode: Sendable { "View graph updated without parent window present in environment" ) - if proposedSize == lastProposedSize && !resultCache.isEmpty && (!parentEnvironment.allowLayoutCaching || environment.allowLayoutCaching), let currentLayout { + if proposedSize == lastProposedSize && !resultCache.isEmpty + && (!parentEnvironment.allowLayoutCaching || environment.allowLayoutCaching), + let currentLayout + { // If the previous proposal is the same as the current one, and our // cache hasn't been invalidated, then we can reuse the current layout. // But only if the previous layout was computed without caching, or the @@ -240,10 +243,14 @@ public class ViewGraphNode: Sendable { } if parentEnvironment.allowLayoutCaching { - print("warning: Committing layout computed with caching enabled. Results may be invalid. NodeView = \(NodeView.self)") + print( + "warning: Committing layout computed with caching enabled. Results may be invalid. NodeView = \(NodeView.self)" + ) } if currentLayout.size.height == .infinity || currentLayout.size.width == .infinity { - print("warning: \(NodeView.self) has infinite height or width on commit, currentLayout.size: \(currentLayout.size), lastProposedSize: \(lastProposedSize)") + print( + "warning: \(NodeView.self) has infinite height or width on commit, currentLayout.size: \(currentLayout.size), lastProposedSize: \(lastProposedSize)" + ) } view.commit( diff --git a/Sources/SwiftCrossUI/Views/List.swift b/Sources/SwiftCrossUI/Views/List.swift index 258edad9cc9..8253c78c393 100644 --- a/Sources/SwiftCrossUI/Views/List.swift +++ b/Sources/SwiftCrossUI/Views/List.swift @@ -166,7 +166,8 @@ public struct List: TypeSafeView, View Double(minimumRowSize.y) ) }.reduce(0, +) - let minimumWidth = (childResults.map(\.size.width).max() ?? 0) + Double(horizontalBasePadding) + let minimumWidth = + (childResults.map(\.size.width).max() ?? 0) + Double(horizontalBasePadding) let size = ViewSize( max(proposedSize.width ?? minimumWidth, minimumWidth), height diff --git a/Sources/SwiftCrossUI/Views/Modifiers/PresentationModifiers.swift b/Sources/SwiftCrossUI/Views/Modifiers/PresentationModifiers.swift index 3b5738a6151..0b6d460f852 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/PresentationModifiers.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/PresentationModifiers.swift @@ -30,7 +30,7 @@ extension View { /// 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 diff --git a/Sources/SwiftCrossUI/Views/Modifiers/SheetModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/SheetModifier.swift index 7f5f1dadd79..edfe1c577f8 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/SheetModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/SheetModifier.swift @@ -6,14 +6,14 @@ extension View { /// /// `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. + /// by the user. public func sheet( isPresented: Binding, onDismiss: (() -> Void)? = nil, diff --git a/Sources/SwiftCrossUI/Views/Modifiers/TextSelectionModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/TextSelectionModifier.swift index 07b0ec1df60..6d044b163a3 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/TextSelectionModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/TextSelectionModifier.swift @@ -1,10 +1,11 @@ extension View { - /// Set selectability of contained text. Ignored on tvOS. + /// Sets 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/SplitView.swift b/Sources/SwiftCrossUI/Views/SplitView.swift index 86f2d25fd96..86b8231ba89 100644 --- a/Sources/SwiftCrossUI/Views/SplitView.swift +++ b/Sources/SwiftCrossUI/Views/SplitView.swift @@ -49,23 +49,25 @@ struct SplitView: TypeSafeView, View { // TODO: If computeLayout ever becomes a pure requirement of View, then we // can delay this until commit. - children.minimumLeadingWidth = children.leadingChild.computeLayout( - with: body.view0, - proposedSize: ProposedViewSize( - 0, - proposedSize.height - ), - environment: environment - ).size.width - - children.minimumTrailingWidth = children.trailingChild.computeLayout( - with: body.view1, - proposedSize: ProposedViewSize( - 0, - proposedSize.height - ), - environment: environment - ).size.width + children.minimumLeadingWidth = + children.leadingChild.computeLayout( + with: body.view0, + proposedSize: ProposedViewSize( + 0, + proposedSize.height + ), + environment: environment + ).size.width + + children.minimumTrailingWidth = + children.trailingChild.computeLayout( + with: body.view1, + proposedSize: ProposedViewSize( + 0, + proposedSize.height + ), + environment: environment + ).size.width // TODO: Figure out proper fixedSize behaviour (when width is unspecified) // Update pane children @@ -127,10 +129,11 @@ struct SplitView: TypeSafeView, View { backend.setSidebarWidthBounds( ofSplitView: widget, minimum: LayoutSystem.roundSize(children.minimumLeadingWidth), - maximum: LayoutSystem.roundSize(max( - children.minimumLeadingWidth, - layout.size.width - children.minimumTrailingWidth - )) + maximum: LayoutSystem.roundSize( + max( + children.minimumLeadingWidth, + layout.size.width - children.minimumTrailingWidth + )) ) // Center pane children diff --git a/Sources/SwiftCrossUI/Views/TableRowContent.swift b/Sources/SwiftCrossUI/Views/TableRowContent.swift index 91219482cc1..f76429dcffb 100644 --- a/Sources/SwiftCrossUI/Views/TableRowContent.swift +++ b/Sources/SwiftCrossUI/Views/TableRowContent.swift @@ -52,7 +52,9 @@ public struct TupleTableRowContent2: T [column0.label, column1.label] } - public init(_ column0: TableColumn, _ column1: TableColumn) { + public init( + _ column0: TableColumn, _ column1: TableColumn + ) { self.column0 = column0 self.column1 = column1 } @@ -62,7 +64,9 @@ public struct TupleTableRowContent2: T } } -public struct TupleTableRowContent3: TableRowContent { +public struct TupleTableRowContent3: + TableRowContent +{ public typealias RowContent = TupleView3 public var column0: TableColumn @@ -73,7 +77,10 @@ public struct TupleTableRowContent3, _ column1: TableColumn, _ column2: TableColumn) { + public init( + _ column0: TableColumn, _ column1: TableColumn, + _ column2: TableColumn + ) { self.column0 = column0 self.column1 = column1 self.column2 = column2 @@ -84,7 +91,9 @@ public struct TupleTableRowContent3: TableRowContent { +public struct TupleTableRowContent4< + RowValue, Content0: View, Content1: View, Content2: View, Content3: View +>: TableRowContent { public typealias RowContent = TupleView4 public var column0: TableColumn @@ -96,7 +105,10 @@ public struct TupleTableRowContent4, _ column1: TableColumn, _ column2: TableColumn, _ column3: TableColumn) { + public init( + _ column0: TableColumn, _ column1: TableColumn, + _ column2: TableColumn, _ column3: TableColumn + ) { self.column0 = column0 self.column1 = column1 self.column2 = column2 @@ -104,11 +116,14 @@ public struct TupleTableRowContent4 RowContent { - TupleView4(column0.content(row), column1.content(row), column2.content(row), column3.content(row)) + TupleView4( + column0.content(row), column1.content(row), column2.content(row), column3.content(row)) } } -public struct TupleTableRowContent5: TableRowContent { +public struct TupleTableRowContent5< + RowValue, Content0: View, Content1: View, Content2: View, Content3: View, Content4: View +>: TableRowContent { public typealias RowContent = TupleView5 public var column0: TableColumn @@ -121,7 +136,11 @@ public struct TupleTableRowContent5, _ column1: TableColumn, _ column2: TableColumn, _ column3: TableColumn, _ column4: TableColumn) { + public init( + _ column0: TableColumn, _ column1: TableColumn, + _ column2: TableColumn, _ column3: TableColumn, + _ column4: TableColumn + ) { self.column0 = column0 self.column1 = column1 self.column2 = column2 @@ -130,12 +149,19 @@ public struct TupleTableRowContent5 RowContent { - TupleView5(column0.content(row), column1.content(row), column2.content(row), column3.content(row), column4.content(row)) + TupleView5( + column0.content(row), column1.content(row), column2.content(row), column3.content(row), + column4.content(row)) } } -public struct TupleTableRowContent6: TableRowContent { - public typealias RowContent = TupleView6 +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 @@ -148,7 +174,11 @@ public struct TupleTableRowContent6, _ column1: TableColumn, _ column2: TableColumn, _ column3: TableColumn, _ column4: TableColumn, _ column5: TableColumn) { + public init( + _ column0: TableColumn, _ column1: TableColumn, + _ column2: TableColumn, _ column3: TableColumn, + _ column4: TableColumn, _ column5: TableColumn + ) { self.column0 = column0 self.column1 = column1 self.column2 = column2 @@ -158,12 +188,19 @@ public struct TupleTableRowContent6 RowContent { - TupleView6(column0.content(row), column1.content(row), column2.content(row), column3.content(row), column4.content(row), column5.content(row)) + TupleView6( + column0.content(row), column1.content(row), column2.content(row), column3.content(row), + column4.content(row), column5.content(row)) } } -public struct TupleTableRowContent7: TableRowContent { - public typealias RowContent = TupleView7 +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 @@ -174,10 +211,18 @@ public struct TupleTableRowContent7 public var labels: [String] { - [column0.label, column1.label, column2.label, column3.label, column4.label, column5.label, column6.label] + [ + 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) { + 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 @@ -188,12 +233,19 @@ public struct TupleTableRowContent7 RowContent { - TupleView7(column0.content(row), column1.content(row), column2.content(row), column3.content(row), column4.content(row), column5.content(row), column6.content(row)) + 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: TableRowContent { - public typealias RowContent = TupleView8 +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 @@ -205,10 +257,18 @@ public struct TupleTableRowContent8 public var labels: [String] { - [column0.label, column1.label, column2.label, column3.label, column4.label, column5.label, column6.label, column7.label] + [ + 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) { + 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 @@ -220,12 +280,19 @@ public struct TupleTableRowContent8 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)) + 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: TableRowContent { - public typealias RowContent = TupleView9 +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 @@ -238,10 +305,19 @@ public struct TupleTableRowContent9 public var labels: [String] { - [column0.label, column1.label, column2.label, column3.label, column4.label, column5.label, column6.label, column7.label, column8.label] + [ + 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) { + 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 @@ -254,12 +330,21 @@ public struct TupleTableRowContent9 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)) + 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: TableRowContent { - public typealias RowContent = TupleView10 +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 @@ -273,10 +358,19 @@ public struct TupleTableRowContent10 public var labels: [String] { - [column0.label, column1.label, column2.label, column3.label, column4.label, column5.label, column6.label, column7.label, column8.label, column9.label] + [ + 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) { + 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 @@ -290,12 +384,21 @@ public struct TupleTableRowContent10 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)) + 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: TableRowContent { - public typealias RowContent = TupleView11 +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 @@ -310,10 +413,21 @@ public struct TupleTableRowContent11 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] + [ + 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) { + 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 @@ -328,12 +442,22 @@ public struct TupleTableRowContent11 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)) + 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: TableRowContent { - public typealias RowContent = TupleView12 +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 @@ -349,10 +473,21 @@ public struct TupleTableRowContent12 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] + [ + 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) { + 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 @@ -368,12 +503,23 @@ public struct TupleTableRowContent12 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)) + 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: TableRowContent { - public typealias RowContent = TupleView13 +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 @@ -390,10 +536,22 @@ public struct TupleTableRowContent13 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] + [ + 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) { + 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 @@ -410,12 +568,23 @@ public struct TupleTableRowContent13 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)) + 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: TableRowContent { - public typealias RowContent = TupleView14 +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 @@ -433,10 +602,22 @@ public struct TupleTableRowContent14 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] + [ + 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) { + 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 @@ -454,12 +635,23 @@ public struct TupleTableRowContent14 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)) + 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: TableRowContent { - public typealias RowContent = TupleView15 +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 @@ -478,10 +670,23 @@ public struct TupleTableRowContent15 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] + [ + 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) { + 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 @@ -500,12 +705,24 @@ public struct TupleTableRowContent15 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)) + 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: TableRowContent { - public typealias RowContent = TupleView16 +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 @@ -525,10 +742,24 @@ public struct TupleTableRowContent16 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] + [ + 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) { + 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 @@ -548,12 +779,25 @@ public struct TupleTableRowContent16 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)) + 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: TableRowContent { - public typealias RowContent = TupleView17 +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 @@ -574,10 +818,25 @@ public struct TupleTableRowContent17 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] + [ + 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) { + 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 @@ -598,12 +857,26 @@ public struct TupleTableRowContent17 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)) + 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: TableRowContent { - public typealias RowContent = TupleView18 +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 @@ -625,10 +898,25 @@ public struct TupleTableRowContent18 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] + [ + 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) { + 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 @@ -650,12 +938,27 @@ public struct TupleTableRowContent18 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)) + 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: TableRowContent { - public typealias RowContent = TupleView19 +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 @@ -678,10 +981,26 @@ public struct TupleTableRowContent19 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] + [ + 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) { + 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 @@ -704,12 +1023,27 @@ public struct TupleTableRowContent19 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)) + 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: TableRowContent { - public typealias RowContent = TupleView20 +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 @@ -733,10 +1067,26 @@ public struct TupleTableRowContent20 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] + [ + 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) { + 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 @@ -760,7 +1110,12 @@ public struct TupleTableRowContent20 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)) + 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/Text.swift b/Sources/SwiftCrossUI/Views/Text.swift index 779899a7cac..3c0f54b7908 100644 --- a/Sources/SwiftCrossUI/Views/Text.swift +++ b/Sources/SwiftCrossUI/Views/Text.swift @@ -1,4 +1,48 @@ -/// A text view. +/// A view the displays text. +/// +/// ``Text`` truncates its content to fit within its proposed size. To wrap +/// without truncation, put the ``Text`` (or its enclosing view hierarchy) into +/// an ideal height context such as a ``ScrollView``. Alternatively, use +/// ``View/fixedSize(horizontal:vertical:)`` with `horizontal` set to false and +/// `vertical` set to true, but be aware that this may lead to unintuitive +/// minimum sizing behaviour when used within a window. Often when developers +/// use ``fixedSize`` on text, what they really need is a ``ScrollView``. +/// +/// To avoid wrapping and truncation entirely, use ``View/fixedSize()``. +/// +/// ## Technical notes +/// +/// The reason that ``Text`` truncates its content to fit its proposed size is +/// that SwiftCrossUI's layout system behaves rather unintuitively with views that +/// trade off width for height. The layout system used to support this behaviour +/// well, but when overhauling the layout system with performance in mind, we +/// discovered that it's not possible to handle minimum view sizing in the +/// intuitive way that we were, without a large performance cost or layout system +/// complexity cost. +/// +/// With the current system, windows determine the minimum size of their content +/// by proposing a size of 0x0. A text view that doesn't truncate its content +/// would take on a width of 0 and then lay out each character on a new line (as +/// that's what most UI frameworks do when text is given a small width). This leads +/// to the window thinking that its minimum height is `characterCount * lineHeight`, +/// even though when given a width larger than zero, the text view would be shorter +/// than this 'minimum height'. The underlying cause is the assumption that +/// 'minimum size' is a sensible notion for every view. A text view without +/// truncation doesn't have a 'minimum size'; are we minimizing width? minimizing +/// height? minimizing width + height? minimizing area? +/// +/// SwiftCrossUI's old layout system separated the concept of minimum size into +/// 'minimum width for current height', and 'minimum height for current width'. +/// This led to much more intuitive window sizing behaviour. If you had +/// non-truncating text inside a window, and resized the width of the window +/// such that the height of the text became taller than the window, then the window +/// would become taller, and if you resized the height of the window then you'd reach +/// the window's minimum height before the text could overflow the window horizontally. +/// Unfortunately this required a lot of book-keeping, and was deemed to be unfeasible +/// to do without significantly hurting performance due to all the layout assumptions +/// that we'd have to drop from our stack layout algorithm. +/// +/// The new layout system behaviour is in-line with SwiftUI's layout behaviour. public struct Text: Sendable { /// The string to be shown in the text view. var string: String @@ -31,24 +75,41 @@ extension Text: ElementaryView { // properties and such (via Pango). backend.updateTextView(widget, content: string, environment: environment) - 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 size = backend.size( + // UI frameworks often handle the zero proposal specially. We want to + // have standard text sizing behaviour so it's better for us to never + // propose zero in either dimension and then fix up the resulting size + // to match our expectations. + // + // Our desired behaviour is for a zero width proposal to result in at least + // one line's worth of height (for a non-empty string). Furthermore, if + // proposed more than one line's worth of height, then a zero width + // proposal should result in height equivalent to however many lines are + // required to put each character of the text on a new line (excluding + // whitespace). + // + // A zero height proposal should result in the text using at least one + // line of height (if non-empty). + var size = backend.size( of: string, whenDisplayedIn: widget, - proposedFrame: proposedFrame, + proposedWidth: proposedSize.width.flatMap { + // For text, an infinite proposal is the same as an unspecified + // proposal, and this works nicer with most backends than converting + // .infinity to a large integer (which is the alternative). + $0 == .infinity ? nil : $0 + }.map(LayoutSystem.roundSize).map { max(1, $0) }, + proposedHeight: proposedSize.height.flatMap { + $0 == .infinity ? nil : $0 + }.map(LayoutSystem.roundSize).map { max(1, $0) }, environment: environment ) + // If the proposed width was 0 and the resuling width was 1, then set the + // resulting width to 0. See above for more detail. + if proposedSize.width == 0 && size.x == 1 { + size.x = 0 + } + return ViewLayoutResult.leafView(size: ViewSize(size)) } diff --git a/Sources/SwiftCrossUI/Views/TupleView.swift b/Sources/SwiftCrossUI/Views/TupleView.swift index 0c24ef041b2..7818e012003 100644 --- a/Sources/SwiftCrossUI/Views/TupleView.swift +++ b/Sources/SwiftCrossUI/Views/TupleView.swift @@ -72,7 +72,6 @@ extension TupleView { } } - /// A view with exactly 1 children. Autogenerated as an alternative to Swift's not yet /// production ready variadic generics. /// @@ -111,7 +110,7 @@ extension TupleView1: TupleView { children: Children ) -> [LayoutSystem.LayoutableChild] { [ - layoutableChild(node: children.child0, view: view0), + layoutableChild(node: children.child0, view: view0) ] } } @@ -322,7 +321,9 @@ extension TupleView5: TupleView { /// production ready variadic generics. /// /// Has the same behaviour as ``Group`` when rendered directly. -public struct TupleView6 { +public struct TupleView6< + View0: View, View1: View, View2: View, View3: View, View4: View, View5: View +> { public var view0: View0 public var view1: View1 public var view2: View2 @@ -333,7 +334,10 @@ public struct TupleView6 { +public struct TupleView7< + View0: View, View1: View, View2: View, View3: View, View4: View, View5: View, View6: View +> { public var view0: View0 public var view1: View1 public var view2: View2 @@ -392,7 +398,10 @@ public struct TupleView7 { +public struct TupleView8< + View0: View, View1: View, View2: View, View3: View, View4: View, View5: View, View6: View, + View7: View +> { public var view0: View0 public var view1: View1 public var view2: View2 @@ -454,7 +466,10 @@ public struct TupleView8 { +public struct TupleView9< + View0: View, View1: View, View2: View, View3: View, View4: View, View5: View, View6: View, + View7: View, View8: View +> { public var view0: View0 public var view1: View1 public var view2: View2 @@ -519,7 +537,10 @@ public struct TupleView9 + typealias Children = TupleViewChildren9< + View0, View1, View2, View3, View4, View5, View6, View7, View8 + > func children( backend: Backend, @@ -572,7 +595,10 @@ extension TupleView9: TupleView { /// production ready variadic generics. /// /// Has the same behaviour as ``Group`` when rendered directly. -public struct TupleView10 { +public struct TupleView10< + View0: View, View1: View, View2: View, View3: View, View4: View, View5: View, View6: View, + View7: View, View8: View, View9: View +> { public var view0: View0 public var view1: View1 public var view2: View2 @@ -587,7 +613,10 @@ public struct TupleView10 + typealias Children = TupleViewChildren10< + View0, View1, View2, View3, View4, View5, View6, View7, View8, View9 + > func children( backend: Backend, @@ -642,7 +673,10 @@ extension TupleView10: TupleView { /// production ready variadic generics. /// /// Has the same behaviour as ``Group`` when rendered directly. -public struct TupleView11 { +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 var view0: View0 public var view1: View1 public var view2: View2 @@ -658,7 +692,11 @@ public struct TupleView11 + typealias Children = TupleViewChildren11< + View0, View1, View2, View3, View4, View5, View6, View7, View8, View9, View10 + > func children( backend: Backend, @@ -715,7 +755,10 @@ extension TupleView11: TupleView { /// production ready variadic generics. /// /// Has the same behaviour as ``Group`` when rendered directly. -public struct TupleView12 { +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 var view0: View0 public var view1: View1 public var view2: View2 @@ -732,7 +775,11 @@ public struct TupleView12 + typealias Children = TupleViewChildren12< + View0, View1, View2, View3, View4, View5, View6, View7, View8, View9, View10, View11 + > func children( backend: Backend, @@ -791,7 +840,10 @@ extension TupleView12: TupleView { /// production ready variadic generics. /// /// Has the same behaviour as ``Group`` when rendered directly. -public struct TupleView13 { +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 var view0: View0 public var view1: View1 public var view2: View2 @@ -809,7 +861,11 @@ public struct TupleView13 + typealias Children = TupleViewChildren13< + View0, View1, View2, View3, View4, View5, View6, View7, View8, View9, View10, View11, View12 + > func children( backend: Backend, @@ -839,7 +897,8 @@ 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 ) } @@ -870,7 +929,10 @@ extension TupleView13: TupleView { /// production ready variadic generics. /// /// Has the same behaviour as ``Group`` when rendered directly. -public struct TupleView14 { +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 var view0: View0 public var view1: View1 public var view2: View2 @@ -889,7 +951,11 @@ public struct TupleView14 + typealias Children = TupleViewChildren14< + View0, View1, View2, View3, View4, View5, View6, View7, View8, View9, View10, View11, + View12, View13 + > func children( backend: Backend, @@ -920,7 +989,8 @@ 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 ) } @@ -952,7 +1022,11 @@ extension TupleView14: TupleView { /// production ready variadic generics. /// /// Has the same behaviour as ``Group`` when rendered directly. -public struct TupleView15 { +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 var view0: View0 public var view1: View1 public var view2: View2 @@ -972,7 +1046,11 @@ public struct TupleView15 + typealias Children = TupleViewChildren15< + View0, View1, View2, View3, View4, View5, View6, View7, View8, View9, View10, View11, + View12, View13, View14 + > func children( backend: Backend, @@ -1004,7 +1085,8 @@ 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 ) } @@ -1037,7 +1119,11 @@ extension TupleView15: TupleView { /// production ready variadic generics. /// /// Has the same behaviour as ``Group`` when rendered directly. -public struct TupleView16 { +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 var view0: View0 public var view1: View1 public var view2: View2 @@ -1058,7 +1144,12 @@ public struct TupleView16 + typealias Children = TupleViewChildren16< + View0, View1, View2, View3, View4, View5, View6, View7, View8, View9, View10, View11, + View12, View13, View14, View15 + > func children( backend: Backend, @@ -1091,7 +1185,8 @@ 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 ) } @@ -1125,7 +1220,11 @@ extension TupleView16: TupleView { /// production ready variadic generics. /// /// Has the same behaviour as ``Group`` when rendered directly. -public struct TupleView17 { +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 var view0: View0 public var view1: View1 public var view2: View2 @@ -1147,7 +1246,12 @@ public struct TupleView17 + typealias Children = TupleViewChildren17< + View0, View1, View2, View3, View4, View5, View6, View7, View8, View9, View10, View11, + View12, View13, View14, View15, View16 + > func children( backend: Backend, @@ -1181,7 +1288,8 @@ 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 ) } @@ -1216,7 +1324,11 @@ extension TupleView17: TupleView { /// production ready variadic generics. /// /// Has the same behaviour as ``Group`` when rendered directly. -public struct TupleView18 { +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 var view0: View0 public var view1: View1 public var view2: View2 @@ -1239,7 +1351,12 @@ public struct TupleView18 + typealias Children = TupleViewChildren18< + View0, View1, View2, View3, View4, View5, View6, View7, View8, View9, View10, View11, + View12, View13, View14, View15, View16, View17 + > func children( backend: Backend, @@ -1274,7 +1394,8 @@ 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 ) } @@ -1310,7 +1431,11 @@ extension TupleView18: TupleView { /// production ready variadic generics. /// /// Has the same behaviour as ``Group`` when rendered directly. -public struct TupleView19 { +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 var view0: View0 public var view1: View1 public var view2: View2 @@ -1334,7 +1459,12 @@ public struct TupleView19 + typealias Children = TupleViewChildren19< + View0, View1, View2, View3, View4, View5, View6, View7, View8, View9, View10, View11, + View12, View13, View14, View15, View16, View17, View18 + > func children( backend: Backend, @@ -1370,7 +1503,8 @@ 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 ) } @@ -1407,7 +1541,11 @@ extension TupleView19: TupleView { /// production ready variadic generics. /// /// Has the same behaviour as ``Group`` when rendered directly. -public struct TupleView20 { +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 var view0: View0 public var view1: View1 public var view2: View2 @@ -1432,7 +1570,12 @@ public struct TupleView20 + typealias Children = TupleViewChildren20< + View0, View1, View2, View3, View4, View5, View6, View7, View8, View9, View10, View11, + View12, View13, View14, View15, View16, View17, View18, View19 + > func children( backend: Backend, @@ -1469,7 +1615,8 @@ 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/TupleViewChildren.swift b/Sources/SwiftCrossUI/Views/TupleViewChildren.swift index 55f5de387d9..1b7dc209c1c 100644 --- a/Sources/SwiftCrossUI/Views/TupleViewChildren.swift +++ b/Sources/SwiftCrossUI/Views/TupleViewChildren.swift @@ -30,7 +30,6 @@ private func node( ) } - /// A fixed-length strongly-typed collection of 1 child nodes. A counterpart to /// ``TupleView1``. public class TupleViewChildren1: TupleViewChildren { @@ -40,7 +39,7 @@ public class TupleViewChildren1: TupleViewChildren { public var erasedNodes: [ErasedViewGraphNode] { return [ - ErasedViewGraphNode(wrapping: child0), + ErasedViewGraphNode(wrapping: child0) ] } @@ -93,7 +92,7 @@ public class TupleViewChildren2: TupleViewChildren { 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) @@ -133,7 +132,8 @@ public class TupleViewChildren3: Tuple 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) @@ -144,7 +144,9 @@ public class TupleViewChildren3: Tuple /// A fixed-length strongly-typed collection of 4 child nodes. A counterpart to /// ``TupleView4``. -public class TupleViewChildren4: TupleViewChildren { +public class TupleViewChildren4: + TupleViewChildren +{ public var widgets: [AnyWidget] { return [child0.widget, child1.widget, child2.widget, child3.widget] } @@ -177,7 +179,8 @@ public class TupleViewChildren4: TupleViewChildren { +public class TupleViewChildren5< + Child0: View, Child1: View, Child2: View, Child3: View, Child4: View +>: TupleViewChildren { public var widgets: [AnyWidget] { return [child0.widget, child1.widget, child2.widget, child3.widget, child4.widget] } @@ -225,7 +230,9 @@ public class TupleViewChildren5: TupleViewChildren { +public class TupleViewChildren6< + Child0: View, Child1: View, Child2: View, Child3: View, Child4: View, Child5: View +>: 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] { @@ -271,13 +283,16 @@ public class TupleViewChildren6( - _ 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) @@ -291,9 +306,14 @@ public class TupleViewChildren6: TupleViewChildren { +public class TupleViewChildren7< + Child0: View, Child1: View, Child2: View, Child3: View, Child4: View, Child5: View, Child6: View +>: 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] { @@ -327,13 +347,17 @@ public class TupleViewChildren7( - _ 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) @@ -348,9 +372,15 @@ public class TupleViewChildren7: TupleViewChildren { +public class TupleViewChildren8< + Child0: View, Child1: View, Child2: View, Child3: View, Child4: View, Child5: View, + Child6: View, Child7: View +>: 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] { @@ -387,13 +417,17 @@ public class TupleViewChildren8( - _ 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) @@ -409,9 +443,15 @@ public class TupleViewChildren8: TupleViewChildren { +public class TupleViewChildren9< + Child0: View, Child1: View, Child2: View, Child3: View, Child4: View, Child5: View, + Child6: View, Child7: View, Child8: View +>: 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] { @@ -451,13 +491,18 @@ public class TupleViewChildren9( - _ 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) @@ -474,9 +519,15 @@ public class TupleViewChildren9: TupleViewChildren { +public class TupleViewChildren10< + Child0: View, Child1: View, Child2: View, Child3: View, Child4: View, Child5: View, + Child6: View, Child7: View, Child8: View, Child9: View +>: 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] { @@ -519,13 +570,18 @@ public class TupleViewChildren10( - _ 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) @@ -543,9 +599,16 @@ public class TupleViewChildren10: TupleViewChildren { +public class TupleViewChildren11< + Child0: View, Child1: View, Child2: View, Child3: View, Child4: View, Child5: View, + Child6: View, Child7: View, Child8: View, Child9: View, Child10: View +>: 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] { @@ -591,13 +654,20 @@ public class TupleViewChildren11( - _ 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) @@ -616,9 +686,16 @@ public class TupleViewChildren11: TupleViewChildren { +public class 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 +>: 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] { @@ -667,13 +744,21 @@ public class TupleViewChildren12( - _ 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) @@ -693,9 +778,17 @@ public class TupleViewChildren12: TupleViewChildren { +public class 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 +>: 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] { @@ -747,13 +840,22 @@ public class TupleViewChildren13( - _ 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) @@ -774,9 +876,17 @@ public class TupleViewChildren13: TupleViewChildren { +public class 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 +>: 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] { @@ -831,13 +941,23 @@ public class TupleViewChildren14( - _ 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) @@ -859,9 +979,17 @@ public class TupleViewChildren14: TupleViewChildren { +public class 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 +>: 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] { @@ -919,13 +1047,25 @@ public class TupleViewChildren15( - _ 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) @@ -948,9 +1088,18 @@ public class TupleViewChildren15: TupleViewChildren { +public class 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 +>: 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] { @@ -1011,13 +1160,26 @@ public class TupleViewChildren16( - _ 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) @@ -1041,9 +1203,18 @@ public class TupleViewChildren16: TupleViewChildren { +public class 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 +>: 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] { @@ -1107,13 +1278,27 @@ public class TupleViewChildren17( - _ 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) @@ -1138,9 +1323,18 @@ public class TupleViewChildren17: TupleViewChildren { +public class 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 +>: 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] { @@ -1207,13 +1401,28 @@ public class TupleViewChildren18( - _ 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) @@ -1239,9 +1448,19 @@ public class TupleViewChildren18: TupleViewChildren { +public class 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 +>: 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] { @@ -1311,13 +1530,30 @@ public class TupleViewChildren19( - _ 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) @@ -1344,9 +1580,19 @@ public class TupleViewChildren19: TupleViewChildren { +public class 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 +>: 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] { @@ -1419,13 +1665,31 @@ public class TupleViewChildren20( - _ 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/UIKitBackend/UIKitBackend+Passive.swift b/Sources/UIKitBackend/UIKitBackend+Passive.swift index 9a5512da7ba..67f9b2474d5 100644 --- a/Sources/UIKitBackend/UIKitBackend+Passive.swift +++ b/Sources/UIKitBackend/UIKitBackend+Passive.swift @@ -36,9 +36,7 @@ extension UIKitBackend { } public func createTextView() -> Widget { - let widget = WrapperWidget() - widget.child.numberOfLines = 0 - return widget + WrapperWidget() } public func updateTextView( @@ -46,7 +44,7 @@ extension UIKitBackend { content: String, environment: EnvironmentValues ) { - let wrapper = textView as! WrapperWidget + let wrapper = textView as! WrapperWidget wrapper.child.overrideUserInterfaceStyle = environment.colorScheme.userInterfaceStyle wrapper.child.attributedText = UIKitBackend.attributedString( text: content, @@ -58,19 +56,17 @@ extension UIKitBackend { public func size( of text: String, whenDisplayedIn widget: Widget, - proposedFrame: SIMD2?, + proposedWidth: Int?, + proposedHeight: Int?, environment: EnvironmentValues ) -> SIMD2 { let attributedString = UIKitBackend.attributedString(text: text, environment: environment) - let boundingSize = - if let proposedFrame { - CGSize(width: CGFloat(proposedFrame.x), height: .greatestFiniteMagnitude) - } else { - CGSize(width: .greatestFiniteMagnitude, height: environment.resolvedFont.lineHeight) - } let size = attributedString.boundingRect( - with: boundingSize, - options: proposedFrame == nil ? [] : [.usesLineFragmentOrigin], + with: CGSize( + width: proposedWidth.map(Double.init) ?? .greatestFiniteMagnitude, + height: proposedHeight.map(Double.init) ?? .greatestFiniteMagnitude + ), + options: [.usesLineFragmentOrigin, .truncatesLastVisibleLine], context: nil ) return SIMD2( @@ -106,26 +102,43 @@ extension UIKitBackend { } } -// Inspired by https://medium.com/kinandcartacreated/making-uilabel-accessible-5f3d5c342df4 -// Thank you to Sam Dods for the base idea -final class OptionallySelectableLabel: UILabel { +final class CustomTextView: UIView { var isSelectable: Bool = false - override init(frame: CGRect) { - super.init(frame: frame) - setupTextSelection() + var attributedText: NSAttributedString { + get { + textStorage + } + set { + textStorage.setAttributedString(newValue) + setNeedsDisplay() + } } - required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - setupTextSelection() + var text: String { + attributedText.string } - override var canBecomeFirstResponder: Bool { - isSelectable - } + var layoutManager: NSLayoutManager + var textStorage: NSTextStorage + var textContainer: NSTextContainer + + override init(frame: CGRect) { + layoutManager = NSLayoutManager() + + textStorage = NSTextStorage(attributedString: NSAttributedString(string: "")) + textStorage.addLayoutManager(layoutManager) + + textContainer = NSTextContainer(size: frame.size) + textContainer.lineBreakMode = .byTruncatingTail + layoutManager.addTextContainer(textContainer) + + super.init(frame: frame) + + isOpaque = false - private func setupTextSelection() { + // Inspired by https://medium.com/kinandcartacreated/making-uilabel-accessible-5f3d5c342df4 + // Thank you to Sam Dods for the base idea #if !os(tvOS) let longPress = UILongPressGestureRecognizer( target: self, action: #selector(didLongPress)) @@ -134,12 +147,33 @@ final class OptionallySelectableLabel: UILabel { #endif } + required init?(coder aDecoder: NSCoder) { + fatalError("init?(coder:) not implemented") + } + + override var canBecomeFirstResponder: Bool { + isSelectable + } + + override func layoutSubviews() { + super.layoutSubviews() + if textContainer.size != bounds.size { + textContainer.size = bounds.size + setNeedsDisplay() + } + } + + override func draw(_ rect: CGRect) { + let range = layoutManager.glyphRange(for: textContainer) + layoutManager.drawBackground(forGlyphRange: range, at: bounds.origin) + layoutManager.drawGlyphs(forGlyphRange: range, at: bounds.origin) + } + @objc private func didLongPress(_ gesture: UILongPressGestureRecognizer) { #if !os(tvOS) guard isSelectable, gesture.state == .began, - let text = self.attributedText?.string, !text.isEmpty else { return @@ -149,7 +183,7 @@ final class OptionallySelectableLabel: UILabel { let menu = UIMenuController.shared if !menu.isMenuVisible { - menu.showMenu(from: self, rect: textRect()) + menu.showMenu(from: self, rect: bounds) } #endif } @@ -158,12 +192,6 @@ final class OptionallySelectableLabel: UILabel { return action == #selector(copy(_:)) } - private func textRect() -> CGRect { - let inset: CGFloat = -4 - return textRect(forBounds: bounds, limitedToNumberOfLines: numberOfLines) - .insetBy(dx: inset, dy: inset) - } - private func cancelSelection() { #if !os(tvOS) let menu = UIMenuController.shared diff --git a/Sources/UIKitBackend/UIKitBackend+Sheet.swift b/Sources/UIKitBackend/UIKitBackend+Sheet.swift index ef085c3f537..9370394b7a4 100644 --- a/Sources/UIKitBackend/UIKitBackend+Sheet.swift +++ b/Sources/UIKitBackend/UIKitBackend+Sheet.swift @@ -16,7 +16,7 @@ extension UIKitBackend { // Fetch the child controller before adding the child to the view // hierarchy. Otherwise, if the child doesn't have its own controller, we'd // get back a reference to the sheet controller and attempt to add it as a - // child of itself. + // child of itself. if let childController = content.controller { sheet.addChild(childController) } @@ -52,7 +52,8 @@ extension UIKitBackend { sheet.onDismiss = onDismiss setPresentationDetents(of: sheet, to: detents) setPresentationCornerRadius(of: sheet, to: cornerRadius) - setPresentationDragIndicatorVisibility(of: sheet, to: dragIndicatorVisibility, detents: detents) + setPresentationDragIndicatorVisibility( + of: sheet, to: dragIndicatorVisibility, detents: detents) let defaultColor: UIColor? #if targetEnvironment(macCatalyst) diff --git a/Sources/WinUIBackend/WinUIBackend.swift b/Sources/WinUIBackend/WinUIBackend.swift index 948012ee16b..506db77b924 100644 --- a/Sources/WinUIBackend/WinUIBackend.swift +++ b/Sources/WinUIBackend/WinUIBackend.swift @@ -67,6 +67,8 @@ public final class WinUIBackend: AppBackend { private var windows: [Window] = [] + private var measurementTextBlock: TextBlock! + public init() { internalState = InternalState() } @@ -115,6 +117,8 @@ public final class WinUIBackend: AppBackend { // let pv: __ABI_Windows_Foundation.IPropertyValue = try! iinspectable.QueryInterface() // let value = try! pv.GetDoubleImpl() + self.measurementTextBlock = self.createTextView() as! TextBlock + callback() } WinUIApplication.main() @@ -547,19 +551,44 @@ public final class WinUIBackend: AppBackend { public func size( of text: String, whenDisplayedIn widget: Widget, - proposedFrame: SIMD2?, + proposedWidth: Int?, + proposedHeight: Int?, environment: EnvironmentValues ) -> SIMD2 { - let block = createTextView() - updateTextView(block, content: text, environment: environment) + // Update the text view's environment and measure its desired line height + updateTextView(measurementTextBlock, content: "a", environment: environment) + let lineHeight = Self.measure( + measurementTextBlock, + proposedWidth: nil, + proposedHeight: nil + ).y + + // Measure the text's size + measurementTextBlock.text = text + var size = Self.measure( + measurementTextBlock, + proposedWidth: proposedWidth, + proposedHeight: proposedHeight + ) + // Make sure the text doesn't get shorter than a single line of text even if + // it's empty. + size.y = max(size.y, lineHeight) + return size + } + + private static func measure( + _ textBlock: TextBlock, + proposedWidth: Int?, + proposedHeight: Int? + ) -> SIMD2 { let allocation = WindowsFoundation.Size( - width: (proposedFrame?.x).map(Float.init(_:)) ?? .infinity, - height: .infinity + width: proposedWidth.map(Float.init) ?? .infinity, + height: proposedHeight.map(Float.init) ?? .infinity ) - try! block.measure(allocation) + try! textBlock.measure(allocation) - let computedSize = block.desiredSize + let computedSize = textBlock.desiredSize return SIMD2( Int(computedSize.width), Int(computedSize.height) @@ -569,6 +598,7 @@ public final class WinUIBackend: AppBackend { public func createTextView() -> Widget { let textBlock = TextBlock() textBlock.textWrapping = .wrap + textBlock.textTrimming = .characterEllipsis return textBlock } From daf571d9c0db9b3f2d41ead8656bb19b0b6d9b0d Mon Sep 17 00:00:00 2001 From: stackotter Date: Mon, 29 Dec 2025 23:10:04 +1000 Subject: [PATCH 06/15] Fix layout caching bug: don't treat cached layout as 'current layout' This led to situations where the last accessed cached layout would replace the real current layout and then the layout system would commit a cached layout instead of the current layout, leading to strange results. I noticed this bug when testing StressTestExample. Open the app and then click Generate without interacting with the window in any way beforehand to reproduce the bug (resizing the window first seems to stop the bug). I only tried reproducing the bug on macOS with AppKitBackend. --- Sources/SwiftCrossUI/ViewGraph/ViewGraphNode.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Sources/SwiftCrossUI/ViewGraph/ViewGraphNode.swift b/Sources/SwiftCrossUI/ViewGraph/ViewGraphNode.swift index 20d8ca2779d..7a355d9a670 100644 --- a/Sources/SwiftCrossUI/ViewGraph/ViewGraphNode.swift +++ b/Sources/SwiftCrossUI/ViewGraph/ViewGraphNode.swift @@ -193,7 +193,6 @@ public class ViewGraphNode: Sendable { // If this layout pass is a probing pass (not a final pass), then we // can reuse any layouts that we've computed since the cache was last // cleared. The cache gets cleared on commit. - currentLayout = cachedResult return cachedResult } From 4369ce21015d0b12b783690b90ee62b80361fc1b Mon Sep 17 00:00:00 2001 From: stackotter Date: Mon, 29 Dec 2025 23:10:04 +1000 Subject: [PATCH 07/15] Fix NotesExample layout issues (post-rewrite): frame, scroll, txt editor The frame modifier had undesirable behaviour for ideal-size proposals. You'd end up with a child taking on its ideal size and being much smaller than the frame's clamped bounds, making it impossible to grow views in scroll views. ScrollView didn't grow to use up available space along scrolling axes. TextEditor didn't grow to use up available width. Used GeometryReader to grow the TextEditor to at least fill the ScrollView which used to (somewhat erroneously) be the default behaviour. --- .../Sources/NotesExample/ContentView.swift | 28 ++++--- Sources/SwiftCrossUI/Backend/AppBackend.swift | 3 +- .../Modifiers/Layout/FrameModifier.swift | 79 ++++++++++++------- Sources/SwiftCrossUI/Views/ScrollView.swift | 14 ++-- Sources/SwiftCrossUI/Views/TextEditor.swift | 16 ++-- 5 files changed, 84 insertions(+), 56 deletions(-) diff --git a/Examples/Sources/NotesExample/ContentView.swift b/Examples/Sources/NotesExample/ContentView.swift index 05f74d7c5d7..23b36df3caf 100644 --- a/Examples/Sources/NotesExample/ContentView.swift +++ b/Examples/Sources/NotesExample/ContentView.swift @@ -130,21 +130,25 @@ struct ContentView: View { } .frame(minWidth: 200) } detail: { - ScrollView { - VStack(alignment: .center) { - if let selectedNote = selectedNote { - HStack(spacing: 4) { - Text("Title") - TextField("Title", text: selectedNote.title) - } + GeometryReader { proxy in + ScrollView { + VStack(alignment: .center) { + if let selectedNote = selectedNote { + HStack(spacing: 4) { + Text("Title") + TextField("Title", text: selectedNote.title) + } - TextEditor(text: selectedNote.content) - .padding() - .background(textEditorBackground) - .cornerRadius(4) + TextEditor(text: selectedNote.content) + .padding() + .background(textEditorBackground) + .cornerRadius(4) + .frame(maxHeight: .infinity) + } } + .padding() + .frame(minHeight: Int(proxy.size.height)) } - .padding() } } } diff --git a/Sources/SwiftCrossUI/Backend/AppBackend.swift b/Sources/SwiftCrossUI/Backend/AppBackend.swift index 90b7632b20c..c0209a76e7e 100644 --- a/Sources/SwiftCrossUI/Backend/AppBackend.swift +++ b/Sources/SwiftCrossUI/Backend/AppBackend.swift @@ -930,7 +930,8 @@ extension AppBackend { public func size( of text: String, whenDisplayedIn widget: Widget, - proposedFrame: SIMD2?, + proposedWidth: Int?, + proposedHeight: Int?, environment: EnvironmentValues ) -> SIMD2 { todo() diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Layout/FrameModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Layout/FrameModifier.swift index 3c297c28587..9ffa61711bf 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/Layout/FrameModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/Layout/FrameModifier.swift @@ -173,6 +173,29 @@ struct FlexibleFrameView: TypeSafeView { return container } + func clampSize(_ size: ViewSize) -> ViewSize { + var size = size + size.width = clampWidth(size.width) + size.height = clampHeight(size.height) + return size + } + + func clampHeight(_ height: Double) -> Double { + LayoutSystem.clamp( + height, + minimum: minHeight.map(Double.init), + maximum: maxHeight + ) + } + + func clampWidth(_ width: Double) -> Double { + LayoutSystem.clamp( + width, + minimum: minWidth.map(Double.init), + maximum: maxWidth + ) + } + func computeLayout( _ widget: Backend.Widget, children: TupleViewChildren1, @@ -183,50 +206,48 @@ struct FlexibleFrameView: TypeSafeView { var proposedFrameSize = proposedSize if let proposedWidth = proposedSize.width { - proposedFrameSize.width = LayoutSystem.clamp( - proposedWidth, - minimum: minWidth.map(Double.init), - maximum: maxWidth - ) + proposedFrameSize.width = clampWidth(proposedWidth) } if let proposedHeight = proposedSize.height { - proposedFrameSize.height = LayoutSystem.clamp( - proposedHeight, - minimum: minHeight.map(Double.init), - maximum: maxHeight - ) + proposedFrameSize.height = clampHeight(proposedHeight) } - let childResult = children.child0.computeLayout( + var childResult = children.child0.computeLayout( with: body.view0, proposedSize: proposedFrameSize, environment: environment ) let childSize = childResult.size - // TODO: Fix idealSize propagation. When idealSize isn't possible, we - // have to use idealWidthForProposedHeight and - // idealHeightForProposedWidth, and sometimes we may also have to - // perform an additional dryRun update to probe the child view. - - var frameSize = childSize - 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 the child view has at least one unspecified axis, compute its + // layout again with the clamped frame size. This allows the view to + // fill the space that the frame is going to take up anyway. E.g. + // + // ScrollView { + // Color.blue + // .frame(minHeight: 100) + // } + // + // Without this second layout computation, the blue rectangle would + // take on its ideal size of 10 within a frame of height 100, instead + // of using up the min height as developers may expect. + var frameSize = clampSize(childSize) + if proposedFrameSize.width == nil || proposedFrameSize.height == nil { + childResult = children.child0.computeLayout( + with: nil, + proposedSize: ProposedViewSize(frameSize), + environment: environment + ) + frameSize = childResult.size + } if maxWidth == .infinity, let proposedWidth = proposedSize.width { - frameSize.width = proposedWidth + frameSize.width = max(frameSize.width, proposedWidth) } + if maxHeight == .infinity, let proposedHeight = proposedSize.height { - frameSize.height = proposedHeight + frameSize.height = max(frameSize.height, proposedHeight) } return ViewLayoutResult( diff --git a/Sources/SwiftCrossUI/Views/ScrollView.swift b/Sources/SwiftCrossUI/Views/ScrollView.swift index 257f91dee89..b526c23350b 100644 --- a/Sources/SwiftCrossUI/Views/ScrollView.swift +++ b/Sources/SwiftCrossUI/Views/ScrollView.swift @@ -77,7 +77,7 @@ public struct ScrollView: TypeSafeView, View { let contentSize = childResult.size - // An axis is present when its a scroll axis AND the corresponding + // An axis is present when it's 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 @@ -119,19 +119,15 @@ public struct ScrollView: TypeSafeView, View { // Compute the outer size. var outerSize = finalChildResult.size if axes.contains(.horizontal) { - outerSize.width = min( - finalChildResult.size.width + verticalScrollBarWidth, - proposedSize.width ?? 0 - ) + outerSize.width = proposedSize.width + ?? (finalChildResult.size.width + verticalScrollBarWidth) } else { outerSize.width += verticalScrollBarWidth } if axes.contains(.vertical) { - outerSize.height = min( - finalChildResult.size.height + horizontalScrollBarHeight, - proposedSize.height ?? 0 - ) + outerSize.height = proposedSize.height + ?? (finalChildResult.size.height + horizontalScrollBarHeight) } else { outerSize.height += horizontalScrollBarHeight } diff --git a/Sources/SwiftCrossUI/Views/TextEditor.swift b/Sources/SwiftCrossUI/Views/TextEditor.swift index c0ac84829f8..3e64f7b2f6f 100644 --- a/Sources/SwiftCrossUI/Views/TextEditor.swift +++ b/Sources/SwiftCrossUI/Views/TextEditor.swift @@ -23,15 +23,21 @@ public struct TextEditor: ElementaryView { if proposedSize == .unspecified { size = ViewSize(10, 10) } else if let width = proposedSize.width, proposedSize.height == nil { - let idealHeight = backend.size( + // See ``Text``'s computeLayout for a more details on why we clamp + // the width to be positive. + let idealSize = backend.size( of: content, whenDisplayedIn: widget, - proposedFrame: SIMD2(LayoutSystem.roundSize(width), 1), + // For text, an infinite proposal is the same as an unspecified + // proposal, and this works nicer with most backends than converting + // .infinity to a large integer (which is the alternative). + proposedWidth: width == .infinity ? nil : max(1, LayoutSystem.roundSize(width)), + proposedHeight: nil, environment: environment - ).y + ) size = ViewSize( - width, - Double(idealHeight) + max(width, Double(idealSize.x)), + Double(idealSize.y) ) } else { size = proposedSize.replacingUnspecifiedDimensions(by: ViewSize(10, 10)) From 1cc0e4cdf8e9a8a3b4a45187991357958ed30b96 Mon Sep 17 00:00:00 2001 From: stackotter Date: Mon, 29 Dec 2025 23:10:04 +1000 Subject: [PATCH 08/15] Fix PathsExample compilation, and update WebViewExample to respect Enter --- Examples/Sources/PathsExample/PathsApp.swift | 13 ++----------- .../Sources/WebViewExample/WebViewApp.swift | 17 +++++++++++++---- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Examples/Sources/PathsExample/PathsApp.swift b/Examples/Sources/PathsExample/PathsApp.swift index 1f657eff3cb..754ff2bad46 100644 --- a/Examples/Sources/PathsExample/PathsApp.swift +++ b/Examples/Sources/PathsExample/PathsApp.swift @@ -23,17 +23,8 @@ struct ArcShape: StyledShape { } func size(fitting proposal: SIMD2) -> ViewSize { - let diameter = max(11, min(proposal.x, proposal.y)) - return ViewSize( - size: SIMD2(x: diameter, y: diameter), - idealSize: SIMD2(x: 100, y: 100), - idealWidthForProposedHeight: proposal.y, - idealHeightForProposedWidth: proposal.x, - minimumWidth: 11, - minimumHeight: 11, - maximumWidth: nil, - maximumHeight: nil - ) + let diameter = Double(max(11, min(proposal.x, proposal.y))) + return ViewSize(diameter, diameter) } } diff --git a/Examples/Sources/WebViewExample/WebViewApp.swift b/Examples/Sources/WebViewExample/WebViewApp.swift index fb11609c57e..a1510c7dff6 100644 --- a/Examples/Sources/WebViewExample/WebViewApp.swift +++ b/Examples/Sources/WebViewExample/WebViewApp.swift @@ -13,18 +13,26 @@ struct WebViewApp: App { @State var url = URL(string: "https://stackotter.dev")! + func go(_ url: String) { + guard let url = URL(string: urlInput) else { + return + } + + self.url = url + } + var body: some Scene { WindowGroup("WebViewExample") { #hotReloadable { VStack { HStack { TextField("URL", text: $urlInput) - Button("Go") { - guard let url = URL(string: urlInput) else { - return // disabled + .onSubmit { + go(urlInput) } - self.url = url + Button("Go") { + go(urlInput) }.disabled(URL(string: urlInput) == nil) } .padding() @@ -36,5 +44,6 @@ struct WebViewApp: App { } } } + .defaultSize(width: 800, height: 800) } } From 05aa83726f00056d9c8d50773f9de065bcc8bd5a Mon Sep 17 00:00:00 2001 From: stackotter Date: Tue, 30 Dec 2025 01:27:33 +1000 Subject: [PATCH 09/15] Warn when LayoutSystem.roundSize is called with infinite size --- Sources/SwiftCrossUI/Layout/LayoutSystem.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sources/SwiftCrossUI/Layout/LayoutSystem.swift b/Sources/SwiftCrossUI/Layout/LayoutSystem.swift index 60a60edf213..e4a912e3205 100644 --- a/Sources/SwiftCrossUI/Layout/LayoutSystem.swift +++ b/Sources/SwiftCrossUI/Layout/LayoutSystem.swift @@ -8,6 +8,10 @@ public enum LayoutSystem { } package static func roundSize(_ size: Double) -> Int { + if size.isInfinite { + print("warning: LayoutSystem.roundSize called with infinite size") + } + let size = size.rounded(.towardZero) return if size >= Double(Int.max) { Int.max From fab102073c72ccfa19992d6fc348824fda1dbdc2 Mon Sep 17 00:00:00 2001 From: stackotter Date: Tue, 30 Dec 2025 01:37:00 +1000 Subject: [PATCH 10/15] Fix DismissAction concurrency warning --- Sources/SwiftCrossUI/Environment/Actions/DismissAction.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Sources/SwiftCrossUI/Environment/Actions/DismissAction.swift b/Sources/SwiftCrossUI/Environment/Actions/DismissAction.swift index 1258d67193f..2f92edf3531 100644 --- a/Sources/SwiftCrossUI/Environment/Actions/DismissAction.swift +++ b/Sources/SwiftCrossUI/Environment/Actions/DismissAction.swift @@ -20,9 +20,9 @@ /// ``` @MainActor public struct DismissAction { - private let action: () -> Void + private nonisolated let action: @Sendable @MainActor () -> Void - internal init(action: @escaping () -> Void) { + nonisolated internal init(action: @escaping @Sendable @MainActor () -> Void) { self.action = action } @@ -34,7 +34,6 @@ public struct DismissAction { /// Environment key for the dismiss action. private struct DismissActionKey: EnvironmentKey { - @MainActor static var defaultValue: DismissAction { DismissAction(action: { #if DEBUG From c6fd9eee3032d7822d03c90042bfe2b650cf51c3 Mon Sep 17 00:00:00 2001 From: stackotter Date: Tue, 30 Dec 2025 10:25:04 +1000 Subject: [PATCH 11/15] Fix Axis.Set compilation for older compilers (SetAlgebra.Element inference) --- Sources/SwiftCrossUI/Values/Axis.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Sources/SwiftCrossUI/Values/Axis.swift b/Sources/SwiftCrossUI/Values/Axis.swift index eaf1a95a0cf..480aecbd0c8 100644 --- a/Sources/SwiftCrossUI/Values/Axis.swift +++ b/Sources/SwiftCrossUI/Values/Axis.swift @@ -17,6 +17,11 @@ public enum Axis: Sendable, CaseIterable { /// A set of axes represented as an efficient bit field. public struct Set: OptionSet, Sendable { + // Required to satisfy older compilers (5.10) due to our custom + // `contains(_:)` overload below which confuses the inference + // of SetAlgebra's Element associated type. + public typealias Element = Self + /// The horizontal axis. public static let horizontal = Set(rawValue: 1) /// The vertical axis. From fd0ce1de9dd96d946fb1f2e8cd7314df95b56f79 Mon Sep 17 00:00:00 2001 From: stackotter Date: Tue, 30 Dec 2025 10:37:15 +1000 Subject: [PATCH 12/15] Avoid second frame layout pass when possible, and delay it until commit --- .../ViewGraph/AnyViewGraphNode.swift | 12 +++- .../ViewGraph/ViewGraphNode.swift | 13 ++-- .../Modifiers/Layout/FrameModifier.swift | 61 ++++++++++++------- 3 files changed, 58 insertions(+), 28 deletions(-) diff --git a/Sources/SwiftCrossUI/ViewGraph/AnyViewGraphNode.swift b/Sources/SwiftCrossUI/ViewGraph/AnyViewGraphNode.swift index 886a140baf8..5ade76afb99 100644 --- a/Sources/SwiftCrossUI/ViewGraph/AnyViewGraphNode.swift +++ b/Sources/SwiftCrossUI/ViewGraph/AnyViewGraphNode.swift @@ -12,6 +12,11 @@ public class AnyViewGraphNode { _getWidget() } + /// The node's last proposed size. + public var lastProposedSize: ProposedViewSize { + _getLastProposedSize() + } + /// The node's type-erased layout computing method. private var _computeLayoutWithNewView: ( @@ -27,8 +32,10 @@ public class AnyViewGraphNode { private var _getNodeView: () -> NodeView /// The type-erased getter for the node's children. private var _getNodeChildren: () -> any ViewGraphNodeChildren - /// The underlying erased backend. + /// The type-erased getter for the node's underlying erased backend. private var _getBackend: () -> any AppBackend + /// The type-erased getter for the node's last proposed size. + private var _getLastProposedSize: () -> ProposedViewSize /// Type-erases a view graph node. public init(_ node: ViewGraphNode) { @@ -47,6 +54,9 @@ public class AnyViewGraphNode { _getBackend = { node.backend } + _getLastProposedSize = { + node.lastProposedSize + } } /// Creates a new view graph node and immediately type-erases it. diff --git a/Sources/SwiftCrossUI/ViewGraph/ViewGraphNode.swift b/Sources/SwiftCrossUI/ViewGraph/ViewGraphNode.swift index 7a355d9a670..15e0296dd27 100644 --- a/Sources/SwiftCrossUI/ViewGraph/ViewGraphNode.swift +++ b/Sources/SwiftCrossUI/ViewGraph/ViewGraphNode.swift @@ -37,14 +37,17 @@ public class ViewGraphNode: Sendable { /// The backend used to create the view's widget. public var backend: Backend - /// The most recent update result for the wrapped view. + /// The view's most recently computed layout. Doesn't include cached layouts, + /// as this is the layout that is currently 'ready to commit'. 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. + /// A cache of update results keyed by the proposed size they were for. Gets + /// cleared before the results' sizes become invalid. 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: ProposedViewSize + /// view as a result of a state change rather than the parent view updating. Proposals + /// that get cached responses don't update this size, as this size should stay in sync + /// with currentLayout. + private(set) var lastProposedSize: ProposedViewSize /// A cancellable handle to the view's state property observations. private var cancellables: [Cancellable] diff --git a/Sources/SwiftCrossUI/Views/Modifiers/Layout/FrameModifier.swift b/Sources/SwiftCrossUI/Views/Modifiers/Layout/FrameModifier.swift index 9ffa61711bf..1ebf5e98240 100644 --- a/Sources/SwiftCrossUI/Views/Modifiers/Layout/FrameModifier.swift +++ b/Sources/SwiftCrossUI/Views/Modifiers/Layout/FrameModifier.swift @@ -213,35 +213,22 @@ struct FlexibleFrameView: TypeSafeView { proposedFrameSize.height = clampHeight(proposedHeight) } - var childResult = children.child0.computeLayout( + if let idealWidth, proposedSize.width == nil { + proposedFrameSize.width = Double(idealWidth) + } + + if let idealHeight, proposedSize.height == nil { + proposedFrameSize.height = Double(idealHeight) + } + + let childResult = children.child0.computeLayout( with: body.view0, proposedSize: proposedFrameSize, environment: environment ) let childSize = childResult.size - // If the child view has at least one unspecified axis, compute its - // layout again with the clamped frame size. This allows the view to - // fill the space that the frame is going to take up anyway. E.g. - // - // ScrollView { - // Color.blue - // .frame(minHeight: 100) - // } - // - // Without this second layout computation, the blue rectangle would - // take on its ideal size of 10 within a frame of height 100, instead - // of using up the min height as developers may expect. var frameSize = clampSize(childSize) - if proposedFrameSize.width == nil || proposedFrameSize.height == nil { - childResult = children.child0.computeLayout( - with: nil, - proposedSize: ProposedViewSize(frameSize), - environment: environment - ) - frameSize = childResult.size - } - if maxWidth == .infinity, let proposedWidth = proposedSize.width { frameSize.width = max(frameSize.width, proposedWidth) } @@ -264,6 +251,36 @@ struct FlexibleFrameView: TypeSafeView { backend: Backend ) { let frameSize = layout.size + + // If the child view has at least one unspecified axis which this frame + // is constraining with a minimum or maximum, then compute its + // layout again with the clamped frame size. This allows the view to + // fill the space that the frame is going to take up anyway. E.g. consider, + // + // ScrollView { + // Color.blue + // .frame(minHeight: 100) + // } + // + // Without this second layout computation, the blue rectangle would + // take on its ideal size of 10 within a frame of height 100, instead + // of using up the min height of the frame as developers may expect. + // + // This doesn't apply to unconstrained axes which we have a corresponding + // ideal length for. + let widthConstrained = minWidth != nil || maxWidth != nil + let heightConstrained = minHeight != nil || maxHeight != nil + let proposedFrameSize = children.child0.lastProposedSize + if (proposedFrameSize.width == nil && widthConstrained) + || (proposedFrameSize.height == nil && heightConstrained) + { + _ = children.child0.computeLayout( + with: nil, + proposedSize: ProposedViewSize(frameSize), + environment: environment + ) + } + let childSize = children.child0.commit().size let childPosition = alignment.position( From dd6dcd2af2718df71d2ef5dc7e78bc9554f52547 Mon Sep 17 00:00:00 2001 From: stackotter Date: Tue, 30 Dec 2025 11:44:50 +1000 Subject: [PATCH 13/15] Backport count(where:) for pre Swift 6, and fix nonisolated warning --- .../Environment/Actions/DismissAction.swift | 2 +- Sources/SwiftCrossUI/Extensions/Sequence.swift | 12 ++++++++++++ Sources/SwiftCrossUI/Layout/LayoutSystem.swift | 1 + 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 Sources/SwiftCrossUI/Extensions/Sequence.swift diff --git a/Sources/SwiftCrossUI/Environment/Actions/DismissAction.swift b/Sources/SwiftCrossUI/Environment/Actions/DismissAction.swift index 2f92edf3531..e3c3c8529d2 100644 --- a/Sources/SwiftCrossUI/Environment/Actions/DismissAction.swift +++ b/Sources/SwiftCrossUI/Environment/Actions/DismissAction.swift @@ -20,7 +20,7 @@ /// ``` @MainActor public struct DismissAction { - private nonisolated let action: @Sendable @MainActor () -> Void + private let action: @Sendable @MainActor () -> Void nonisolated internal init(action: @escaping @Sendable @MainActor () -> Void) { self.action = action diff --git a/Sources/SwiftCrossUI/Extensions/Sequence.swift b/Sources/SwiftCrossUI/Extensions/Sequence.swift new file mode 100644 index 00000000000..2d2fbdfc942 --- /dev/null +++ b/Sources/SwiftCrossUI/Extensions/Sequence.swift @@ -0,0 +1,12 @@ +#if swift(<6.0) + extension Sequence { + /// Back-ported implementation of `count(where:)` for pre Swift 6. + func count(where predicate: (Element) -> Bool) -> Int { + var count = 0 + for element in self where predicate(element) { + count += 1 + } + return count + } + } +#endif diff --git a/Sources/SwiftCrossUI/Layout/LayoutSystem.swift b/Sources/SwiftCrossUI/Layout/LayoutSystem.swift index e4a912e3205..e2473821bf7 100644 --- a/Sources/SwiftCrossUI/Layout/LayoutSystem.swift +++ b/Sources/SwiftCrossUI/Layout/LayoutSystem.swift @@ -277,6 +277,7 @@ public enum LayoutSystem { } var spaceUsedAlongStackAxis: Double = 0 + // Avoid a trailing closure here because Swift 5.10 gets confused let visibleChildrenCount = cache.lastHiddenChildren.count { isHidden in !isHidden } From 4b67e21ee1d64534aaccbda857dceb91e4df5532 Mon Sep 17 00:00:00 2001 From: stackotter Date: Tue, 30 Dec 2025 16:41:01 +1000 Subject: [PATCH 14/15] Fix ScrollView; wasn't setting backend scroll bar presence (+ unit test) First unit test using DummyBackend! --- Package.swift | 1 + Sources/DummyBackend/DummyBackend.swift | 59 +++++++++++++++++++ Sources/SwiftCrossUI/Views/ScrollView.swift | 3 + .../SwiftCrossUITests/SwiftCrossUITests.swift | 42 +++++++++++++ 4 files changed, 105 insertions(+) diff --git a/Package.swift b/Package.swift index 6d49268f6f2..2bc9c38117b 100644 --- a/Package.swift +++ b/Package.swift @@ -163,6 +163,7 @@ let package = Package( name: "SwiftCrossUITests", dependencies: [ "SwiftCrossUI", + "DummyBackend", .target(name: "AppKitBackend", condition: .when(platforms: [.macOS])), ] ), diff --git a/Sources/DummyBackend/DummyBackend.swift b/Sources/DummyBackend/DummyBackend.swift index 5ee44962f6a..28dbabab667 100644 --- a/Sources/DummyBackend/DummyBackend.swift +++ b/Sources/DummyBackend/DummyBackend.swift @@ -17,6 +17,23 @@ public final class DummyBackend: AppBackend { } } + public class BreadthFirstWidgetIterator: IteratorProtocol { + var queue: [Widget] + + init(for widget: Widget) { + queue = [widget] + } + + public func next() -> Widget? { + guard let next = queue.first else { + return nil + } + queue.removeFirst() + queue.append(contentsOf: next.getChildren()) + return next + } + } + public class Widget { public var tag: String? public var cornerRadius = 0 @@ -24,6 +41,22 @@ public final class DummyBackend: AppBackend { public var naturalSize: SIMD2 { SIMD2.zero } + + public func getChildren() -> [Widget] { + [] + } + + /// Finds the first widget of type `T` in the hierarchy defined by this + /// widget (including the widget itself). + public func firstWidget(ofType type: T.Type) -> T? { + let iterator = BreadthFirstWidgetIterator(for: self) + while let child = iterator.next() { + if let child = child as? T { + return child + } + } + return nil + } } public class Button: Widget { @@ -95,10 +128,18 @@ public final class DummyBackend: AppBackend { public var columnLabels: [String] = [] public var cells: [Widget] = [] public var rowHeights: [Int] = [] + + public override func getChildren() -> [Widget] { + cells + } } public class Container: Widget { public var children: [(widget: Widget, position: SIMD2)] = [] + + public override func getChildren() -> [Widget] { + children.map(\.widget) + } } public class ScrollContainer: Widget { @@ -109,6 +150,10 @@ public final class DummyBackend: AppBackend { public init(child: Widget) { self.child = child } + + public override func getChildren() -> [Widget] { + [child] + } } public class SelectableListView: Widget { @@ -116,6 +161,10 @@ public final class DummyBackend: AppBackend { public var rowHeights: [Int] = [] public var selectionHandler: ((Int) -> Void)? public var selectedIndex: Int? + + public override func getChildren() -> [Widget] { + items + } } public class Rectangle: Widget { @@ -175,6 +224,10 @@ public final class DummyBackend: AppBackend { self.leadingChild = leadingChild self.trailingChild = trailingChild } + + public override func getChildren() -> [Widget] { + [leadingChild, trailingChild] + } } public class Menu {} @@ -195,6 +248,8 @@ public final class DummyBackend: AppBackend { public var deviceClass = DeviceClass.desktop public var canRevealFiles = false + public var incomingURLHandler: ((URL) -> Void)? + public init() {} public func runMainLoop(_ callback: @escaping @MainActor () -> Void) { @@ -264,6 +319,10 @@ public final class DummyBackend: AppBackend { of window: Window, to action: @escaping () -> Void ) {} + public func setIncomingURLHandler(to action: @escaping (URL) -> Void) { + incomingURLHandler = action + } + public func show(widget: Widget) {} public func tag(widget: Widget, as tag: String) { diff --git a/Sources/SwiftCrossUI/Views/ScrollView.swift b/Sources/SwiftCrossUI/Views/ScrollView.swift index b526c23350b..caa14b14e91 100644 --- a/Sources/SwiftCrossUI/Views/ScrollView.swift +++ b/Sources/SwiftCrossUI/Views/ScrollView.swift @@ -72,6 +72,7 @@ public struct ScrollView: TypeSafeView, View { ) if willEarlyExit { + print("early exit: childResult.size=\(childResult.size)") return childResult } @@ -86,6 +87,7 @@ public struct ScrollView: TypeSafeView, View { } else { hasHorizontalScrollBar = false } + children.hasHorizontalScrollBar = hasHorizontalScrollBar let hasVerticalScrollBar: Bool if axes.contains(.vertical), let proposedHeight = proposedSize.height { @@ -93,6 +95,7 @@ public struct ScrollView: TypeSafeView, View { } else { hasVerticalScrollBar = false } + children.hasVerticalScrollBar = hasVerticalScrollBar let scrollBarWidth = Double(backend.scrollBarWidth) let verticalScrollBarWidth = hasVerticalScrollBar ? scrollBarWidth : 0 diff --git a/Tests/SwiftCrossUITests/SwiftCrossUITests.swift b/Tests/SwiftCrossUITests/SwiftCrossUITests.swift index 818759e27c1..7b02705f83d 100644 --- a/Tests/SwiftCrossUITests/SwiftCrossUITests.swift +++ b/Tests/SwiftCrossUITests/SwiftCrossUITests.swift @@ -1,6 +1,7 @@ import Testing import Foundation +import DummyBackend @testable import SwiftCrossUI #if canImport(AppKitBackend) @@ -70,6 +71,47 @@ struct SwiftCrossUITests { return original == decoded } + @Test("Ensure that ScrollView satisfies basic invariants") + @MainActor + func testBasicScrollView() async throws { + let backend = DummyBackend() + let window = backend.createWindow(withDefaultSize: nil) + let environment = EnvironmentValues(backend: backend) + .with(\.window, window) + + let blueRectangleHeight = Double(100) + let view = ScrollView { + Color.blue.frame(height: Int(blueRectangleHeight)) + } + + let viewGraph = ViewGraph( + for: view, + backend: backend, + environment: environment + ) + let proposedSize = ViewSize(80, 80) + let result = viewGraph.computeLayout( + proposedSize: ProposedViewSize(proposedSize), + environment: environment + ) + viewGraph.commit() + + #expect(result.size == ViewSize(80, 80)) + + let rootWidget: DummyBackend.Widget = viewGraph.rootNode.widget.into() + let scrollView = try #require(rootWidget.firstWidget(ofType: DummyBackend.ScrollContainer.self)) + + #expect(scrollView.hasVerticalScrollBar) + #expect(!scrollView.hasHorizontalScrollBar) + + #expect(scrollView.size == proposedSize.vector) + let expectedSize = ViewSize( + proposedSize.width - Double(backend.scrollBarWidth), + blueRectangleHeight + ) + #expect(scrollView.child.size == expectedSize.vector) + } + #if canImport(AppKitBackend) @Test("Ensure that a basic view has the expected dimensions under AppKitBackend") @MainActor From 920b144569cb4c97cf1e136cbe8e6c13a3a30f5e Mon Sep 17 00:00:00 2001 From: stackotter Date: Tue, 30 Dec 2025 17:24:02 +1000 Subject: [PATCH 15/15] Update benchmark viz to set frame of app to match actual benchmark --- .../LayoutPerformanceBenchmark.swift | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Benchmarks/LayoutPerformanceBenchmark/LayoutPerformanceBenchmark.swift b/Benchmarks/LayoutPerformanceBenchmark/LayoutPerformanceBenchmark.swift index 720b8ec8e73..9ccc3277753 100644 --- a/Benchmarks/LayoutPerformanceBenchmark/LayoutPerformanceBenchmark.swift +++ b/Benchmarks/LayoutPerformanceBenchmark/LayoutPerformanceBenchmark.swift @@ -10,10 +10,13 @@ protocol TestCaseView: View { #if BENCHMARK_VIZ import DefaultBackend + @MainActor + var visualizationSize: (width: Int?, height: Int?) = (nil, nil) + struct VizApp: App { var body: some Scene { - WindowGroup("Benchmark visualisation") { - V() + WindowGroup("Benchmark visualization") { + V().frame(width: visualizationSize.width, height: visualizationSize.height) } } } @@ -40,7 +43,9 @@ struct Benchmarks { } #if BENCHMARK_VIZ - var benchmarkVisualizations: [(name: String, main: () -> Never)] = [] + var benchmarkVisualizations: [ + (name: String, size: ProposedViewSize, main: () -> Never) + ] = [] #endif @MainActor @@ -48,6 +53,7 @@ struct Benchmarks { #if BENCHMARK_VIZ benchmarkVisualizations.append(( label, + size, { VizApp.main() exit(0) @@ -82,6 +88,11 @@ struct Benchmarks { exit(1) } + visualizationSize = ( + benchmark.size.width.map(Int.init), + benchmark.size.height.map(Int.init) + ) + benchmark.main() #else await Benchmark.main()