diff --git a/Packages/ShapsBenkau_SwiftUIBackports/.gitignore b/Packages/ShapsBenkau_SwiftUIBackports/.gitignore deleted file mode 100755 index 3b298120..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -.DS_Store -/.build -/Packages -/*.xcodeproj -xcuserdata/ -DerivedData/ -.swiftpm/config/registries.json -.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata -.netrc diff --git a/Packages/ShapsBenkau_SwiftUIBackports/.spi.yml b/Packages/ShapsBenkau_SwiftUIBackports/.spi.yml deleted file mode 100755 index ac7ecfd8..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/.spi.yml +++ /dev/null @@ -1,5 +0,0 @@ -version: 1.8.2 -builder: - configs: - - platform: ios - documentation_targets: [SwiftUIBackports] \ No newline at end of file diff --git a/Packages/ShapsBenkau_SwiftUIBackports/LICENSE.md b/Packages/ShapsBenkau_SwiftUIBackports/LICENSE.md deleted file mode 100755 index 129722a6..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/LICENSE.md +++ /dev/null @@ -1,9 +0,0 @@ -MIT License - -Copyright (c) 2021 Shaps Benkau - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Package.swift b/Packages/ShapsBenkau_SwiftUIBackports/Package.swift deleted file mode 100755 index 823340bc..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Package.swift +++ /dev/null @@ -1,22 +0,0 @@ -// swift-tools-version:5.3 -import PackageDescription - -let package = Package( - name: "SwiftUIBackports", - platforms: [ - .iOS(.v13), - .tvOS(.v13), - .watchOS(.v6), - .macOS(.v10_11), - ], - products: [ - .library( - name: "SwiftUIBackports", - targets: ["SwiftUIBackports"] - ), - ], - targets: [ - .target(name: "SwiftUIBackports"), - ], - swiftLanguageVersions: [.v5] -) diff --git a/Packages/ShapsBenkau_SwiftUIBackports/README.md b/Packages/ShapsBenkau_SwiftUIBackports/README.md deleted file mode 100755 index 0b3eb879..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/README.md +++ /dev/null @@ -1,131 +0,0 @@ -## NOTICE - -- This package copy is clang-formatted according to vChewing's clang-format style, with removal of certain dependencies / features not-required by vChewing. -- **If you want to use this package**, you might want to consult its original repository: https://github.com/shaps80/SwiftUIBackports - -![watchOS](https://img.shields.io/badge/watchOS-DE1F51) -![macOS](https://img.shields.io/badge/macOS-EE751F) -![tvOS](https://img.shields.io/badge/tvOS-00B9BB) -![ios](https://img.shields.io/badge/iOS-0C62C7) -[![swift](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fshaps80%2FSwiftUIBackports%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/shaps80/SwiftUIBackports) - -# SwiftUI Backports - -Introducing a collection of SwiftUI backports to make your iOS development easier. - -Many backports support iOS 13+ but where UIKIt features were introduced in later versions, the same will be applicable to these backports, to keep parity with UIKit. - -In some cases, I've also included additional APIs that bring more features to your SwiftUI development. - -> Note, **all** backports will be API-matching to Apple's offical APIs, any additional features will be provided separately. - -All backports are fully documented, in most cases using Apple's own documentation for consistency. Please refer to the header docs or Apple's original documentation for more details. - -There is also a [Demo project](https://github.com/shaps80/SwiftUIBackportsDemo) available where you can see full demonstrations of all backports and additional features, including reference code to help you get started. - -> Lastly, I hope this repo also serves as a great resource for _how_ you can backport effectively with minimal hacks πŸ‘ - -## Sponsor - -Building useful libraries like these, takes time away from my family. I build these tools in my spare time because I feel its important to give back to the community. Please consider [Sponsoring](https://github.com/sponsors/shaps80) me as it helps keep me working on useful libraries like these 😬 - -You can also give me a follow and a 'thanks' anytime. - -[![Twitter](https://img.shields.io/badge/Twitter-@shaps-4AC71B)](http://twitter.com/shaps) - -## Usage - -The library adopts a backport design by [Dave DeLong](https://davedelong.com/blog/2021/10/09/simplifying-backwards-compatibility-in-swift/) that makes use of a single type to improve discoverability and maintainability when the time comes to remove your backport implementations, in favour of official APIs. - -Backports of pure types, can easily be discovered under the `Backport` namespace. Similarly, modifiers are discoverable under the `.backport` namespace. - -> Unfortuantely `Environment` backports cannot be access this way, in those cases the Apple API values will be prefixed with `backport` to simplify discovery. - -Types: - -```swift -@Backport.AppStorage("filter-enabled") -private var filterEnabled: Bool = false -``` - -Modifier: - -```swift -Button("Show Prompt") { - showPrompt = true -} -.sheet(isPresented: $showPrompt) { - Prompt() - .backport.presentationDetents([.medium, .large]) -} -``` - -Environment: - -```swift -@Environment(\.backportRefresh) private var refreshAction -``` - -## Backports - -**SwiftUI** - -- `asyncImage` -- `AppStorage` -- `background` – ViewBuilder API -- `DismissAction` -- `DynamicTypeSize` -– `Label` -– `LabeledContent` -- `NavigationDestination` – uses a standard NavigationView -- `navigationTitle` – newer API -- `overlay` – ViewBuilder API -- `onChange` -- `openURL` -- `ProgressView` -- `presentationDetents` -- `presentationDragIndicator` -- `quicklookPreview` -- `requestReview` -- `Refreshable` – includes pull-to-refreshΒ  -- `ScaledMetric` -- `StateObject` -- `scrollDisabled` -- `scrollDismissesKeyboard` -- `scrollIndicators` -- `Section(_ header:)` -- `task` – async/await modifier - -**UIKit** - -- `UIHostingConfiguration` – simplifies embedding SwiftUI in `UICollectionViewCell` and `UITableViewCell` - -## Extras - -**Modal Presentations** - -Adding this to your presented view, you can use the provided closure to present an `ActionSheet` to a user when they attempt to dismiss interactively. You can also use this to disable interactive dismissals entirely. - -```swift -presentation(isModal: true) { /* attempt */ } -``` - -**FittingGeometryReader** - -A custom `GeometryReader` implementation that correctly auto-sizes itself to its content. This is useful in many cases where you need a `GeometryReader` but don't want it to implicitly take up its parent View's bounds. - -**FittingScrollView** - -A custom `ScrollView` that respects `Spacer`'s when the content is not scrollable. This is useful when you need to place a view at the edges of your scrollview while its content is small enough to not require scrolling. Another great use case is vertically centered content that becomes `top` aligned once the content requires scrolling. - -**PageView** - -A pure SwiftUI implementation of a page-based view, using the native `TabView` and my custom `FittingGeometryReader` to size itself correctly. Since this uses a `TabView` under-the-hood, this allows you to use the same APIs and features from that view. - -## Installation - -You can install manually (by copying the files in the `Sources` directory) or using Swift Package Manager (**preferred**) - -To install using Swift Package Manager, add this to the `dependencies` section of your `Package.swift` file: - -`.package(url: "https://github.com/shaps80/SwiftUIBackports.git", .upToNextMinor(from: "1.0.0"))` diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Backport.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Backport.swift deleted file mode 100755 index 15d3115e..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Backport.swift +++ /dev/null @@ -1,62 +0,0 @@ -import ObjectiveC -import SwiftUI - -@available(macOS 10.15, *) -/// Provides a convenient method for backporting API, -/// including types, functions, properties, property wrappers and more. -/// -/// To backport a SwiftUI Label for example, you could apply the -/// following extension: -/// -/// extension Backport where Content == Any { -/// public struct Label { } -/// } -/// -/// Now if we want to provide further extensions to our backport type, -/// we need to ensure we retain the `Content == Any` generic requirement: -/// -/// extension Backport.Label where Content == Any, Title == Text, Icon == Image { -/// public init(_ title: S, systemName: String) { } -/// } -/// -/// In addition to types, we can also provide backports for properties -/// and methods: -/// -/// extension Backport.Label where Content: View { -/// func onChange(of value: Value, perform action: (Value) -> Void) -> some View { -/// // `content` provides access to the extended type -/// content.modifier(OnChangeModifier(value, action)) -/// } -/// } -/// -public struct Backport { - /// The underlying content this backport represents. - public let content: Wrapped - - @available(macOS 10.15, *) - /// Initializes a new Backport for the specified content. - /// - Parameter content: The content (type) that's being backported - public init(_ content: Wrapped) { - self.content = content - } -} - -@available(macOS 10.15, *) -public extension View { - /// Wraps a SwiftUI `View` that can be extended to provide backport functionality. - var backport: Backport { .init(self) } -} - -@available(macOS 10.15, *) -public extension NSObjectProtocol { - /// Wraps an `NSObject` that can be extended to provide backport functionality. - var backport: Backport { .init(self) } -} - -@available(macOS 10.15, *) -public extension AnyTransition { - /// Wraps an `AnyTransition` that can be extended to provide backport functionality. - static var backport: Backport { - Backport(.identity) - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Extras/FittingGeometryReader.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Extras/FittingGeometryReader.swift deleted file mode 100755 index c2fa4ec5..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Extras/FittingGeometryReader.swift +++ /dev/null @@ -1,52 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -/// A geometry reader that automatically sizes its height to 'fit' its content. -public struct FittingGeometryReader: View where Content: View { - @State private var height: CGFloat = 10 // must be non-zero - private var content: (GeometryProxy) -> Content - - @available(macOS 10.15, *) - public init(@ViewBuilder content: @escaping (GeometryProxy) -> Content) { - self.content = content - } - - @available(macOS 10.15, *) - public var body: some View { - GeometryReader { geo in - content(geo) - .fixedSize(horizontal: false, vertical: true) - .modifier(SizeModifier()) - .onPreferenceChange(SizePreferenceKey.self) { - height = $0.height - } - } - .frame(height: height) - } -} - -@available(macOS 10.15, *) -private struct SizePreferenceKey: PreferenceKey { - static var defaultValue: CGSize = .zero - static func reduce(value: inout CGSize, nextValue: () -> CGSize) { - value = nextValue() - } -} - -@available(macOS 10.15, *) -private struct SizeModifier: ViewModifier { - func body(content: Content) -> some View { - content.overlay( - GeometryReader { geo in - Color.clear.preference( - key: SizePreferenceKey.self, - value: geo.size - ) - } - ) - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Extras/FittingScrollView.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Extras/FittingScrollView.swift deleted file mode 100755 index 1e357059..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Extras/FittingScrollView.swift +++ /dev/null @@ -1,37 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -/// A scrollview that behaves more similarly to a `VStack` when its content size is small enough. -public struct FittingScrollView: View { - private let content: Content - private let showsIndicators: Bool - - @available(macOS 10.15, *) - /// A new scrollview - /// - Parameters: - /// - showsIndicators: If true, the scroll view will show indicators when necessary - /// - content: The content for this scroll view - public init(showsIndicators: Bool = true, @ViewBuilder content: () -> Content) { - self.showsIndicators = showsIndicators - self.content = content() - } - - @available(macOS 10.15, *) - public var body: some View { - GeometryReader { geo in - SwiftUI.ScrollView(showsIndicators: showsIndicators) { - VStack(spacing: 10) { - content - } - .frame( - maxWidth: geo.size.width, - minHeight: geo.size.height - ) - } - } - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Extras/Presenatation+Deprecations.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Extras/Presenatation+Deprecations.swift deleted file mode 100755 index 847cb2d3..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Extras/Presenatation+Deprecations.swift +++ /dev/null @@ -1,47 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -@available(tvOS, deprecated: 13) -@available(macOS, deprecated: 10.15) -@available(watchOS, deprecated: 6) -extension View { - /// Sets whether this presentation should act as a `modal`, preventing interactive dismissals - /// - Parameter isModal: If `true` the user will not be able to interactively dismiss - @ViewBuilder - @available(iOS, deprecated: 13, renamed: "backport.interactiveDismissDisabled(_:)") - public func presentation(isModal: Bool) -> some View { - #if os(iOS) - if #available(iOS 15, *) { - backport.interactiveDismissDisabled(isModal) - } else { - self - } - #else - self - #endif - } - - @available(macOS 10.15, *) - /// Provides fine-grained control over the dismissal. - /// - Parameters: - /// - isModal: If `true`, the user will not be able to interactively dismiss - /// - onAttempt: A closure that will be called when an interactive dismiss attempt occurs. - /// You can use this as an opportunity to present an ActionSheet to prompt the user. - @ViewBuilder - @available(iOS, deprecated: 13, renamed: "backport.interactiveDismissDisabled(_:onAttempt:)") - public func presentation(isModal: Bool = true, _ onAttempt: @escaping () -> Void) -> some View { - #if os(iOS) - if #available(iOS 15, *) { - backport.interactiveDismissDisabled(isModal, onAttempt: onAttempt) - } else { - self - } - #else - self - #endif - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Internal/Environment+String.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Internal/Environment+String.swift deleted file mode 100755 index 1635fc45..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Internal/Environment+String.swift +++ /dev/null @@ -1,152 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -private extension EnvironmentValues { - func containsValue(forKey key: String) -> Bool { - value(forKey: key) != nil - } - - func value(forKey key: String, from mirror: Mirror, as _: T.Type) -> T? { - // Found a match - if let value = mirror.descendant("value", "some") { - if let typedValue = value as? T { - print("Found value") - return typedValue - } else { - print( - "Value for key '\(key)' in the environment is of type '\(type(of: value))', but we expected '\(String(describing: T.self))'." - ) - } - } else { - print( - "Found key '\(key)' in the environment, but it doesn't have the expected structure. The type hierarchy may have changed in your SwiftUI version." - ) - } - - return nil - } - - /// Extracts a value from the environment by the name of its associated EnvironmentKey. - /// Can be used to grab private environment values such as foregroundColor ("ForegroundColorKey"). - func value(forKey key: String, as _: T.Type) -> T? { - if let mirror = value(forKey: key) as? Mirror { - return value(forKey: key, from: mirror, as: T.self) - } else if let value = value(forKey: key) as? T { - return value - } else { - return nil - } - } - - func value(forKey key: String) -> Any? { - func keyFromTypeName(typeName: String) -> String? { - let expectedPrefix = "TypedElement>` - /// TypedElement.value contains the value of the key. - func extract(startingAt environmentNode: Any) -> Any? { - let mirror = Mirror(reflecting: environmentNode) - - let typeName = String(describing: type(of: environmentNode)) - if let nodeKey = keyFromTypeName(typeName: typeName) { - if key == nodeKey { - return mirror - } - } - - // Environment values are stored in a doubly linked list. The "before" and "after" keys point - // to the next environment member. - if let linkedListMirror = mirror.superclassMirror, - let nextNode = linkedListMirror.descendant("after", "some") - { - return extract(startingAt: nextNode) - } - - return nil - } - - let mirror = Mirror(reflecting: self) - - if let firstEnvironmentValue = mirror.descendant("_plist", "elements", "some") { - if let node = extract(startingAt: firstEnvironmentValue) { - return node - } else { - return nil - } - } else { - return nil - } - } -} - -@available(macOS 10.15, *) -@propertyWrapper -internal struct StringlyTypedEnvironment { - final class Store: ObservableObject { - var value: StoredValue? - } - - @Environment(\.self) private var env - @ObservedObject private var store = Store() - - var key: String - - init(key: String) { - self.key = key - } - - private(set) var wrappedValue: Value? { - get { store.value } - nonmutating set { store.value = newValue } - } -} - -@available(macOS 10.15, *) -extension StringlyTypedEnvironment: DynamicProperty { - func update() { - wrappedValue = env.value(forKey: key, as: Value.self) - } -} - -@available(macOS 10.15, *) -@propertyWrapper -internal struct EnvironmentContains: DynamicProperty { - final class Store: ObservableObject { - var contains: Bool = false - } - - @Environment(\.self) private var env - - var key: String - @ObservedObject private var store = Store() - - init(key: String) { - self.key = key - } - - var wrappedValue: Bool { - get { store.contains } - nonmutating set { store.contains = newValue } - } - - func update() { - wrappedValue = env.containsValue(forKey: key) - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Internal/Environment.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Internal/Environment.swift deleted file mode 100755 index ccd1ea17..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Internal/Environment.swift +++ /dev/null @@ -1,44 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -/* - The following code is for debugging purposes only! - */ - -#if DEBUG - @available(macOS 10.15, *) - extension EnvironmentValues: CustomDebugStringConvertible { - public var debugDescription: String { - "\(self)" - .trimmingCharacters(in: .init(["[", "]"])) - .replacingOccurrences(of: "EnvironmentPropertyKey", with: "") - .replacingOccurrences(of: ", ", with: "\n") - } - } - - @available(macOS 10.15, *) - struct EnvironmentOutputModifier: ViewModifier { - @Environment(\.self) private var environment - - func body(content: Content) -> some View { - content - .onAppear { - print(environment.debugDescription) - } - } - } - - @available(macOS 10.15, *) - extension View { - func printEnvironment() -> some View { - modifier(EnvironmentOutputModifier()) - } - } -#endif diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Internal/Inspect+UIKit.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Internal/Inspect+UIKit.swift deleted file mode 100755 index 5bbd97d1..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Internal/Inspect+UIKit.swift +++ /dev/null @@ -1,175 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -#if os(iOS) - internal typealias PlatformView = UIView - internal typealias PlatformViewController = UIViewController -#elseif os(macOS) - internal typealias PlatformView = NSView - internal typealias PlatformViewController = NSViewController -#endif - -#if os(iOS) || os(macOS) - extension PlatformView { - func ancestor(ofType _: ViewType.Type) -> ViewType? { - var view = superview - - while let s = view { - if let typed = s as? ViewType { - return typed - } - view = s.superview - } - - return nil - } - - var host: PlatformView? { - var view = superview - - while let s = view { - if NSStringFromClass(type(of: s)).contains("ViewHost") { - return s - } - view = s.superview - } - - return nil - } - - func sibling(ofType type: ViewType.Type) -> ViewType? { - guard let superview = superview, let index = superview.subviews.firstIndex(of: self) else { return nil } - - var views = superview.subviews - views.remove(at: index) - - for subview in views.reversed() { - if let typed = subview.child(ofType: type) { - return typed - } - } - - return nil - } - - func child(ofType type: ViewType.Type) -> ViewType? { - for subview in subviews { - if let typed = subview as? ViewType { - return typed - } else if let typed = subview.child(ofType: type) { - return typed - } - } - - return nil - } - } - - internal struct Inspector { - var hostView: PlatformView - var sourceView: PlatformView - var sourceController: PlatformViewController - - func ancestor(ofType _: ViewType.Type) -> ViewType? { - hostView.ancestor(ofType: ViewType.self) - } - - func sibling(ofType _: ViewType.Type) -> ViewType? { - hostView.sibling(ofType: ViewType.self) - } - } - - @available(macOS 10.15, *) - extension View { - private func inject(_ content: Wrapped) -> some View where Wrapped: View { - overlay(content.frame(width: 0, height: 0)) - } - - func inspect( - selector: @escaping (_ inspector: Inspector) -> ViewType?, customize: @escaping (ViewType) -> Void - ) -> some View { - inject(InspectionView(selector: selector, customize: customize)) - } - } - - @available(macOS 10.15, *) - private struct InspectionView: View { - let selector: (Inspector) -> ViewType? - let customize: (ViewType) -> Void - - var body: some View { - Representable(parent: self) - } - } - - private class SourceView: PlatformView { - required init() { - super.init(frame: .zero) - isHidden = true - #if os(iOS) - isUserInteractionEnabled = false - #endif - } - - @available(*, unavailable) - required init?(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - } -#endif - -#if os(iOS) - extension InspectionView { - struct Representable: UIViewRepresentable { - let parent: InspectionView - - func makeUIView(context _: Context) -> UIView { .init() } - func updateUIView(_ view: UIView, context _: Context) { - DispatchQueue.main.async { - guard let host = view.host else { return } - - let inspector = Inspector( - hostView: host, - sourceView: view, - sourceController: view.parentController - ?? view.window?.rootViewController - ?? UIViewController() - ) - - guard let targetView = parent.selector(inspector) else { return } - parent.customize(targetView) - } - } - } - } - -#elseif os(macOS) - @available(macOS 10.15, *) - extension InspectionView { - struct Representable: NSViewRepresentable { - let parent: InspectionView - - func makeNSView(context _: Context) -> NSView { - .init(frame: .zero) - } - - func updateNSView(_ view: NSView, context _: Context) { - DispatchQueue.main.async { - guard let host = view.host else { return } - - let inspector = Inspector( - hostView: host, - sourceView: view, - sourceController: view.parentController ?? NSViewController(nibName: nil, bundle: nil) - ) - - guard let targetView = parent.selector(inspector) else { return } - parent.customize(targetView) - } - } - } - } -#endif diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Internal/OwningController.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Internal/OwningController.swift deleted file mode 100755 index 18d52078..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Internal/OwningController.swift +++ /dev/null @@ -1,40 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -#if os(iOS) - import UIKit - - public extension UIView { - var parentController: UIViewController? { - var responder: UIResponder? = self - - while !(responder is UIViewController), superview != nil { - if let next = responder?.next { - responder = next - } - } - - return responder as? UIViewController - } - } -#endif - -#if os(macOS) - import AppKit - - @available(macOS 10.15, *) - public extension NSView { - var parentController: NSViewController? { - var responder: NSResponder? = self - - while !(responder is NSViewController), superview != nil { - if let next = responder?.nextResponder { - responder = next - } - } - - return responder as? NSViewController - } - } -#endif diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Internal/SafeArea.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Internal/SafeArea.swift deleted file mode 100755 index 32e88e5f..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Internal/SafeArea.swift +++ /dev/null @@ -1,47 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -#if os(iOS) || os(tvOS) - /* - Since UICollectionView is not designed to support SwiftUI out of the box, - we need to use a little trick to get the SwiftUI View's to ignore safeArea - insets, otherwise our cell's will not always layout correctly. - */ - extension UIHostingController { - convenience init(rootView: Content, ignoreSafeArea: Bool) { - self.init(rootView: rootView) - - if ignoreSafeArea { - disableSafeArea() - } - } - - func disableSafeArea() { - guard let viewClass = object_getClass(view) else { return } - - let viewSubclassName = String(cString: class_getName(viewClass)).appending("_IgnoreSafeArea") - if let viewSubclass = NSClassFromString(viewSubclassName) { - object_setClass(view, viewSubclass) - } else { - guard let viewClassNameUtf8 = (viewSubclassName as NSString).utf8String else { return } - guard let viewSubclass = objc_allocateClassPair(viewClass, viewClassNameUtf8, 0) else { return } - - if let method = class_getInstanceMethod(UIView.self, #selector(getter: UIView.safeAreaInsets)) { - let safeAreaInsets: @convention(block) (AnyObject) -> UIEdgeInsets = { _ in - .zero - } - class_addMethod( - viewSubclass, #selector(getter: UIView.safeAreaInsets), imp_implementationWithBlock(safeAreaInsets), - method_getTypeEncoding(method) - ) - } - - objc_registerClassPair(viewSubclass) - object_setClass(view, viewSubclass) - } - } - } -#endif diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Internal/UIScene.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Internal/UIScene.swift deleted file mode 100755 index 986471c7..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Internal/UIScene.swift +++ /dev/null @@ -1,15 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -#if os(iOS) - import UIKit - - extension UIApplication { - static var activeScene: UIWindowScene? { - shared.connectedScenes - .first { $0.activationState == .foregroundActive } - as? UIWindowScene - } - } -#endif diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/AppStorage/AppStorage.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/AppStorage/AppStorage.swift deleted file mode 100755 index 867d83e3..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/AppStorage/AppStorage.swift +++ /dev/null @@ -1,408 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(iOS, deprecated: 14) -@available(macOS, deprecated: 11) -@available(tvOS, deprecated: 14) -@available(watchOS, deprecated: 7) -@available(macOS 10.15, *) -extension Backport where Wrapped == Any { - /// A property wrapper type that reflects a value from `Store` and - /// invalidates a view on a change in value in that store. - @propertyWrapper - public struct AppStorage: DynamicProperty { - @ObservedObject - private var _value: RefStorage - private let commitHandler: (Value) -> Void - - public var wrappedValue: Value { - get { _value.value } - nonmutating set { - commitHandler(newValue) - _value.value = newValue - } - } - - public var projectedValue: Binding { - Binding( - get: { wrappedValue }, - set: { wrappedValue = $0 } - ) - } - - private init( - value: Value, store: UserDefaults, key: String, get: @escaping (Any?) -> Value?, set: @escaping (Value) -> Void - ) { - _value = RefStorage(value: value, store: store, key: key, transform: get) - commitHandler = set - } - } -} - -@available(macOS 10.15, *) -public extension Backport.AppStorage { - /// Creates a property that can read and write to a boolean user default. - /// - /// - Parameters: - /// - wrappedValue: The default value if a boolean value is not specified - /// for the given key. - /// - key: The key to read and write the value to in the store - /// store. - /// - store: The store to read and write to. A value - /// of `nil` will use the user default store from the environment. - init(wrappedValue: Value, _ key: String, store: UserDefaults = .standard) where Value == Bool { - let value = store.value(forKey: key) as? Value ?? wrappedValue - self.init( - value: value, store: store, key: key, - get: { $0 as? Value }, - set: { store.set($0, forKey: key) } - ) - } - - /// Creates a property that can read and write to an integer user default. - /// - /// - Parameters: - /// - wrappedValue: The default value if an integer value is not specified - /// for the given key. - /// - key: The key to read and write the value to in the store - /// store. - /// - store: The store to read and write to. A value - /// of `nil` will use the user default store from the environment. - init(wrappedValue: Value, _ key: String, store: UserDefaults = .standard) where Value == Int { - let value = store.value(forKey: key) as? Value ?? wrappedValue - self.init( - value: value, store: store, key: key, - get: { $0 as? Value }, - set: { store.set($0, forKey: key) } - ) - } - - /// Creates a property that can read and write to a double user default. - /// - /// - Parameters: - /// - wrappedValue: The default value if a double value is not specified - /// for the given key. - /// - key: The key to read and write the value to in the store - /// store. - /// - store: The store to read and write to. A value - /// of `nil` will use the user default store from the environment. - init(wrappedValue: Value, _ key: String, store: UserDefaults = .standard) where Value == Double { - let value = store.value(forKey: key) as? Value ?? wrappedValue - self.init( - value: value, store: store, key: key, - get: { $0 as? Value }, - set: { store.set($0, forKey: key) } - ) - } - - /// Creates a property that can read and write to a string user default. - /// - /// - Parameters: - /// - wrappedValue: The default value if a string value is not specified - /// for the given key. - /// - key: The key to read and write the value to in the store - /// store. - /// - store: The store to read and write to. A value - /// of `nil` will use the user default store from the environment. - init(wrappedValue: Value, _ key: String, store: UserDefaults = .standard) where Value == String { - let value = store.value(forKey: key) as? Value ?? wrappedValue - self.init( - value: value, store: store, key: key, - get: { $0 as? Value }, - set: { store.set($0, forKey: key) } - ) - } - - /// Creates a property that can read and write to a [string] user default. - /// - /// - Parameters: - /// - wrappedValue: The default value if a string value is not specified - /// for the given key. - /// - key: The key to read and write the value to in the store - /// store. - /// - store: The store to read and write to. A value - /// of `nil` will use the user default store from the environment. - init(wrappedValue: Value, _ key: String, store: UserDefaults = .standard) where Value == [String] { - let value = store.value(forKey: key) as? Value ?? wrappedValue - self.init( - value: value, store: store, key: key, - get: { $0 as? Value }, - set: { store.set($0, forKey: key) } - ) - } - - /// Creates a property that can read and write to a [String: Bool] user default. - /// - /// - Parameters: - /// - wrappedValue: The default value if a string value is not specified - /// for the given key. - /// - key: The key to read and write the value to in the store - /// store. - /// - store: The store to read and write to. A value - /// of `nil` will use the user default store from the environment. - init(wrappedValue: Value, _ key: String, store: UserDefaults = .standard) where Value == [String: Bool] { - let value = store.value(forKey: key) as? Value ?? wrappedValue - self.init( - value: value, store: store, key: key, - get: { $0 as? Value }, - set: { store.set($0, forKey: key) } - ) - } - - /// Creates a property that can read and write to a url user default. - /// - /// - Parameters: - /// - wrappedValue: The default value if a url value is not specified for - /// the given key. - /// - key: The key to read and write the value to in the store - /// store. - /// - store: The store to read and write to. A value - /// of `nil` will use the user default store from the environment. - init(wrappedValue: Value, _ key: String, store: UserDefaults = .standard) where Value == URL { - let value = store.url(forKey: key) ?? wrappedValue - self.init( - value: value, store: store, key: key, - get: { ($0 as? String).flatMap(URL.init) }, - set: { store.set($0.absoluteString, forKey: key) } - ) - } - - /// Creates a property that can read and write to a user default as data. - /// - /// Avoid storing large data blobs in store, such as image data, - /// as it can negatively affect performance of your app. - /// - /// - Parameters: - /// - wrappedValue: The default value if a data value is not specified for - /// the given key. - /// - key: The key to read and write the value to in the store - /// store. - /// - store: The store to read and write to. A value - /// of `nil` will use the user default store from the environment. - init(wrappedValue: Value, _ key: String, store: UserDefaults = .standard) where Value == Data { - let value = store.value(forKey: key) as? Data ?? wrappedValue - self.init( - value: value, store: store, key: key, - get: { $0 as? Value }, - set: { store.set($0, forKey: key) } - ) - } -} - -@available(macOS 10.15, *) -public extension Backport.AppStorage where Wrapped == Any, Value: ExpressibleByNilLiteral { - /// Creates a property that can read and write an Optional boolean user - /// default. - /// - /// Defaults to nil if there is no restored value. - /// - /// - Parameters: - /// - key: The key to read and write the value to in the store - /// store. - /// - store: The store to read and write to. A value - /// of `nil` will use the user default store from the environment. - init(_ key: String, store: UserDefaults = .standard) where Value == Bool? { - let value = store.value(forKey: key) as? Value ?? .none - self.init( - value: value, store: store, key: key, - get: { $0 as? Value }, - set: { store.set($0, forKey: key) } - ) - } - - /// Creates a property that can read and write an Optional integer user - /// default. - /// - /// Defaults to nil if there is no restored value. - /// - /// - Parameters: - /// - key: The key to read and write the value to in the store - /// store. - /// - store: The store to read and write to. A value - /// of `nil` will use the user default store from the environment. - init(_ key: String, store: UserDefaults = .standard) where Value == Int? { - let value = store.value(forKey: key) as? Value ?? .none - self.init( - value: value, store: store, key: key, - get: { $0 as? Value }, - set: { store.set($0, forKey: key) } - ) - } - - /// Creates a property that can read and write an Optional double user - /// default. - /// - /// Defaults to nil if there is no restored value. - /// - /// - Parameters: - /// - key: The key to read and write the value to in the store - /// store. - /// - store: The store to read and write to. A value - /// of `nil` will use the user default store from the environment. - init(_ key: String, store: UserDefaults = .standard) where Value == Double? { - let value = store.value(forKey: key) as? Value ?? .none - self.init( - value: value, store: store, key: key, - get: { $0 as? Value }, - set: { store.set($0, forKey: key) } - ) - } - - /// Creates a property that can read and write an Optional string user - /// default. - /// - /// Defaults to nil if there is no restored value. - /// - /// - Parameters: - /// - key: The key to read and write the value to in the store - /// store. - /// - store: The store to read and write to. A value - /// of `nil` will use the user default store from the environment. - init(_ key: String, store: UserDefaults = .standard) where Value == String? { - let value = store.value(forKey: key) as? Value ?? .none - self.init( - value: value, store: store, key: key, - get: { $0 as? Value }, - set: { store.set($0, forKey: key) } - ) - } - - /// Creates a property that can read and write an Optional URL user - /// default. - /// - /// Defaults to nil if there is no restored value. - /// - /// - Parameters: - /// - key: The key to read and write the value to in the store - /// store. - /// - store: The store to read and write to. A value - /// of `nil` will use the user default store from the environment. - init(_ key: String, store: UserDefaults = .standard) where Value == URL? { - let value = store.url(forKey: key) ?? .none - self.init( - value: value, store: store, key: key, - get: { ($0 as? String).flatMap(URL.init) }, - set: { store.set($0?.absoluteString, forKey: key) } - ) - } - - /// Creates a property that can read and write an Optional data user - /// default. - /// - /// Defaults to nil if there is no restored value. - /// - /// - Parameters: - /// - key: The key to read and write the value to in the store - /// store. - /// - store: The store to read and write to. A value - /// of `nil` will use the user default store from the environment. - init(_ key: String, store: UserDefaults = .standard) where Value == Data? { - let value = store.value(forKey: key) as? Value ?? .none - self.init( - value: value, store: store, key: key, - get: { $0 as? Value }, - set: { store.set($0, forKey: key) } - ) - } -} - -@available(macOS 10.15, *) -public extension Backport.AppStorage where Wrapped == Any, Value: RawRepresentable { - /// Creates a property that can read and write to a string user default, - /// transforming that to `RawRepresentable` data type. - /// - /// A common usage is with enumerations: - /// - /// enum MyEnum: String { - /// case a - /// case b - /// case c - /// } - /// - /// @AppStorage("MyEnumValue") private var value = MyEnum.a - /// - /// - Parameters: - /// - wrappedValue: The default value if a string value - /// is not specified for the given key. - /// - key: The key to read and write the value to in the store - /// store. - /// - store: The store to read and write to. A value - /// of `nil` will use the user default store from the environment. - init(wrappedValue: Value, _ key: String, store: UserDefaults = .standard) where Value.RawValue == String { - let rawValue = store.value(forKey: key) as? Value.RawValue - let value = rawValue.flatMap(Value.init) ?? wrappedValue - self.init( - value: value, store: store, key: key, - get: { $0 as? Value }, - set: { store.setValue($0.rawValue, forKey: key) } - ) - } - - /// Creates a property that can read and write to an integer user default, - /// transforming that to `RawRepresentable` data type. - /// - /// A common usage is with enumerations: - /// - /// enum MyEnum: Int { - /// case a - /// case b - /// case c - /// } - /// - /// @AppStorage("MyEnumValue") private var value = MyEnum.a - /// - /// - Parameters: - /// - wrappedValue: The default value if an integer value - /// is not specified for the given key. - /// - key: The key to read and write the value to in the store - /// store. - /// - store: The store to read and write to. A value - /// of `nil` will use the user default store from the environment. - init(wrappedValue: Value, _ key: String, store: UserDefaults = .standard) where Value.RawValue == Int { - let rawValue = store.value(forKey: key) as? Value.RawValue - let value = rawValue.flatMap(Value.init) ?? wrappedValue - self.init( - value: value, store: store, key: key, - get: { $0 as? Value }, - set: { store.setValue($0.rawValue, forKey: key) } - ) - } -} - -@available(macOS 10.15, *) -private final class RefStorage: NSObject, ObservableObject { - @Published - fileprivate var value: Value - - private let defaultValue: Value - private let store: UserDefaults - private let key: String - private let transform: (Any?) -> Value? - - deinit { - store.removeObserver(self, forKeyPath: key) - } - - init(value: Value, store: UserDefaults, key: String, transform: @escaping (Any?) -> Value?) { - self.value = value - defaultValue = value - self.store = store - self.key = key - self.transform = transform - - super.init() - store.addObserver(self, forKeyPath: key, options: .new, context: nil) - } - - override func observeValue( - forKeyPath _: String?, - of _: Any?, - change: [NSKeyValueChangeKey: Any]?, - context _: UnsafeMutableRawPointer? - ) { - value = change?[.newKey].flatMap(transform) ?? defaultValue - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/AsyncImage/AsyncImage.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/AsyncImage/AsyncImage.swift deleted file mode 100755 index e41d7b17..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/AsyncImage/AsyncImage.swift +++ /dev/null @@ -1,226 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(iOS, deprecated: 15.0) -@available(macOS 10.15, *) -extension Backport where Wrapped == Any { - /// Loads and displays an image from the specified URL. - /// - /// Until the image loads, SwiftUI displays a default placeholder. When - /// the load operation completes successfully, SwiftUI updates the - /// view to show the loaded image. If the operation fails, SwiftUI - /// continues to display the placeholder. The following example loads - /// and displays an icon from an example server: - /// - /// asyncImage(url: URL(string: "https://example.com/icon.png")) - /// - /// If you want to customize the placeholder or apply image-specific - /// modifiers --- like ``Image/resizable(capInsets:resizingMode:)`` --- - /// to the loaded image, use the ``init(url:scale:content:placeholder:)`` - /// initializer instead. - /// - /// - Parameters: - /// - url: The URL of the image to display. - /// - scale: The scale to use for the image. The default is `1`. Set a - /// different value when loading images designed for higher resolution - /// displays. For example, set a value of `2` for an image that you - /// would name with the `@2x` suffix if stored in a file on disk. - @ViewBuilder - public static func asyncImage(url: URL?, scale: CGFloat = 1) -> some View { - _asyncImage(url: url, scale: scale) - } - - /// Loads and displays a modifiable image from the specified URL using - /// a custom placeholder until the image loads. - /// - /// Until the image loads, SwiftUI displays the placeholder view that - /// you specify. When the load operation completes successfully, SwiftUI - /// updates the view to show content that you specify, which you - /// create using the loaded image. For example, you can show a green - /// placeholder, followed by a tiled version of the loaded image: - /// - /// asyncImage(url: URL(string: "https://example.com/icon.png")) { image in - /// image.resizable(resizingMode: .tile) - /// } placeholder: { - /// Color.green - /// } - /// - /// If the load operation fails, SwiftUI continues to display the - /// placeholder. To be able to display a different view on a load error, - /// use the ``init(url:scale:transaction:content:)`` initializer instead. - /// - /// - Parameters: - /// - url: The URL of the image to display. - /// - scale: The scale to use for the image. The default is `1`. Set a - /// different value when loading images designed for higher resolution - /// displays. For example, set a value of `2` for an image that you - /// would name with the `@2x` suffix if stored in a file on disk. - /// - content: A closure that takes the loaded image as an input, and - /// returns the view to show. You can return the image directly, or - /// modify it as needed before returning it. - /// - placeholder: A closure that returns the view to show until the - /// load operation completes successfully. - @ViewBuilder - public static func asyncImage( - url: URL?, scale: CGFloat = 1, @ViewBuilder content: @escaping (Image) -> I, - @ViewBuilder placeholder: @escaping () -> P - ) -> some View { - _asyncImage(url: url, scale: scale, content: content, placeholder: placeholder) - } - - /// Loads and displays a modifiable image from the specified URL in phases. - /// - /// If you set the asynchronous image's URL to `nil`, or after you set the - /// URL to a value but before the load operation completes, the phase is - /// ``asyncImagePhase/empty``. After the operation completes, the phase - /// becomes either ``asyncImagePhase/failure(_:)`` or - /// ``asyncImagePhase/success(_:)``. In the first case, the phase's - /// ``asyncImagePhase/error`` value indicates the reason for failure. - /// In the second case, the phase's ``asyncImagePhase/image`` property - /// contains the loaded image. Use the phase to drive the output of the - /// `content` closure, which defines the view's appearance: - /// - /// asyncImage(url: URL(string: "https://example.com/icon.png")) { phase in - /// if let image = phase.image { - /// image // Displays the loaded image. - /// } else if phase.error != nil { - /// Color.red // Indicates an error. - /// } else { - /// Color.blue // Acts as a placeholder. - /// } - /// } - /// - /// To add transitions when you change the URL, apply an identifier to the - /// ``asyncImage``. - /// - /// - Parameters: - /// - url: The URL of the image to display. - /// - scale: The scale to use for the image. The default is `1`. Set a - /// different value when loading images designed for higher resolution - /// displays. For example, set a value of `2` for an image that you - /// would name with the `@2x` suffix if stored in a file on disk. - /// - transaction: The transaction to use when the phase changes. - /// - content: A closure that takes the load phase as an input, and - /// returns the view to display for the specified phase. - @ViewBuilder - public static func asyncImage( - url: URL?, scale: CGFloat = 1, transaction: Transaction = Transaction(), - @ViewBuilder content: @escaping (asyncImagePhase) -> Content - ) -> some View { - _asyncImage(url: url, scale: scale, transaction: transaction, content: content) - } - - /// The current phase of the asynchronous image loading operation. - /// - /// When you create an ``asyncImage`` instance with the - /// ``asyncImage/init(url:scale:transaction:content:)`` initializer, you define - /// the appearance of the view using a `content` closure. SwiftUI calls the - /// closure with a phase value at different points during the load operation - /// to indicate the current state. Use the phase to decide what to draw. - /// For example, you can draw the loaded image if it exists, a view that - /// indicates an error, or a placeholder: - /// - /// asyncImage(url: URL(string: "https://example.com/icon.png")) { phase in - /// if let image = phase.image { - /// image // Displays the loaded image. - /// } else if phase.error != nil { - /// Color.red // Indicates an error. - /// } else { - /// Color.blue // Acts as a placeholder. - /// } - /// } - public enum asyncImagePhase { - /// No image is loaded. - case empty - /// An image succesfully loaded. - case success(Image) - /// An image failed to load with an error. - case failure(Error) - - /// The loaded image, if any. - public var image: Image? { - guard case let .success(image) = self else { return nil } - return image - } - - /// The error that occurred when attempting to load an image, if any. - public var error: Error? { - guard case let .failure(error) = self else { return nil } - return error - } - } - - // An iOS 13+ async/await backport implementation - private struct _asyncImage: View { - @State private var phase: asyncImagePhase = .empty - - var url: URL? - var scale: CGFloat = 1 - var transaction: Transaction = .init() - var content: (Backport.asyncImagePhase) -> Content - - public var body: some View { - ZStack { - content(phase) - } - .backport.task(id: url) { - do { - guard !Task.isCancelled, let url = url else { return } - let (data, _) = try await URLSession.shared.backport.data(from: url) - guard !Task.isCancelled else { return } - - #if os(macOS) - if let image = NSImage(data: data) { - withTransaction(transaction) { - phase = .success(Image(nsImage: image)) - } - } - #else - if let image = UIImage(data: data, scale: scale) { - withTransaction(transaction) { - phase = .success(Image(uiImage: image)) - } - } - #endif - } catch { - phase = .failure(error) - } - } - } - - init(url: URL?, scale: CGFloat = 1) where Content == AnyView { - self.url = url - self.scale = scale - content = { AnyView($0.image) } - } - - init( - url: URL?, scale: CGFloat = 1, @ViewBuilder content: @escaping (Image) -> I, - @ViewBuilder placeholder: @escaping () -> P - ) where Content == _ConditionalContent { - self.url = url - self.scale = scale - transaction = Transaction() - self.content = { phase -> _ConditionalContent in - if let image = phase.image { - return ViewBuilder.buildEither(first: content(image)) - } else { - return ViewBuilder.buildEither(second: placeholder()) - } - } - } - - init( - url: URL?, scale: CGFloat = 1, transaction: Transaction = Transaction(), - @ViewBuilder content: @escaping (Backport.asyncImagePhase) -> Content - ) { - self.url = url - self.scale = scale - self.transaction = transaction - self.content = content - } - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Background/Background.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Background/Background.swift deleted file mode 100755 index f2bcf4ab..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Background/Background.swift +++ /dev/null @@ -1,136 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -public extension Backport where Wrapped: View { - /// Layers the views that you specify behind this view. - /// - /// Use this modifier to place one or more views behind another view. - /// For example, you can place a collection of stars beind a ``Text`` view: - /// - /// Text("ABCDEF") - /// .background(alignment: .leading) { Star(color: .red) } - /// .background(alignment: .center) { Star(color: .green) } - /// .background(alignment: .trailing) { Star(color: .blue) } - /// - /// The example above assumes that you've defined a `Star` view with a - /// parameterized color: - /// - /// struct Star: View { - /// var color: Color - /// - /// var body: some View { - /// Image(systemName: "star.fill") - /// .foregroundStyle(color) - /// } - /// } - /// - /// By setting different `alignment` values for each modifier, you make the - /// stars appear in different places behind the text: - /// - /// ![A screenshot of the letters A, B, C, D, E, and F written in front of - /// three stars. The stars, from left to right, are red, green, and - /// blue.](View-background-2) - /// - /// If you specify more than one view in the `content` closure, the modifier - /// collects all of the views in the closure into an implicit ``ZStack``, - /// taking them in order from back to front. For example, you can layer a - /// vertical bar behind a circle, with both of those behind a horizontal - /// bar: - /// - /// Color.blue - /// .frame(width: 200, height: 10) // Creates a horizontal bar. - /// .background { - /// Color.green - /// .frame(width: 10, height: 100) // Creates a vertical bar. - /// Circle() - /// .frame(width: 50, height: 50) - /// } - /// - /// Both the background modifier and the implicit ``ZStack`` composed from - /// the background content --- the circle and the vertical bar --- use a - /// default ``Alignment/center`` alignment. The vertical bar appears - /// centered behind the circle, and both appear as a composite view centered - /// behind the horizontal bar: - /// - /// ![A screenshot of a circle with a horizontal blue bar layered on top - /// and a vertical green bar layered underneath. All of the items are center - /// aligned.](View-background-3) - /// - /// If you specify an alignment for the background, it applies to the - /// implicit stack rather than to the individual views in the closure. You - /// can see this if you add the ``Alignment/leading`` alignment: - /// - /// Color.blue - /// .frame(width: 200, height: 10) - /// .background(alignment: .leading) { - /// Color.green - /// .frame(width: 10, height: 100) - /// Circle() - /// .frame(width: 50, height: 50) - /// } - /// - /// The vertical bar and the circle move as a unit to align the stack - /// with the leading edge of the horizontal bar, while the - /// vertical bar remains centered on the circle: - /// - /// ![A screenshot of a horizontal blue bar in front of a circle, which - /// is in front of a vertical green bar. The horizontal bar and the circle - /// are center aligned with each other; the left edges of the circle - /// and the horizontal are aligned.](View-background-3a) - /// - /// To control the placement of individual items inside the `content` - /// closure, either use a different background modifier for each item, as - /// the earlier example of stars under text demonstrates, or add an explicit - /// ``ZStack`` inside the content closure with its own alignment: - /// - /// Color.blue - /// .frame(width: 200, height: 10) - /// .background(alignment: .leading) { - /// ZStack(alignment: .leading) { - /// Color.green - /// .frame(width: 10, height: 100) - /// Circle() - /// .frame(width: 50, height: 50) - /// } - /// } - /// - /// The stack alignment ensures that the circle's leading edge aligns with - /// the vertical bar's, while the background modifier aligns the composite - /// view with the horizontal bar: - /// - /// ![A screenshot of a horizontal blue bar in front of a circle, which - /// is in front of a vertical green bar. All items are aligned on their - /// left edges.](View-background-4) - /// - /// You can achieve layering without a background modifier by putting both - /// the modified view and the background content into a ``ZStack``. This - /// produces a simpler view hierarchy, but it changes the layout priority - /// that SwiftUI applies to the views. Use the background modifier when you - /// want the modified view to dominate the layout. - /// - /// If you want to specify a ``ShapeStyle`` like a - /// ``HierarchicalShapeStyle`` or a ``Material`` as the background, use - /// ``View/background(_:ignoresSafeAreaEdges:)`` instead. - /// To specify a ``Shape`` or ``InsettableShape``, use - /// ``View/background(_:in:fillStyle:)-89n7j`` or - /// ``View/background(_:in:fillStyle:)-20tq5``, respectively. - /// - /// - Parameters: - /// - alignment: The alignment that the modifier uses to position the - /// implicit ``ZStack`` that groups the background views. The default - /// is ``Alignment/center``. - /// - content: A ``ViewBuilder`` that you use to declare the views to draw - /// behind this view, stacked in a cascading order from bottom to top. - /// The last view that you list appears at the front of the stack. - /// - /// - Returns: A view that uses the specified content as a background. - func background(alignment: Alignment = .center, @ViewBuilder _ content: () -> Content) - -> some View - { - self.content.background(content(), alignment: alignment) - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Dismiss/Dismiss.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Dismiss/Dismiss.swift deleted file mode 100755 index 0a27f290..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Dismiss/Dismiss.swift +++ /dev/null @@ -1,191 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -public extension EnvironmentValues { - /// An action that dismisses the current presentation. - /// - /// Use this environment value to get the ``Backport.DismissAction`` instance - /// for the current ``Environment``. Then call the instance - /// to perform the dismissal. You call the instance directly because - /// it defines a ``Backport.DismissAction/callAsFunction()`` - /// method that Swift calls when you call the instance. - /// - /// For example, you can create a button that calls the ``Backport.DismissAction``: - /// - /// private struct SheetContents: View { - /// @Environment(\.backportDismiss) private var dismiss - /// - /// var body: some View { - /// Button("Done") { - /// dismiss() - /// } - /// } - /// } - /// - /// If you present the `SheetContents` view in a sheet, the user can dismiss - /// the sheet by tapping or clicking the sheet's button: - /// - /// private struct DetailView: View { - /// @State private var isSheetPresented = false - /// - /// var body: some View { - /// Button("Show Sheet") { - /// isSheetPresented = true - /// } - /// .sheet(isPresented: $isSheetPresented) { - /// SheetContents() - /// } - /// } - /// } - /// - /// Be sure that you define the action in the appropriate environment. - /// For example, don't reorganize the `DetailView` in the example above - /// so that it creates the `dismiss` property and calls it from the - /// ``View/sheet(item:onDismiss:content:)`` view modifier's `content` - /// closure: - /// - /// private struct DetailView: View { - /// @State private var isSheetPresented = false - /// @Environment(\.backportDismiss) private var dismiss // Applies to DetailView. - /// - /// var body: some View { - /// Button("Show Sheet") { - /// isSheetPresented = true - /// } - /// .sheet(isPresented: $isSheetPresented) { - /// Button("Done") { - /// dismiss() // Fails to dismiss the sheet. - /// } - /// } - /// } - /// } - /// - /// If you do this, the sheet fails to dismiss because the action applies - /// to the environment where you declared it, which is that of the detail - /// view, rather than the sheet. In fact, if you've presented the detail - /// view in a ``NavigationView``, the dismissal pops the detail view - /// the navigation stack. - /// - /// The dismiss action has no effect on a view that isn't currently - /// presented. If you need to query whether SwiftUI is currently presenting - /// a view, read the ``EnvironmentValues/backportIsPresented`` environment value. - var backportDismiss: Backport.DismissAction { - .init(presentation: presentationMode) - } - - @available(macOS 10.15, *) - /// A Boolean value that indicates whether the view associated with this - /// environment is currently presented. - /// - /// You can read this value like any of the other ``EnvironmentValues`` - /// by creating a property with the ``Environment`` property wrapper: - /// - /// @Environment(\.backportIsPresented) private var isPresented - /// - /// Read the value inside a view if you need to know when SwiftUI - /// presents that view. For example, you can take an action when SwiftUI - /// presents a view by using the ``View/onChange(of:perform:)`` - /// modifier: - /// - /// .onChange(of: isPresented) { isPresented in - /// if isPresented { - /// // Do something when first presented. - /// } - /// } - /// - /// This behaves differently than ``View/onAppear(perform:)``, which - /// SwiftUI can call more than once for a given presentation, like - /// when you navigate back to a view that's already in the - /// navigation hierarchy. - /// - /// To dismiss the currently presented view, use - /// ``EnvironmentValues/backportDismiss``. - var backportIsPresented: Bool { - presentationMode.wrappedValue.isPresented - } -} - -@available(macOS 10.15, *) -@available(iOS, deprecated: 15) -@available(macOS, deprecated: 12) -@available(tvOS, deprecated: 15) -@available(watchOS, deprecated: 8) -extension Backport where Wrapped: Any { - /// An action that dismisses a presentation. - /// - /// Use the ``EnvironmentValues/dismiss`` environment value to get the instance - /// of this structure for a given ``Environment``. Then call the instance - /// to perform the dismissal. You call the instance directly because - /// it defines a ``DismissAction/callAsFunction()`` - /// method that Swift calls when you call the instance. - /// - /// For example, you can create a button that calls the ``DismissAction``: - /// - /// private struct SheetContents: View { - /// @Environment(\.backportDismiss) private var dismiss - /// - /// var body: some View { - /// Button("Done") { - /// dismiss() - /// } - /// } - /// } - /// - /// If you present the `SheetContents` view in a sheet, the user can dismiss - /// the sheet by tapping or clicking the sheet's button: - /// - /// private struct DetailView: View { - /// @State private var isSheetPresented = false - /// - /// var body: some View { - /// Button("Show Sheet") { - /// isSheetPresented = true - /// } - /// .sheet(isPresented: $isSheetPresented) { - /// SheetContents() - /// } - /// } - /// } - /// - /// Be sure that you define the action in the appropriate environment. - /// For example, don't reorganize the `DetailView` in the example above - /// so that it creates the `dismiss` property and calls it from the - /// ``View/sheet(item:onDismiss:content:)`` view modifier's `content` - /// closure: - /// - /// private struct DetailView: View { - /// @State private var isSheetPresented = false - /// @Environment(\.backportDismiss) private var dismiss // Applies to DetailView. - /// - /// var body: some View { - /// Button("Show Sheet") { - /// isSheetPresented = true - /// } - /// .sheet(isPresented: $isSheetPresented) { - /// Button("Done") { - /// dismiss() // Fails to dismiss the sheet. - /// } - /// } - /// } - /// } - /// - /// If you do this, the sheet fails to dismiss because the action applies - /// to the environment where you declared it, which is that of the detail - /// view, rather than the sheet. In fact, if you've presented the detail - /// view in a ``NavigationView``, the dismissal pops the detail view - /// from the navigation stack. - /// - /// The dismiss action has no effect on a view that isn't currently - /// presented. If you need to query whether SwiftUI is currently presenting - /// a view, read the ``EnvironmentValues/backportIsPresented`` environment value. - public struct DismissAction { - var presentation: Binding - public func callAsFunction() { - presentation.wrappedValue.dismiss() - } - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/DynamicType/DynamicType+Environment.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/DynamicType/DynamicType+Environment.swift deleted file mode 100755 index dffbdbbd..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/DynamicType/DynamicType+Environment.swift +++ /dev/null @@ -1,64 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -private struct BackportDynamicTypeKey: EnvironmentKey { - static var defaultValue: Backport.DynamicTypeSize = .large -} - -@available(macOS 10.15, *) -@available(iOS, deprecated: 15) -@available(tvOS, deprecated: 15) -@available(macOS, deprecated: 12) -@available(watchOS, deprecated: 8) -extension EnvironmentValues { - /// Sets the Dynamic Type size within the view to the given value. - /// - /// As an example, you can set a Dynamic Type size in `ContentView` to be - /// ``DynamicTypeSize/xLarge`` (this can be useful in previews to see your - /// content at a different size) like this: - /// - /// ContentView() - /// .backport.dynamicTypeSize(.xLarge) - /// - /// If a Dynamic Type size range is applied after setting a value, - /// the value is limited by that range: - /// - /// ContentView() // Dynamic Type size will be .large - /// .backport.dynamicTypeSize(...DynamicTypeSize.large) - /// .backport.dynamicTypeSize(.xLarge) - /// - /// When limiting the Dynamic Type size, consider if adding a - /// large content view with ``View/accessibilityShowsLargeContentViewer()`` - /// would be appropriate. - /// - /// - Parameter size: The size to set for this view. - /// - /// - Returns: A view that sets the Dynamic Type size to the specified - /// `size`. - public var backportDynamicTypeSize: Backport.DynamicTypeSize { - get { .init(self[keyPath: \.sizeCategory]) } - set { self[keyPath: \.sizeCategory] = newValue.sizeCategory } - } -} - -@available(macOS 10.15, *) -private struct DynamicTypeRangeKey: EnvironmentKey { - static var defaultValue: Range.DynamicTypeSize> { - .init(uncheckedBounds: (lower: .xSmall, upper: .accessibility5)) - } -} - -@available(macOS 10.15, *) -extension EnvironmentValues { - var dynamicTypeRange: Range.DynamicTypeSize> { - get { self[DynamicTypeRangeKey.self] } - set { - let current = self[DynamicTypeRangeKey.self] - self[DynamicTypeRangeKey.self] = current.clamped(to: newValue) - } - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/DynamicType/DynamicType+Modifiers.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/DynamicType/DynamicType+Modifiers.swift deleted file mode 100755 index a617ee40..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/DynamicType/DynamicType+Modifiers.swift +++ /dev/null @@ -1,116 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -public extension Backport where Wrapped: View { - /// Sets the Dynamic Type size within the view to the given value. - /// - /// As an example, you can set a Dynamic Type size in `ContentView` to be - /// ``DynamicTypeSize/xLarge`` (this can be useful in previews to see your - /// content at a different size) like this: - /// - /// ContentView() - /// .dynamicTypeSize(.xLarge) - /// - /// If a Dynamic Type size range is applied after setting a value, - /// the value is limited by that range: - /// - /// ContentView() // Dynamic Type size will be .large - /// .dynamicTypeSize(...DynamicTypeSize.large) - /// .dynamicTypeSize(.xLarge) - /// - /// When limiting the Dynamic Type size, consider if adding a - /// large content view with ``View/accessibilityShowsLargeContentViewer()`` - /// would be appropriate. - /// - /// - Parameter size: The size to set for this view. - /// - /// - Returns: A view that sets the Dynamic Type size to the specified - /// `size`. - @ViewBuilder - func dynamicTypeSize(_ size: Backport.DynamicTypeSize) -> some View { - content.environment(\.backportDynamicTypeSize, size) - } - - @available(macOS 10.15, *) - /// Limits the Dynamic Type size within the view to the given range. - /// - /// As an example, you can constrain the maximum Dynamic Type size in - /// `ContentView` to be no larger than ``DynamicTypeSize/large``: - /// - /// ContentView() - /// .dynamicTypeSize(...DynamicTypeSize.large) - /// - /// If the Dynamic Type size is limited to multiple ranges, the result is - /// their intersection: - /// - /// ContentView() // Dynamic Type sizes are from .small to .large - /// .dynamicTypeSize(.small...) - /// .dynamicTypeSize(...DynamicTypeSize.large) - /// - /// A specific Dynamic Type size can still be set after a range is applied: - /// - /// ContentView() // Dynamic Type size is .xLarge - /// .dynamicTypeSize(.xLarge) - /// .dynamicTypeSize(...DynamicTypeSize.large) - /// - /// When limiting the Dynamic Type size, consider if adding a - /// large content view with ``View/accessibilityShowsLargeContentViewer()`` - /// would be appropriate. - /// - /// - Parameter range: The range of sizes that are allowed in this view. - /// - /// - Returns: A view that constrains the Dynamic Type size of this view - /// within the specified `range`. - @ViewBuilder - func dynamicTypeSize(_ range: T) -> some View - where T: RangeExpression, T.Bound == Backport.DynamicTypeSize - { - if let range = range as? Range { - content - .modifier(DynamicTypeRangeModifier()) - .environment(\.dynamicTypeRange, range) - } else if let range = range as? ClosedRange { - content - .modifier(DynamicTypeRangeModifier()) - .environment(\.dynamicTypeRange, .init(uncheckedBounds: (lower: range.lowerBound, upper: range.upperBound))) - } else if let range = range as? PartialRangeFrom { - content - .modifier(DynamicTypeRangeModifier()) - .environment(\.dynamicTypeRange, .init(uncheckedBounds: (range.lowerBound, .accessibility5))) - } else if let range = range as? PartialRangeUpTo { - content - .modifier(DynamicTypeRangeModifier()) - .environment(\.dynamicTypeRange, .init(uncheckedBounds: (.xSmall, range.upperBound))) - } else if let range = range as? PartialRangeThrough { - content - .modifier(DynamicTypeRangeModifier()) - .environment(\.dynamicTypeRange, .init(uncheckedBounds: (.xSmall, range.upperBound))) - } else { - content - .modifier(DynamicTypeRangeModifier()) - } - } -} - -@available(macOS 10.15, *) -private struct DynamicTypeRangeModifier: ViewModifier { - @Environment(\.dynamicTypeRange) private var range - @Environment(\.backportDynamicTypeSize) private var size - - @available(macOS 10.15, *) - private var resolvedSize: Backport.DynamicTypeSize { - print(range) - return range.contains(size) - ? size - : max(range.lowerBound, min(range.upperBound, size)) - } - - @available(macOS 10.15, *) - func body(content: Content) -> some View { - content.environment(\.backportDynamicTypeSize, resolvedSize) - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/DynamicType/DynamicType.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/DynamicType/DynamicType.swift deleted file mode 100755 index e048828a..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/DynamicType/DynamicType.swift +++ /dev/null @@ -1,225 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(iOS, deprecated: 15) -@available(tvOS, deprecated: 15) -@available(macOS, deprecated: 12) -@available(watchOS, deprecated: 8) -@available(macOS 10.15, *) -extension Backport where Wrapped == Any { - /// A Dynamic Type size, which specifies how large scalable content should be. - /// - /// For more information about Dynamic Type sizes in iOS, see iOS Human Interface Guidelines > - /// [Dynamic Type Sizes](https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/typography/#dynamic-type-sizes). - /// For more information about Dynamic Type sizes in watchOS, see watchOS Human Interface Guidelines > - /// [Dynamic Type Sizes](https://developer.apple.com/design/human-interface-guidelines/watchos/visual/typography/#dynamic-type-sizes). - public enum DynamicTypeSize: Hashable, Comparable, CaseIterable { - /// An extra small size. - case xSmall - - /// A small size. - case small - - /// A medium size. - case medium - - /// A large size. - case large - - /// An extra large size. - case xLarge - - /// An extra extra large size. - case xxLarge - - /// An extra extra extra large size. - case xxxLarge - - /// The first accessibility size. - case accessibility1 - - /// The second accessibility size. - case accessibility2 - - /// The third accessibility size. - case accessibility3 - - /// The fourth accessibility size. - case accessibility4 - - /// The fifth accessibility size. - case accessibility5 - - /// A Boolean value indicating whether the size is one that is associated - /// with accessibility. - public var isAccessibilitySize: Bool { - self >= .accessibility1 - } - - #if os(iOS) || os(tvOS) - /// Create a Dynamic Type size from its `UIContentSizeCategory` equivalent. - public init?(_ uiSizeCategory: UIContentSizeCategory) { - switch uiSizeCategory { - case .extraSmall: - self = .xSmall - case .small: - self = .small - case .medium: - self = .medium - case .large: - self = .medium - case .extraLarge: - self = .xLarge - case .extraExtraLarge: - self = .xxLarge - case .extraExtraExtraLarge: - self = .xxxLarge - case .accessibilityMedium: - self = .accessibility1 - case .accessibilityLarge: - self = .accessibility2 - case .accessibilityExtraLarge: - self = .accessibility3 - case .accessibilityExtraExtraLarge: - self = .accessibility4 - case .accessibilityExtraExtraExtraLarge: - self = .accessibility5 - default: - return nil - } - } - #endif - - internal init(_ sizeCategory: ContentSizeCategory) { - switch sizeCategory { - case .extraSmall: - self = .xSmall - case .small: - self = .small - case .medium: - self = .medium - case .large: - self = .large - case .extraLarge: - self = .xLarge - case .extraExtraLarge: - self = .xxLarge - case .extraExtraExtraLarge: - self = .xxxLarge - case .accessibilityMedium: - self = .accessibility1 - case .accessibilityLarge: - self = .accessibility2 - case .accessibilityExtraLarge: - self = .accessibility3 - case .accessibilityExtraExtraLarge: - self = .accessibility4 - case .accessibilityExtraExtraExtraLarge: - self = .accessibility5 - default: - self = .large - } - } - - var sizeCategory: ContentSizeCategory { - switch self { - case .xSmall: - return .extraSmall - case .small: - return .small - case .medium: - return .medium - case .large: - return .large - case .xLarge: - return .extraLarge - case .xxLarge: - return .extraExtraLarge - case .xxxLarge: - return .extraExtraExtraLarge - case .accessibility1: - return .accessibilityMedium - case .accessibility2: - return .accessibilityLarge - case .accessibility3: - return .accessibilityExtraLarge - case .accessibility4: - return .accessibilityExtraExtraLarge - case .accessibility5: - return .accessibilityExtraExtraExtraLarge - } - } - } -} - -@available(iOS 15, tvOS 15, macOS 12, watchOS 8, *) -extension Backport.DynamicTypeSize { - var dynamicTypeSize: DynamicTypeSize { - switch self { - case .xSmall: - return .xSmall - case .small: - return .small - case .medium: - return .medium - case .large: - return .large - case .xLarge: - return .xLarge - case .xxLarge: - return .xxLarge - case .xxxLarge: - return .xxxLarge - case .accessibility1: - return .accessibility1 - case .accessibility2: - return .accessibility2 - case .accessibility3: - return .accessibility3 - case .accessibility4: - return .accessibility4 - case .accessibility5: - return .accessibility5 - } - } -} - -#if os(iOS) || os(tvOS) - @available(iOS, deprecated: 15) - @available(tvOS, deprecated: 15) - extension UIContentSizeCategory { - public init(_ dynamicTypeSize: Backport.DynamicTypeSize?) { - switch dynamicTypeSize { - case .xSmall: - self = .extraSmall - case .small: - self = .small - case .medium: - self = .medium - case .large: - self = .large - case .xLarge: - self = .extraLarge - case .xxLarge: - self = .extraExtraLarge - case .xxxLarge: - self = .extraExtraExtraLarge - case .accessibility1: - self = .accessibilityMedium - case .accessibility2: - self = .accessibilityLarge - case .accessibility3: - self = .accessibilityExtraLarge - case .accessibility4: - self = .accessibilityExtraExtraLarge - case .accessibility5: - self = .accessibilityExtraExtraExtraLarge - case .none: - self = .large - } - } - } -#endif diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Label/Label.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Label/Label.swift deleted file mode 100755 index 2c293fc0..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Label/Label.swift +++ /dev/null @@ -1,184 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(iOS, deprecated: 14) -@available(macOS, deprecated: 11) -@available(tvOS, deprecated: 14) -@available(watchOS, deprecated: 7) -@available(macOS 10.15, *) -extension Backport where Wrapped == Any { - /// A standard label for user interface items, consisting of an icon with a - /// title. - /// - /// One of the most common and recognizable user interface components is the - /// combination of an icon and a label. This idiom appears across many kinds of - /// apps and shows up in collections, lists, menus of action items, and - /// disclosable lists, just to name a few. - /// - /// You create a label, in its simplest form, by providing a title and the name - /// of an image, such as an icon from the - /// [SF Symbols](https://developer.apple.com/design/human-interface-guidelines/sf-symbols/overview/) - /// collection: - /// - /// Label("Lightning", systemImage: "bolt.fill") - /// - /// You can also apply styles to labels in several ways. In the case of dynamic - /// changes to the view after device rotation or change to a window size you - /// might want to show only the text portion of the label using the - /// ``LabelStyle/titleOnly`` label style: - /// - /// Label("Lightning", systemImage: "bolt.fill") - /// .labelStyle(.titleOnly) - /// - /// Conversely, there's also an icon-only label style: - /// - /// Label("Lightning", systemImage: "bolt.fill") - /// .labelStyle(.iconOnly) - /// - /// Some containers might apply a different default label style, such as only - /// showing icons within toolbars on macOS and iOS. To opt in to showing both - /// the title and the icon, you can apply the ``LabelStyle/titleAndIcon`` label - /// style: - /// - /// Label("Lightning", systemImage: "bolt.fill") - /// .labelStyle(.titleAndIcon) - /// - /// You can also create a customized label style by modifying an existing - /// style; this example adds a red border to the default label style: - /// - /// struct RedBorderedLabelStyle: LabelStyle { - /// func makeBody(configuration: Configuration) -> some View { - /// Label(configuration) - /// .border(Color.red) - /// } - /// } - /// - /// For more extensive customization or to create a completely new label style, - /// you'll need to adopt the ``LabelStyle`` protocol and implement a - /// ``LabelStyleConfiguration`` for the new style. - /// - /// To apply a common label style to a group of labels, apply the style - /// to the view hierarchy that contains the labels: - /// - /// VStack { - /// Label("Rain", systemImage: "cloud.rain") - /// Label("Snow", systemImage: "snow") - /// Label("Sun", systemImage: "sun.max") - /// } - /// .labelStyle(.iconOnly) - /// - /// It's also possible to make labels using views to compose the label's icon - /// programmatically, rather than using a pre-made image. In this example, the - /// icon portion of the label uses a filled ``Circle`` overlaid - /// with the user's initials: - /// - /// Label { - /// Text(person.fullName) - /// .font(.body) - /// .foregroundColor(.primary) - /// Text(person.title) - /// .font(.subheadline) - /// .foregroundColor(.secondary) - /// } icon: { - /// Circle() - /// .fill(person.profileColor) - /// .frame(width: 44, height: 44, alignment: .center) - /// .overlay(Text(person.initials)) - /// } - /// - public struct Label: View where Title: View, Icon: View { - @Environment(\.self) private var environment - @Environment(\.backportLabelStyle) private var style - private var config: Backport.LabelStyleConfiguration - - /// Creates a label with a custom title and icon. - public init(@ViewBuilder title: () -> Title, @ViewBuilder icon: () -> Icon) { - config = .init(title: .init(content: title()), icon: .init(content: icon())) - } - - @MainActor public var body: some View { - if let style = style { - style.makeBody(configuration: config.environment(environment)) - } else { - DefaultLabelStyle() - .makeBody(configuration: config.environment(environment)) - } - } - } -} - -@available(macOS 10.15, *) -public extension Backport.Label where Wrapped == Any, Title == Text, Icon == Image { - /// Creates a label with an icon image and a title generated from a - /// localized string. - /// - /// - Parameters: - /// - titleKey: A title generated from a localized string. - /// - image: The name of the image resource to lookup. - init(_ titleKey: LocalizedStringKey, image name: String) { - self.init(title: { Text(titleKey) }, icon: { Image(name) }) - } - - /// Creates a label with an icon image and a title generated from a string. - /// - /// - Parameters: - /// - title: A string used as the label's title. - /// - image: The name of the image resource to lookup. - init(_ title: S, image name: String) where S: StringProtocol { - self.init(title: { Text(title) }, icon: { Image(name) }) - } -} - -@available(macOS, introduced: 11, message: "SFSymbols support was only introduced in macOS 11") -extension Backport.Label where Wrapped == Any, Title == Text, Icon == Image { - /// Creates a label with a system icon image and a title generated from a - /// localized string. - /// - /// - Parameters: - /// - titleKey: A title generated from a localized string. - /// - systemImage: The name of the image resource to lookup. - public init(_ titleKey: LocalizedStringKey, systemImage name: String) { - self.init(title: { Text(titleKey) }, icon: { Image(systemName: name) }) - } - - /// Creates a label with a system icon image and a title generated from a - /// string. - /// - /// - Parameters: - /// - title: A string used as the label's title. - /// - systemImage: The name of the image resource to lookup. - public init(_ title: S, systemImage name: String) where S: StringProtocol { - self.init(title: { Text(title) }, icon: { Image(systemName: name) }) - } -} - -@available(macOS 10.15, *) -public extension Backport.Label - where Wrapped == Any, Title == Backport.LabelStyleConfiguration.Title, Icon == Backport.LabelStyleConfiguration.Icon -{ - /// Creates a label representing the configuration of a style. - /// - /// You can use this initializer within the ``LabelStyle/makeBody(configuration:)`` - /// method of a ``LabelStyle`` instance to create an instance of the label - /// that's being styled. This is useful for custom label styles that only - /// wish to modify the current style, as opposed to implementing a brand new - /// style. - /// - /// For example, the following style adds a red border around the label, - /// but otherwise preserves the current style: - /// - /// struct RedBorderedLabelStyle: LabelStyle { - /// func makeBody(configuration: Configuration) -> some View { - /// Label(configuration) - /// .border(Color.red) - /// } - /// } - /// - /// - Parameter configuration: The label style to use. - init(_ configuration: Backport.LabelStyleConfiguration) { - config = configuration - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Label/LabelConfiguration.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Label/LabelConfiguration.swift deleted file mode 100755 index 803f1d0b..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Label/LabelConfiguration.swift +++ /dev/null @@ -1,52 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -@available(iOS, deprecated: 14) -@available(macOS, deprecated: 11) -@available(tvOS, deprecated: 14) -@available(watchOS, deprecated: 7) -extension Backport where Wrapped == Any { - /// The properties of a label. - public struct LabelStyleConfiguration { - /// A type-erased title view of a label. - public struct Title: View { - let content: AnyView - public var body: some View { content } - init(content: Content) { - self.content = .init(content) - } - } - - @available(macOS 10.15, *) - /// A type-erased icon view of a label. - public struct Icon: View { - let content: AnyView - public var body: some View { content } - init(content: Content) { - self.content = .init(content) - } - } - - @available(macOS 10.15, *) - /// A description of the labeled item. - public internal(set) var title: LabelStyleConfiguration.Title - - @available(macOS 10.15, *) - /// A symbolic representation of the labeled item. - public internal(set) var icon: LabelStyleConfiguration.Icon - - @available(macOS 10.15, *) - internal var environment: EnvironmentValues = .init() - - @available(macOS 10.15, *) - func environment(_ values: EnvironmentValues) -> Self { - var config = self - config.environment = values - return config - } - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Label/LabelStyle.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Label/LabelStyle.swift deleted file mode 100755 index b81ba6f7..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Label/LabelStyle.swift +++ /dev/null @@ -1,72 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -@available(iOS, deprecated: 14) -@available(macOS, deprecated: 11) -@available(tvOS, deprecated: 14) -@available(watchOS, deprecated: 7) -/// A type that applies a custom appearance to all labels within a view. -/// -/// To configure the current label style for a view hierarchy, use the -/// ``View/labelStyle(_:)`` modifier. -public protocol BackportLabelStyle { - /// The properties of a label. - typealias Configuration = Backport.LabelStyleConfiguration - - /// A view that represents the body of a label. - associatedtype Body: View - - @available(macOS 10.15, *) - /// Creates a view that represents the body of a label. - /// - /// The system calls this method for each ``Label`` instance in a view - /// hierarchy where this style is the current label style. - /// - /// - Parameter configuration: The properties of the label. - @ViewBuilder func makeBody(configuration: Configuration) -> Body -} - -@available(macOS 10.15, *) -@available(iOS, deprecated: 14) -@available(macOS, deprecated: 11) -@available(tvOS, deprecated: 14.0) -@available(watchOS, deprecated: 7.0) -extension Backport where Wrapped: View { - public func labelStyle(_ style: S) -> some View { - content.environment(\.backportLabelStyle, .init(style)) - } -} - -@available(macOS 10.15, *) -internal struct AnyLabelStyle: BackportLabelStyle { - let _makeBody: (Backport.LabelStyleConfiguration) -> AnyView - - @available(macOS 10.15, *) - init(_ style: S) { - _makeBody = { config in - AnyView(style.makeBody(configuration: config)) - } - } - - @available(macOS 10.15, *) - func makeBody(configuration: Configuration) -> some View { - _makeBody(configuration) - } -} - -@available(macOS 10.15, *) -private struct BackportLabelStyleEnvironmentKey: EnvironmentKey { - static var defaultValue: AnyLabelStyle? -} - -@available(macOS 10.15, *) -extension EnvironmentValues { - var backportLabelStyle: AnyLabelStyle? { - get { self[BackportLabelStyleEnvironmentKey.self] } - set { self[BackportLabelStyleEnvironmentKey.self] = newValue } - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Label/Styles/DefaultLabelStyle.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Label/Styles/DefaultLabelStyle.swift deleted file mode 100755 index 0bc39b75..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Label/Styles/DefaultLabelStyle.swift +++ /dev/null @@ -1,44 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -@available(iOS, deprecated: 14) -@available(macOS, deprecated: 11) -@available(tvOS, deprecated: 14) -@available(watchOS, deprecated: 7) -extension Backport where Wrapped == Any { - /// The default label style in the current context. - /// - /// You can also use ``LabelStyle/automatic`` to construct this style. - public struct DefaultLabelStyle: BackportLabelStyle { - public init() {} - - @available(macOS 10.15, *) - /// Creates a view that represents the body of a label. - /// - /// The system calls this method for each ``Label`` instance in a view - /// hierarchy where this style is the current label style. - /// - /// - Parameter configuration: The properties of the label. - public func makeBody(configuration: DefaultLabelStyle.Configuration) -> some View { - HStack { - configuration.icon - configuration.title - } - } - } -} - -@available(macOS 10.15, *) -@available(iOS, deprecated: 14) -@available(macOS, deprecated: 11) -@available(tvOS, deprecated: 14) -@available(watchOS, deprecated: 7) -extension BackportLabelStyle where Self == Backport.DefaultLabelStyle { - /// A label style that resolves its appearance automatically based on the - /// current context. - public static var automatic: Self { .init() } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Label/Styles/IconOnlyLabelStyle.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Label/Styles/IconOnlyLabelStyle.swift deleted file mode 100755 index 005e1e18..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Label/Styles/IconOnlyLabelStyle.swift +++ /dev/null @@ -1,44 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -@available(iOS, deprecated: 14) -@available(macOS, deprecated: 11) -@available(tvOS, deprecated: 14) -@available(watchOS, deprecated: 7) -extension Backport where Wrapped == Any { - /// A label style that only displays the icon of the label. - /// - /// You can also use ``LabelStyle/iconOnly`` to construct this style. - public struct IconOnlyLabelStyle: BackportLabelStyle { - /// Creates an icon-only label style. - public init() {} - - @available(macOS 10.15, *) - /// Creates a view that represents the body of a label. - /// - /// The system calls this method for each ``Label`` instance in a view - /// hierarchy where this style is the current label style. - /// - /// - Parameter configuration: The properties of the label. - public func makeBody(configuration: Configuration) -> some View { - configuration.icon - } - } -} - -@available(macOS 10.15, *) -@available(iOS, deprecated: 14) -@available(macOS, deprecated: 11) -@available(tvOS, deprecated: 14) -@available(watchOS, deprecated: 7) -extension BackportLabelStyle where Self == Backport.IconOnlyLabelStyle { - /// A label style that only displays the icon of the label. - /// - /// The title of the label is still used for non-visual descriptions, such as - /// VoiceOver. - public static var iconOnly: Self { .init() } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Label/Styles/TitleAndIconLabelStyle.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Label/Styles/TitleAndIconLabelStyle.swift deleted file mode 100755 index cd7970ac..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Label/Styles/TitleAndIconLabelStyle.swift +++ /dev/null @@ -1,70 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -@available(iOS, deprecated: 14) -@available(macOS, deprecated: 11) -@available(tvOS, deprecated: 14) -@available(watchOS, deprecated: 7) -extension Backport where Wrapped == Any { - /// A label style that shows both the title and icon of the label using a - /// system-standard layout. - /// - /// You can also use ``LabelStyle/titleAndIcon`` to construct this style. - public struct TitleAndIconLabelStyle: BackportLabelStyle { - /// Creates a label style that shows both the title and icon of the label - /// using a system-standard layout. - public init() {} - - @available(macOS 10.15, *) - /// Creates a view that represents the body of a label. - /// - /// The system calls this method for each ``Label`` instance in a view - /// hierarchy where this style is the current label style. - /// - /// - Parameter configuration: The properties of the label. - public func makeBody(configuration: Configuration) -> some View { - HStack { - configuration.icon - configuration.title - } - } - } -} - -@available(macOS 10.15, *) -@available(iOS, deprecated: 14) -@available(macOS, deprecated: 11) -@available(tvOS, deprecated: 14) -@available(watchOS, deprecated: 7) -extension BackportLabelStyle where Self == Backport.TitleAndIconLabelStyle { - /// A label style that shows both the title and icon of the label using a - /// system-standard layout. - /// - /// In most cases, labels show both their title and icon by default. However, - /// some containers might apply a different default label style to their - /// content, such as only showing icons within toolbars on macOS and iOS. To - /// opt in to showing both the title and the icon, you can apply the title - /// and icon label style: - /// - /// Label("Lightning", systemImage: "bolt.fill") - /// .labelStyle(.titleAndIcon) - /// - /// To apply the title and icon style to a group of labels, apply the style - /// to the view hierarchy that contains the labels: - /// - /// VStack { - /// Label("Rain", systemImage: "cloud.rain") - /// Label("Snow", systemImage: "snow") - /// Label("Sun", systemImage: "sun.max") - /// } - /// .labelStyle(.titleAndIcon) - /// - /// The relative layout of the title and icon is dependent on the context it - /// is displayed in. In most cases, however, the label is arranged - /// horizontally with the icon leading. - public static var titleAndIcon: Self { .init() } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Label/Styles/TitleOnlyLabelStyle.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Label/Styles/TitleOnlyLabelStyle.swift deleted file mode 100755 index 6fb012a6..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Label/Styles/TitleOnlyLabelStyle.swift +++ /dev/null @@ -1,41 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -@available(iOS, deprecated: 14) -@available(macOS, deprecated: 11) -@available(tvOS, deprecated: 14) -@available(watchOS, deprecated: 7) -extension Backport where Wrapped == Any { - // A label style that only displays the title of the label. - /// - /// You can also use ``LabelStyle/titleOnly`` to construct this style. - public struct TitleOnlyLabelStyle: BackportLabelStyle { - /// Creates a title-only label style. - public init() {} - - @available(macOS 10.15, *) - /// Creates a view that represents the body of a label. - /// - /// The system calls this method for each ``Label`` instance in a view - /// hierarchy where this style is the current label style. - /// - /// - Parameter configuration: The properties of the label. - public func makeBody(configuration: Configuration) -> some View { - configuration.title - } - } -} - -@available(macOS 10.15, *) -@available(iOS, deprecated: 14) -@available(macOS, deprecated: 11) -@available(tvOS, deprecated: 14) -@available(watchOS, deprecated: 7) -extension BackportLabelStyle where Self == Backport.TitleOnlyLabelStyle { - /// A label style that only displays the title of the label. - public static var titleOnly: Self { .init() } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/LabeledContent/LabeledContent.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/LabeledContent/LabeledContent.swift deleted file mode 100755 index 3cc9cec2..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/LabeledContent/LabeledContent.swift +++ /dev/null @@ -1,275 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -@available(iOS, deprecated: 16) -@available(tvOS, deprecated: 16) -@available(macOS, deprecated: 13) -@available(watchOS, deprecated: 9) -extension Backport where Wrapped == Any { - /// A container for attaching a label to a value-bearing view. - /// - /// The instance's content represents a read-only or read-write value, and its - /// label identifies or describes the purpose of that value. - /// The resulting element has a layout that's consistent with other framework - /// controls and automatically adapts to its container, like a form or toolbar. - /// Some styles of labeled content also apply styling or behaviors to the value - /// content, like making ``Text`` views selectable. - /// - /// The following example associates a label with a custom view and has - /// a layout that matches the label of the ``Picker``: - /// - /// Form { - /// Backport.LabeledContent("Custom Value") { - /// MyCustomView(value: $value) - /// } - /// Picker("Selected Value", selection: $selection) { - /// PickerOption("Option 1", 1) - /// PickerOption("Option 2", 2) - /// } - /// } - /// - /// ### Custom view labels - /// - /// You can assemble labeled content with an explicit view for its label - /// using the ``init(content:label:)`` initializer. For example, you can - /// rewrite the previous labeled content example using a ``Text`` view: - /// - /// LabeledContent { - /// MyCustomView(value: $value) - /// } label: { - /// Text("Custom Value") - /// } - /// - /// The `label` view builder accepts any kind of view, like a ``Label``: - /// - /// Backport.LabeledContent { - /// MyCustomView(value: $value) - /// } label: { - /// Label("Custom Value", systemImage: "hammer") - /// } - /// - /// ### Textual labeled content - /// - /// You can construct labeled content with string values or formatted values - /// to create read-only displays of textual values: - /// - /// Form { - /// Section("Information") { - /// Backport.LabeledContent("Name", value: person.name) - /// } - /// if !person.pets.isEmpty { - /// Section("Pets") { - /// ForEach(pet) { pet in - /// Backport.LabeledContent(pet.species, value: pet.name) - /// } - /// } - /// } - /// } - /// - /// Wherever possible, SwiftUI makes this text selectable. - /// - /// ### Compositional elements - /// - /// You can use labeled content as the label for other elements. For example, - /// a ``NavigationLink`` can present a summary value for the destination it - /// links to: - /// - /// Form { - /// NavigationLink(value: Settings.wifiDetail) { - /// Backport.LabeledContent("Wi-Fi", value: ssidName) - /// } - /// } - /// - /// In some cases, the styling of views used as the value content is - /// specialized as well. For example, while a ``Toggle`` in an inset group - /// form on macOS is styled as a switch by default, it's styled as a checkbox - /// when used as a value element within a surrounding `LabeledContent` - /// instance: - /// - /// Form { - /// Backport.LabeledContent("Source Control") { - /// Toggle("Refresh local status automatically", - /// isOn: $refreshLocalStatus) - /// Toggle("Fetch and refresh server status automatically", - /// isOn: $refreshServerStatus) - /// Toggle("Add and remove files automatically", - /// isOn: $addAndRemoveFiles) - /// Toggle("Select files to commit automatically", - /// isOn: $selectFiles) - /// } - /// } - /// - /// ### Controlling label visibility - /// - /// A label communicates the identity or purpose of the value, which is - /// important for accessibility. However, you might want to hide the label - /// in the display, and some controls or contexts may visually hide their label - /// by default. The ``View/labels(_:)`` modifier allows controlling that - /// visibility. The following example hides both labels, producing only a - /// group of the two value views: - /// - /// Group { - /// LabeledContent("Custom Value") { - /// MyCustomView(value: $value) - /// } - /// Picker("Selected Value", selection: $selection) { - /// PickerOption("Option 1", 1) - /// PickerOption("Option 2", 2) - /// } - /// } - /// .labelsHidden() - /// - /// ### Styling labeled content - /// - /// You can set label styles using the ``View/labeledContentStyle(_:)`` - /// modifier. You can also build custom styles using ``LabeledContentStyle``. - public struct LabeledContent { - @Environment(\.backportLabeledContentStyle) private var style - let config: LabeledContentStyleConfiguration - public var body: some View { - style.makeBody(configuration: config) - } - } -} - -@available(macOS 10.15, *) -public extension Backport.LabeledContent - where - Wrapped == Any, Label == Backport.LabeledContentStyleConfiguration.Label, - Content == Backport.LabeledContentStyleConfiguration.Content -{ - /// Creates labeled content based on a labeled content style configuration. - /// - /// You can use this initializer within the - /// ``LabeledContentStyle/makeBody(configuration:)`` method of a - /// ``LabeledContentStyle`` to create a labeled content instance. - /// This is useful for custom styles that only modify the current style, - /// as opposed to implementing a brand new style. - /// - /// For example, the following style adds a red border around the labeled - /// content, but otherwise preserves the current style: - /// - /// struct RedBorderLabeledContentStyle: LabeledContentStyle { - /// func makeBody(configuration: Configuration) -> some View { - /// LabeledContent(configuration) - /// .border(.red) - /// } - /// } - /// - /// - Parameter configuration: The properties of the labeled content - init(_ config: Backport.LabeledContentStyleConfiguration) { - self.config = config - } -} - -@available(macOS 10.15, *) -public extension Backport.LabeledContent where Wrapped == Any, Label == Text, Content: View { - /// Creates a labeled view that generates its label from a localized string - /// key. - /// - /// This initializer creates a ``Text`` label on your behalf, and treats the - /// localized key similar to ``Text/init(_:tableName:bundle:comment:)``. See - /// `Text` for more information about localizing strings. - /// - /// - Parameters: - /// - titleKey: The key for the view's localized title, that describes - /// the purpose of the view. - /// - content: The value content being labeled. - init(_ titleKey: LocalizedStringKey, @ViewBuilder content: () -> Content) { - config = .init( - label: Text(titleKey), - content: content() - ) - } - - @available(macOS 10.15, *) - /// Creates a labeled view that generates its label from a string. - /// - /// This initializer creates a ``Text`` label on your behalf, and treats the - /// title similar to ``Text/init(_:)-9d1g4``. See `Text` for more - /// information about localizing strings. - /// - /// - Parameters: - /// - title: A string that describes the purpose of the view. - /// - content: The value content being labeled. - init(_ title: S, @ViewBuilder content: () -> Content) where S: StringProtocol { - config = .init( - label: Text(title), - content: content() - ) - } -} - -@available(macOS 10.15, *) -extension Backport.LabeledContent: View where Wrapped == Any, Label: View, Content: View { - /// Creates a labeled view that generates its label from a localized string - /// key. - /// - /// This initializer creates a ``Text`` label on your behalf, and treats the - /// localized key similar to ``Text/init(_:tableName:bundle:comment:)``. See - /// `Text` for more information about localizing strings. - /// - /// - Parameters: - /// - titleKey: The key for the view's localized title, that describes - /// the purpose of the view. - /// - content: The value content being labeled. - public init(@ViewBuilder content: () -> Content, @ViewBuilder label: () -> Label) { - config = .init( - label: label(), - content: content() - ) - } -} - -@available(macOS 10.15, *) -public extension Backport.LabeledContent where Wrapped == Any, Label == Text, Content == Text { - /// Creates a labeled informational view. - /// - /// This initializer creates a ``Text`` label on your behalf, and treats the - /// localized key similar to ``Text/init(_:tableName:bundle:comment:)``. See - /// `Text` for more information about localizing strings. - /// - /// Form { - /// LabeledContent("Name", value: person.name) - /// } - /// - /// In some contexts, this text will be selectable by default. - /// - /// - Parameters: - /// - titleKey: The key for the view's localized title, that describes - /// the purpose of the view. - /// - value: The value being labeled. - init(_ titleKey: LocalizedStringKey, value: S) { - config = .init( - label: Text(titleKey), - content: Text(value) - ) - } - - @available(macOS 10.15, *) - /// Creates a labeled informational view. - /// - /// This initializer creates a ``Text`` label on your behalf, and treats the - /// title similar to ``Text/init(_:)-9d1g4``. See `Text` for more - /// information about localizing strings. - /// - /// Form { - /// ForEach(person.pet) { pet in - /// LabeledContent(pet.species, value: pet.name) - /// } - /// } - /// - /// - Parameters: - /// - title: A string that describes the purpose of the view. - /// - value: The value being labeled. - init(_ title: S1, value: S2) { - config = .init( - label: Text(title), - content: Text(value) - ) - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/LabeledContent/LabeledContentStyle.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/LabeledContent/LabeledContentStyle.swift deleted file mode 100755 index 8acbfdc0..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/LabeledContent/LabeledContentStyle.swift +++ /dev/null @@ -1,63 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -@available(iOS, deprecated: 16) -@available(tvOS, deprecated: 16) -@available(macOS, deprecated: 13) -@available(watchOS, deprecated: 9) -extension Backport where Wrapped: View { - /// Sets a style for labeled content. - public func labeledContentStyle(_ style: S) -> some View where S: BackportLabeledContentStyle { - content.environment(\.backportLabeledContentStyle, .init(style)) - } -} - -@available(macOS 10.15, *) -@available(iOS, deprecated: 16) -@available(tvOS, deprecated: 16) -@available(macOS, deprecated: 13) -@available(watchOS, deprecated: 9) -public protocol BackportLabeledContentStyle { - typealias Configuration = Backport.LabeledContentStyleConfiguration - associatedtype Body: View - @ViewBuilder func makeBody(configuration: Configuration) -> Body -} - -@available(macOS 10.15, *) -internal struct AnyLabeledContentStyle: BackportLabeledContentStyle { - typealias Configuration = Backport.LabeledContentStyleConfiguration - let _makeBody: (Configuration) -> AnyView - - @available(macOS 10.15, *) - init(_ style: S) { - _makeBody = { config in - AnyView(style.makeBody(configuration: config)) - } - } - - @available(macOS 10.15, *) - func makeBody(configuration: Configuration) -> some View { - _makeBody(configuration) - } -} - -@available(macOS 10.15, *) -private struct BackportLabeledContentStyleEnvironmentKey: EnvironmentKey { - static var defaultValue: AnyLabeledContentStyle = .init(.automatic) -} - -@available(macOS 10.15, *) -@available(iOS, deprecated: 16) -@available(tvOS, deprecated: 16) -@available(macOS, deprecated: 13) -@available(watchOS, deprecated: 9) -extension EnvironmentValues { - var backportLabeledContentStyle: AnyLabeledContentStyle { - get { self[BackportLabeledContentStyleEnvironmentKey.self] } - set { self[BackportLabeledContentStyleEnvironmentKey.self] = newValue } - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/LabeledContent/LabeledContentStyleConfiguration.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/LabeledContent/LabeledContentStyleConfiguration.swift deleted file mode 100755 index 12db46a5..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/LabeledContent/LabeledContentStyleConfiguration.swift +++ /dev/null @@ -1,70 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -@available(iOS, deprecated: 16) -@available(tvOS, deprecated: 16) -@available(macOS, deprecated: 13) -@available(watchOS, deprecated: 9) -extension Backport where Wrapped == Any { - /// The properties of a labeled content instance. - public struct LabeledContentStyleConfiguration { - /// A type-erased label of a labeled content instance. - public struct Label: View { - @EnvironmentContains(key: "LabelsHiddenKey") private var isHidden - let view: AnyView - public var body: some View { - if isHidden { - EmptyView() - } else { - view - } - } - - @available(macOS 10.15, *) - init(_ view: V) { - self.view = .init(view) - } - } - - @available(macOS 10.15, *) - /// A type-erased content of a labeled content instance. - public struct Content: View { - @EnvironmentContains(key: "LabelsHiddenKey") private var isHidden - let view: AnyView - public var body: some View { - view - .foregroundColor(isHidden ? .primary : .secondary) - .frame(maxWidth: .infinity, alignment: isHidden ? .leading : .trailing) - } - - @available(macOS 10.15, *) - init(_ view: V) { - self.view = .init(view) - } - } - - @available(macOS 10.15, *) - /// The label of the labeled content instance. - public let label: Label - - @available(macOS 10.15, *) - /// The content of the labeled content instance. - public let content: Content - - @available(macOS 10.15, *) - internal init(label: L, content: C) { - self.label = .init(label) - self.content = .init(content) - } - - @available(macOS 10.15, *) - internal init(@ViewBuilder content: () -> C, @ViewBuilder label: () -> L) { - self.content = .init(content()) - self.label = .init(label()) - } - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/LabeledContent/Styles/AutomaticLabeledContentStyle.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/LabeledContent/Styles/AutomaticLabeledContentStyle.swift deleted file mode 100755 index 1d922ab0..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/LabeledContent/Styles/AutomaticLabeledContentStyle.swift +++ /dev/null @@ -1,24 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -public extension Backport where Wrapped == Any { - struct AutomaticLabeledContentStyle: BackportLabeledContentStyle { - public func makeBody(configuration: Configuration) -> some View { - HStack(alignment: .firstTextBaseline) { - configuration.label - Spacer() - configuration.content - .multilineTextAlignment(.trailing) - } - } - } -} - -@available(macOS 10.15, *) -extension BackportLabeledContentStyle where Self == Backport.AutomaticLabeledContentStyle { - static var automatic: Self { .init() } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Navigation/NavigationDestination.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Navigation/NavigationDestination.swift deleted file mode 100755 index 26b4a9ec..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Navigation/NavigationDestination.swift +++ /dev/null @@ -1,144 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -@available(iOS, deprecated: 16) -@available(tvOS, deprecated: 16) -@available(watchOS, deprecated: 9) -@available(macOS, deprecated: 13) -extension Backport where Wrapped: View { - /// Associates a destination view with a presented data type for use within - /// a navigation stack. - /// - /// Add this view modifer to a view inside a ``NavigationStack`` to - /// describe the view that the stack displays when presenting - /// a particular kind of data. Use a ``NavigationLink`` to present - /// the data. For example, you can present a `ColorDetail` view for - /// each presentation of a ``Color`` instance: - /// - /// NavigationStack { - /// List { - /// NavigationLink("Mint", value: Color.mint) - /// NavigationLink("Pink", value: Color.pink) - /// NavigationLink("Teal", value: Color.teal) - /// } - /// .navigationDestination(for: Color.self) { color in - /// ColorDetail(color: color) - /// } - /// .navigationTitle("Colors") - /// } - /// - /// You can add more than one navigation destination modifier to the stack - /// if it needs to present more than one kind of data. - /// - /// - Parameters: - /// - data: The type of data that this destination matches. - /// - destination: A view builder that defines a view to display - /// when the stack's navigation state contains a value of - /// type `data`. The closure takes one argument, which is the value - /// of the data to present. - public func navigationDestination(for _: D.Type, @ViewBuilder destination: @escaping (D) -> C) - -> some View - { - content - .environment( - \.navigationDestinations, - [ - .init(type: D.self): .init { destination($0 as! D) }, - ] - ) - } -} - -@available(macOS 10.15, *) -@available(iOS, deprecated: 16) -@available(tvOS, deprecated: 16) -@available(watchOS, deprecated: 9) -@available(macOS, deprecated: 13) -extension Backport where Wrapped == Any { - public struct NavigationLink: View where Label: View, Destination: View { - @Environment(\.navigationDestinations) private var destinations - - @available(macOS 10.15, *) - private let valueType: AnyMetaType - private let value: Any? - private let label: Label - private let destination: () -> Destination - - @available(macOS 10.15, *) - public init

(value: P?, @ViewBuilder label: () -> Label) where Destination == Never { - self.value = value - valueType = .init(type: P.self) - destination = { fatalError() } - self.label = label() - } - - @available(macOS 10.15, *) - public var body: some View { - SwiftUI.NavigationLink { - if let value = value { - destinations[valueType.type]?.content(value) - } - } label: { - label - } - .disabled(value == nil) - } - } -} - -@available(macOS 10.15, *) -private struct NavigationDestinationsEnvironmentKey: EnvironmentKey { - static var defaultValue: [AnyMetaType: DestinationView] = [:] -} - -@available(macOS 10.15, *) -private extension EnvironmentValues { - var navigationDestinations: [AnyMetaType: DestinationView] { - get { self[NavigationDestinationsEnvironmentKey.self] } - set { - var current = self[NavigationDestinationsEnvironmentKey.self] - newValue.forEach { current[$0] = $1 } - self[NavigationDestinationsEnvironmentKey.self] = current - } - } -} - -@available(macOS 10.15, *) -private struct AnyMetaType { - let type: Any.Type -} - -@available(macOS 10.15, *) -extension AnyMetaType: Equatable { - static func == (lhs: Self, rhs: Self) -> Bool { - lhs.type == rhs.type - } -} - -@available(macOS 10.15, *) -extension AnyMetaType: Hashable { - func hash(into hasher: inout Hasher) { - hasher.combine(ObjectIdentifier(type)) - } -} - -@available(macOS 10.15, *) -private extension Dictionary { - subscript(_ key: Any.Type) -> Value? where Key == AnyMetaType { - get { self[.init(type: key)] } - _modify { yield &self[.init(type: key)] } - } -} - -@available(macOS 10.15, *) -private struct DestinationView: View { - let content: (Any) -> AnyView - var body: Never { fatalError() } - init(content: @escaping (Any) -> Content) { - self.content = { AnyView(content($0)) } - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Navigation/NavigationTitle.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Navigation/NavigationTitle.swift deleted file mode 100755 index 1f592823..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Navigation/NavigationTitle.swift +++ /dev/null @@ -1,38 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -@available(iOS, deprecated: 14) -@available(watchOS, deprecated: 7) -@available(tvOS, deprecated: 14) -extension Backport where Wrapped: View { - @ViewBuilder - public func navigationTitle(_ title: S) -> some View { - #if os(macOS) - if #available(macOS 11, *) { - content.navigationTitle(title) - } else { - content - } - #else - content.navigationBarTitle(title) - #endif - } - - @available(macOS 10.15, *) - @ViewBuilder - public func navigationTitle(_ titleKey: LocalizedStringKey) -> some View { - #if os(macOS) - if #available(macOS 11, *) { - content.navigationTitle(titleKey) - } else { - content - } - #else - content.navigationBarTitle(titleKey) - #endif - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/OnChange/OnChange.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/OnChange/OnChange.swift deleted file mode 100755 index 76fe1dc8..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/OnChange/OnChange.swift +++ /dev/null @@ -1,55 +0,0 @@ -import Combine -import SwiftUI - -@available(macOS 10.15, *) -@available(iOS, deprecated: 14.0) -@available(macOS, deprecated: 11.0) -@available(tvOS, deprecated: 14.0) -@available(watchOS, deprecated: 7.0) -extension Backport where Wrapped: View { - /// Adds a modifier for this view that fires an action when a specific - /// value changes. - /// - /// `onChange` is called on the main thread. Avoid performing long-running - /// tasks on the main thread. If you need to perform a long-running task in - /// response to `value` changing, you should dispatch to a background queue. - /// - /// The new value is passed into the closure. - /// - /// - Parameters: - /// - value: The value to observe for changes - /// - action: A closure to run when the value changes. - /// - newValue: The new value that changed - /// - /// - Returns: A view that fires an action when the specified value changes. - @ViewBuilder - public func onChange(of value: Value, perform action: @escaping (Value) -> Void) -> some View { - content.modifier(ChangeModifier(value: value, action: action)) - } -} - -@available(macOS 10.15, *) -private struct ChangeModifier: ViewModifier { - let value: Value - let action: (Value) -> Void - - @available(macOS 10.15, *) - @State var oldValue: Value? - - @available(macOS 10.15, *) - init(value: Value, action: @escaping (Value) -> Void) { - self.value = value - self.action = action - _oldValue = .init(initialValue: value) - } - - @available(macOS 10.15, *) - func body(content: Content) -> some View { - content - .onReceive(Just(value)) { newValue in - guard newValue != oldValue else { return } - action(newValue) - oldValue = newValue - } - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/OpenURL/OpenURL.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/OpenURL/OpenURL.swift deleted file mode 100755 index d5472bf3..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/OpenURL/OpenURL.swift +++ /dev/null @@ -1,177 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -#if canImport(WatchKit) - import WatchKit -#endif - -@available(macOS 10.15, *) -@available(iOS, deprecated: 14) -@available(tvOS, deprecated: 14) -@available(macOS, deprecated: 11) -@available(watchOS, deprecated: 7) -@available(macOS 10.15, *) -extension Backport where Wrapped == Any { - /// An action that opens a URL. - /// - /// Read the ``EnvironmentValues.backportOpenURL`` environment value to get an - /// instance of this structure for a given ``Environment``. Call the - /// instance to open a URL. You call the instance directly because it - /// defines a ``Backport.OpenURLAction.callAsFunction(_:)`` method that Swift - /// calls when you call the instance. - /// - /// For example, you can open a web site when the user taps a button: - /// - /// struct OpenURLExample: View { - /// @Environment(\.backportOpenURL) private var openURL - /// - /// var body: some View { - /// Button { - /// if let url = URL(string: "https://www.example.com") { - /// openURL(url) - /// } - /// } label: { - /// Label("Get Help", systemImage: "person.fill.questionmark") - /// } - /// } - /// } - /// - /// If you want to know whether the action succeeds, add a completion - /// handler that takes a Boolean value. In this case, Swift implicitly - /// calls the ``Backport.OpenURLAction.callAsFunction(_:completion:)`` method - /// instead. That method calls your completion handler after it determines - /// whether it can open the URL, but possibly before it finishes opening - /// the URL. You can add a handler to the example above so that - /// it prints the outcome to the console: - /// - /// openURL(url) { accepted in - /// print(accepted ? "Success" : "Failure") - /// } - /// - /// The system provides a default open URL action with behavior - /// that depends on the contents of the URL. For example, the default - /// action opens a Universal Link in the associated app if possible, - /// or in the user’s default web browser if not. - /// - /// You can also set a custom action using the ``View.environment(_:_:)`` - /// view modifier. Any views that read the action from the environment, - /// including the built-in ``Link`` view and ``Text`` views with markdown - /// links, or links in attributed strings, use your action. Initialize an - /// action by calling the ``Backport.OpenURLAction.init(handler:)`` initializer with - /// a handler that takes a URL and returns an ``Backport.OpenURLAction.Result``: - /// - /// Text("Visit [Example Company](https://www.example.com) for details.") - /// .environment(\.backportOpenURL, Backport.OpenURLAction { url in - /// handleURL(url) // Define this method to take appropriate action. - /// return .handled - /// }) - /// - /// SwiftUI translates the value that your custom action's handler - /// returns into an appropriate Boolean result for the action call. - /// For example, a view that uses the action declared above - /// receives `true` when calling the action, because the - /// handler always returns ``Backport.OpenURLAction.Result.handled``. - public struct OpenURLAction { - @available(iOS, deprecated: 15) - @available(tvOS, deprecated: 15) - @available(macOS, deprecated: 12) - @available(watchOS, deprecated: 8) - public struct Result { - enum Value { - case handled - case discarded - case systemAction(_ url: URL?) - - @available(macOS 10.15, *) - var accepted: Bool { - if case .discarded = self { - return false - } else { - return true - } - } - } - - @available(macOS 10.15, *) - let value: Value - - @available(macOS 10.15, *) - public static var handled: Result { .init(value: .handled) } - public static var discarded: Result { .init(value: .discarded) } - public static var systemAction: Result { .init(value: .systemAction(nil)) } - public static func systemAction(_ url: URL) -> Result { .init(value: .systemAction(url)) } - } - - @available(macOS 10.15, *) - let handler: (URL) -> Result - - @available(macOS 10.15, *) - public init(handler: @escaping (URL) -> Result) { - self.handler = handler - } - - @available(macOS 10.15, *) - @available(watchOS, unavailable) - public func callAsFunction(_ url: URL) { - handleUrl(url) - } - - @available(macOS 10.15, *) - @available(watchOS, unavailable) - public func callAsFunction(_ url: URL, completion: @escaping (_ accepted: Bool) -> Void) { - let result = handleUrl(url) - completion(result.accepted) - } - - @available(macOS 10.15, *) - @discardableResult - private func handleUrl(_ url: URL) -> Result.Value { - let result = handler(url).value - - switch result { - case .handled, .discarded: break - case let .systemAction(updatedUrl): - let resolved = updatedUrl ?? url - #if os(macOS) - NSWorkspace.shared.open(resolved) - #elseif os(iOS) || os(tvOS) - UIApplication.shared.open(resolved) - #else - WKExtension.shared().openSystemURL(resolved) - #endif - } - - return result - } - } -} - -@available(macOS 10.15, *) -private struct BackportOpenURLKey: EnvironmentKey { - static var defaultValue: Backport.OpenURLAction { - .init { url in - #if os(macOS) - return .systemAction - #elseif os(iOS) || os(tvOS) - if UIApplication.shared.canOpenURL(url) { - return .systemAction - } else { - return .discarded - } - #else - return .systemAction - #endif - } - } -} - -@available(macOS 10.15, *) -public extension EnvironmentValues { - var backportOpenURL: Backport.OpenURLAction { - get { self[BackportOpenURLKey.self] } - set { self[BackportOpenURLKey.self] = newValue } - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Overlay/Overlay.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Overlay/Overlay.swift deleted file mode 100755 index 62aa2941..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Overlay/Overlay.swift +++ /dev/null @@ -1,127 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -public extension Backport where Wrapped: View { - /// Layers the views that you specify in front of this view. - /// - /// Use this modifier to place one or more views in front of another view. - /// For example, you can place a group of stars on a ``RoundedRectangle``: - /// - /// RoundedRectangle(cornerRadius: 8) - /// .frame(width: 200, height: 100) - /// .overlay(alignment: .topLeading) { Star(color: .red) } - /// .overlay(alignment: .topTrailing) { Star(color: .yellow) } - /// .overlay(alignment: .bottomLeading) { Star(color: .green) } - /// .overlay(alignment: .bottomTrailing) { Star(color: .blue) } - /// - /// The example above assumes that you've defined a `Star` view with a - /// parameterized color: - /// - /// struct Star: View { - /// var color = Color.yellow - /// - /// var body: some View { - /// Image(systemName: "star.fill") - /// .foregroundStyle(color) - /// } - /// } - /// - /// By setting different `alignment` values for each modifier, you make the - /// stars appear in different places on the rectangle: - /// - /// ![A screenshot of a rounded rectangle with a star in each corner. The - /// star in the upper-left is red; the start in the upper-right is yellow; - /// the star in the lower-left is green; the star the lower-right is - /// blue.](View-overlay-2) - /// - /// If you specify more than one view in the `content` closure, the modifier - /// collects all of the views in the closure into an implicit ``ZStack``, - /// taking them in order from back to front. For example, you can place a - /// star and a ``Circle`` on a field of ``ShapeStyle/blue``: - /// - /// Color.blue - /// .frame(width: 200, height: 200) - /// .overlay { - /// Circle() - /// .frame(width: 100, height: 100) - /// Star() - /// } - /// - /// Both the overlay modifier and the implicit ``ZStack`` composed from the - /// overlay content --- the circle and the star --- use a default - /// ``Alignment/center`` alignment. The star appears centered on the circle, - /// and both appear as a composite view centered in front of the square: - /// - /// ![A screenshot of a star centered on a circle, which is - /// centered on a square.](View-overlay-3) - /// - /// If you specify an alignment for the overlay, it applies to the implicit - /// stack rather than to the individual views in the closure. You can see - /// this if you add the ``Alignment/bottom`` alignment: - /// - /// Color.blue - /// .frame(width: 200, height: 200) - /// .overlay(alignment: .bottom) { - /// Circle() - /// .frame(width: 100, height: 100) - /// Star() - /// } - /// - /// The circle and the star move down as a unit to align the stack's bottom - /// edge with the bottom edge of the square, while the star remains - /// centered on the circle: - /// - /// ![A screenshot of a star centered on a circle, which is on a square. - /// The circle's bottom edge is aligned with the square's bottom - /// edge.](View-overlay-3a) - /// - /// To control the placement of individual items inside the `content` - /// closure, either use a different overlay modifier for each item, as the - /// earlier example of stars in the corners of a rectangle demonstrates, or - /// add an explicit ``ZStack`` inside the content closure with its own - /// alignment: - /// - /// Color.blue - /// .frame(width: 200, height: 200) - /// .overlay(alignment: .bottom) { - /// ZStack(alignment: .bottom) { - /// Circle() - /// .frame(width: 100, height: 100) - /// Star() - /// } - /// } - /// - /// The stack alignment ensures that the star's bottom edge aligns with the - /// circle's, while the overlay aligns the composite view with the square: - /// - /// ![A screenshot of a star, a circle, and a square with all their - /// bottom edges aligned.](View-overlay-4) - /// - /// You can achieve layering without an overlay modifier by putting both the - /// modified view and the overlay content into a ``ZStack``. This can - /// produce a simpler view hierarchy, but changes the layout priority that - /// SwiftUI applies to the views. Use the overlay modifier when you want the - /// modified view to dominate the layout. - /// - /// If you want to specify a ``ShapeStyle`` like a ``Color`` or a - /// ``Material`` as the overlay, use - /// ``View/overlay(_:ignoresSafeAreaEdges:)`` instead. To specify a - /// ``Shape``, use ``View/overlay(_:in:fillStyle:)``. - /// - /// - Parameters: - /// - alignment: The alignment that the modifier uses to position the - /// implicit ``ZStack`` that groups the foreground views. The default - /// is ``Alignment/center``. - /// - content: A ``ViewBuilder`` that you use to declare the views to - /// draw in front of this view, stacked in the order that you list them. - /// The last view that you list appears at the front of the stack. - /// - /// - Returns: A view that uses the specified content as a foreground. - func overlay(alignment: Alignment = .center, @ViewBuilder _ content: () -> Content) -> some View { - self.content.overlay(content(), alignment: alignment) - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/ProgressView.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/ProgressView.swift deleted file mode 100755 index e619a734..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/ProgressView.swift +++ /dev/null @@ -1,256 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -@available(iOS, deprecated: 14) -@available(macOS, deprecated: 11) -@available(tvOS, deprecated: 14.0) -@available(watchOS, deprecated: 7.0) -extension Backport where Wrapped == Any { - public struct ProgressView: View { - @Environment(\.backportProgressViewStyle) private var style - let config: Backport.ProgressViewStyleConfiguration - - @available(macOS 10.15, *) - public var body: some View { - Group { - if let style = style { - style.makeBody(configuration: config) - } else { - DefaultProgressViewStyle().makeBody(configuration: config) - } - } - } - } -} - -@available(macOS 10.15, *) -@available(iOS, deprecated: 14) -@available(macOS, deprecated: 11) -@available(tvOS, deprecated: 14.0) -@available(watchOS, unavailable) -extension Backport.ProgressView where Wrapped == Any, CurrentValueLabel == EmptyView { - /// Creates a progress view for showing indeterminate progress, without a - /// label. - public init() where Label == EmptyView { - self.init(config: .init(fractionCompleted: nil, preferredKind: .circular)) - } - - @available(macOS 10.15, *) - /// Creates a progress view for showing indeterminate progress that displays - /// a custom label. - /// - /// - Parameters: - /// - label: A view builder that creates a view that describes the task - /// in progress. - public init(@ViewBuilder label: () -> Label) { - config = .init(fractionCompleted: nil, label: .init(content: label()), preferredKind: .circular) - } - - @available(macOS 10.15, *) - /// Creates a progress view for showing indeterminate progress that - /// generates its label from a localized string. - /// - /// This initializer creates a ``Text`` view on your behalf, and treats the - /// localized key similar to ``Text/init(_:tableName:bundle:comment:)``. See - /// ``Text`` for more information about localizing strings. To initialize a - /// indeterminate progress view with a string variable, use - /// the corresponding initializer that takes a `StringProtocol` instance. - /// - /// - Parameters: - /// - titleKey: The key for the progress view's localized title that - /// describes the task in progress. - public init(_ titleKey: LocalizedStringKey) where Label == Text { - config = .init(fractionCompleted: nil, label: .init(content: Text(titleKey)), preferredKind: .circular) - } - - @available(macOS 10.15, *) - /// Creates a progress view for showing indeterminate progress that - /// generates its label from a string. - /// - /// - Parameters: - /// - title: A string that describes the task in progress. - /// - /// This initializer creates a ``Text`` view on your behalf, and treats the - /// title similar to ``Text/init(verbatim:)``. See ``Text`` for more - /// information about localizing strings. To initialize a progress view with - /// a localized string key, use the corresponding initializer that takes a - /// `LocalizedStringKey` instance. - public init(_ title: S) where Label == Text, S: StringProtocol { - config = .init(fractionCompleted: nil, label: .init(content: Text(title)), preferredKind: .circular) - } -} - -@available(macOS 10.15, *) -@available(iOS, deprecated: 14) -@available(macOS, deprecated: 11) -@available(tvOS, deprecated: 14.0) -@available(watchOS, deprecated: 7.0) -extension Backport.ProgressView where Wrapped == Any { - /// Creates a progress view for showing determinate progress. - /// - /// If the value is non-`nil`, but outside the range of `0.0` through - /// `total`, the progress view pins the value to those limits, rounding to - /// the nearest possible bound. A value of `nil` represents indeterminate - /// progress, in which case the progress view ignores `total`. - /// - /// - Parameters: - /// - value: The completed amount of the task to this point, in a range - /// of `0.0` to `total`, or `nil` if the progress is indeterminate. - /// - total: The full amount representing the complete scope of the - /// task, meaning the task is complete if `value` equals `total`. The - /// default value is `1.0`. - public init(value: V?, total: V = 1.0) - where Label == EmptyView, CurrentValueLabel == EmptyView, V: BinaryFloatingPoint - { - if let value = value { - config = .init(fractionCompleted: Double(value) / Double(total), preferredKind: .linear, max: Double(total)) - } else { - config = .init(fractionCompleted: nil, preferredKind: .linear) - } - } - - @available(macOS 10.15, *) - /// Creates a progress view for showing determinate progress, with a - /// custom label. - /// - /// If the value is non-`nil`, but outside the range of `0.0` through - /// `total`, the progress view pins the value to those limits, rounding to - /// the nearest possible bound. A value of `nil` represents indeterminate - /// progress, in which case the progress view ignores `total`. - /// - /// - Parameters: - /// - value: The completed amount of the task to this point, in a range - /// of `0.0` to `total`, or `nil` if the progress is indeterminate. - /// - total: The full amount representing the complete scope of the - /// task, meaning the task is complete if `value` equals `total`. The - /// default value is `1.0`. - /// - label: A view builder that creates a view that describes the task - /// in progress. - public init(value: V?, total: V = 1.0, @ViewBuilder label: () -> Label) - where CurrentValueLabel == EmptyView, V: BinaryFloatingPoint - { - if let value = value { - config = .init( - fractionCompleted: Double(value) / Double(total), label: .init(content: label()), preferredKind: .linear - ) - } else { - config = .init(fractionCompleted: nil, label: .init(content: label()), preferredKind: .linear, max: Double(total)) - } - } - - @available(macOS 10.15, *) - /// Creates a progress view for showing determinate progress, with a - /// custom label. - /// - /// If the value is non-`nil`, but outside the range of `0.0` through - /// `total`, the progress view pins the value to those limits, rounding to - /// the nearest possible bound. A value of `nil` represents indeterminate - /// progress, in which case the progress view ignores `total`. - /// - /// - Parameters: - /// - value: The completed amount of the task to this point, in a range - /// of `0.0` to `total`, or `nil` if the progress is indeterminate. - /// - total: The full amount representing the complete scope of the - /// task, meaning the task is complete if `value` equals `total`. The - /// default value is `1.0`. - /// - label: A view builder that creates a view that describes the task - /// in progress. - /// - currentValueLabel: A view builder that creates a view that - /// describes the level of completed progress of the task. - public init( - value: V?, total: V = 1.0, @ViewBuilder label: () -> Label, @ViewBuilder currentValueLabel: () -> CurrentValueLabel - ) where V: BinaryFloatingPoint { - if let value = value { - config = .init( - fractionCompleted: Double(value) / Double(total), label: .init(content: label()), - currentValueLabel: .init(content: currentValueLabel()), preferredKind: .linear, max: Double(total) - ) - } else { - config = .init( - fractionCompleted: nil, label: .init(content: label()), currentValueLabel: .init(content: currentValueLabel()), - preferredKind: .linear, max: Double(total) - ) - } - } - - @available(macOS 10.15, *) - /// Creates a progress view for showing determinate progress that generates - /// its label from a localized string. - /// - /// If the value is non-`nil`, but outside the range of `0.0` through - /// `total`, the progress view pins the value to those limits, rounding to - /// the nearest possible bound. A value of `nil` represents indeterminate - /// progress, in which case the progress view ignores `total`. - /// - /// This initializer creates a ``Text`` view on your behalf, and treats the - /// localized key similar to ``Text/init(_:tableName:bundle:comment:)``. See - /// ``Text`` for more information about localizing strings. To initialize a - /// determinate progress view with a string variable, use - /// the corresponding initializer that takes a `StringProtocol` instance. - /// - /// - Parameters: - /// - titleKey: The key for the progress view's localized title that - /// describes the task in progress. - /// - value: The completed amount of the task to this point, in a range - /// of `0.0` to `total`, or `nil` if the progress is - /// indeterminate. - /// - total: The full amount representing the complete scope of the - /// task, meaning the task is complete if `value` equals `total`. The - /// default value is `1.0`. - public init(_ titleKey: LocalizedStringKey, value: V?, total: V = 1.0) - where Label == Text, CurrentValueLabel == EmptyView, V: BinaryFloatingPoint - { - if let value = value { - config = .init( - fractionCompleted: Double(value) / Double(total), label: .init(content: Text(titleKey)), preferredKind: .linear, - max: Double(total) - ) - } else { - config = .init( - fractionCompleted: nil, label: .init(content: Text(titleKey)), preferredKind: .linear, max: Double(total) - ) - } - } - - @available(macOS 10.15, *) - /// Creates a progress view for showing determinate progress that generates - /// its label from a string. - /// - /// If the value is non-`nil`, but outside the range of `0.0` through - /// `total`, the progress view pins the value to those limits, rounding to - /// the nearest possible bound. A value of `nil` represents indeterminate - /// progress, in which case the progress view ignores `total`. - /// - /// This initializer creates a ``Text`` view on your behalf, and treats the - /// title similar to ``Text/init(verbatim:)``. See ``Text`` for more - /// information about localizing strings. To initialize a determinate - /// progress view with a localized string key, use the corresponding - /// initializer that takes a `LocalizedStringKey` instance. - /// - /// - Parameters: - /// - title: The string that describes the task in progress. - /// - value: The completed amount of the task to this point, in a range - /// of `0.0` to `total`, or `nil` if the progress is - /// indeterminate. - /// - total: The full amount representing the complete scope of the - /// task, meaning the task is complete if `value` equals `total`. The - /// default value is `1.0`. - public init(_ title: S, value: V?, total: V = 1.0) - where Label == Text, CurrentValueLabel == EmptyView, S: StringProtocol, V: BinaryFloatingPoint - { - if let value = value { - config = .init( - fractionCompleted: Double(value) / Double(total), label: .init(content: Text(title)), preferredKind: .linear, - max: Double(total) - ) - } else { - config = .init( - fractionCompleted: nil, label: .init(content: Text(title)), preferredKind: .linear, max: Double(total) - ) - } - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/ProgressViewConfiguration.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/ProgressViewConfiguration.swift deleted file mode 100755 index ee794649..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/ProgressViewConfiguration.swift +++ /dev/null @@ -1,73 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -@available(iOS, deprecated: 14) -@available(macOS, deprecated: 11) -@available(tvOS, deprecated: 14.0) -@available(watchOS, deprecated: 7.0) -extension Backport where Wrapped == Any { - /// The properties of a progress view instance. - public struct ProgressViewStyleConfiguration { - internal enum Kind { - case circular - case linear - } - - @available(macOS 10.15, *) - /// A type-erased label describing the task represented by the progress - /// view. - public struct Label: View { - let content: AnyView - public var body: some View { content } - init(content: Content) { - self.content = .init(content) - } - } - - @available(macOS 10.15, *) - /// A type-erased label that describes the current value of a progress view. - public struct CurrentValueLabel: View { - let content: AnyView - public var body: some View { content } - init(content: Content) { - self.content = .init(content) - } - } - - @available(macOS 10.15, *) - /// The completed fraction of the task represented by the progress view, - /// from `0.0` (not yet started) to `1.0` (fully complete), or `nil` if the - /// progress is indeterminate or relative to a date interval. - public let fractionCompleted: Double? - - @available(macOS 10.15, *) - /// A view that describes the task represented by the progress view. - /// - /// If `nil`, then the task is self-evident from the surrounding context, - /// and the style does not need to provide any additional description. - /// - /// If the progress view is defined using a `Progress` instance, then this - /// label is equivalent to its `localizedDescription`. - public var label: Label? - - @available(macOS 10.15, *) - /// A view that describes the current value of a progress view. - /// - /// If `nil`, then the value of the progress view is either self-evident - /// from the surrounding context or unknown, and the style does not need to - /// provide any additional description. - /// - /// If the progress view is defined using a `Progress` instance, then this - /// label is equivalent to its `localizedAdditionalDescription`. - public var currentValueLabel: CurrentValueLabel? - - @available(macOS 10.15, *) - internal let preferredKind: Kind - internal var min: Double = 0 - internal var max: Double = 1 - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/ProgressViewStyle.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/ProgressViewStyle.swift deleted file mode 100755 index dc1a4bc2..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/ProgressViewStyle.swift +++ /dev/null @@ -1,74 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(iOS, deprecated: 14) -@available(macOS, deprecated: 11) -@available(tvOS, deprecated: 14.0) -@available(watchOS, deprecated: 7.0) -@available(macOS 10.15, *) -/// A type that applies standard interaction behavior to all progress views -/// within a view hierarchy. -/// -/// To configure the current progress view style for a view hierarchy, use the -/// ``View/progressViewStyle(_:)`` modifier. -public protocol BackportProgressViewStyle { - /// A type alias for the properties of a progress view instance. - typealias Configuration = Backport.ProgressViewStyleConfiguration - - /// A view representing the body of a progress view. - associatedtype Body: View - - /// Creates a view representing the body of a progress view. - /// - /// - Parameter configuration: The properties of the progress view being - /// created. - /// - /// The view hierarchy calls this method for each progress view where this - /// style is the current progress view style. - /// - /// - Parameter configuration: The properties of the progress view, such as - /// its preferred progress type. - @ViewBuilder func makeBody(configuration: Configuration) -> Body -} - -@available(iOS, deprecated: 14) -@available(macOS, deprecated: 11) -@available(tvOS, deprecated: 14.0) -@available(watchOS, deprecated: 7.0) -@available(macOS 10.15, *) -extension Backport where Wrapped: View { - public func progressViewStyle(_ style: S) -> some View { - content.environment(\.backportProgressViewStyle, .init(style)) - } -} - -@available(macOS 10.15, *) -internal struct AnyProgressViewStyle: BackportProgressViewStyle { - let _makeBody: (Backport.ProgressViewStyleConfiguration) -> AnyView - - init(_ style: S) { - _makeBody = { config in - AnyView(style.makeBody(configuration: config)) - } - } - - func makeBody(configuration: Configuration) -> some View { - _makeBody(configuration) - } -} - -@available(macOS 10.15, *) -private struct BackportProgressViewStyleEnvironmentKey: EnvironmentKey { - static var defaultValue: AnyProgressViewStyle? -} - -@available(macOS 10.15, *) -extension EnvironmentValues { - var backportProgressViewStyle: AnyProgressViewStyle? { - get { self[BackportProgressViewStyleEnvironmentKey.self] } - set { self[BackportProgressViewStyleEnvironmentKey.self] = newValue } - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/Styles/CircularProgressViewStyle.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/Styles/CircularProgressViewStyle.swift deleted file mode 100755 index aa5e02e1..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/Styles/CircularProgressViewStyle.swift +++ /dev/null @@ -1,89 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -@available(iOS, deprecated: 14) -@available(macOS, deprecated: 11) -@available(tvOS, deprecated: 14.0) -@available(watchOS, deprecated: 7.0) -extension Backport where Wrapped == Any { - /// A progress view that visually indicates its progress using a circular gauge. - /// - /// You can also use ``ProgressViewStyle/circular`` to construct this style. - public struct CircularProgressViewStyle: BackportProgressViewStyle { - /// Creates a circular progress view style. - public init() {} - - @available(macOS 10.15, *) - /// Creates a view representing the body of a progress view. - /// - /// - Parameter configuration: The properties of the progress view being - /// created. - /// - /// The view hierarchy calls this method for each progress view where this - /// style is the current progress view style. - /// - /// - Parameter configuration: The properties of the progress view, such as - /// its preferred progress type. - public func makeBody(configuration: Configuration) -> some View { - VStack { - #if !os(watchOS) - CircularRepresentable(configuration: configuration) - #endif - - configuration.label? - .foregroundColor(.secondary) - } - } - } -} - -@available(macOS 10.15, *) -public extension BackportProgressViewStyle where Self == Backport.CircularProgressViewStyle { - static var circular: Self { .init() } -} - -#if os(macOS) - @available(macOS 10.15, *) - private struct CircularRepresentable: NSViewRepresentable { - let configuration: Backport.ProgressViewStyleConfiguration - - @available(macOS 10.15, *) - func makeNSView(context _: Context) -> NSProgressIndicator { - .init() - } - - @available(macOS 10.15, *) - func updateNSView(_ view: NSProgressIndicator, context _: Context) { - if let value = configuration.fractionCompleted { - view.doubleValue = value - view.maxValue = configuration.max - } - - view.isIndeterminate = configuration.fractionCompleted == nil - view.style = .spinning - view.isDisplayedWhenStopped = true - view.startAnimation(nil) - } - } - -#elseif !os(watchOS) - @available(macOS 10.15, *) - private struct CircularRepresentable: UIViewRepresentable { - let configuration: Backport.ProgressViewStyleConfiguration - - @available(macOS 10.15, *) - func makeUIView(context _: Context) -> UIActivityIndicatorView { - .init(style: .medium) - } - - @available(macOS 10.15, *) - func updateUIView(_ view: UIActivityIndicatorView, context _: Context) { - view.hidesWhenStopped = false - view.startAnimating() - } - } -#endif diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/Styles/DefaultProgressViewStyle.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/Styles/DefaultProgressViewStyle.swift deleted file mode 100755 index a80edbbe..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/Styles/DefaultProgressViewStyle.swift +++ /dev/null @@ -1,54 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -@available(iOS, deprecated: 14) -@available(macOS, deprecated: 11) -@available(tvOS, deprecated: 14.0) -@available(watchOS, deprecated: 7.0) -extension Backport where Wrapped == Any { - /// The default progress view style in the current context of the view being - /// styled. - /// - /// You can also use ``ProgressViewStyle/automatic`` to construct this style. - public struct DefaultProgressViewStyle: BackportProgressViewStyle { - /// Creates a default progress view style. - public init() {} - - @available(macOS 10.15, *) - /// Creates a view representing the body of a progress view. - /// - /// - Parameter configuration: The properties of the progress view being - /// created. - /// - /// The view hierarchy calls this method for each progress view where this - /// style is the current progress view style. - /// - /// - Parameter configuration: The properties of the progress view, such as - /// its preferred progress type. - public func makeBody(configuration: Configuration) -> some View { - switch configuration.preferredKind { - case .circular: - Backport.CircularProgressViewStyle().makeBody(configuration: configuration) - case .linear: - #if os(iOS) - if configuration.fractionCompleted == nil { - Backport.CircularProgressViewStyle().makeBody(configuration: configuration) - } else { - Backport.LinearProgressViewStyle().makeBody(configuration: configuration) - } - #else - Backport.LinearProgressViewStyle().makeBody(configuration: configuration) - #endif - } - } - } -} - -@available(macOS 10.15, *) -public extension BackportProgressViewStyle where Self == Backport.DefaultProgressViewStyle { - static var automatic: Self { .init() } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/Styles/LinearProgressViewStyle.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/Styles/LinearProgressViewStyle.swift deleted file mode 100755 index c11e7aa0..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/ProgressView/Styles/LinearProgressViewStyle.swift +++ /dev/null @@ -1,111 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -@available(iOS, deprecated: 14) -@available(macOS, deprecated: 11) -@available(tvOS, deprecated: 14.0) -@available(watchOS, deprecated: 7.0) -extension Backport where Wrapped == Any { - /// A progress view that visually indicates its progress using a horizontal bar. - /// - /// You can also use ``ProgressViewStyle/linear`` to construct this style. - public struct LinearProgressViewStyle: BackportProgressViewStyle { - /// Creates a linear progress view style. - public init() {} - - /// Creates a view representing the body of a progress view. - /// - /// - Parameter configuration: The properties of the progress view being - /// created. - /// - /// The view hierarchy calls this method for each progress view where this - /// style is the current progress view style. - /// - /// - Parameter configuration: The properties of the progress view, such as - /// its preferred progress type. - @available(macOS 10.15, *) - public func makeBody(configuration: Configuration) -> some View { - #if os(macOS) - VStack(alignment: .leading, spacing: 0) { - configuration.label - .foregroundColor(.primary) - - LinearRepresentable(configuration: configuration) - - configuration.currentValueLabel - .foregroundColor(.secondary) - } - .controlSize(.small) - #else - VStack(alignment: .leading, spacing: 5) { - if configuration.fractionCompleted == nil { - CircularProgressViewStyle().makeBody(configuration: configuration) - } else { - configuration.label? - .foregroundColor(.primary) - - #if !os(watchOS) - LinearRepresentable(configuration: configuration) - #endif - - configuration.currentValueLabel? - .foregroundColor(.secondary) - .font(.caption) - } - } - #endif - } - } -} - -@available(macOS 10.15, *) -public extension BackportProgressViewStyle where Self == Backport.LinearProgressViewStyle { - static var linear: Self { .init() } -} - -#if os(macOS) - @available(macOS 10.15, *) - private struct LinearRepresentable: NSViewRepresentable { - let configuration: Backport.ProgressViewStyleConfiguration - - @available(macOS 10.15, *) - func makeNSView(context _: Context) -> NSProgressIndicator { - .init() - } - - @available(macOS 10.15, *) - func updateNSView(_ view: NSProgressIndicator, context _: Context) { - if let value = configuration.fractionCompleted { - view.doubleValue = value - view.maxValue = configuration.max - - view.display() - } - - view.style = .bar - view.isIndeterminate = configuration.fractionCompleted == nil - view.isDisplayedWhenStopped = true - view.startAnimation(nil) - } - } - -#elseif !os(watchOS) - @available(macOS 10.15, *) - private struct LinearRepresentable: UIViewRepresentable { - let configuration: Backport.ProgressViewStyleConfiguration - - @available(macOS 10.15, *) - func makeUIView(context _: Context) -> UIProgressView { - .init(progressViewStyle: .default) - } - - @available(macOS 10.15, *) - func updateUIView(_ view: UIProgressView, context _: Context) { - view.progress = Float(configuration.fractionCompleted ?? 0) - } - } -#endif diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Quicklook/Quicklook+iOS.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Quicklook/Quicklook+iOS.swift deleted file mode 100755 index dba98d2b..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Quicklook/Quicklook+iOS.swift +++ /dev/null @@ -1,88 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -#if os(iOS) - import QuickLook - - final class PreviewController: UIViewController, UIAdaptivePresentationControllerDelegate, - QLPreviewControllerDelegate, QLPreviewControllerDataSource - where Items: RandomAccessCollection, Items.Element == URL - { - var items: Items - - var selection: Binding { - didSet { - updateControllerLifecycle( - from: oldValue.wrappedValue, - to: selection.wrappedValue - ) - } - } - - init(selection: Binding, in items: Items) { - self.selection = selection - self.items = items - super.init(nibName: nil, bundle: nil) - } - - @available(*, unavailable) - required init?(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func updateControllerLifecycle(from oldValue: Items.Element?, to newValue: Items.Element?) { - switch (oldValue, newValue) { - case (.none, .some): - presentController() - case (.some, .some): - updateController() - case (.some, .none): - dismissController() - case (.none, .none): - break - } - } - - private func presentController() { - print("Present") - let controller = QLPreviewController(nibName: nil, bundle: nil) - controller.dataSource = self - controller.delegate = self - present(controller, animated: true) - updateController() - } - - private func updateController() { - let controller = presentedViewController as? QLPreviewController - controller?.reloadData() - let index = selection.wrappedValue.flatMap { items.firstIndex(of: $0) } - controller?.currentPreviewItemIndex = items.distance(from: items.startIndex, to: index ?? items.startIndex) - } - - private func dismissController() { - DispatchQueue.main.async { - self.selection.wrappedValue = nil - } - } - - func numberOfPreviewItems(in _: QLPreviewController) -> Int { - items.isEmpty ? 1 : items.count - } - - func previewController(_: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem { - if items.isEmpty { - return (selection.wrappedValue ?? URL(fileURLWithPath: "")) as NSURL - } else { - let index = items.index(items.startIndex, offsetBy: index) - return items[index] as NSURL - } - } - - func previewControllerDidDismiss(_: QLPreviewController) { - dismissController() - } - } -#endif diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Quicklook/Quicklook+macOS.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Quicklook/Quicklook+macOS.swift deleted file mode 100755 index 74731927..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Quicklook/Quicklook+macOS.swift +++ /dev/null @@ -1,115 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -#if os(macOS) - import QuickLook - import QuickLookUI - - @available(macOS 10.15, *) - final class PreviewController: NSViewController, QLPreviewPanelDataSource, QLPreviewPanelDelegate - where Items: RandomAccessCollection, Items.Element == URL - { - private let panel = QLPreviewPanel.shared()! - private weak var windowResponder: NSResponder? - - var items: Items - - var selection: Binding { - didSet { - updateControllerLifecycle( - from: oldValue.wrappedValue, - to: selection.wrappedValue - ) - } - } - - private func updateControllerLifecycle(from oldValue: Items.Element?, to newValue: Items.Element?) { - switch (oldValue, newValue) { - case (.none, .some): - present() - case (.some, .some): - update() - case (.some, .none): - dismiss() - case (.none, .none): - break - } - } - - init(selection: Binding, in items: Items) { - self.selection = selection - self.items = items - super.init(nibName: nil, bundle: nil) - windowResponder = NSApp.mainWindow?.nextResponder - } - - @available(*, unavailable) - required init?(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func loadView() { - view = .init(frame: .zero) - } - - var isVisible: Bool { - QLPreviewPanel.sharedPreviewPanelExists() && panel.isVisible - } - - private func present() { - print("Present") - - NSApp.mainWindow?.nextResponder = self - - if isVisible { - panel.updateController() - let index = selection.wrappedValue.flatMap { items.firstIndex(of: $0) } - panel.currentPreviewItemIndex = items.distance(from: items.startIndex, to: index ?? items.startIndex) - } else { - panel.makeKeyAndOrderFront(nil) - } - } - - private func update() { - present() - } - - private func dismiss() { - selection.wrappedValue = nil - } - - func numberOfPreviewItems(in _: QLPreviewPanel!) -> Int { - items.isEmpty ? 1 : items.count - } - - func previewPanel(_: QLPreviewPanel!, previewItemAt index: Int) -> QLPreviewItem! { - if items.isEmpty { - return selection.wrappedValue as? NSURL - } else { - let index = items.index(items.startIndex, offsetBy: index) - return items[index] as NSURL - } - } - - override func acceptsPreviewPanelControl(_: QLPreviewPanel!) -> Bool { - print("Accept") - return true - } - - override func beginPreviewPanelControl(_ panel: QLPreviewPanel!) { - print("Begin") - panel.dataSource = self - panel.reloadData() - } - - override func endPreviewPanelControl(_ panel: QLPreviewPanel!) { - print("End") - panel.dataSource = nil - dismiss() - } - } - -#endif diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Quicklook/Quicklook.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Quicklook/Quicklook.swift deleted file mode 100755 index e9d3f962..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Quicklook/Quicklook.swift +++ /dev/null @@ -1,101 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -#if canImport(QuickLook) - import QuickLook -#endif - -@available(iOS, deprecated: 14) -@available(macOS, deprecated: 11) -@available(tvOS, unavailable) -@available(watchOS, unavailable) -@available(macOS 10.15, *) -extension Backport where Wrapped: View { - /// Presents a Quick Look preview of the URLs you provide. - /// - /// The Quick Look preview appears when you set the binding to a non-`nil` item. - /// When you set the item back to `nil`, Quick Look dismisses the preview. - /// If the value of the selection binding isn’t contained in the items collection, Quick Look treats it the same as a `nil` selection. - /// - /// Quick Look updates the value of the selection binding to match the URL of the file the user is previewing. - /// Upon dismissal by the user, Quick Look automatically sets the item binding to `nil`. - /// - /// - Parameters: - /// - selection: A to an element that’s part of the items collection. This is the URL that you currently want to preview. - /// - items: A collection of URLs to preview. - /// - /// - Returns: A view that presents the preview of the contents of the URL. - public func quickLookPreview(_ selection: Binding, in items: Items) -> some View - where Items: RandomAccessCollection, Items.Element == URL - { - #if os(iOS) || os(macOS) - content.background(QuicklookSheet(selection: selection, items: items)) - #else - content - #endif - } - - /// Presents a Quick Look preview of the contents of a single URL. - /// - /// The Quick Look preview appears when you set the binding to a non-`nil` item. - /// When you set the item back to `nil`, Quick Look dismisses the preview. - /// - /// Upon dismissal by the user, Quick Look automatically sets the item binding to `nil`. - /// Quick Look displays the preview when a non-`nil` item is set. - /// Set `item` to `nil` to dismiss the preview. - /// - /// - Parameters: - /// - item: A to a URL that should be previewed. - /// - /// - Returns: A view that presents the preview of the contents of the URL. - public func quickLookPreview(_ item: Binding) -> some View { - #if os(iOS) || os(macOS) - content.background(QuicklookSheet(selection: item, items: [item.wrappedValue].compactMap { $0 })) - #else - content - #endif - } -} - -#if os(macOS) - import QuickLookUI - - @available(macOS 10.15, *) - private struct QuicklookSheet: NSViewControllerRepresentable - where Items: RandomAccessCollection, Items.Element == URL - { - let selection: Binding - let items: Items - - func makeNSViewController(context _: Context) -> PreviewController { - .init(selection: selection, in: items) - } - - func updateNSViewController(_ controller: PreviewController, context _: Context) { - controller.selection = selection - controller.items = items - } - } - -#elseif os(iOS) - - private struct QuicklookSheet: UIViewControllerRepresentable - where Items: RandomAccessCollection, Items.Element == URL - { - let selection: Binding - let items: Items - - func makeUIViewController(context _: Context) -> PreviewController { - .init(selection: selection, in: items) - } - - func updateUIViewController(_ controller: PreviewController, context _: Context) { - controller.items = items - controller.selection = selection - } - } - -#endif diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Refreshable/Refreshable.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Refreshable/Refreshable.swift deleted file mode 100755 index 749a7b22..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Refreshable/Refreshable.swift +++ /dev/null @@ -1,194 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -@available(iOS, deprecated: 15) -@available(macOS, deprecated: 12) -@available(tvOS, deprecated: 15) -@available(watchOS, deprecated: 8) -extension Backport where Wrapped: View { - /// Marks this view as refreshable. - /// - /// Apply this modifier to a view to set the ``EnvironmentValues/refresh`` - /// value in the view's environment to a ``RefreshAction`` instance that - /// uses the specified `action` as its handler. Views that detect the - /// presence of the instance can change their appearance to provide a - /// way for the user to execute the handler. - /// - /// You can add refresh capability to your own views as well. For - /// information on how to do that, see ``RefreshAction``. - /// - /// - Parameters: - /// - action: An asynchronous handler that SwiftUI executes when the - /// user requests a refresh. Use this handler to initiate - /// an update of model data displayed in the modified view. Use - /// `await` in front of any asynchronous calls inside the handler. - /// - Returns: A view with a new refresh action in its environment. - public func refreshable(action: @escaping @Sendable () async -> Void) -> some View { - #if os(iOS) - content - .environment(\.backportRefresh, Backport.RefreshAction(action)) - .inspect { inspector in - inspector.sibling(ofType: UITableView.self) - } customize: { scrollView in - guard scrollView.refreshControl == nil else { return } - scrollView.refreshControl = RefreshControl { - await action() - } - } - #else - content - .environment(\.backportRefresh, Backport.RefreshAction(action)) - #endif - } -} - -#if os(iOS) - private final class RefreshControl: UIRefreshControl { - var handler: (() async -> Void)? - - init(_ handler: @escaping () async -> Void) { - super.init() - self.handler = { [weak self] in - Task { [weak self] in - guard let self = self else { return } - await handler() - endRefreshing() - } - } - - addTarget(self, action: #selector(update), for: .valueChanged) - } - - @MainActor - override func endRefreshing() { - super.endRefreshing() - } - - @objc private func update() { - Task { await handler?() } - } - - @available(*, unavailable) - required init?(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - } -#endif - -@available(iOS, deprecated: 15) -@available(macOS, deprecated: 12) -@available(tvOS, deprecated: 15) -@available(watchOS, deprecated: 8) -@available(macOS 10.15, *) -extension Backport where Wrapped == Any { - /// An action that initiates a refresh operation. - /// - /// Unlike the official implementation, this backport does not affect any - /// view's like `List` to provide automatic pull-to-refresh behaviour. - /// - /// You can use this to offer refresh capability in your custom views. - /// Read the ``EnvironmentValues/refresh`` environment value to get the - /// `RefreshAction` instance for a given ``Environment``. If you find - /// a non-`nil` value, change your view's appearance or behavior to offer - /// the refresh to the user, and call the instance to conduct the - /// refresh. You can call the refresh instance directly because it defines - /// a ``RefreshAction/callAsFunction()`` method that Swift calls - /// when you call the instance: - /// - /// struct RefreshableView: View { - /// @Environment(\.refresh) private var refresh - /// - /// var body: some View { - /// Button("Refresh") { - /// Task { - /// await refresh?() - /// } - /// } - /// .disabled(refresh == nil) - /// } - /// } - /// - /// Be sure to call the handler asynchronously by preceding it - /// with `await`. Because the call is asynchronous, you can use - /// its lifetime to indicate progress to the user. For example, - /// you might reveal an indeterminate ``ProgressView`` before - /// calling the handler, and hide it when the handler completes. - /// - /// If your code isn't already in an asynchronous context, create a - /// for the - /// method to run in. If you do this, consider adding a way for the - /// user to cancel the task. For more information, see - /// [Concurrency](https://docs.swift.org/swift-book/LanguageGuide/Concurrency.html) - /// in *The Swift Programming Language*. - public struct RefreshAction { - private var action: () async -> Void - - internal init(_ action: @escaping () async -> Void) { - self.action = action - } - - public func callAsFunction() async { - await action() - } - } -} - -@available(macOS 10.15, *) -private struct RefreshEnvironmentKey: EnvironmentKey { - static let defaultValue: Backport.RefreshAction? = nil -} - -@available(iOS, deprecated: 15) -@available(macOS, deprecated: 12) -@available(tvOS, deprecated: 15) -@available(watchOS, deprecated: 8) -@available(macOS 10.15, *) -extension EnvironmentValues { - /// An action that initiates a refresh operation. - /// - /// Unlike the official implementation, this backport does not affect any - /// view's like `List` to provide automatic pull-to-refresh behaviour. - /// - /// You can use this to offer refresh capability in your custom views. - /// Read the ``EnvironmentValues/refresh`` environment value to get the - /// `RefreshAction` instance for a given ``Environment``. If you find - /// a non-`nil` value, change your view's appearance or behavior to offer - /// the refresh to the user, and call the instance to conduct the - /// refresh. You can call the refresh instance directly because it defines - /// a ``RefreshAction/callAsFunction()`` method that Swift calls - /// when you call the instance: - /// - /// struct RefreshableView: View { - /// @Environment(\.refresh) private var refresh - /// - /// var body: some View { - /// Button("Refresh") { - /// Task { - /// await refresh?() - /// } - /// } - /// .disabled(refresh == nil) - /// } - /// } - /// - /// Be sure to call the handler asynchronously by preceding it - /// with `await`. Because the call is asynchronous, you can use - /// its lifetime to indicate progress to the user. For example, - /// you might reveal an indeterminate ``ProgressView`` before - /// calling the handler, and hide it when the handler completes. - /// - /// If your code isn't already in an asynchronous context, create a - /// for the - /// method to run in. If you do this, consider adding a way for the - /// user to cancel the task. For more information, see - /// [Concurrency](https://docs.swift.org/swift-book/LanguageGuide/Concurrency.html) - /// in *The Swift Programming Language*. - public var backportRefresh: Backport.RefreshAction? { - get { self[RefreshEnvironmentKey.self] } - set { self[RefreshEnvironmentKey.self] = newValue } - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/RequestReview/RequestReview.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/RequestReview/RequestReview.swift deleted file mode 100755 index 3eb0d135..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/RequestReview/RequestReview.swift +++ /dev/null @@ -1,43 +0,0 @@ -import StoreKit -import SwiftUI - -#if os(iOS) || os(macOS) - @available(macOS 10.15, *) - public extension EnvironmentValues { - /// An instance that tells StoreKit to request an App Store rating or review from the user, if appropriate. - /// Read the requestReview environment value to get an instance of this structure for a given Environment. Call the instance to tell StoreKit to ask the user to rate or review your app, if appropriate. You call the instance directly because it defines a callAsFunction() method that Swift calls when you call the instance. - /// - /// Although you normally call this instance to request a review when it makes sense in the user experience flow of your app, the App Store policy governs the actual display of the rating and review request view. Because calling this instance may not present an alert, don’t call it in response to a user action, such as a button tap. - /// - /// > When you call this instance while your app is in development mode, the system always displays a rating and review request view so you can test the user interface and experience. This instance has no effect when you call it in an app that you distribute using TestFlight. - @MainActor var backportRequestReview: Backport.RequestReviewAction { .init() } - } - - /// An instance that tells StoreKit to request an App Store rating or review from the user, if appropriate. - /// Read the requestReview environment value to get an instance of this structure for a given Environment. Call the instance to tell StoreKit to ask the user to rate or review your app, if appropriate. You call the instance directly because it defines a callAsFunction() method that Swift calls when you call the instance. - /// - /// Although you normally call this instance to request a review when it makes sense in the user experience flow of your app, the App Store policy governs the actual display of the rating and review request view. Because calling this instance may not present an alert, don’t call it in response to a user action, such as a button tap. - /// - /// > When you call this instance while your app is in development mode, the system always displays a rating and review request view so you can test the user interface and experience. This instance has no effect when you call it in an app that you distribute using TestFlight. - /// - @available(macOS 10.15, *) - @available(iOS, deprecated: 16) - @available(macOS, deprecated: 13) - extension Backport where Wrapped == Any { - @MainActor public struct RequestReviewAction { - public func callAsFunction() { - #if os(macOS) - SKStoreReviewController.requestReview() - #else - if #available(iOS 14, *) { - guard let scene = UIApplication.activeScene else { return } - SKStoreReviewController.requestReview(in: scene) - } else { - SKStoreReviewController.requestReview() - } - #endif - } - } - } - -#endif diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Section/Section.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Section/Section.swift deleted file mode 100755 index d071b231..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Section/Section.swift +++ /dev/null @@ -1,58 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -@available(iOS, deprecated: 15) -@available(tvOS, deprecated: 15) -@available(macOS, deprecated: 12) -@available(watchOS, deprecated: 8) -extension Backport where Wrapped == Any { - /// A container view that you can use to add hierarchy to certain collection views. - /// - /// Use `Section` instances in views like ``List``, ``Picker``, and - /// ``Form`` to organize content into separate sections. Each section has - /// custom content that you provide on a per-instance basis. You can also - /// provide headers and footers for each section. - public struct Section: View { - @ViewBuilder let content: () -> Content - @ViewBuilder let header: () -> Parent - @ViewBuilder let footer: () -> Footer - - @available(macOS 10.15, *) - public var body: some View { - SwiftUI.Section( - content: content, - header: header, - footer: footer - ) - } - } -} - -@available(macOS 10.15, *) -public extension Backport.Section where Wrapped == Any, Parent == Text, Footer == EmptyView { - /// Creates a section with the provided section content. - /// - Parameters: - /// - titleKey: The key for the section's localized title, which describes - /// the contents of the section. - /// - content: The section's content. - init(_ titleKey: LocalizedStringKey, @ViewBuilder content: @escaping () -> Content) { - header = { Text(titleKey) } - self.content = content - footer = { EmptyView() } - } - - @available(macOS 10.15, *) - /// Creates a section with the provided section content. - /// - Parameters: - /// - title: A string that describes the contents of the section. - /// - content: The section's content. - init(_ title: S, @ViewBuilder content: @escaping () -> Content) where S: StringProtocol { - header = { Text(title) } - self.content = content - footer = { EmptyView() } - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/StateObject/StateObject.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/StateObject/StateObject.swift deleted file mode 100755 index 4afca500..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/StateObject/StateObject.swift +++ /dev/null @@ -1,159 +0,0 @@ -import Combine -import SwiftUI - -@available(macOS 10.15, *) -@available(iOS, deprecated: 14.0) -@available(macOS, deprecated: 11.0) -@available(tvOS, deprecated: 14.0) -@available(watchOS, deprecated: 7.0) -extension Backport where Wrapped: ObservableObject { - /// A property wrapper type that instantiates an observable object. - /// - /// Create a state object in a ``SwiftUI/View``, ``SwiftUI/App``, or - /// ``SwiftUI/Scene`` by applying the `@Backport.StateObject` attribute to a property - /// declaration and providing an initial value that conforms to the - /// - /// protocol: - /// - /// @Backport.StateObject var model = DataModel() - /// - /// SwiftUI creates a new instance of the object only once for each instance of - /// the structure that declares the object. When published properties of the - /// observable object change, SwiftUI updates the parts of any view that depend - /// on those properties: - /// - /// Text(model.title) // Updates the view any time `title` changes. - /// - /// You can pass the state object into a property that has the - /// ``SwiftUI/ObservedObject`` attribute. You can alternatively add the object - /// to the environment of a view hierarchy by applying the - /// ``SwiftUI/View/environmentObject(_:)`` modifier: - /// - /// ContentView() - /// .environmentObject(model) - /// - /// If you create an environment object as shown in the code above, you can - /// read the object inside `ContentView` or any of its descendants - /// using the ``SwiftUI/EnvironmentObject`` attribute: - /// - /// @EnvironmentObject var model: DataModel - /// - /// Get a ``SwiftUI/Binding`` to one of the state object's properties using the - /// `$` operator. Use a binding when you want to create a two-way connection to - /// one of the object's properties. For example, you can let a - /// ``SwiftUI/Toggle`` control a Boolean value called `isEnabled` stored in the - /// model: - /// - /// Toggle("Enabled", isOn: $model.isEnabled) - @propertyWrapper public struct StateObject: DynamicProperty { - private final class Wrapper: ObservableObject { - private var subject = PassthroughSubject() - - @available(macOS 10.15, *) - var value: Wrapped? { - didSet { - cancellable = nil - cancellable = value?.objectWillChange - .sink { [subject] _ in subject.send() } - } - } - - @available(macOS 10.15, *) - private var cancellable: AnyCancellable? - - @available(macOS 10.15, *) - var objectWillChange: AnyPublisher { - subject.eraseToAnyPublisher() - } - } - - @available(macOS 10.15, *) - @State private var state = Wrapper() - - @available(macOS 10.15, *) - @ObservedObject private var observedObject = Wrapper() - - @available(macOS 10.15, *) - private var thunk: () -> Wrapped - - @available(macOS 10.15, *) - /// The underlying value referenced by the state object. - /// - /// The wrapped value property provides primary access to the value's data. - /// However, you don't access `wrappedValue` directly. Instead, use the - /// property variable created with the `@Backport.StateObject` attribute: - /// - /// @Backport.StateObject var contact = Contact() - /// - /// var body: some View { - /// Text(contact.name) // Accesses contact's wrapped value. - /// } - /// - /// When you change a property of the wrapped value, you can access the new - /// value immediately. However, SwiftUI updates views displaying the value - /// asynchronously, so the user interface might not update immediately. - public var wrappedValue: Wrapped { - if let object = state.value { - return object - } else { - let object = thunk() - state.value = object - return object - } - } - - @available(macOS 10.15, *) - /// A projection of the state object that creates bindings to its - /// properties. - /// - /// Use the projected value to pass a binding value down a view hierarchy. - /// To get the projected value, prefix the property variable with `$`. For - /// example, you can get a binding to a model's `isEnabled` Boolean so that - /// a ``SwiftUI/Toggle`` view can control the value: - /// - /// struct MyView: View { - /// @Backport.StateObject var model = DataModel() - /// - /// var body: some View { - /// Toggle("Enabled", isOn: $model.isEnabled) - /// } - /// } - public var projectedValue: ObservedObject.Wrapper { - ObservedObject(wrappedValue: wrappedValue).projectedValue - } - - @available(macOS 10.15, *) - /// Creates a new state object with an initial wrapped value. - /// - /// You don’t call this initializer directly. Instead, declare a property - /// with the `@Backport.StateObject` attribute in a ``SwiftUI/View``, - /// ``SwiftUI/App``, or ``SwiftUI/Scene``, and provide an initial value: - /// - /// struct MyView: View { - /// @Backport.StateObject var model = DataModel() - /// - /// // ... - /// } - /// - /// SwiftUI creates only one instance of the state object for each - /// container instance that you declare. In the code above, SwiftUI - /// creates `model` only the first time it initializes a particular instance - /// of `MyView`. On the other hand, each different instance of `MyView` - /// receives a distinct copy of the data model. - /// - /// - Parameter thunk: An initial value for the state object. - public init(wrappedValue thunk: @autoclosure @escaping () -> Wrapped) { - self.thunk = thunk - } - - @available(macOS 10.15, *) - public mutating func update() { - if state.value == nil { - state.value = thunk() - } - if observedObject.value !== state.value { - observedObject.value = state.value - } - } - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Task/Task.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Task/Task.swift deleted file mode 100755 index 07d4665c..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Task/Task.swift +++ /dev/null @@ -1,178 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -@available(iOS, deprecated: 15.0) -@available(macOS, deprecated: 12.0) -@available(tvOS, deprecated: 15.0) -@available(watchOS, deprecated: 8.0) -extension Backport where Wrapped: View { - /// Adds an asynchronous task to perform when this view appears. - /// - /// Use this modifier to perform an asynchronous task with a lifetime that - /// matches that of the modified view. If the task doesn't finish - /// before SwiftUI removes the view or the view changes identity, SwiftUI - /// cancels the task. - /// - /// Use the `await` keyword inside the task to - /// wait for an asynchronous call to complete. - /// - /// let url = URL(string: "https://example.com")! - /// @State private var message = "Loading..." - /// - /// var body: some View { - /// Text(message) - /// .task { - /// do { - /// var receivedLines = [String]() - /// for try await line in url.lines { - /// receivedLines.append(line) - /// message = "Received \(receivedLines.count) lines" - /// } - /// } catch { - /// message = "Failed to load" - /// } - /// } - /// } - /// - /// When each new line arrives, the body of the `for`-`await`-`in` - /// loop stores the line in an array of strings and updates the content of the - /// text view to report the latest line count. - /// - /// - Parameters: - /// - priority: The task priority to use when creating the asynchronous - /// task. The default priority is `.userInitiated` - /// - action: A closure that SwiftUI calls as an asynchronous task - /// when the view appears. SwiftUI automatically cancels the task - /// if the view disappears before the action completes. - /// - /// - /// - Returns: A view that runs the specified action asynchronously when - /// the view appears. - @ViewBuilder - public func task(priority: TaskPriority = .userInitiated, _ action: @MainActor @escaping @Sendable () async -> Void) - -> some View - { - content.modifier( - TaskModifier( - id: 0, - priority: priority, - action: action - ) - ) - } - - @available(macOS 10.15, *) - /// Adds a task to perform when this view appears or when a specified - /// value changes. - /// - /// This method behaves like ``View/task(priority:_:)``, except that it also - /// cancels and recreates the task when a specified value changes. To detect - /// a change, the modifier tests whether a new value for the `id` parameter - /// equals the previous value. For this to work, - /// the value's type must conform to the `Equatable` protocol. - /// - /// For example, if you define an equatable `Server` type that posts custom - /// notifications whenever its state changes --- for example, from _signed - /// out_ to _signed in_ --- you can use the task modifier to update - /// the contents of a ``Text`` view to reflect the state of the - /// currently selected server: - /// - /// Text(status ?? "Signed Out") - /// .task(id: server) { - /// let sequence = NotificationCenter.default.notifications( - /// named: .didChangeStatus, - /// object: server) - /// for try await notification in sequence { - /// status = notification.userInfo["status"] as? String - /// } - /// } - /// - /// Elsewhere, the server defines a custom `didUpdateStatus` notification: - /// - /// extension NSNotification.Name { - /// static var didUpdateStatus: NSNotification.Name { - /// NSNotification.Name("didUpdateStatus") - /// } - /// } - /// - /// The server then posts a notification of this type whenever its status - /// changes, like after the user signs in: - /// - /// let notification = Notification( - /// name: .didUpdateStatus, - /// object: self, - /// userInfo: ["status": "Signed In"]) - /// NotificationCenter.default.post(notification) - /// - /// The task attached to the ``Text`` view gets and displays the status - /// value from the notification's user information dictionary. When the user - /// chooses a different server, SwiftUI cancels the task and creates a new - /// one, which then starts waiting for notifications from the new server. - /// - /// - Parameters: - /// - id: The value to observe for changes. The value must conform - /// to the `Equatable` protocol. - /// - priority: The task priority to use when creating the asynchronous - /// task. The default priority is `.userInitiated` - /// - action: A closure that SwiftUI calls as an asynchronous task - /// when the view appears. SwiftUI automatically cancels the task - /// if the view disappears before the action completes. If the - /// `id` value changes, SwiftUI cancels and restarts the task. - /// - /// - Returns: A view that runs the specified action asynchronously when - /// the view appears, or restarts the task with the `id` value changes. - @ViewBuilder - public func task( - id: T, priority: TaskPriority = .userInitiated, _ action: @MainActor @escaping @Sendable () async -> Void - ) -> some View { - content.modifier( - TaskModifier( - id: id, - priority: priority, - action: action - ) - ) - } -} - -@available(macOS 10.15, *) -private struct TaskModifier: ViewModifier { - var id: ID - var priority: TaskPriority - var action: () async -> Void - - @available(macOS 10.15, *) - @State private var task: Task? - - @available(macOS 10.15, *) - init(id: ID, priority: TaskPriority, action: @MainActor @escaping () async -> Void) { - self.id = id - self.priority = priority - self.action = action - } - - @available(macOS 10.15, *) - func body(content: Content) -> some View { - content - .backport.onChange(of: id) { _ in - task?.cancel() - task = Task(priority: priority) { - await action() - } - } - .onAppear { - task?.cancel() - task = Task(priority: priority) { - await action() - } - } - .onDisappear { - task?.cancel() - task = nil - } - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Transition/PushTransition.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Transition/PushTransition.swift deleted file mode 100755 index 4fed461c..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Transition/PushTransition.swift +++ /dev/null @@ -1,34 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -public extension Backport where Wrapped == AnyTransition { - /// Creates a transition that when added to a view will animate the view’s insertion by moving it in from the specified edge while fading it in, and animate its removal by moving it out towards the opposite edge and fading it out. - /// - Parameter edge: the edge from which the view will be animated in. - /// - Returns: A transition that animates a view by moving and fading it. - @available(iOS, deprecated: 16.0) - @available(watchOS, deprecated: 9.0) - @available(macOS, deprecated: 13.0) - @available(tvOS, deprecated: 16.0) - func push(from edge: Edge) -> AnyTransition { - var oppositeEdge: Edge - switch edge { - case .top: - oppositeEdge = .bottom - case .leading: - oppositeEdge = .trailing - case .bottom: - oppositeEdge = .top - case .trailing: - oppositeEdge = .leading - } - - return .asymmetric( - insertion: .move(edge: edge), - removal: .move(edge: oppositeEdge) - ).combined(with: .opacity) - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/URLSession/URLSession+Async.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/URLSession/URLSession+Async.swift deleted file mode 100755 index f1537508..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/URLSession/URLSession+Async.swift +++ /dev/null @@ -1,168 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import Foundation - -@available(macOS 10.15, *) -@available(iOS, deprecated: 15.0) -@available(macOS, deprecated: 12.0) -@available(tvOS, deprecated: 15.0) -@available(watchOS, deprecated: 8.0) -extension Backport where Wrapped: URLSession { - /// Start a data task with a URL using async/await. - /// - parameter url: The URL to send a request to. - /// - returns: A tuple containing the binary `Data` that was downloaded, - /// as well as a `URLResponse` representing the server's response. - /// - throws: Any error encountered while performing the data task. - public func data(from url: URL) async throws -> (Data, URLResponse) { - try await data(for: URLRequest(url: url)) - } - - /// Start a data task with a `URLRequest` using async/await. - /// - parameter request: The `URLRequest` that the data task should perform. - /// - returns: A tuple containing the binary `Data` that was downloaded, - /// as well as a `URLResponse` representing the server's response. - /// - throws: Any error encountered while performing the data task. - public func data(for request: URLRequest) async throws -> (Data, URLResponse) { - let sessionTask = URLSessionTaskActor() - - return try await withTaskCancellationHandler { - try await withCheckedThrowingContinuation { continuation in - Task { - await sessionTask.start( - content.dataTask(with: request) { data, response, error in - guard let data = data, let response = response else { - let error = error ?? URLError(.badServerResponse) - continuation.resume(throwing: error) - return - } - - continuation.resume(returning: (data, response)) - }) - } - } - } onCancel: { - Task { await sessionTask.cancel() } - } - } - - public func upload(for request: URLRequest, fromFile fileURL: URL) async throws -> (Data, URLResponse) { - let sessionTask = URLSessionTaskActor() - return try await withTaskCancellationHandler { - try await withCheckedThrowingContinuation { continuation in - Task { - await sessionTask.start( - content.uploadTask(with: request, fromFile: fileURL) { data, response, error in - guard let data = data, let response = response else { - let error = error ?? URLError(.badServerResponse) - return continuation.resume(throwing: error) - } - - continuation.resume(returning: (data, response)) - }) - } - } - } onCancel: { - Task { await sessionTask.cancel() } - } - } - - public func upload(for request: URLRequest, from bodyData: Data) async throws -> (Data, URLResponse) { - let sessionTask = URLSessionTaskActor() - return try await withTaskCancellationHandler { - try await withCheckedThrowingContinuation { continuation in - Task { - await sessionTask.start( - content.uploadTask(with: request, from: bodyData) { data, response, error in - guard let data = data, let response = response else { - let error = error ?? URLError(.badServerResponse) - return continuation.resume(throwing: error) - } - - continuation.resume(returning: (data, response)) - }) - } - } - } onCancel: { - Task { await sessionTask.cancel() } - } - } - - public func download(for request: URLRequest) async throws -> (URL, URLResponse) { - let sessionTask = URLSessionTaskActor() - return try await withTaskCancellationHandler { - try await withCheckedThrowingContinuation { continuation in - Task { - await sessionTask.start( - content.downloadTask(with: request) { data, response, error in - guard let data = data, let response = response else { - let error = error ?? URLError(.badServerResponse) - return continuation.resume(throwing: error) - } - - continuation.resume(returning: (data, response)) - }) - } - } - } onCancel: { - Task { await sessionTask.cancel() } - } - } - - public func download(from url: URL) async throws -> (URL, URLResponse) { - let sessionTask = URLSessionTaskActor() - return try await withTaskCancellationHandler { - try await withCheckedThrowingContinuation { continuation in - Task { - await sessionTask.start( - content.downloadTask(with: url) { data, response, error in - guard let data = data, let response = response else { - let error = error ?? URLError(.badServerResponse) - return continuation.resume(throwing: error) - } - - continuation.resume(returning: (data, response)) - }) - } - } - } onCancel: { - Task { await sessionTask.cancel() } - } - } - - public func download(resumeFrom resumeData: Data) async throws -> (URL, URLResponse) { - let sessionTask = URLSessionTaskActor() - return try await withTaskCancellationHandler { - try await withCheckedThrowingContinuation { continuation in - Task { - await sessionTask.start( - content.downloadTask(withResumeData: resumeData) { data, response, error in - guard let data = data, let response = response else { - let error = error ?? URLError(.badServerResponse) - return continuation.resume(throwing: error) - } - - continuation.resume(returning: (data, response)) - }) - } - } - } onCancel: { - Task { await sessionTask.cancel() } - } - } -} - -@available(macOS 10.15, *) -private actor URLSessionTaskActor { - weak var task: URLSessionTask? - - func start(_ task: URLSessionTask) { - self.task = task - task.resume() - } - - func cancel() { - task?.cancel() - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Visibility/Visibility.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Visibility/Visibility.swift deleted file mode 100755 index 775e608a..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/Shared/Visibility/Visibility.swift +++ /dev/null @@ -1,42 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -@available(iOS, deprecated: 15) -@available(macOS, deprecated: 12) -@available(tvOS, deprecated: 15) -@available(watchOS, deprecated: 8) -extension Backport where Wrapped == Any { - public enum Visibility: Hashable, CaseIterable { - /// The element may be visible or hidden depending on the policies of the - /// component accepting the visibility configuration. - /// - /// For example, some components employ different automatic behavior - /// depending on factors including the platform, the surrounding container, - /// user settings, etc. - case automatic - - /// The element may be visible. - /// - /// Some APIs may use this value to represent a hint or preference, rather - /// than a mandatory assertion. For example, setting list row separator - /// visibility to `visible` using the - /// ``View/listRowSeparator(_:edges:)`` modifier may not always - /// result in any visible separators, especially for list styles that do not - /// include separators as part of their design. - case visible - - /// The element may be hidden. - /// - /// Some APIs may use this value to represent a hint or preference, rather - /// than a mandatory assertion. For example, setting confirmation dialog - /// title visibility to `hidden` using the - /// ``View/confirmationDialog(_:isPresented:titleVisibility:actions:)-87n66`` - /// modifier may not always hide the dialog title, which is required on - /// some platforms. - case hidden - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/PresentationDetents/Detents.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/PresentationDetents/Detents.swift deleted file mode 100755 index 01b0f7ba..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/PresentationDetents/Detents.swift +++ /dev/null @@ -1,315 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -@available(tvOS, deprecated: 16) -@available(macOS, deprecated: 13) -@available(watchOS, deprecated: 9) -extension Backport where Wrapped: View { - /// Sets the available detents for the enclosing sheet. - /// - /// By default, sheets support the ``PresentationDetent/large`` detent. - /// - /// struct ContentView: View { - /// @State private var showSettings = false - /// - /// var body: some View { - /// Button("View Settings") { - /// showSettings = true - /// } - /// .sheet(isPresented: $showSettings) { - /// SettingsView() - /// .presentationDetents([.medium, .large]) - /// } - /// } - /// } - /// - /// - Parameter detents: A set of supported detents for the sheet. - /// If you provide more than one detent, people can drag the sheet - /// to resize it. - @ViewBuilder - @available(iOS, introduced: 15, deprecated: 16, message: "Presentation detents are only supported in iOS 15+") - public func presentationDetents(_ detents: Set.PresentationDetent>) -> some View { - #if os(iOS) - content.background(Backport.Representable(detents: detents, selection: nil, largestUndimmed: .large)) - #else - content - #endif - } - - @available(macOS 10.15, *) - /// Sets the available detents for the enclosing sheet, giving you - /// programmatic control of the currently selected detent. - /// - /// By default, sheets support the ``PresentationDetent/large`` detent. - /// - /// struct ContentView: View { - /// @State private var showSettings = false - /// @State private var settingsDetent = PresentationDetent.medium - /// - /// var body: some View { - /// Button("View Settings") { - /// showSettings = true - /// } - /// .sheet(isPresented: $showSettings) { - /// SettingsView() - /// .presentationDetents:( - /// [.medium, .large], - /// selection: $settingsDetent - /// ) - /// } - /// } - /// } - /// - /// - Parameters: - /// - detents: A set of supported detents for the sheet. - /// If you provide more that one detent, people can drag the sheet - /// to resize it. - /// - selection: A ``Binding`` to the currently selected detent. - /// Ensure that the value matches one of the detents that you - /// provide for the `detents` parameter. - @ViewBuilder - @available(iOS, introduced: 15, deprecated: 16, message: "Presentation detents are only supported in iOS 15+") - public func presentationDetents( - _ detents: Set.PresentationDetent>, selection: Binding.PresentationDetent> - ) -> some View { - #if os(iOS) - content.background(Backport.Representable(detents: detents, selection: selection, largestUndimmed: .large)) - #else - content - #endif - } - - @available(macOS 10.15, *) - /// Sets the available detents for the enclosing sheet, giving you - /// programmatic control of the currently selected detent. - /// - /// By default, sheets support the ``PresentationDetent/large`` detent. - /// - /// struct ContentView: View { - /// @State private var showSettings = false - /// @State private var settingsDetent = PresentationDetent.medium - /// - /// var body: some View { - /// Button("View Settings") { - /// showSettings = true - /// } - /// .sheet(isPresented: $showSettings) { - /// SettingsView() - /// .presentationDetents:( - /// [.medium, .large], - /// selection: $settingsDetent - /// ) - /// } - /// } - /// } - /// - /// - Parameters: - /// - detents: A set of supported detents for the sheet. - /// If you provide more that one detent, people can drag the sheet - /// to resize it. - /// - selection: A ``Binding`` to the currently selected detent. - /// Ensure that the value matches one of the detents that you - /// provide for the `detents` parameter. - @ViewBuilder - @available(iOS, introduced: 15, deprecated: 16, message: "Presentation detents are only supported in iOS 15+") - public func presentationDetents( - _ detents: Set.PresentationDetent>, selection: Binding.PresentationDetent>, - largestUndimmedDetent: Backport.PresentationDetent? = nil - ) -> some View { - #if os(iOS) - content.background( - Backport.Representable(detents: detents, selection: selection, largestUndimmed: largestUndimmedDetent)) - #else - content - #endif - } -} - -@available(macOS 10.15, *) -@available(iOS, deprecated: 16) -@available(tvOS, deprecated: 16) -@available(macOS, deprecated: 13) -@available(watchOS, deprecated: 9) -extension Backport where Wrapped == Any { - /// A type that represents a height where a sheet naturally rests. - public struct PresentationDetent: Hashable, Comparable { - public struct Identifier: RawRepresentable, Hashable { - public var rawValue: String - public init(rawValue: String) { - self.rawValue = rawValue - } - - @available(macOS 10.15, *) - public static var medium: Identifier { - .init(rawValue: "com.apple.UIKit.medium") - } - - @available(macOS 10.15, *) - public static var large: Identifier { - .init(rawValue: "com.apple.UIKit.large") - } - } - - @available(macOS 10.15, *) - public let id: Identifier - - @available(macOS 10.15, *) - /// The system detent for a sheet that's approximately half the height of - /// the screen, and is inactive in compact height. - public static var medium: PresentationDetent { - .init(id: .medium) - } - - @available(macOS 10.15, *) - /// The system detent for a sheet at full height. - public static var large: PresentationDetent { - .init(id: .large) - } - - @available(macOS 10.15, *) - fileprivate static var none: PresentationDetent { - .init(id: .init(rawValue: "")) - } - - @available(macOS 10.15, *) - public static func < (lhs: PresentationDetent, rhs: PresentationDetent) -> Bool { - switch (lhs, rhs) { - case (.large, .medium): - return false - default: - return true - } - } - } -} - -#if os(iOS) - @available(iOS 15, *) - fileprivate extension Backport where Wrapped == Any { - struct Representable: UIViewControllerRepresentable { - let detents: Set.PresentationDetent> - let selection: Binding.PresentationDetent>? - let largestUndimmed: Backport.PresentationDetent? - - func makeUIViewController(context _: Context) -> Backport.Representable.Controller { - Controller(detents: detents, selection: selection, largestUndimmed: largestUndimmed) - } - - func updateUIViewController(_ controller: Backport.Representable.Controller, context _: Context) { - controller.update(detents: detents, selection: selection, largestUndimmed: largestUndimmed) - } - } - } - - @available(macOS 10.15, *) - @available(iOS 15, *) - fileprivate extension Backport.Representable { - final class Controller: UIViewController, UISheetPresentationControllerDelegate { - var detents: Set.PresentationDetent> - var selection: Binding.PresentationDetent>? - var largestUndimmed: Backport.PresentationDetent? - weak var _delegate: UISheetPresentationControllerDelegate? - - @available(macOS 10.15, *) - init( - detents: Set.PresentationDetent>, selection: Binding.PresentationDetent>?, - largestUndimmed: Backport.PresentationDetent? - ) { - self.detents = detents - self.selection = selection - self.largestUndimmed = largestUndimmed - super.init(nibName: nil, bundle: nil) - } - - @available(macOS 10.15, *) - @available(*, unavailable) - required init?(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - @available(macOS 10.15, *) - override func willMove(toParent parent: UIViewController?) { - super.willMove(toParent: parent) - if let controller = parent?.sheetPresentationController { - if controller.delegate !== self { - _delegate = controller.delegate - controller.delegate = self - } - } - update(detents: detents, selection: selection, largestUndimmed: largestUndimmed) - } - - @available(macOS 10.15, *) - func update( - detents: Set.PresentationDetent>, selection: Binding.PresentationDetent>?, - largestUndimmed: Backport.PresentationDetent? - ) { - self.detents = detents - self.selection = selection - self.largestUndimmed = largestUndimmed - - if let controller = parent?.sheetPresentationController { - controller.animateChanges { - controller.detents = detents.sorted().map { - switch $0 { - case .medium: - return .medium() - default: - return .large() - } - } - - controller.largestUndimmedDetentIdentifier = largestUndimmed.flatMap { - .init(rawValue: $0.id.rawValue) - } - - if let selection = selection { - controller.selectedDetentIdentifier = .init(selection.wrappedValue.id.rawValue) - } - - controller.prefersScrollingExpandsWhenScrolledToEdge = true - } - - UIView.animate(withDuration: 0.25) { - if let undimmed = largestUndimmed { - controller.presentingViewController.view.tintAdjustmentMode = - (selection?.wrappedValue ?? .large) >= undimmed ? .automatic : .normal - } else { - controller.presentingViewController.view.tintAdjustmentMode = .automatic - } - } - } - } - - @available(macOS 10.15, *) - func sheetPresentationControllerDidChangeSelectedDetentIdentifier( - _ sheetPresentationController: UISheetPresentationController - ) { - guard - let selection = selection, - let id = sheetPresentationController.selectedDetentIdentifier?.rawValue, - selection.wrappedValue.id.rawValue != id - else { return } - - selection.wrappedValue = .init(id: .init(rawValue: id)) - } - - @available(macOS 10.15, *) - override func responds(to aSelector: Selector!) -> Bool { - if super.responds(to: aSelector) { return true } - if _delegate?.responds(to: aSelector) ?? false { return true } - return false - } - - @available(macOS 10.15, *) - override func forwardingTarget(for aSelector: Selector!) -> Any? { - if super.responds(to: aSelector) { return self } - return _delegate - } - } - } -#endif diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/PresentationDetents/DragIndicator.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/PresentationDetents/DragIndicator.swift deleted file mode 100755 index 8794ad40..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/PresentationDetents/DragIndicator.swift +++ /dev/null @@ -1,102 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -@available(iOS, deprecated: 16) -@available(tvOS, deprecated: 16) -@available(macOS, deprecated: 13) -@available(watchOS, deprecated: 9) -extension Backport where Wrapped: View { - /// Sets the visibility of the drag indicator on top of a sheet. - /// - /// You can show a drag indicator when it isn't apparent that a - /// sheet can resize or when the sheet can't dismiss interactively. - /// - /// struct ContentView: View { - /// @State private var showSettings = false - /// - /// var body: some View { - /// Button("View Settings") { - /// showSettings = true - /// } - /// .sheet(isPresented: $showSettings) { - /// SettingsView() - /// .presentationDetents:([.medium, .large]) - /// .presentationDragIndicator(.visible) - /// } - /// } - /// } - /// - /// - Parameter visibility: The preferred visibility of the drag indicator. - @ViewBuilder - public func presentationDragIndicator(_ visibility: Backport.Visibility) -> some View { - #if os(iOS) - if #available(iOS 15, *) { - content.background(Backport.Representable(visibility: visibility)) - } else { - content - } - #else - content - #endif - } -} - -#if os(iOS) - @available(iOS 15, *) - @available(macOS 10.15, *) - fileprivate extension Backport where Wrapped == Any { - struct Representable: UIViewControllerRepresentable { - let visibility: Backport.Visibility - - func makeUIViewController(context _: Context) -> Backport.Representable.Controller { - Controller(visibility: visibility) - } - - func updateUIViewController(_ controller: Backport.Representable.Controller, context _: Context) { - controller.update(visibility: visibility) - } - } - } - - @available(macOS 10.15, *) - @available(iOS 15, *) - fileprivate extension Backport.Representable { - final class Controller: UIViewController { - var visibility: Backport.Visibility - - @available(macOS 10.15, *) - init(visibility: Backport.Visibility) { - self.visibility = visibility - super.init(nibName: nil, bundle: nil) - } - - @available(macOS 10.15, *) - @available(*, unavailable) - required init?(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - @available(macOS 10.15, *) - override func willMove(toParent parent: UIViewController?) { - super.willMove(toParent: parent) - update(visibility: visibility) - } - - @available(macOS 10.15, *) - func update(visibility: Backport.Visibility) { - self.visibility = visibility - - if let controller = parent?.sheetPresentationController { - controller.animateChanges { - controller.prefersGrabberVisible = visibility == .visible - controller.prefersScrollingExpandsWhenScrolledToEdge = true - } - } - } - } - } -#endif diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/PresentationDetents/InteractiveDetent.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/PresentationDetents/InteractiveDetent.swift deleted file mode 100755 index a8126360..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/PresentationDetents/InteractiveDetent.swift +++ /dev/null @@ -1,104 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(iOS, deprecated: 16) -@available(tvOS, deprecated: 16) -@available(macOS, deprecated: 13) -@available(watchOS, deprecated: 9) -@available(macOS 10.15, *) -extension Backport where Wrapped: View { - /// Removes dimming from detents higher (and including) the provided identifier - /// - /// This has two affects on dentents higher than the identifier provided: - /// 1. Touches will passthrough to the views below the sheet. - /// 2. Touches will no longer dismiss the sheet automatically when tapping outside of the sheet. - /// - /// ``` - /// struct ContentView: View { - /// @State private var showSettings = false - /// - /// var body: some View { - /// Button("View Settings") { - /// showSettings = true - /// } - /// .sheet(isPresented: $showSettings) { - /// SettingsView() - /// .presentationDetents:([.medium, .large]) - /// .presentationUndimmed(from: .medium) - /// } - /// } - /// } - /// ``` - /// - /// - Parameter identifier: The identifier of the largest detent that is not dimmed. - @ViewBuilder - @available( - iOS, deprecated: 13, message: "Please use backport.presentationDetents(_:selection:largestUndimmedDetent:)" - ) - public func presentationUndimmed(from identifier: Backport.PresentationDetent.Identifier?) -> some View { - #if os(iOS) - if #available(iOS 15, *) { - content.background(Backport.Representable(identifier: identifier)) - } else { - content - } - #else - content - #endif - } -} - -#if os(iOS) - @available(iOS 15, *) - fileprivate extension Backport where Wrapped == Any { - struct Representable: UIViewControllerRepresentable { - let identifier: Backport.PresentationDetent.Identifier? - - func makeUIViewController(context _: Context) -> Backport.Representable.Controller { - Controller(identifier: identifier) - } - - func updateUIViewController(_ controller: Backport.Representable.Controller, context _: Context) { - controller.update(identifier: identifier) - } - } - } - - @available(iOS 15, *) - fileprivate extension Backport.Representable { - final class Controller: UIViewController { - var identifier: Backport.PresentationDetent.Identifier? - - init(identifier: Backport.PresentationDetent.Identifier?) { - self.identifier = identifier - super.init(nibName: nil, bundle: nil) - } - - @available(*, unavailable) - required init?(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func willMove(toParent parent: UIViewController?) { - super.willMove(toParent: parent) - update(identifier: identifier) - } - - func update(identifier: Backport.PresentationDetent.Identifier?) { - self.identifier = identifier - - if let controller = parent?.sheetPresentationController { - controller.animateChanges { - controller.presentingViewController.view.tintAdjustmentMode = .normal - controller.largestUndimmedDetentIdentifier = identifier.flatMap { - .init(rawValue: $0.rawValue) - } - } - } - } - } - } -#endif diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/PresentationDetents/InteractiveDismiss.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/PresentationDetents/InteractiveDismiss.swift deleted file mode 100755 index fc6d5ca7..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/PresentationDetents/InteractiveDismiss.swift +++ /dev/null @@ -1,256 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -public extension Backport where Wrapped: View { - /// Conditionally prevents interactive dismissal of a popover or a sheet. - /// - /// Users can dismiss certain kinds of presentations using built-in - /// gestures. In particular, a user can dismiss a sheet by dragging it down, - /// or a popover by clicking or tapping outside of the presented view. Use - /// the `interactiveDismissDisabled(_:)` modifier to conditionally prevent - /// this kind of dismissal. You typically do this to prevent the user from - /// dismissing a presentation before providing needed data or completing - /// a required action. - /// - /// For instance, suppose you have a view that displays a licensing - /// agreement that the user must acknowledge before continuing: - /// - /// struct TermsOfService: View { - /// @Binding var areTermsAccepted: Bool - /// @Environment(\.backportDismiss) private var dismiss - /// - /// var body: some View { - /// Form { - /// Text("License Agreement") - /// .font(.title) - /// Text("Terms and conditions go here.") - /// Button("Accept") { - /// areTermsAccepted = true - /// dismiss() - /// } - /// } - /// } - /// } - /// - /// If you present this view in a sheet, the user can dismiss it by either - /// tapping the button --- which calls ``EnvironmentValues/backportDismiss`` - /// from its `action` closure --- or by dragging the sheet down. To - /// ensure that the user accepts the terms by tapping the button, - /// disable interactive dismissal, conditioned on the `areTermsAccepted` - /// property: - /// - /// struct ContentView: View { - /// @State private var isSheetPresented = false - /// @State private var areTermsAccepted = false - /// - /// var body: some View { - /// Button("Use Service") { - /// isSheetPresented = true - /// } - /// .sheet(isPresented: $isSheetPresented) { - /// TermsOfService() - /// .backport.interactiveDismissDisabled(!areTermsAccepted) - /// } - /// } - /// } - /// - /// You can apply the modifier to any view in the sheet's view hierarchy, - /// including to the sheet's top level view, as the example demonstrates, - /// or to any child view, like the ``Form`` or the Accept ``Button``. - /// - /// The modifier has no effect on programmatic dismissal, which you can - /// invoke by updating the ``Binding`` that controls the presentation, or - /// by calling the environment's ``EnvironmentValues/backportDismiss`` action. - /// - /// > This modifier currently has no effect on macOS, tvOS or watchOS. - /// - /// - Parameter isDisabled: A Boolean value that indicates whether to - /// prevent nonprogrammatic dismissal of the containing view hierarchy - /// when presented in a sheet or popover. - @ViewBuilder - @available(iOS, deprecated: 16) - @available(tvOS, deprecated: 16) - @available(macOS, deprecated: 13) - @available(watchOS, deprecated: 9) - func interactiveDismissDisabled(_ isDisabled: Bool = true) -> some View { - #if os(iOS) - if #available(iOS 15, *) { - content.background(Backport.Representable(isModal: isDisabled, onAttempt: nil)) - } else { - content - } - #else - content - #endif - } - - @available(macOS 10.15, *) - /// Conditionally prevents interactive dismissal of a popover or a sheet. In addition, provides fine-grained control over the dismissal - /// - /// Users can dismiss certain kinds of presentations using built-in - /// gestures. In particular, a user can dismiss a sheet by dragging it down, - /// or a popover by clicking or tapping outside of the presented view. Use - /// the `interactiveDismissDisabled(_:)` modifier to conditionally prevent - /// this kind of dismissal. You typically do this to prevent the user from - /// dismissing a presentation before providing needed data or completing - /// a required action. - /// - /// For instance, suppose you have a view that displays a licensing - /// agreement that the user must acknowledge before continuing: - /// - /// struct TermsOfService: View { - /// @Binding var areTermsAccepted: Bool - /// @Environment(\.backportDismiss) private var dismiss - /// - /// var body: some View { - /// Form { - /// Text("License Agreement") - /// .font(.title) - /// Text("Terms and conditions go here.") - /// Button("Accept") { - /// areTermsAccepted = true - /// dismiss() - /// } - /// } - /// } - /// } - /// - /// If you present this view in a sheet, the user can dismiss it by either - /// tapping the button --- which calls ``EnvironmentValues/backportDismiss`` - /// from its `action` closure --- or by dragging the sheet down. To - /// ensure that the user accepts the terms by tapping the button, - /// disable interactive dismissal, conditioned on the `areTermsAccepted` - /// property: - /// - /// struct ContentView: View { - /// @State private var isSheetPresented = false - /// @State private var areTermsAccepted = false - /// - /// var body: some View { - /// Button("Use Service") { - /// isSheetPresented = true - /// } - /// .sheet(isPresented: $isSheetPresented) { - /// TermsOfService() - /// .backport.interactiveDismissDisabled(!areTermsAccepted) - /// } - /// } - /// } - /// - /// You can apply the modifier to any view in the sheet's view hierarchy, - /// including to the sheet's top level view, as the example demonstrates, - /// or to any child view, like the ``Form`` or the Accept ``Button``. - /// - /// The modifier has no effect on programmatic dismissal, which you can - /// invoke by updating the ``Binding`` that controls the presentation, or - /// by calling the environment's ``EnvironmentValues/backportDismiss`` action. - /// - /// > This modifier currently has no effect on macOS, tvOS or watchOS. - /// - /// - Parameter isDisabled: A Boolean value that indicates whether to - /// prevent nonprogrammatic dismissal of the containing view hierarchy - /// when presented in a sheet or popover. - /// - Parameter onAttempt: A closure that will be called when an interactive dismiss attempt occurs. - /// You can use this as an opportunity to present an confirmation or prompt to the user. - @ViewBuilder - func interactiveDismissDisabled(_ isDisabled: Bool = true, onAttempt: @escaping () -> Void) -> some View { - #if os(iOS) - if #available(iOS 15, *) { - content.background(Backport.Representable(isModal: isDisabled, onAttempt: onAttempt)) - } else { - content - } - #else - content - #endif - } -} - -#if os(iOS) - @available(macOS 10.15, *) - fileprivate extension Backport where Wrapped == Any { - struct Representable: UIViewControllerRepresentable { - let isModal: Bool - let onAttempt: (() -> Void)? - - @available(macOS 10.15, *) - func makeUIViewController(context _: Context) -> Backport.Representable.Controller { - Controller(isModal: isModal, onAttempt: onAttempt) - } - - @available(macOS 10.15, *) - func updateUIViewController(_ controller: Backport.Representable.Controller, context _: Context) { - controller.update(isModal: isModal, onAttempt: onAttempt) - } - } - } - - @available(macOS 10.15, *) - fileprivate extension Backport.Representable { - final class Controller: UIViewController, UIAdaptivePresentationControllerDelegate { - var isModal: Bool - var onAttempt: (() -> Void)? - weak var _delegate: UIAdaptivePresentationControllerDelegate? - - @available(macOS 10.15, *) - init(isModal: Bool, onAttempt: (() -> Void)?) { - self.isModal = isModal - self.onAttempt = onAttempt - super.init(nibName: nil, bundle: nil) - } - - @available(macOS 10.15, *) - @available(*, unavailable) - required init?(coder _: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - @available(macOS 10.15, *) - override func willMove(toParent parent: UIViewController?) { - super.willMove(toParent: parent) - if let controller = parent?.presentationController { - if controller.delegate !== self { - _delegate = controller.delegate - controller.delegate = self - } - } - update(isModal: isModal, onAttempt: onAttempt) - } - - @available(macOS 10.15, *) - func update(isModal: Bool, onAttempt: (() -> Void)?) { - self.isModal = isModal - self.onAttempt = onAttempt - - parent?.isModalInPresentation = isModal - } - - @available(macOS 10.15, *) - func presentationControllerDidAttemptToDismiss(_: UIPresentationController) { - onAttempt?() - } - - @available(macOS 10.15, *) - func presentationControllerShouldDismiss(_: UIPresentationController) -> Bool { - parent?.isModalInPresentation == false - } - - @available(macOS 10.15, *) - override func responds(to aSelector: Selector!) -> Bool { - if super.responds(to: aSelector) { return true } - if _delegate?.responds(to: aSelector) ?? false { return true } - return false - } - - @available(macOS 10.15, *) - override func forwardingTarget(for aSelector: Selector!) -> Any? { - if super.responds(to: aSelector) { return self } - return _delegate - } - } - } -#endif diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/ScaledMetric/ScaledMetric.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/ScaledMetric/ScaledMetric.swift deleted file mode 100755 index 2b40cbb7..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/ScaledMetric/ScaledMetric.swift +++ /dev/null @@ -1,81 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(iOS, deprecated: 14) -@available(macOS, deprecated: 11) -@available(tvOS, deprecated: 14) -@available(watchOS, deprecated: 7) -@available(macOS 10.15, *) -extension Backport where Wrapped == Any { - /// A dynamic property that scales a numeric value. - @propertyWrapper - public struct ScaledMetric: DynamicProperty where Value: BinaryFloatingPoint { - @Environment(\.sizeCategory) var sizeCategory - - private let baseValue: Value - - #if os(iOS) || os(tvOS) - private let metrics: UIFontMetrics - #endif - - public var wrappedValue: Value { - #if os(iOS) || os(tvOS) - let traits = UITraitCollection(traitsFrom: [ - UITraitCollection(preferredContentSizeCategory: UIContentSizeCategory(sizeCategory: sizeCategory)), - ]) - - return Value(metrics.scaledValue(for: CGFloat(baseValue), compatibleWith: traits)) - #else - return baseValue - #endif - } - - #if os(iOS) || os(tvOS) - /// Creates the scaled metric with an unscaled value using the default scaling. - public init(baseValue: Value, metrics: UIFontMetrics) { - self.baseValue = baseValue - self.metrics = metrics - } - - /// Creates the scaled metric with an unscaled value using the default scaling. - public init(wrappedValue: Value) { - self.init(baseValue: wrappedValue, metrics: UIFontMetrics(forTextStyle: .body)) - } - - /// Creates the scaled metric with an unscaled value and a text style to scale relative to. - public init(wrappedValue: Value, relativeTo textStyle: UIFont.TextStyle) { - self.init(baseValue: wrappedValue, metrics: UIFontMetrics(forTextStyle: textStyle)) - } - #else - /// Creates the scaled metric with an unscaled value using the default scaling. - public init(wrappedValue: Value) { - baseValue = wrappedValue - } - #endif - } -} - -#if os(iOS) || os(tvOS) - fileprivate extension UIContentSizeCategory { - init(sizeCategory: ContentSizeCategory?) { - switch sizeCategory { - case .accessibilityExtraExtraExtraLarge: self = .accessibilityExtraExtraExtraLarge - case .accessibilityExtraExtraLarge: self = .accessibilityExtraExtraLarge - case .accessibilityExtraLarge: self = .accessibilityExtraLarge - case .accessibilityLarge: self = .accessibilityLarge - case .accessibilityMedium: self = .accessibilityMedium - case .extraExtraExtraLarge: self = .extraExtraExtraLarge - case .extraExtraLarge: self = .extraExtraLarge - case .extraLarge: self = .extraLarge - case .extraSmall: self = .extraSmall - case .large: self = .large - case .medium: self = .medium - case .small: self = .small - default: self = .unspecified - } - } - } -#endif diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/ScrollView/Scroll+Environment.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/ScrollView/Scroll+Environment.swift deleted file mode 100755 index 3f50dbb1..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/ScrollView/Scroll+Environment.swift +++ /dev/null @@ -1,69 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -@available(iOS, deprecated: 16) -@available(tvOS, deprecated: 16) -@available(macOS, deprecated: 13) -@available(watchOS, deprecated: 9) -extension EnvironmentValues { - /// The visiblity to apply to scroll indicators of any - /// vertically scrollable content. - public var backportVerticalScrollIndicatorVisibility: Backport.ScrollIndicatorVisibility { - get { self[BackportVerticalIndicatorKey.self] } - set { self[BackportVerticalIndicatorKey.self] = newValue } - } - - @available(macOS 10.15, *) - /// The visibility to apply to scroll indicators of any - /// horizontally scrollable content. - public var backportHorizontalScrollIndicatorVisibility: Backport.ScrollIndicatorVisibility { - get { self[BackportHorizontalIndicatorKey.self] } - set { self[BackportHorizontalIndicatorKey.self] = newValue } - } - - @available(macOS 10.15, *) - /// The way that scrollable content interacts with the software keyboard. - /// - /// The default value is ``Backport.ScrollDismissesKeyboardMode.automatic``. Use the - /// ``View.backport.scrollDismissesKeyboard(_:)`` modifier to configure this - /// property. - public var backportScrollDismissesKeyboardMode: Backport.ScrollDismissesKeyboardMode { - get { self[BackportKeyboardDismissKey.self] } - set { self[BackportKeyboardDismissKey.self] = newValue } - } - - @available(macOS 10.15, *) - /// A Boolean value that indicates whether any scroll views associated - /// with this environment allow scrolling to occur. - /// - /// The default value is `true`. Use the ``View.backport.scrollDisabled(_:)`` - /// modifier to configure this property. - public var backportIsScrollEnabled: Bool { - get { self[BackportScrollEnabledKey.self] } - set { self[BackportScrollEnabledKey.self] = newValue } - } -} - -@available(macOS 10.15, *) -private struct BackportVerticalIndicatorKey: EnvironmentKey { - static var defaultValue: Backport.ScrollIndicatorVisibility = .automatic -} - -@available(macOS 10.15, *) -private struct BackportHorizontalIndicatorKey: EnvironmentKey { - static var defaultValue: Backport.ScrollIndicatorVisibility = .automatic -} - -@available(macOS 10.15, *) -private struct BackportKeyboardDismissKey: EnvironmentKey { - static var defaultValue: Backport.ScrollDismissesKeyboardMode = .automatic -} - -@available(macOS 10.15, *) -private struct BackportScrollEnabledKey: EnvironmentKey { - static var defaultValue: Bool = true -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/ScrollView/ScrollDismissesKeyboardMode.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/ScrollView/ScrollDismissesKeyboardMode.swift deleted file mode 100755 index f5880950..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/ScrollView/ScrollDismissesKeyboardMode.swift +++ /dev/null @@ -1,63 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(iOS, deprecated: 16) -@available(tvOS, deprecated: 16) -@available(macOS, deprecated: 13) -@available(watchOS, deprecated: 9) -@available(macOS 10.15, *) -extension Backport where Wrapped == Any { - /// The ways that scrollable content can interact with the software keyboard. - /// - /// Use this type in a call to the ``View.backport.scrollDismissesKeyboard(_:)`` - /// modifier to specify the dismissal behavior of scrollable views. - public struct ScrollDismissesKeyboardMode: Hashable, CustomStringConvertible { - internal enum DismissMode: Hashable { - case automatic - case immediately - case interactively - case never - } - - let dismissMode: DismissMode - - #if os(iOS) - var scrollViewDismissMode: UIScrollView.KeyboardDismissMode { - switch dismissMode { - case .automatic: return .none - case .immediately: return .onDrag - case .interactively: return .interactive - case .never: return .none - } - } - #endif - - public var description: String { - String(describing: dismissMode) - } - - /// Determine the mode automatically based on the surrounding context. - /// - /// By default, a ``TextEditor`` is interactive while a ``List`` - /// of scrollable content always dismiss the keyboard on a scroll - public static var automatic: Self { .init(dismissMode: .automatic) } - - /// Dismiss the keyboard as soon as scrolling starts. - public static var immediately: Self { .init(dismissMode: .immediately) } - - /// Enable people to interactively dismiss the keyboard as part of the - /// scroll operation. - /// - /// The software keyboard's position tracks the gesture that drives the - /// scroll operation if the gesture crosses into the keyboard's area of the - /// display. People can dismiss the keyboard by scrolling it off the - /// display, or reverse the direction of the scroll to cancel the dismissal. - public static var interactively: Self { .init(dismissMode: .interactively) } - - /// Never dismiss the keyboard automatically as a result of scrolling. - public static var never: Self { .init(dismissMode: .never) } - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/ScrollView/ScrollEnabled.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/ScrollView/ScrollEnabled.swift deleted file mode 100755 index 119a12b0..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/ScrollView/ScrollEnabled.swift +++ /dev/null @@ -1,71 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -@available(iOS, deprecated: 16) -@available(tvOS, deprecated: 16) -@available(macOS, deprecated: 13) -@available(watchOS, deprecated: 9) -extension Backport where Wrapped: View { - /// Disables or enables scrolling in scrollable views. - /// - /// Use this modifier to control whether a ``ScrollView`` can scroll: - /// - /// @State private var isScrollDisabled = false - /// - /// var body: some View { - /// ScrollView { - /// VStack { - /// Toggle("Disable", isOn: $isScrollDisabled) - /// MyContent() - /// } - /// } - /// .backport.scrollDisabled(isScrollDisabled) - /// } - /// - /// SwiftUI passes the disabled property through the environment, which - /// means you can use this modifier to disable scrolling for all scroll - /// views within a view hierarchy. In the following example, the modifier - /// affects both scroll views: - /// - /// ScrollView { - /// ForEach(rows) { row in - /// ScrollView(.horizontal) { - /// RowContent(row) - /// } - /// } - /// } - /// .backport.scrollDisabled(true) - /// - /// You can also use this modifier to disable scrolling for other kinds - /// of scrollable views, like a ``List`` or a ``TextEditor``. - /// - /// - Parameter disabled: A Boolean that indicates whether scrolling is - /// disabled. - public func scrollDisabled(_ disabled: Bool) -> some View { - #if os(iOS) - content - .environment(\.backportIsScrollEnabled, !disabled) - .inspect { inspector in - #if os(iOS) - inspector.sibling(ofType: UIScrollView.self) - #elseif os(macOS) - inspector.sibling(ofType: NSScrollView.self) - #endif - } customize: { scrollView in - #if os(iOS) - scrollView.isScrollEnabled = !disabled - #elseif os(macOS) - scrollView.hasHorizontalScroller = false - scrollView.hasVerticalScroller = false - #endif - } - #else - content - .environment(\.backportIsScrollEnabled, !disabled) - #endif - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/ScrollView/ScrollIndicatorVisibility.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/ScrollView/ScrollIndicatorVisibility.swift deleted file mode 100755 index 8d2a3516..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/ScrollView/ScrollIndicatorVisibility.swift +++ /dev/null @@ -1,64 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -@available(iOS, deprecated: 16) -@available(tvOS, deprecated: 16) -@available(macOS, deprecated: 13) -@available(watchOS, deprecated: 9) -extension Backport where Wrapped == Any { - /// The visibility of scroll indicators of a UI element. - /// - /// Pass a value of this type to the ``View.backport.scrollIndicators(_:axes:)`` method - /// to specify the preferred scroll indicator visibility of a view hierarchy. - public struct ScrollIndicatorVisibility: Hashable, CustomStringConvertible { - internal enum IndicatorVisibility: Hashable { - case automatic - case visible - case hidden - } - - @available(macOS 10.15, *) - let visibility: Backport.Visibility - - @available(macOS 10.15, *) - var scrollViewVisible: Bool { - visibility != .hidden - } - - @available(macOS 10.15, *) - public var description: String { - String(describing: visibility) - } - - @available(macOS 10.15, *) - /// Scroll indicator visibility depends on the - /// policies of the component accepting the visibility configuration. - public static var automatic: ScrollIndicatorVisibility { - .init(visibility: .automatic) - } - - @available(macOS 10.15, *) - /// Show the scroll indicators. - /// - /// The actual visibility of the indicators depends on platform - /// conventions like auto-hiding behaviors in iOS or user preference - /// behaviors in macOS. - public static var visible: ScrollIndicatorVisibility { - .init(visibility: .visible) - } - - @available(macOS 10.15, *) - /// Hide the scroll indicators. - /// - /// By default, scroll views in macOS show indicators when a - /// mouse is connected. Use ``never`` to indicate - /// a stronger preference that can override this behavior. - public static var hidden: ScrollIndicatorVisibility { - .init(visibility: .hidden) - } - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/ScrollView/ScrollIndicators.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/ScrollView/ScrollIndicators.swift deleted file mode 100755 index eb03be66..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/ScrollView/ScrollIndicators.swift +++ /dev/null @@ -1,76 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(iOS, deprecated: 16) -@available(tvOS, deprecated: 16) -@available(macOS, deprecated: 13) -@available(watchOS, deprecated: 9) -@available(macOS 10.15, *) -extension Backport where Wrapped: View { - /// Sets the visibility of scroll indicators within this view. - /// - /// Use this modifier to hide or show scroll indicators on scrollable - /// content in views like a ``ScrollView``, ``List``, or ``TextEditor``. - /// This modifier applies the prefered visibility to any - /// scrollable content within a view hierarchy. - /// - /// ScrollView { - /// VStack(alignment: .leading) { - /// ForEach(0..<100) { - /// Text("Row \($0)") - /// } - /// } - /// } - /// .backport.scrollIndicators(.hidden) - /// - /// Use the ``Backport.ScrollIndicatorVisibility.hidden`` value to indicate that you - /// prefer that views never show scroll indicators along a given axis. - /// Use ``Backport.ScrollIndicatorVisibility.visible`` when you prefer that - /// views show scroll indicators. Depending on platform conventions, - /// visible scroll indicators might only appear while scrolling. Pass - /// ``Backport.ScrollIndicatorVisibility.automatic`` to allow views to - /// decide whether or not to show their indicators. - /// - /// - Parameters: - /// - visibility: The visibility to apply to scrollable views. - /// - axes: The axes of scrollable views that the visibility applies to. - /// - /// - Returns: A view with the specified scroll indicator visibility. - public func scrollIndicators( - _ visibility: Backport.ScrollIndicatorVisibility, axes: Axis.Set = [.vertical, .horizontal] - ) -> some View { - #if os(iOS) - content - .environment( - \.backportHorizontalScrollIndicatorVisibility, axes.contains(.horizontal) ? visibility : .automatic - ) - .environment(\.backportVerticalScrollIndicatorVisibility, axes.contains(.vertical) ? visibility : .automatic) - .inspect { inspector in - #if os(iOS) - inspector.sibling(ofType: UIScrollView.self) - #else - inspector.sourceView - #endif - } customize: { scrollView in - #if os(iOS) - if axes.contains(.horizontal) { - scrollView.showsHorizontalScrollIndicator = visibility.scrollViewVisible - } - - if axes.contains(.vertical) { - scrollView.showsVerticalScrollIndicator = visibility.scrollViewVisible - } - #endif - } - #else - content - .environment( - \.backportHorizontalScrollIndicatorVisibility, axes.contains(.horizontal) ? visibility : .automatic - ) - .environment(\.backportVerticalScrollIndicatorVisibility, axes.contains(.vertical) ? visibility : .automatic) - #endif - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/ScrollView/ScrollKeyboardDismiss.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/ScrollView/ScrollKeyboardDismiss.swift deleted file mode 100755 index a218eca0..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/ScrollView/ScrollKeyboardDismiss.swift +++ /dev/null @@ -1,68 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -@available(iOS, deprecated: 16) -@available(tvOS, deprecated: 16) -@available(macOS, deprecated: 13) -@available(watchOS, deprecated: 9) -extension Backport where Wrapped: View { - /// Configures the behavior in which scrollable content interacts with - /// the software keyboard. - /// - /// You use this modifier to customize how scrollable content interacts - /// with the software keyboard. For example, you can specify a value of - /// ``ScrollDismissesKeyboardMode/immediately`` to indicate that you - /// would like scrollable content to immediately dismiss the keyboard if - /// present when a scroll drag gesture begins. - /// - /// @State var text = "" - /// - /// ScrollView { - /// TextField("Prompt", text: $text) - /// ForEach(0 ..< 50) { index in - /// Text("\(index)") - /// .padding() - /// } - /// } - /// .scrollDismissesKeyboard(.immediately) - /// - /// You can also use this modifier to customize the keyboard dismissal - /// behavior for other kinds of scrollable views, like a ``List`` or a - /// ``TextEditor``. - /// - /// By default, a ``TextEditor`` is interactive while other kinds - /// of scrollable content always dismiss the keyboard on a scroll - /// when linked against iOS 16 or later. Pass a value of - /// ``ScrollDismissesKeyboardMode/never`` to indicate that scrollable - /// content should never automatically dismiss the keyboard. - /// - /// - Parameter mode: The keyboard dismissal mode that scrollable content - /// uses. - /// - /// - Returns: A view that uses the specified keyboard dismissal mode. - public func scrollDismissesKeyboard(_ mode: Backport.ScrollDismissesKeyboardMode) -> some View { - #if os(iOS) - content - .environment(\.backportScrollDismissesKeyboardMode, mode) - .inspect { inspector in - #if os(iOS) - inspector.sibling(ofType: UIScrollView.self) - #else - inspector.sourceView - #endif - } customize: { scrollView in - #if os(iOS) - guard scrollView.keyboardDismissMode != mode.scrollViewDismissMode else { return } - scrollView.keyboardDismissMode = mode.scrollViewDismissMode - #endif - } - #else - content - .environment(\.backportScrollDismissesKeyboardMode, mode) - #endif - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/UIHostingConfiguration/Cells/UICollectionViewCell.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/UIHostingConfiguration/Cells/UICollectionViewCell.swift deleted file mode 100755 index 8402d873..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/UIHostingConfiguration/Cells/UICollectionViewCell.swift +++ /dev/null @@ -1,76 +0,0 @@ -import ObjectiveC -import SwiftUI - -#if os(iOS) || os(tvOS) - - extension UICollectionViewCell { - private static var configuredViewAssociatedKey: Void? - - fileprivate var configuredView: UIView? { - get { objc_getAssociatedObject(self, &Self.configuredViewAssociatedKey) as? UIView } - set { objc_setAssociatedObject(self, &Self.configuredViewAssociatedKey, newValue, .OBJC_ASSOCIATION_ASSIGN) } - } - } - - @available(iOS, deprecated: 14) - @available(tvOS, deprecated: 14) - @available(macOS, unavailable) - @available(watchOS, unavailable) - extension Backport where Wrapped: UICollectionViewCell { - /// The current content configuration of the cell. - /// - /// Setting a content configuration replaces the existing contentView of the - /// cell with a new content view instance from the configuration. - public var contentConfiguration: BackportUIContentConfiguration? { - get { nil } // we can't really support anything here, so for now we'll return nil - nonmutating set { - content.configuredView?.removeFromSuperview() - - guard let configuration = newValue else { return } - let contentView = content.contentView - - let configuredView = configuration.makeContentView() - configuredView.translatesAutoresizingMaskIntoConstraints = false - - content.clipsToBounds = false - contentView.clipsToBounds = false - contentView.preservesSuperviewLayoutMargins = false - contentView.addSubview(configuredView) - - let insets = - Mirror(reflecting: configuration) - .children.first(where: { $0.label == "insets" })?.value as? ProposedInsets - ?? .unspecified - - insets.top.flatMap { contentView.directionalLayoutMargins.top = $0 } - insets.bottom.flatMap { contentView.directionalLayoutMargins.bottom = $0 } - insets.leading.flatMap { contentView.directionalLayoutMargins.leading = $0 } - insets.trailing.flatMap { contentView.directionalLayoutMargins.trailing = $0 } - - NSLayoutConstraint.activate([ - configuredView.topAnchor.constraint(equalTo: contentView.layoutMarginsGuide.topAnchor), - configuredView.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor), - configuredView.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor), - configuredView.trailingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.trailingAnchor), - ]) - - var background: AnyView? { - Mirror(reflecting: configuration) - .children.first(where: { $0.label == "background" })?.value as? AnyView - } - - background.flatMap { - let host = UIHostingController(rootView: $0, ignoreSafeArea: true) - content.backgroundView = host.view - } - - background.flatMap { - let host = UIHostingController(rootView: $0, ignoreSafeArea: true) - content.selectedBackgroundView = host.view - } - - content.configuredView = configuredView - } - } - } -#endif diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/UIHostingConfiguration/Cells/UITableViewCell.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/UIHostingConfiguration/Cells/UITableViewCell.swift deleted file mode 100755 index f8c6155a..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/UIHostingConfiguration/Cells/UITableViewCell.swift +++ /dev/null @@ -1,76 +0,0 @@ -import ObjectiveC -import SwiftUI - -#if os(iOS) || os(tvOS) - - extension UITableViewCell { - private static var configuredViewAssociatedKey: Void? - - fileprivate var configuredView: UIView? { - get { objc_getAssociatedObject(self, &Self.configuredViewAssociatedKey) as? UIView } - set { objc_setAssociatedObject(self, &Self.configuredViewAssociatedKey, newValue, .OBJC_ASSOCIATION_ASSIGN) } - } - } - - @available(iOS, deprecated: 14) - @available(tvOS, deprecated: 14) - @available(macOS, unavailable) - @available(watchOS, unavailable) - extension Backport where Wrapped: UITableViewCell { - /// The current content configuration of the cell. - /// - /// Setting a content configuration replaces the existing contentView of the - /// cell with a new content view instance from the configuration. - public var contentConfiguration: BackportUIContentConfiguration? { - get { nil } // we can't really support anything here, so for now we'll return nil - nonmutating set { - content.configuredView?.removeFromSuperview() - - guard let configuration = newValue else { return } - let contentView = content.contentView - - let configuredView = configuration.makeContentView() - configuredView.translatesAutoresizingMaskIntoConstraints = false - - content.clipsToBounds = false - contentView.clipsToBounds = false - contentView.preservesSuperviewLayoutMargins = false - contentView.addSubview(configuredView) - - let insets = - Mirror(reflecting: configuration) - .children.first(where: { $0.label == "insets" })?.value as? ProposedInsets - ?? .unspecified - - insets.top.flatMap { contentView.directionalLayoutMargins.top = $0 } - insets.bottom.flatMap { contentView.directionalLayoutMargins.bottom = $0 } - insets.leading.flatMap { contentView.directionalLayoutMargins.leading = $0 } - insets.trailing.flatMap { contentView.directionalLayoutMargins.trailing = $0 } - - NSLayoutConstraint.activate([ - configuredView.topAnchor.constraint(equalTo: contentView.layoutMarginsGuide.topAnchor), - configuredView.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor), - configuredView.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor), - configuredView.trailingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.trailingAnchor), - ]) - - var background: AnyView? { - Mirror(reflecting: configuration) - .children.first(where: { $0.label == "background" })?.value as? AnyView - } - - background.flatMap { - let host = UIHostingController(rootView: $0, ignoreSafeArea: true) - content.backgroundView = host.view - } - - background.flatMap { - let host = UIHostingController(rootView: $0, ignoreSafeArea: true) - content.selectedBackgroundView = host.view - } - - content.configuredView = configuredView - } - } - } -#endif diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/UIHostingConfiguration/ProposedInsets.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/UIHostingConfiguration/ProposedInsets.swift deleted file mode 100755 index 831b2834..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/UIHostingConfiguration/ProposedInsets.swift +++ /dev/null @@ -1,40 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -/// Provides optional inset values. `nil` is interpreted as: use system default -internal struct ProposedInsets: Equatable { - /// The proposed leading margin measured in points. - /// - /// A value of `nil` tells the system to use a default value - public var leading: CGFloat? - - @available(macOS 10.15, *) - /// The proposed trailing margin measured in points. - /// - /// A value of `nil` tells the system to use a default value - public var trailing: CGFloat? - - @available(macOS 10.15, *) - /// The proposed top margin measured in points. - /// - /// A value of `nil` tells the system to use a default value - public var top: CGFloat? - - @available(macOS 10.15, *) - /// The proposed bottom margin measured in points. - /// - /// A value of `nil` tells the system to use a default value - public var bottom: CGFloat? - - @available(macOS 10.15, *) - /// An insets proposal with all dimensions left unspecified. - public static var unspecified: ProposedInsets { .init() } - - @available(macOS 10.15, *) - /// An insets proposal that contains zero for all dimensions. - public static var zero: ProposedInsets { .init(leading: 0, trailing: 0, top: 0, bottom: 0) } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/UIHostingConfiguration/ProposedSize.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/UIHostingConfiguration/ProposedSize.swift deleted file mode 100755 index a0470881..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/UIHostingConfiguration/ProposedSize.swift +++ /dev/null @@ -1,62 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -@available(macOS 10.15, *) -/// A proposal for the size -/// -/// * The ``zero`` proposal; the size responds with its minimum size. -/// * The ``infinity`` proposal; the size responds with its maximum size. -/// * The ``unspecified`` proposal; the size responds with its system default size. -internal struct ProposedSize: Equatable, Sendable { - /// The proposed horizontal size measured in points. - /// - /// A value of `nil` represents an unspecified width proposal. - public var width: CGFloat? - - @available(macOS 10.15, *) - /// The proposed vertical size measured in points. - /// - /// A value of `nil` represents an unspecified height proposal. - public var height: CGFloat? - - @available(macOS 10.15, *) - /// A size proposal that contains zero in both dimensions. - public static var zero: ProposedSize { .init(width: 0, height: 0) } - - @available(macOS 10.15, *) - /// The proposed size with both dimensions left unspecified. - /// - /// Both dimensions contain `nil` in this size proposal. - public static var unspecified: ProposedSize { .init(width: nil, height: nil) } - - @available(macOS 10.15, *) - /// A size proposal that contains infinity in both dimensions. - /// - /// Both dimensions contain .infinity in this size proposal. - public static var infinity: ProposedSize { .init(width: .infinity, height: .infinity) } - - @available(macOS 10.15, *) - /// Creates a new proposed size using the specified width and height. - /// - /// - Parameters: - /// - width: A proposed width in points. Use a value of `nil` to indicate - /// that the width is unspecified for this proposal. - /// - height: A proposed height in points. Use a value of `nil` to - /// indicate that the height is unspecified for this proposal. - @inlinable public init(width: CGFloat?, height: CGFloat?) { - self.width = width - self.height = height - } - - @available(macOS 10.15, *) - /// Creates a new proposed size from a specified size. - /// - /// - Parameter size: A proposed size with dimensions measured in points. - @inlinable public init(_ size: CGSize) { - width = size.width - height = size.height - } -} diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/UIHostingConfiguration/UIContentConfiguration.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/UIHostingConfiguration/UIContentConfiguration.swift deleted file mode 100755 index 6492e5a2..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/UIHostingConfiguration/UIContentConfiguration.swift +++ /dev/null @@ -1,22 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -#if os(iOS) || os(tvOS) - /// The requirements for an object that provides the configuration for a content view. - /// - /// This protocol provides a blueprint for a content configuration object, which encompasses - /// default styling and content for a content view. The content configuration encapsulates - /// all of the supported properties and behaviors for content view customization. - /// You use the configuration to create the content view. - @available(iOS, deprecated: 14) - @available(tvOS, deprecated: 14) - @available(macOS, unavailable) - @available(watchOS, unavailable) - public protocol BackportUIContentConfiguration { - /// Initializes and returns a new instance of the content view using this configuration. - func makeContentView() -> UIView - } -#endif diff --git a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/UIHostingConfiguration/UIHostingConfiguration.swift b/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/UIHostingConfiguration/UIHostingConfiguration.swift deleted file mode 100755 index fa45cc8b..00000000 --- a/Packages/ShapsBenkau_SwiftUIBackports/Sources/SwiftUIBackports/iOS/UIHostingConfiguration/UIHostingConfiguration.swift +++ /dev/null @@ -1,187 +0,0 @@ -// (c) 2022 and onwards Shaps Benkau (MIT License). -// ==================== -// This code is released under the MIT license (SPDX-License-Identifier: MIT) - -import SwiftUI - -#if os(iOS) || os(tvOS) - @available(iOS, deprecated: 16) - @available(tvOS, deprecated: 16) - @available(macOS, unavailable) - @available(watchOS, unavailable) - extension Backport where Wrapped == Any { - /** - A content configuration suitable for hosting a hierarchy of SwiftUI views. - Use a value of this type, which conforms to the UIContentConfiguration protocol, with a UICollectionViewCell or UITableViewCell to host a hierarchy of SwiftUI views in a collection or table view, respectively. For example, the following shows a stack with an image and text inside the cell: - - myCell.contentConfiguration = UIHostingConfiguration { - HStack { - Image(systemName: "star").foregroundStyle(.purple) - Text("Favorites") - Spacer() - } - } - - You can also customize the background of the containing cell. The following example draws a blue background: - - myCell.contentConfiguration = UIHostingConfiguration { - HStack { - Image(systemName: "star").foregroundStyle(.purple) - Text("Favorites") - Spacer() - } - } - .background { - Color.blue - } - */ - public struct UIHostingConfiguration: BackportUIContentConfiguration - where Label: View, Background: View - { - var content: Label - var background: AnyView? - var insets: ProposedInsets - var minSize: ProposedSize - - /// Sets the background contents for the hosting configuration's enclosing - /// cell. - /// - /// The following example sets a custom view to the background of the cell: - /// - /// UIHostingConfiguration { - /// Text("My Contents") - /// } - /// .background { - /// MyBackgroundView() - /// } - /// - /// - Parameter background: The contents of the SwiftUI hierarchy to be - /// shown inside the background of the cell. - public func background(@ViewBuilder background: () -> B) -> Backport.UIHostingConfiguration - where B: View - { - .init(content: content, background: AnyView(background()), insets: insets, minSize: minSize) - } - - /// Sets the background contents for the hosting configuration's enclosing - /// cell. - /// - /// The following example sets a custom view to the background of the cell: - /// - /// UIHostingConfiguration { - /// Text("My Contents") - /// } - /// .background(Color.blue) - /// - /// - Parameter style: The shape style to be used as the background of the - /// cell. - public func background(_ style: S) -> Backport.UIHostingConfiguration where S: ShapeStyle { - .init(content: content, background: AnyView(style), insets: insets, minSize: minSize) - } - - /// Initializes and returns a new instance of the content view using this configuration. - public func makeContentView() -> UIView { - let view = UIHostingController( - rootView: ZStack { - background - content - }, - ignoreSafeArea: true - ).view! - - view.backgroundColor = .clear - view.clipsToBounds = false - - return view - } - } - } - - public extension Backport.UIHostingConfiguration { - /// Sets the margins around the content of the configuration. - /// - /// Use this modifier to replace the default margins applied to the root of - /// the configuration. The following example creates 20 points of space - /// between the content and the background on the horizontal edges. - /// - /// UIHostingConfiguration { - /// Text("My Contents") - /// } - /// .margins(.horizontal, 20.0) - /// - /// - Parameters: - /// - edges: The edges to apply the insets. Any edges not specified will - /// use the system default values. The default value is - /// ``Edge/Set/all``. - /// - length: The amount to apply. - func margins(_ edges: Edge.Set = .all, _ length: CGFloat) -> Self { - var view = self - if edges.contains(.leading) { view.insets.leading = length } - if edges.contains(.trailing) { view.insets.trailing = length } - if edges.contains(.top) { view.insets.top = length } - if edges.contains(.bottom) { view.insets.bottom = length } - return view - } - - /// Sets the margins around the content of the configuration. - /// - /// Use this modifier to replace the default margins applied to the root of - /// the configuration. The following example creates 10 points of space - /// between the content and the background on the leading edge and 20 points - /// of space on the trailing edge: - /// - /// UIHostingConfiguration { - /// Text("My Contents") - /// } - /// .margins(.horizontal, 20.0) - /// - /// - Parameters: - /// - edges: The edges to apply the insets. Any edges not specified will - /// use the system default values. The default value is - /// ``Edge/Set/all``. - /// - insets: The insets to apply. - func margins(_ edges: Edge.Set = .all, _ insets: EdgeInsets) -> Self { - var view = self - if edges.contains(.leading) { view.insets.leading = insets.leading } - if edges.contains(.trailing) { view.insets.trailing = insets.trailing } - if edges.contains(.top) { view.insets.top = insets.top } - if edges.contains(.bottom) { view.insets.bottom = insets.bottom } - return view - } - - /// Sets the minimum size for the configuration. - /// - /// Use this modifier to indicate that a configuration's associated cell can - /// be resized to a specific minimum. The following example allows the cell - /// to be compressed to zero size: - /// - /// UIHostingConfiguration { - /// Text("My Contents") - /// } - /// .minSize(width: 0, height: 0) - /// - /// - Parameter width: The value to use for the width dimension. A value of - /// `nil` indicates that the system default should be used. - /// - Parameter height: The value to use for the height dimension. A value - /// of `nil` indicates that the system default should be used. - // public func minSize(width: CGFloat? = nil, height: CGFloat? = nil) -> Self { - // var view = self - // view.minSize = .init(width: width, height: height) - // return view - // } - } - - @available(iOS, deprecated: 16) - @available(tvOS, deprecated: 16) - @available(macOS, unavailable) - @available(watchOS, unavailable) - extension Backport.UIHostingConfiguration where Wrapped == Any, Background == EmptyView { - /// Creates a hosting configuration with the given contents. - /// - /// - Parameter content: The contents of the SwiftUI hierarchy to be shown - /// inside the cell. - public init(@ViewBuilder content: () -> Label) { - self.init(content: content(), background: nil, insets: .init(), minSize: .unspecified) - } - } -#endif diff --git a/Packages/vChewing_CandidateWindow/Package.swift b/Packages/vChewing_CandidateWindow/Package.swift index 821fb7ae..31e8a625 100644 --- a/Packages/vChewing_CandidateWindow/Package.swift +++ b/Packages/vChewing_CandidateWindow/Package.swift @@ -14,14 +14,12 @@ let package = Package( ], dependencies: [ .package(path: "../vChewing_Shared"), - .package(path: "../ShapsBenkau_SwiftUIBackports"), ], targets: [ .target( name: "CandidateWindow", dependencies: [ .product(name: "Shared", package: "vChewing_Shared"), - .product(name: "SwiftUIBackports", package: "ShapsBenkau_SwiftUIBackports"), ] ), .testTarget( diff --git a/Packages/vChewing_PhraseEditorUI/Package.swift b/Packages/vChewing_PhraseEditorUI/Package.swift index 17097949..068c748b 100644 --- a/Packages/vChewing_PhraseEditorUI/Package.swift +++ b/Packages/vChewing_PhraseEditorUI/Package.swift @@ -15,14 +15,12 @@ let package = Package( dependencies: [ .package(path: "../vChewing_LangModelAssembly"), .package(path: "../vChewing_Shared"), - .package(path: "../ShapsBenkau_SwiftUIBackports"), ], targets: [ .target( name: "PhraseEditorUI", dependencies: [ .product(name: "LangModelAssembly", package: "vChewing_LangModelAssembly"), - .product(name: "SwiftUIBackports", package: "ShapsBenkau_SwiftUIBackports"), .product(name: "Shared", package: "vChewing_Shared"), ] ), diff --git a/Source/Modules/UIModules/PrefUI/VwrPrefPaneBehavior.swift b/Source/Modules/UIModules/PrefUI/VwrPrefPaneBehavior.swift index ae4b9aae..77b305d3 100644 --- a/Source/Modules/UIModules/PrefUI/VwrPrefPaneBehavior.swift +++ b/Source/Modules/UIModules/PrefUI/VwrPrefPaneBehavior.swift @@ -10,63 +10,57 @@ import MainAssembly import Shared import SwiftExtension import SwiftUI -import SwiftUIBackports @available(macOS 13, *) struct VwrPrefPaneBehavior: View { // MARK: - AppStorage Variables - @Backport.AppStorage(wrappedValue: true, UserDef.kChooseCandidateUsingSpace.rawValue) + @AppStorage(wrappedValue: true, UserDef.kChooseCandidateUsingSpace.rawValue) private var chooseCandidateUsingSpace: Bool - @Backport.AppStorage(wrappedValue: true, UserDef.kEscToCleanInputBuffer.rawValue) + @AppStorage(wrappedValue: true, UserDef.kEscToCleanInputBuffer.rawValue) private var escToCleanInputBuffer: Bool - @Backport.AppStorage(wrappedValue: true, UserDef.kAcceptLeadingIntonations.rawValue) + @AppStorage(wrappedValue: true, UserDef.kAcceptLeadingIntonations.rawValue) private var acceptLeadingIntonations: Bool - @Backport.AppStorage(wrappedValue: 0, UserDef.kSpecifyIntonationKeyBehavior.rawValue) + @AppStorage(wrappedValue: 0, UserDef.kSpecifyIntonationKeyBehavior.rawValue) private var specifyIntonationKeyBehavior: Int - @Backport.AppStorage(wrappedValue: 0, UserDef.kSpecifyShiftBackSpaceKeyBehavior.rawValue) + @AppStorage(wrappedValue: 0, UserDef.kSpecifyShiftBackSpaceKeyBehavior.rawValue) private var specifyShiftBackSpaceKeyBehavior: Int - @Backport.AppStorage(wrappedValue: false, UserDef.kSpecifyShiftTabKeyBehavior.rawValue) + @AppStorage(wrappedValue: false, UserDef.kSpecifyShiftTabKeyBehavior.rawValue) private var specifyShiftTabKeyBehavior: Bool - @Backport.AppStorage(wrappedValue: false, UserDef.kSpecifyShiftSpaceKeyBehavior.rawValue) + @AppStorage(wrappedValue: false, UserDef.kSpecifyShiftSpaceKeyBehavior.rawValue) private var specifyShiftSpaceKeyBehavior: Bool - @Backport.AppStorage(wrappedValue: true, UserDef.kUseSpaceToCommitHighlightedSCPCCandidate.rawValue) + @AppStorage(wrappedValue: true, UserDef.kUseSpaceToCommitHighlightedSCPCCandidate.rawValue) private var useSpaceToCommitHighlightedSCPCCandidate: Bool - @Backport.AppStorage(wrappedValue: false, UserDef.kAlsoConfirmAssociatedCandidatesByEnter.rawValue) + @AppStorage(wrappedValue: false, UserDef.kAlsoConfirmAssociatedCandidatesByEnter.rawValue) private var alsoConfirmAssociatedCandidatesByEnter: Bool - @Backport.AppStorage(wrappedValue: true, UserDef.kTogglingAlphanumericalModeWithLShift.rawValue) + @AppStorage(wrappedValue: true, UserDef.kTogglingAlphanumericalModeWithLShift.rawValue) private var togglingAlphanumericalModeWithLShift: Bool - @Backport.AppStorage(wrappedValue: true, UserDef.kTogglingAlphanumericalModeWithRShift.rawValue) + @AppStorage(wrappedValue: true, UserDef.kTogglingAlphanumericalModeWithRShift.rawValue) private var togglingAlphanumericalModeWithRShift: Bool - @Backport.AppStorage(wrappedValue: 0, UserDef.kUpperCaseLetterKeyBehavior.rawValue) + @AppStorage(wrappedValue: 0, UserDef.kUpperCaseLetterKeyBehavior.rawValue) private var upperCaseLetterKeyBehavior: Int - @Backport.AppStorage(wrappedValue: false, UserDef.kAlwaysShowTooltipTextsHorizontally.rawValue) + @AppStorage(wrappedValue: false, UserDef.kAlwaysShowTooltipTextsHorizontally.rawValue) private var alwaysShowTooltipTextsHorizontally: Bool - @Backport.AppStorage(wrappedValue: true, UserDef.kShowNotificationsWhenTogglingCapsLock.rawValue) + @AppStorage(wrappedValue: true, UserDef.kShowNotificationsWhenTogglingCapsLock.rawValue) private var showNotificationsWhenTogglingCapsLock: Bool - @Backport.AppStorage(wrappedValue: false, UserDef.kShareAlphanumericalModeStatusAcrossClients.rawValue) + @AppStorage(wrappedValue: false, UserDef.kShareAlphanumericalModeStatusAcrossClients.rawValue) private var shareAlphanumericalModeStatusAcrossClients: Bool - var macOSMontereyOrLaterDetected: Bool { - if #available(macOS 12, *) { - return true - } - return false - } + var macOSMontereyOrLaterDetected: Bool { true } // Always met. // MARK: - Main View diff --git a/Source/Modules/UIModules/PrefUI/VwrPrefPaneCandidates.swift b/Source/Modules/UIModules/PrefUI/VwrPrefPaneCandidates.swift index c10b83e3..47327996 100644 --- a/Source/Modules/UIModules/PrefUI/VwrPrefPaneCandidates.swift +++ b/Source/Modules/UIModules/PrefUI/VwrPrefPaneCandidates.swift @@ -10,43 +10,42 @@ import MainAssembly import Shared import SwiftExtension import SwiftUI -import SwiftUIBackports @available(macOS 13, *) struct VwrPrefPaneCandidates: View { // MARK: - AppStorage Variables - @Backport.AppStorage(wrappedValue: 16, UserDef.kCandidateListTextSize.rawValue) + @AppStorage(wrappedValue: 16, UserDef.kCandidateListTextSize.rawValue) private var candidateListTextSize: Double - @Backport.AppStorage(wrappedValue: true, UserDef.kUseHorizontalCandidateList.rawValue) + @AppStorage(wrappedValue: true, UserDef.kUseHorizontalCandidateList.rawValue) private var useHorizontalCandidateList: Bool - @Backport.AppStorage(wrappedValue: false, UserDef.kCandidateWindowShowOnlyOneLine.rawValue) + @AppStorage(wrappedValue: false, UserDef.kCandidateWindowShowOnlyOneLine.rawValue) private var candidateWindowShowOnlyOneLine: Bool - @Backport.AppStorage(wrappedValue: false, UserDef.kAlwaysExpandCandidateWindow.rawValue) + @AppStorage(wrappedValue: false, UserDef.kAlwaysExpandCandidateWindow.rawValue) private var alwaysExpandCandidateWindow: Bool - @Backport.AppStorage(wrappedValue: true, UserDef.kShowReverseLookupInCandidateUI.rawValue) + @AppStorage(wrappedValue: true, UserDef.kShowReverseLookupInCandidateUI.rawValue) private var showReverseLookupInCandidateUI: Bool - @Backport.AppStorage(wrappedValue: false, UserDef.kUseRearCursorMode.rawValue) + @AppStorage(wrappedValue: false, UserDef.kUseRearCursorMode.rawValue) private var useRearCursorMode: Bool - @Backport.AppStorage(wrappedValue: true, UserDef.kMoveCursorAfterSelectingCandidate.rawValue) + @AppStorage(wrappedValue: true, UserDef.kMoveCursorAfterSelectingCandidate.rawValue) private var moveCursorAfterSelectingCandidate: Bool - @Backport.AppStorage(wrappedValue: true, UserDef.kUseDynamicCandidateWindowOrigin.rawValue) + @AppStorage(wrappedValue: true, UserDef.kUseDynamicCandidateWindowOrigin.rawValue) private var useDynamicCandidateWindowOrigin: Bool - @Backport.AppStorage(wrappedValue: false, UserDef.kUseFixedCandidateOrderOnSelection.rawValue) + @AppStorage(wrappedValue: false, UserDef.kUseFixedCandidateOrderOnSelection.rawValue) private var useFixedCandidateOrderOnSelection: Bool - @Backport.AppStorage(wrappedValue: true, UserDef.kConsolidateContextOnCandidateSelection.rawValue) + @AppStorage(wrappedValue: true, UserDef.kConsolidateContextOnCandidateSelection.rawValue) private var consolidateContextOnCandidateSelection: Bool - @Backport.AppStorage(wrappedValue: false, UserDef.kEnableMouseScrollingForTDKCandidatesCocoa.rawValue) + @AppStorage(wrappedValue: false, UserDef.kEnableMouseScrollingForTDKCandidatesCocoa.rawValue) private var enableMouseScrollingForTDKCandidatesCocoa: Bool // MARK: - Main View @@ -209,7 +208,7 @@ struct VwrPrefPaneCandidates_Previews: PreviewProvider { private struct VwrPrefPaneCandidates_SelectionKeys: View { // MARK: - AppStorage Variables - @Backport.AppStorage(wrappedValue: PrefMgr.kDefaultCandidateKeys, UserDef.kCandidateKeys.rawValue) + @AppStorage(wrappedValue: PrefMgr.kDefaultCandidateKeys, UserDef.kCandidateKeys.rawValue) private var candidateKeys: String // MARK: - Main View diff --git a/Source/Modules/UIModules/PrefUI/VwrPrefPaneCassette.swift b/Source/Modules/UIModules/PrefUI/VwrPrefPaneCassette.swift index af20623e..255ea73f 100644 --- a/Source/Modules/UIModules/PrefUI/VwrPrefPaneCassette.swift +++ b/Source/Modules/UIModules/PrefUI/VwrPrefPaneCassette.swift @@ -11,25 +11,24 @@ import MainAssembly import Shared import SwiftExtension import SwiftUI -import SwiftUIBackports @available(macOS 13, *) struct VwrPrefPaneCassette: View { // MARK: - AppStorage Variables - @Backport.AppStorage(wrappedValue: "", UserDef.kCassettePath.rawValue) + @AppStorage(wrappedValue: "", UserDef.kCassettePath.rawValue) private var cassettePath: String - @Backport.AppStorage(wrappedValue: false, UserDef.kCassetteEnabled.rawValue) + @AppStorage(wrappedValue: false, UserDef.kCassetteEnabled.rawValue) private var cassetteEnabled: Bool - @Backport.AppStorage(wrappedValue: 0, UserDef.kForceCassetteChineseConversion.rawValue) + @AppStorage(wrappedValue: 0, UserDef.kForceCassetteChineseConversion.rawValue) private var forceCassetteChineseConversion: Int - @Backport.AppStorage(wrappedValue: true, UserDef.kShowTranslatedStrokesInCompositionBuffer.rawValue) + @AppStorage(wrappedValue: true, UserDef.kShowTranslatedStrokesInCompositionBuffer.rawValue) private var showTranslatedStrokesInCompositionBuffer: Bool - @Backport.AppStorage(wrappedValue: true, UserDef.kAutoCompositeWithLongestPossibleCassetteKey.rawValue) + @AppStorage(wrappedValue: true, UserDef.kAutoCompositeWithLongestPossibleCassetteKey.rawValue) private var autoCompositeWithLongestPossibleCassetteKey: Bool // MARK: - Main View diff --git a/Source/Modules/UIModules/PrefUI/VwrPrefPaneDevZone.swift b/Source/Modules/UIModules/PrefUI/VwrPrefPaneDevZone.swift index 5d6e6a3d..a5cc03c8 100644 --- a/Source/Modules/UIModules/PrefUI/VwrPrefPaneDevZone.swift +++ b/Source/Modules/UIModules/PrefUI/VwrPrefPaneDevZone.swift @@ -9,19 +9,18 @@ import Shared import SwiftExtension import SwiftUI -import SwiftUIBackports @available(macOS 13, *) struct VwrPrefPaneDevZone: View { // MARK: - AppStorage Variables - @Backport.AppStorage( + @AppStorage( wrappedValue: false, UserDef.kDisableSegmentedThickUnderlineInMarkingModeForManagedClients.rawValue ) private var disableSegmentedThickUnderlineInMarkingModeForManagedClients: Bool - @Backport.AppStorage( + @AppStorage( wrappedValue: false, UserDef.kSecurityHardenedCompositionBuffer.rawValue ) diff --git a/Source/Modules/UIModules/PrefUI/VwrPrefPaneDictionary.swift b/Source/Modules/UIModules/PrefUI/VwrPrefPaneDictionary.swift index d7679f1b..8a2ad8e3 100644 --- a/Source/Modules/UIModules/PrefUI/VwrPrefPaneDictionary.swift +++ b/Source/Modules/UIModules/PrefUI/VwrPrefPaneDictionary.swift @@ -12,37 +12,36 @@ import MainAssembly import Shared import SwiftExtension import SwiftUI -import SwiftUIBackports @available(macOS 13, *) struct VwrPrefPaneDictionary: View { // MARK: - AppStorage Variables - @Backport.AppStorage(wrappedValue: "", UserDef.kUserDataFolderSpecified.rawValue) + @AppStorage(wrappedValue: "", UserDef.kUserDataFolderSpecified.rawValue) private var userDataFolderSpecified: String - @Backport.AppStorage(wrappedValue: true, UserDef.kShouldAutoReloadUserDataFiles.rawValue) + @AppStorage(wrappedValue: true, UserDef.kShouldAutoReloadUserDataFiles.rawValue) private var shouldAutoReloadUserDataFiles: Bool - @Backport.AppStorage(wrappedValue: false, UserDef.kUseExternalFactoryDict.rawValue) + @AppStorage(wrappedValue: false, UserDef.kUseExternalFactoryDict.rawValue) private var useExternalFactoryDict: Bool - @Backport.AppStorage(wrappedValue: true, UserDef.kOnlyLoadFactoryLangModelsIfNeeded.rawValue) + @AppStorage(wrappedValue: true, UserDef.kOnlyLoadFactoryLangModelsIfNeeded.rawValue) private var onlyLoadFactoryLangModelsIfNeeded: Bool - @Backport.AppStorage(wrappedValue: false, UserDef.kCNS11643Enabled.rawValue) + @AppStorage(wrappedValue: false, UserDef.kCNS11643Enabled.rawValue) private var cns11643Enabled: Bool - @Backport.AppStorage(wrappedValue: true, UserDef.kSymbolInputEnabled.rawValue) + @AppStorage(wrappedValue: true, UserDef.kSymbolInputEnabled.rawValue) private var symbolInputEnabled: Bool - @Backport.AppStorage(wrappedValue: true, UserDef.kFetchSuggestionsFromUserOverrideModel.rawValue) + @AppStorage(wrappedValue: true, UserDef.kFetchSuggestionsFromUserOverrideModel.rawValue) private var fetchSuggestionsFromUserOverrideModel: Bool - @Backport.AppStorage(wrappedValue: false, UserDef.kPhraseReplacementEnabled.rawValue) + @AppStorage(wrappedValue: false, UserDef.kPhraseReplacementEnabled.rawValue) private var phraseReplacementEnabled: Bool - @Backport.AppStorage(wrappedValue: false, UserDef.kAllowBoostingSingleKanjiAsUserPhrase.rawValue) + @AppStorage(wrappedValue: false, UserDef.kAllowBoostingSingleKanjiAsUserPhrase.rawValue) private var allowBoostingSingleKanjiAsUserPhrase: Bool // MARK: - Main View diff --git a/Source/Modules/UIModules/PrefUI/VwrPrefPaneGeneral.swift b/Source/Modules/UIModules/PrefUI/VwrPrefPaneGeneral.swift index a123815f..e6a7b81d 100644 --- a/Source/Modules/UIModules/PrefUI/VwrPrefPaneGeneral.swift +++ b/Source/Modules/UIModules/PrefUI/VwrPrefPaneGeneral.swift @@ -10,7 +10,6 @@ import MainAssembly import Shared import SwiftExtension import SwiftUI -import SwiftUIBackports @available(macOS 13, *) struct VwrPrefPaneGeneral: View { @@ -39,31 +38,28 @@ struct VwrPrefPaneGeneral: View { // MARK: - AppStorage Variables - @Backport.AppStorage(wrappedValue: [], UserDef.kAppleLanguages.rawValue) - private var appleLanguages: [String] - - @Backport.AppStorage(wrappedValue: true, UserDef.kAutoCorrectReadingCombination.rawValue) + @AppStorage(wrappedValue: true, UserDef.kAutoCorrectReadingCombination.rawValue) private var autoCorrectReadingCombination: Bool - @Backport.AppStorage(wrappedValue: false, UserDef.kKeepReadingUponCompositionError.rawValue) + @AppStorage(wrappedValue: false, UserDef.kKeepReadingUponCompositionError.rawValue) private var keepReadingUponCompositionError: Bool - @Backport.AppStorage(wrappedValue: false, UserDef.kShowHanyuPinyinInCompositionBuffer.rawValue) + @AppStorage(wrappedValue: false, UserDef.kShowHanyuPinyinInCompositionBuffer.rawValue) private var showHanyuPinyinInCompositionBuffer: Bool - @Backport.AppStorage(wrappedValue: false, UserDef.kClassicHaninKeyboardSymbolModeShortcutEnabled.rawValue) + @AppStorage(wrappedValue: false, UserDef.kClassicHaninKeyboardSymbolModeShortcutEnabled.rawValue) private var classicHaninKeyboardSymbolModeShortcutEnabled: Bool - @Backport.AppStorage(wrappedValue: false, UserDef.kUseSCPCTypingMode.rawValue) + @AppStorage(wrappedValue: false, UserDef.kUseSCPCTypingMode.rawValue) private var useSCPCTypingMode: Bool - @Backport.AppStorage(wrappedValue: true, UserDef.kShouldNotFartInLieuOfBeep.rawValue) + @AppStorage(wrappedValue: true, UserDef.kShouldNotFartInLieuOfBeep.rawValue) private var shouldNotFartInLieuOfBeep: Bool - @Backport.AppStorage(wrappedValue: false, UserDef.kCheckUpdateAutomatically.rawValue) + @AppStorage(wrappedValue: false, UserDef.kCheckUpdateAutomatically.rawValue) private var checkUpdateAutomatically: Bool - @Backport.AppStorage(wrappedValue: false, UserDef.kIsDebugModeEnabled.rawValue) + @AppStorage(wrappedValue: false, UserDef.kIsDebugModeEnabled.rawValue) private var isDebugModeEnabled: Bool // MARK: - Main View diff --git a/Source/Modules/UIModules/PrefUI/VwrPrefPaneKeyboard.swift b/Source/Modules/UIModules/PrefUI/VwrPrefPaneKeyboard.swift index bf2f0195..d4f10adf 100644 --- a/Source/Modules/UIModules/PrefUI/VwrPrefPaneKeyboard.swift +++ b/Source/Modules/UIModules/PrefUI/VwrPrefPaneKeyboard.swift @@ -11,22 +11,21 @@ import MainAssembly import Shared import SwiftExtension import SwiftUI -import SwiftUIBackports @available(macOS 13, *) struct VwrPrefPaneKeyboard: View { // MARK: - AppStorage Variables - @Backport.AppStorage(wrappedValue: 0, UserDef.kKeyboardParser.rawValue) + @AppStorage(wrappedValue: 0, UserDef.kKeyboardParser.rawValue) private var keyboardParser: Int - @Backport.AppStorage( + @AppStorage( wrappedValue: PrefMgr.kDefaultBasicKeyboardLayout, UserDef.kBasicKeyboardLayout.rawValue ) private var basicKeyboardLayout: String - @Backport.AppStorage( + @AppStorage( wrappedValue: PrefMgr.kDefaultAlphanumericalKeyboardLayout, UserDef.kAlphanumericalKeyboardLayout.rawValue ) @@ -135,34 +134,34 @@ struct VwrPrefPaneKeyboard: View { private struct VwrPrefPaneKeyboard_KeyboardShortcuts: View { // MARK: - AppStorage Variables - @Backport.AppStorage(wrappedValue: true, UserDef.kUsingHotKeySCPC.rawValue) + @AppStorage(wrappedValue: true, UserDef.kUsingHotKeySCPC.rawValue) private var usingHotKeySCPC: Bool - @Backport.AppStorage(wrappedValue: true, UserDef.kUsingHotKeyAssociates.rawValue) + @AppStorage(wrappedValue: true, UserDef.kUsingHotKeyAssociates.rawValue) private var usingHotKeyAssociates: Bool - @Backport.AppStorage(wrappedValue: true, UserDef.kUsingHotKeyCNS.rawValue) + @AppStorage(wrappedValue: true, UserDef.kUsingHotKeyCNS.rawValue) private var usingHotKeyCNS: Bool - @Backport.AppStorage(wrappedValue: true, UserDef.kUsingHotKeyKangXi.rawValue) + @AppStorage(wrappedValue: true, UserDef.kUsingHotKeyKangXi.rawValue) private var usingHotKeyKangXi: Bool - @Backport.AppStorage(wrappedValue: true, UserDef.kUsingHotKeyJIS.rawValue) + @AppStorage(wrappedValue: true, UserDef.kUsingHotKeyJIS.rawValue) private var usingHotKeyJIS: Bool - @Backport.AppStorage(wrappedValue: true, UserDef.kUsingHotKeyHalfWidthASCII.rawValue) + @AppStorage(wrappedValue: true, UserDef.kUsingHotKeyHalfWidthASCII.rawValue) private var usingHotKeyHalfWidthASCII: Bool - @Backport.AppStorage(wrappedValue: true, UserDef.kUsingHotKeyCurrencyNumerals.rawValue) + @AppStorage(wrappedValue: true, UserDef.kUsingHotKeyCurrencyNumerals.rawValue) private var usingHotKeyCurrencyNumerals: Bool - @Backport.AppStorage(wrappedValue: true, UserDef.kUsingHotKeyCassette.rawValue) + @AppStorage(wrappedValue: true, UserDef.kUsingHotKeyCassette.rawValue) private var usingHotKeyCassette: Bool - @Backport.AppStorage(wrappedValue: true, UserDef.kUsingHotKeyRevLookup.rawValue) + @AppStorage(wrappedValue: true, UserDef.kUsingHotKeyRevLookup.rawValue) private var usingHotKeyRevLookup: Bool - @Backport.AppStorage(wrappedValue: true, UserDef.kUsingHotKeyInputMode.rawValue) + @AppStorage(wrappedValue: true, UserDef.kUsingHotKeyInputMode.rawValue) private var usingHotKeyInputMode: Bool // MARK: - Main View diff --git a/Source/Modules/UIModules/PrefUI/VwrPrefPaneOutput.swift b/Source/Modules/UIModules/PrefUI/VwrPrefPaneOutput.swift index 5c9be4bd..6fa4b880 100644 --- a/Source/Modules/UIModules/PrefUI/VwrPrefPaneOutput.swift +++ b/Source/Modules/UIModules/PrefUI/VwrPrefPaneOutput.swift @@ -9,25 +9,24 @@ import Shared import SwiftExtension import SwiftUI -import SwiftUIBackports @available(macOS 13, *) struct VwrPrefPaneOutput: View { // MARK: - AppStorage Variables - @Backport.AppStorage(wrappedValue: false, UserDef.kChineseConversionEnabled.rawValue) + @AppStorage(wrappedValue: false, UserDef.kChineseConversionEnabled.rawValue) private var chineseConversionEnabled: Bool - @Backport.AppStorage(wrappedValue: false, UserDef.kShiftJISShinjitaiOutputEnabled.rawValue) + @AppStorage(wrappedValue: false, UserDef.kShiftJISShinjitaiOutputEnabled.rawValue) private var shiftJISShinjitaiOutputEnabled: Bool - @Backport.AppStorage(wrappedValue: false, UserDef.kInlineDumpPinyinInLieuOfZhuyin.rawValue) + @AppStorage(wrappedValue: false, UserDef.kInlineDumpPinyinInLieuOfZhuyin.rawValue) private var inlineDumpPinyinInLieuOfZhuyin: Bool - @Backport.AppStorage(wrappedValue: true, UserDef.kTrimUnfinishedReadingsOnCommit.rawValue) + @AppStorage(wrappedValue: true, UserDef.kTrimUnfinishedReadingsOnCommit.rawValue) private var trimUnfinishedReadingsOnCommit: Bool - @Backport.AppStorage(wrappedValue: false, UserDef.kHardenVerticalPunctuations.rawValue) + @AppStorage(wrappedValue: false, UserDef.kHardenVerticalPunctuations.rawValue) private var hardenVerticalPunctuations: Bool // MARK: - Main View diff --git a/vChewing.xcodeproj/project.pbxproj b/vChewing.xcodeproj/project.pbxproj index 1bdea09d..2c50e9ff 100644 --- a/vChewing.xcodeproj/project.pbxproj +++ b/vChewing.xcodeproj/project.pbxproj @@ -25,7 +25,6 @@ 5B40113928D7050D00A9D4CB /* Shared in Frameworks */ = {isa = PBXBuildFile; productRef = 5B40113828D7050D00A9D4CB /* Shared */; }; 5B40113C28D71C0100A9D4CB /* Uninstaller in Frameworks */ = {isa = PBXBuildFile; productRef = 5B40113B28D71C0100A9D4CB /* Uninstaller */; }; 5B4C59AE2ABCB18700098626 /* VwrSettingsUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B4C59AD2ABCB18700098626 /* VwrSettingsUI.swift */; }; - 5B5A603028E81CC50001AE8D /* SwiftUIBackports in Frameworks */ = {isa = PBXBuildFile; productRef = 5B5A602F28E81CC50001AE8D /* SwiftUIBackports */; }; 5B62A33D27AE7CC100A19448 /* CtlAboutWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33C27AE7CC100A19448 /* CtlAboutWindow.swift */; }; 5B65FB342A9518DA007EEFB0 /* MainAssembly in Frameworks */ = {isa = PBXBuildFile; productRef = 5B65FB332A9518DA007EEFB0 /* MainAssembly */; }; 5B69938C293B811F0057CB8E /* VwrPrefPanePhrases.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B69938B293B811F0057CB8E /* VwrPrefPanePhrases.swift */; }; @@ -189,7 +188,6 @@ 5B312684287800DE001AA720 /* FAQ.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = FAQ.md; sourceTree = ""; }; 5B40113A28D71B8700A9D4CB /* vChewing_Uninstaller */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_Uninstaller; path = Packages/vChewing_Uninstaller; sourceTree = ""; }; 5B4C59AD2ABCB18700098626 /* VwrSettingsUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VwrSettingsUI.swift; sourceTree = ""; }; - 5B5A602E28E81CB00001AE8D /* ShapsBenkau_SwiftUIBackports */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = ShapsBenkau_SwiftUIBackports; path = Packages/ShapsBenkau_SwiftUIBackports; sourceTree = ""; }; 5B62A33C27AE7CC100A19448 /* CtlAboutWindow.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = CtlAboutWindow.swift; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; 5B65B919284D0185007C558B /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 5B65FB322A9518C9007EEFB0 /* vChewing_MainAssembly */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_MainAssembly; path = Packages/vChewing_MainAssembly; sourceTree = ""; }; @@ -313,7 +311,6 @@ 5BC5E02128DDEFE00094E427 /* TooltipUI in Frameworks */, 5B765F09293A253C00122315 /* PhraseEditorUI in Frameworks */, 5BDB7A3928D4824A001AC277 /* BookmarkManager in Frameworks */, - 5B5A603028E81CC50001AE8D /* SwiftUIBackports in Frameworks */, 5B963CA328D5C23600DCEE88 /* SwiftExtension in Frameworks */, 5B40113928D7050D00A9D4CB /* Shared in Frameworks */, 5BFC63C628D48806004A77B7 /* NSAttributedTextView in Frameworks */, @@ -521,7 +518,6 @@ 5BDB7A3028D47587001AC277 /* Jad_BookmarkManager */, 5BDB7A3428D47587001AC277 /* Qwertyyb_ShiftKeyUpChecker */, 5BDB7A3528D47587001AC277 /* RMJay_LineReader */, - 5B5A602E28E81CB00001AE8D /* ShapsBenkau_SwiftUIBackports */, 5BA8C30128DEFE4F004C5CC4 /* vChewing_CandidateWindow */, 5B963C9B28D5BE4100DCEE88 /* vChewing_CocoaExtension */, 5BDB7A3228D47587001AC277 /* vChewing_Hotenka */, @@ -713,7 +709,6 @@ 5BC5E02028DDEFE00094E427 /* TooltipUI */, 5BC5E02328DE07860094E427 /* PopupCompositionBuffer */, 5BA8C30228DF0360004C5CC4 /* CandidateWindow */, - 5B5A602F28E81CC50001AE8D /* SwiftUIBackports */, 5B765F08293A253C00122315 /* PhraseEditorUI */, 5B65FB332A9518DA007EEFB0 /* MainAssembly */, ); @@ -1568,10 +1563,6 @@ isa = XCSwiftPackageProductDependency; productName = Uninstaller; }; - 5B5A602F28E81CC50001AE8D /* SwiftUIBackports */ = { - isa = XCSwiftPackageProductDependency; - productName = SwiftUIBackports; - }; 5B65FB332A9518DA007EEFB0 /* MainAssembly */ = { isa = XCSwiftPackageProductDependency; productName = MainAssembly;