@swiftbrowser/swift 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Package.swift +200 -9
- package/README.md +7 -9
- package/Sources/AVFoundation/AVFoundation.swift +131 -0
- package/Sources/AVFoundation/Asset.swift +487 -0
- package/Sources/AVFoundation/AudioSession.swift +144 -0
- package/Sources/AVFoundation/CMTime.swift +303 -0
- package/Sources/AVFoundation/Capture.swift +688 -0
- package/Sources/AVFoundation/Player.swift +749 -0
- package/Sources/AVFoundation/PlayerView.swift +117 -0
- package/Sources/AVKit/AVKit.swift +132 -0
- package/Sources/Accelerate/Accelerate.swift +122 -0
- package/Sources/AppIntents/AppIntents.swift +144 -0
- package/Sources/AppIntents/Parameters.swift +220 -0
- package/Sources/AppIntents/Shortcuts.swift +159 -0
- package/Sources/AudioToolbox/AudioToolbox.swift +55 -0
- package/Sources/AuthenticationServices/AuthenticationServices.swift +257 -0
- package/Sources/AuthenticationServices/Authorization.swift +251 -0
- package/Sources/Charts/Charts.swift +0 -4
- package/Sources/Combine/AnyCancellable.swift +65 -0
- package/Sources/Combine/AnyPublisher.swift +148 -0
- package/Sources/Combine/AnySubscriber.swift +230 -0
- package/Sources/Combine/Cancellable.swift +35 -0
- package/Sources/Combine/Codable.swift +29 -0
- package/Sources/Combine/CombineIdentifier.swift +45 -0
- package/Sources/Combine/Concurrency/ConcurrencyHelpers.swift +24 -0
- package/Sources/Combine/Concurrency/Future+Concurrency.swift +136 -0
- package/Sources/Combine/Concurrency/GENERATED-Publisher+Concurrency.swift +397 -0
- package/Sources/Combine/CurrentValueSubject.swift +252 -0
- package/Sources/Combine/CustomCombineIdentifierConvertible.swift +28 -0
- package/Sources/Combine/Future.swift +228 -0
- package/Sources/Combine/GENERATED-RootProtocols.swift +426 -0
- package/Sources/Combine/Helpers/ConduitBase.swift +40 -0
- package/Sources/Combine/Helpers/ConduitList.swift +59 -0
- package/Sources/Combine/Helpers/DebugHook.swift +177 -0
- package/Sources/Combine/Helpers/FilterProducer.swift +174 -0
- package/Sources/Combine/Helpers/Locking.swift +30 -0
- package/Sources/Combine/Helpers/PartialCompletion.swift +26 -0
- package/Sources/Combine/Helpers/ReduceProducer.swift +249 -0
- package/Sources/Combine/Helpers/SubjectSubscriber.swift +96 -0
- package/Sources/Combine/Helpers/SubscriberTap.swift +69 -0
- package/Sources/Combine/Helpers/SubscriptionStatus.swift +33 -0
- package/Sources/Combine/Helpers/SubscriptionTap.swift +33 -0
- package/Sources/Combine/Helpers/Utils.swift +30 -0
- package/Sources/Combine/Helpers/Violations.swift +33 -0
- package/Sources/Combine/ImmediateScheduler.swift +178 -0
- package/Sources/Combine/LICENSE +21 -0
- package/Sources/Combine/NotificationPublisher.swift +48 -0
- package/Sources/Combine/ObjectWillChange.swift +46 -0
- package/Sources/Combine/PassthroughSubject.swift +222 -0
- package/Sources/Combine/Publisher+Subscribe.swift +53 -0
- package/Sources/Combine/Publishers/Deferred.swift +48 -0
- package/Sources/Combine/Publishers/Empty.swift +54 -0
- package/Sources/Combine/Publishers/Fail.swift +42 -0
- package/Sources/Combine/Publishers/GENERATED-Publishers.Catch.swift +673 -0
- package/Sources/Combine/Publishers/GENERATED-Publishers.Encode.swift +388 -0
- package/Sources/Combine/Publishers/GENERATED-Publishers.MapKeyPath.swift +384 -0
- package/Sources/Combine/Publishers/Just.swift +313 -0
- package/Sources/Combine/Publishers/Optional.Publisher.swift +315 -0
- package/Sources/Combine/Publishers/Publishers.AllSatisfy.swift +217 -0
- package/Sources/Combine/Publishers/Publishers.AssertNoFailure.swift +158 -0
- package/Sources/Combine/Publishers/Publishers.Autoconnect.swift +193 -0
- package/Sources/Combine/Publishers/Publishers.Breakpoint.swift +231 -0
- package/Sources/Combine/Publishers/Publishers.Buffer.swift +356 -0
- package/Sources/Combine/Publishers/Publishers.Collect.swift +102 -0
- package/Sources/Combine/Publishers/Publishers.CollectByCount.swift +195 -0
- package/Sources/Combine/Publishers/Publishers.CompactMap.swift +241 -0
- package/Sources/Combine/Publishers/Publishers.Comparison.swift +371 -0
- package/Sources/Combine/Publishers/Publishers.Concatenate.swift +474 -0
- package/Sources/Combine/Publishers/Publishers.Contains.swift +293 -0
- package/Sources/Combine/Publishers/Publishers.Count.swift +87 -0
- package/Sources/Combine/Publishers/Publishers.Debounce.swift +332 -0
- package/Sources/Combine/Publishers/Publishers.Delay.swift +269 -0
- package/Sources/Combine/Publishers/Publishers.Drop.swift +155 -0
- package/Sources/Combine/Publishers/Publishers.DropUntilOutput.swift +283 -0
- package/Sources/Combine/Publishers/Publishers.DropWhile.swift +378 -0
- package/Sources/Combine/Publishers/Publishers.Filter.swift +248 -0
- package/Sources/Combine/Publishers/Publishers.First.swift +261 -0
- package/Sources/Combine/Publishers/Publishers.FlatMap.swift +533 -0
- package/Sources/Combine/Publishers/Publishers.HandleEvents.swift +260 -0
- package/Sources/Combine/Publishers/Publishers.IgnoreOutput.swift +109 -0
- package/Sources/Combine/Publishers/Publishers.Last.swift +252 -0
- package/Sources/Combine/Publishers/Publishers.MakeConnectable.swift +82 -0
- package/Sources/Combine/Publishers/Publishers.Map.swift +351 -0
- package/Sources/Combine/Publishers/Publishers.MapError.swift +140 -0
- package/Sources/Combine/Publishers/Publishers.MeasureInterval.swift +187 -0
- package/Sources/Combine/Publishers/Publishers.Multicast.swift +284 -0
- package/Sources/Combine/Publishers/Publishers.Output.swift +241 -0
- package/Sources/Combine/Publishers/Publishers.PrefixUntilOutput.swift +197 -0
- package/Sources/Combine/Publishers/Publishers.PrefixWhile.swift +176 -0
- package/Sources/Combine/Publishers/Publishers.Print.swift +206 -0
- package/Sources/Combine/Publishers/Publishers.ReceiveOn.swift +227 -0
- package/Sources/Combine/Publishers/Publishers.Reduce.swift +204 -0
- package/Sources/Combine/Publishers/Publishers.RemoveDuplicates.swift +264 -0
- package/Sources/Combine/Publishers/Publishers.ReplaceEmpty.swift +201 -0
- package/Sources/Combine/Publishers/Publishers.ReplaceError.swift +211 -0
- package/Sources/Combine/Publishers/Publishers.Retry.swift +274 -0
- package/Sources/Combine/Publishers/Publishers.Scan.swift +331 -0
- package/Sources/Combine/Publishers/Publishers.Sequence.swift +437 -0
- package/Sources/Combine/Publishers/Publishers.SetFailureType.swift +120 -0
- package/Sources/Combine/Publishers/Publishers.Share.swift +104 -0
- package/Sources/Combine/Publishers/Publishers.SubscribeOn.swift +197 -0
- package/Sources/Combine/Publishers/Publishers.SwitchToLatest.swift +444 -0
- package/Sources/Combine/Publishers/Publishers.Throttle.swift +359 -0
- package/Sources/Combine/Publishers/Publishers.Timeout.swift +301 -0
- package/Sources/Combine/Publishers/Publishers.Zip.swift +720 -0
- package/Sources/Combine/Publishers/Publishers.swift +13 -0
- package/Sources/Combine/Publishers/Record.swift +237 -0
- package/Sources/Combine/Publishers/Result.Publisher.swift +337 -0
- package/Sources/Combine/Result.swift +46 -0
- package/Sources/Combine/Scheduler.swift +89 -0
- package/Sources/Combine/Schedulers.swift +211 -0
- package/Sources/Combine/Subject+Void.swift +17 -0
- package/Sources/Combine/Subscriber+Void.swift +20 -0
- package/Sources/Combine/Subscribers/Subscribers.Assign.swift +175 -0
- package/Sources/Combine/Subscribers/Subscribers.Completion.swift +90 -0
- package/Sources/Combine/Subscribers/Subscribers.Demand.swift +476 -0
- package/Sources/Combine/Subscribers/Subscribers.Sink.swift +206 -0
- package/Sources/Combine/Subscribers/Subscribers.swift +9 -0
- package/Sources/Combine/Subscription.swift +22 -0
- package/Sources/Combine/Subscriptions.swift +42 -0
- package/Sources/Combine/SwiftUIObservation.swift +10 -0
- package/Sources/Combine/_Introspection.swift +107 -0
- package/Sources/CommonCrypto/CommonCrypto.swift +80 -0
- package/Sources/Compression/Compression.swift +365 -0
- package/Sources/Compression/Deflate.swift +407 -0
- package/Sources/Compression/Zlib.swift +179 -0
- package/Sources/CoreFoundationTypes/CoreFoundationTypes.swift +101 -0
- package/Sources/CoreGraphics/CoreGraphics.swift +411 -0
- package/Sources/CoreGraphics/ProjectionTransforms.swift +28 -0
- package/Sources/CoreHaptics/CoreHaptics.swift +399 -0
- package/Sources/CoreImage/CIColor.swift +41 -0
- package/Sources/CoreImage/CIContext.swift +31 -0
- package/Sources/CoreImage/CIDetector.swift +57 -0
- package/Sources/CoreImage/CIFilter.swift +243 -0
- package/Sources/CoreImage/CIFilterBuiltins.swift +136 -0
- package/Sources/CoreImage/CIImage.swift +162 -0
- package/Sources/CoreImage/QRCode.swift +393 -0
- package/Sources/CoreLocation/Coordinate.swift +30 -0
- package/Sources/CoreLocation/Location.swift +65 -0
- package/Sources/CoreML/Evaluation.swift +414 -0
- package/Sources/CoreML/FeatureValue.swift +266 -0
- package/Sources/CoreML/Model.swift +269 -0
- package/Sources/CoreML/MultiArray.swift +92 -0
- package/Sources/CoreML/Specification.swift +233 -0
- package/Sources/CoreText/CoreText.swift +64 -0
- package/Sources/CoreTransferable/CoreTransferable.swift +606 -0
- package/Sources/CryptoKit/ChaChaPoly.swift +258 -0
- package/Sources/CryptoKit/CryptoKit.swift +375 -0
- package/Sources/CryptoKit/Curve25519.swift +317 -0
- package/Sources/CryptoKit/P256.swift +321 -0
- package/Sources/FoundationBridge/AppStorage.swift +165 -0
- package/Sources/FoundationBridge/AssetResources.swift +90 -0
- package/Sources/FoundationBridge/AsyncBytes.swift +128 -0
- package/Sources/FoundationBridge/AsyncImage.swift +116 -0
- package/Sources/FoundationBridge/AttributedStrings.swift +16 -0
- package/Sources/{_FoundationCommon → FoundationBridge}/Bundle.swift +41 -4
- package/Sources/FoundationBridge/CombineExports.swift +8 -0
- package/Sources/FoundationBridge/CoreFoundation.swift +77 -0
- package/Sources/FoundationBridge/Dispatch.swift +458 -0
- package/Sources/FoundationBridge/EnvironmentFoundation.swift +41 -0
- package/Sources/FoundationBridge/FileImporter.swift +128 -0
- package/Sources/FoundationBridge/FileSystem.swift +68 -0
- package/Sources/FoundationBridge/FoundationBridge.swift +24 -72
- package/Sources/FoundationBridge/HostResolution.swift +31 -0
- package/Sources/FoundationBridge/ItemProvider.swift +655 -0
- package/Sources/FoundationBridge/KeyValueObserving.swift +138 -0
- package/Sources/FoundationBridge/Links.swift +127 -0
- package/Sources/FoundationBridge/ListFormatter.swift +32 -0
- package/Sources/FoundationBridge/Localization.swift +149 -0
- package/Sources/FoundationBridge/Locks.swift +50 -0
- package/Sources/FoundationBridge/Markdown.swift +527 -0
- package/Sources/FoundationBridge/MeasurementFormat.swift +95 -0
- package/Sources/FoundationBridge/NotificationCenter.swift +154 -0
- package/Sources/FoundationBridge/OpenURLHandlers.swift +60 -0
- package/Sources/FoundationBridge/Operation.swift +195 -0
- package/Sources/FoundationBridge/ProcessInfo.swift +32 -0
- package/Sources/FoundationBridge/RelativeDateTimeFormatter.swift +136 -0
- package/Sources/FoundationBridge/SceneStorage.swift +78 -0
- package/Sources/FoundationBridge/SecurityExports.swift +8 -0
- package/Sources/FoundationBridge/Selector.swift +226 -0
- package/Sources/FoundationBridge/StringComparators.swift +17 -0
- package/Sources/FoundationBridge/Table.swift +211 -0
- package/Sources/FoundationBridge/TextAttributes.swift +42 -0
- package/Sources/FoundationBridge/Timer.swift +6 -1
- package/Sources/FoundationBridge/URLSession.swift +718 -0
- package/Sources/{_FoundationCommon → FoundationBridge}/UserDefaults.swift +17 -4
- package/Sources/FoundationBridge/WebAuthRedirects.swift +227 -0
- package/Sources/FoundationBridge/WebSocket.swift +187 -0
- package/Sources/FoundationModels/FoundationModels.swift +1098 -0
- package/Sources/ImageIO/GIF.swift +72 -0
- package/Sources/ImageIO/ImageIO.swift +248 -0
- package/Sources/ImageIO/ImageTypes.swift +28 -0
- package/Sources/LinkPresentation/LinkPresentation.swift +249 -0
- package/Sources/LocalAuthentication/LAContext.swift +86 -0
- package/Sources/MapKit/Geometry.swift +223 -0
- package/Sources/MapKit/Map.swift +638 -0
- package/Sources/MapKit/MapView.swift +229 -0
- package/Sources/MobileCoreServices/MobileCoreServices.swift +28 -0
- package/Sources/NaturalLanguage/LanguageIdentifier.swift +317 -0
- package/Sources/NaturalLanguage/NaturalLanguage.swift +135 -0
- package/Sources/OSLog/OSLog.swift +6 -0
- package/Sources/Photos/Photos.swift +572 -0
- package/Sources/PhotosUI/PhotosPicker.swift +248 -0
- package/Sources/PhotosUI/PhotosPickerItem.swift +261 -0
- package/Sources/QuickLook/QuickLook.swift +168 -0
- package/Sources/SafariServices/SafariServices.swift +106 -0
- package/Sources/Security/Security.swift +332 -0
- package/Sources/StoreKit/AppStore.swift +207 -0
- package/Sources/StoreKit/Products.swift +280 -0
- package/Sources/StoreKit/RequestReview.swift +28 -0
- package/Sources/StoreKit/StoreKit1.swift +486 -0
- package/Sources/StoreKit/StoreViews.swift +428 -0
- package/Sources/StoreKit/Transactions.swift +197 -0
- package/Sources/SwiftData/Macros.swift +58 -0
- package/Sources/SwiftData/ModelActor.swift +38 -0
- package/Sources/SwiftData/ModelContainer.swift +129 -0
- package/Sources/SwiftData/ModelContext.swift +451 -0
- package/Sources/SwiftData/PersistentModel.swift +238 -0
- package/Sources/SwiftData/Properties.swift +261 -0
- package/Sources/SwiftData/Query.swift +256 -0
- package/Sources/SwiftData/Schema.swift +192 -0
- package/Sources/SwiftData/Store.swift +146 -0
- package/Sources/SwiftData/StringPredicates.swift +125 -0
- package/Sources/SwiftDataMacros/EntryMacro.swift +82 -0
- package/Sources/SwiftDataMacros/GenerableMacro.swift +183 -0
- package/Sources/SwiftDataMacros/ModelActorMacro.swift +46 -0
- package/Sources/SwiftDataMacros/ModelMacro.swift +260 -0
- package/Sources/SwiftDataMacros/Plugin.swift +18 -0
- package/Sources/SwiftDataMacros/PredicateMacro.swift +1244 -0
- package/Sources/SwiftUI/API/Accessibility.swift +55 -6
- package/Sources/SwiftUI/API/AccessibilityExtras.swift +33 -0
- package/Sources/SwiftUI/API/AccessibilityFocus.swift +63 -0
- package/Sources/SwiftUI/API/Alert.swift +1 -1
- package/Sources/SwiftUI/API/AlignmentGuides.swift +68 -0
- package/Sources/SwiftUI/API/Animation.swift +37 -5
- package/Sources/SwiftUI/API/App.swift +31 -1
- package/Sources/SwiftUI/API/BindingCollections.swift +35 -0
- package/Sources/SwiftUI/API/BindingForEach.swift +27 -0
- package/Sources/SwiftUI/API/Bitmaps.swift +254 -0
- package/Sources/SwiftUI/API/ButtonStyles.swift +96 -0
- package/Sources/SwiftUI/API/Canvas.swift +337 -0
- package/Sources/SwiftUI/API/Color.swift +104 -28
- package/Sources/SwiftUI/API/ColorEffects.swift +52 -0
- package/Sources/SwiftUI/API/ColorPickerAndLabeledContent.swift +138 -0
- package/Sources/SwiftUI/API/ColorSpaces.swift +161 -0
- package/Sources/SwiftUI/API/Commands.swift +106 -0
- package/Sources/SwiftUI/API/ContainerBackground.swift +28 -0
- package/Sources/SwiftUI/API/ContentShape.swift +33 -0
- package/Sources/SwiftUI/API/ContentSizeCategory.swift +35 -0
- package/Sources/SwiftUI/API/ContentUnavailable.swift +101 -0
- package/Sources/SwiftUI/API/Controls.swift +59 -27
- package/Sources/SwiftUI/API/CustomLayout.swift +49 -10
- package/Sources/SwiftUI/API/CustomToolbarContent.swift +46 -0
- package/Sources/SwiftUI/API/Decorations.swift +1 -1
- package/Sources/SwiftUI/API/EditMode.swift +25 -82
- package/Sources/SwiftUI/API/Effects.swift +125 -10
- package/Sources/SwiftUI/API/Entry.swift +8 -0
- package/Sources/SwiftUI/API/Environment.swift +100 -9
- package/Sources/SwiftUI/API/EnvironmentSendable.swift +6 -0
- package/Sources/SwiftUI/API/Focus.swift +11 -3
- package/Sources/SwiftUI/API/Gauge.swift +26 -10
- package/Sources/SwiftUI/API/GeometryEffects.swift +232 -0
- package/Sources/SwiftUI/API/GeometryReader.swift +13 -2
- package/Sources/SwiftUI/API/GestureState.swift +65 -0
- package/Sources/SwiftUI/API/Gestures.swift +1 -1
- package/Sources/SwiftUI/API/Glass.swift +127 -0
- package/Sources/SwiftUI/API/Grid.swift +1 -1
- package/Sources/SwiftUI/API/GroupBoxAndControlGroup.swift +130 -0
- package/Sources/SwiftUI/API/KeyPress.swift +54 -0
- package/Sources/SwiftUI/API/LabelStyle.swift +13 -6
- package/Sources/SwiftUI/API/Layout.swift +71 -12
- package/Sources/SwiftUI/API/LazyStacks.swift +49 -0
- package/Sources/SwiftUI/API/LegacyAlert.swift +97 -0
- package/Sources/SwiftUI/API/Lists.swift +61 -6
- package/Sources/SwiftUI/API/Menu.swift +8 -1
- package/Sources/SwiftUI/API/Modifiers.swift +88 -12
- package/Sources/SwiftUI/API/MoreModifiers.swift +100 -0
- package/Sources/SwiftUI/API/Namespace.swift +44 -0
- package/Sources/SwiftUI/API/Navigation.swift +24 -9
- package/Sources/SwiftUI/API/NavigationDestinationBindings.swift +55 -0
- package/Sources/SwiftUI/API/ObservableObject.swift +16 -2
- package/Sources/SwiftUI/API/Path.swift +543 -0
- package/Sources/SwiftUI/API/Phase8Controls.swift +48 -31
- package/Sources/SwiftUI/API/PhaseAnimator.swift +130 -0
- package/Sources/SwiftUI/API/Picker.swift +14 -4
- package/Sources/SwiftUI/API/PointerAndKeyboard.swift +77 -0
- package/Sources/SwiftUI/API/Popover.swift +41 -0
- package/Sources/SwiftUI/API/Preferences.swift +262 -0
- package/Sources/SwiftUI/API/Presentation.swift +17 -4
- package/Sources/SwiftUI/API/PresentationExtras.swift +60 -0
- package/Sources/SwiftUI/API/Primitives.swift +168 -39
- package/Sources/SwiftUI/API/Publishers.swift +4 -4
- package/Sources/SwiftUI/API/Redaction.swift +75 -0
- package/Sources/SwiftUI/API/RowActions.swift +3 -2
- package/Sources/SwiftUI/API/Scenes.swift +131 -0
- package/Sources/SwiftUI/API/ScrollModifiers.swift +132 -0
- package/Sources/SwiftUI/API/ScrollTargets.swift +3 -3
- package/Sources/SwiftUI/API/ScrollViewReader.swift +144 -0
- package/Sources/SwiftUI/API/SectionSupplements.swift +86 -0
- package/Sources/SwiftUI/API/Shape.swift +214 -0
- package/Sources/SwiftUI/API/ShapeStyle.swift +187 -8
- package/Sources/SwiftUI/API/ShapeTrim.swift +78 -0
- package/Sources/SwiftUI/API/Sharing.swift +193 -0
- package/Sources/SwiftUI/API/StackLayouts.swift +199 -0
- package/Sources/SwiftUI/API/State.swift +18 -1
- package/Sources/SwiftUI/API/StatusGaps.swift +121 -0
- package/Sources/SwiftUI/API/SymbolEffects.swift +203 -0
- package/Sources/SwiftUI/API/TabContent.swift +841 -0
- package/Sources/SwiftUI/API/Table.swift +382 -0
- package/Sources/SwiftUI/API/Tabs.swift +148 -4
- package/Sources/SwiftUI/API/Tasks.swift +2 -2
- package/Sources/SwiftUI/API/Text.swift +386 -0
- package/Sources/SwiftUI/API/TextDates.swift +61 -0
- package/Sources/SwiftUI/API/TextDecorations.swift +52 -0
- package/Sources/SwiftUI/API/TextFieldAxis.swift +103 -0
- package/Sources/SwiftUI/API/TextFieldCommit.swift +52 -0
- package/Sources/SwiftUI/API/Toolbar.swift +52 -16
- package/Sources/SwiftUI/API/Transfer.swift +145 -0
- package/Sources/SwiftUI/API/UnevenRoundedRectangle.swift +66 -0
- package/Sources/SwiftUI/API/Video.swift +64 -0
- package/Sources/SwiftUI/API/View.swift +46 -2
- package/Sources/SwiftUI/API/ViewModifier.swift +4 -1
- package/Sources/SwiftUI/API/VisualEffect.swift +74 -1
- package/Sources/SwiftUI/API/Windows.swift +25 -0
- package/Sources/SwiftUI/Charts/Axis.swift +7 -1
- package/Sources/SwiftUI/Charts/AxisFormatsAndRanges.swift +68 -0
- package/Sources/SwiftUI/Charts/Chart.swift +17 -3
- package/Sources/SwiftUI/Charts/ChartProxy.swift +57 -0
- package/Sources/SwiftUI/Localization/Localization.swift +524 -0
- package/Sources/SwiftUI/Localization/PluralRules.swift +127 -0
- package/Sources/SwiftUI/Runtime/Executor.swift +2 -0
- package/Sources/SwiftUI/Runtime/Exports.swift +37 -1
- package/Sources/SwiftUI/Runtime/ImageSource.swift +310 -0
- package/Sources/SwiftUI/Runtime/JSON.swift +76 -4
- package/Sources/SwiftUI/Runtime/LazyRows.swift +99 -0
- package/Sources/SwiftUI/Runtime/Measurements.swift +140 -0
- package/Sources/SwiftUI/Runtime/Ops.swift +58 -12
- package/Sources/SwiftUI/Runtime/Reconciler.swift +10 -1
- package/Sources/SwiftUI/Runtime/Replay.swift +382 -0
- package/Sources/SwiftUI/Runtime/Runtime.swift +977 -198
- package/Sources/SwiftUI/Runtime/Sameness.swift +152 -0
- package/Sources/SwiftUI/Runtime/ViewPath.swift +89 -0
- package/Sources/SwiftUI/Runtime/ViewTraits.swift +175 -0
- package/Sources/SwiftUI/Runtime/WebAuthHeadless.swift +49 -0
- package/Sources/Symbols/Symbols.swift +25 -0
- package/Sources/Translation/Translation.swift +423 -0
- package/Sources/UIKit/Activity.swift +213 -0
- package/Sources/UIKit/Alerts.swift +69 -0
- package/Sources/UIKit/AppDelegate.swift +222 -0
- package/Sources/UIKit/Appearance.swift +84 -0
- package/Sources/UIKit/ColorConversions.swift +70 -0
- package/Sources/UIKit/Controls.swift +225 -0
- package/Sources/UIKit/Device.swift +200 -0
- package/Sources/UIKit/Feedback.swift +65 -0
- package/Sources/UIKit/Font.swift +307 -0
- package/Sources/UIKit/Graphics.swift +204 -0
- package/Sources/UIKit/HostingController.swift +185 -0
- package/Sources/UIKit/Identity.swift +24 -0
- package/Sources/UIKit/Introspectable.swift +510 -0
- package/Sources/UIKit/Pasteboard.swift +363 -0
- package/Sources/UIKit/PhotoAlbum.swift +81 -0
- package/Sources/UIKit/RemoteNotifications.swift +15 -0
- package/Sources/UIKit/Representable.swift +288 -0
- package/Sources/UIKit/SafeArea.swift +12 -0
- package/Sources/UIKit/SceneDelegates.swift +132 -0
- package/Sources/UIKit/Scenes.swift +40 -0
- package/Sources/UIKit/ScrollView.swift +157 -0
- package/Sources/UIKit/ShortcutItems.swift +67 -0
- package/Sources/UIKit/SymbolConfiguration.swift +124 -0
- package/Sources/UIKit/TargetAction.swift +123 -0
- package/Sources/UIKit/TextPaste.swift +161 -0
- package/Sources/UIKit/TextView.swift +538 -0
- package/Sources/UIKit/UIKit.swift +292 -0
- package/Sources/UIKit/UIView.swift +855 -0
- package/Sources/UIKit/ViewTranslation.swift +311 -0
- package/Sources/UniformTypeIdentifiers/UTType.swift +196 -0
- package/Sources/UserNotifications/Content.swift +370 -0
- package/Sources/UserNotifications/NotificationCenter.swift +309 -0
- package/Sources/WeatherKit/WeatherKit.swift +228 -0
- package/Sources/WebKit/WebKit.swift +245 -0
- package/Sources/os/os.swift +376 -0
- package/Sources/zlib/zlib.swift +6 -0
- package/index.d.ts +1 -1
- package/index.mjs +2 -2
- package/manifest.d.mts +2 -0
- package/manifest.mjs +41 -0
- package/package.json +5 -3
- package/sync-sources.mjs +21 -7
- package/Sources/FoundationLite/Common.swift +0 -3
- package/Sources/FoundationLite/FoundationLite.swift +0 -171
- package/Sources/FoundationLite/Interaction.swift +0 -146
- package/Sources/FoundationLite/Timer.swift +0 -48
- package/Sources/_FoundationCommon/_FoundationCommon.swift +0 -10
|
@@ -0,0 +1,538 @@
|
|
|
1
|
+
// UIKit's text views and the attributed strings they show. A representable's
|
|
2
|
+
// `UILabel` is drawn as a `text`, a `UITextView` as a `texteditor` while it is
|
|
3
|
+
// editable and as a `text` (with the rich text `runs`) when it is not
|
|
4
|
+
// (ViewTranslation.swift). What the user types reaches the text view as
|
|
5
|
+
// UIKit's typing would: the delegate is asked
|
|
6
|
+
// `textView(_:shouldChangeTextIn:replacementText:)` for the edit (the
|
|
7
|
+
// range and replacement found by comparing the old and new text), the text
|
|
8
|
+
// changes, the caret moves to the end of the replacement, and the delegate
|
|
9
|
+
// hears `textViewDidChange` and `textViewDidChangeSelection`. Focus is
|
|
10
|
+
// shared both ways: `becomeFirstResponder` focuses the page's text area, and
|
|
11
|
+
// a tap into it (or out) makes the view first responder (or resigns). The
|
|
12
|
+
// page does not report the caret itself, so `selectedRange` is that guess; typing
|
|
13
|
+
// attributes, keyboard settings, data detectors, input accessory views and
|
|
14
|
+
// the like are kept and not used. Sizes (`sizeThatFits`,
|
|
15
|
+
// `intrinsicContentSize`) are estimates from the font's size: Swift has no
|
|
16
|
+
// text metrics, so a label or text view whose size matters is better left
|
|
17
|
+
// to the page (a representable sizes them by measuring what it draws).
|
|
18
|
+
//
|
|
19
|
+
// Corelibs Foundation has `NSAttributedString` without UIKit's attribute
|
|
20
|
+
// keys, and without the conversions from and to `AttributedString` that
|
|
21
|
+
// Apple's Foundation has; both are here, as `import SwiftUI` brings UIKit.
|
|
22
|
+
|
|
23
|
+
import Foundation
|
|
24
|
+
import FoundationBridge
|
|
25
|
+
import SwiftUI
|
|
26
|
+
|
|
27
|
+
// MARK: - Attributed strings
|
|
28
|
+
|
|
29
|
+
extension NSAttributedString.Key {
|
|
30
|
+
public static let font = NSAttributedString.Key("NSFont")
|
|
31
|
+
public static let foregroundColor = NSAttributedString.Key("NSColor")
|
|
32
|
+
public static let backgroundColor = NSAttributedString.Key("NSBackgroundColor")
|
|
33
|
+
public static let link = NSAttributedString.Key("NSLink")
|
|
34
|
+
public static let underlineStyle = NSAttributedString.Key("NSUnderline")
|
|
35
|
+
public static let underlineColor = NSAttributedString.Key("NSUnderlineColor")
|
|
36
|
+
public static let strikethroughStyle = NSAttributedString.Key("NSStrikethrough")
|
|
37
|
+
public static let strikethroughColor = NSAttributedString.Key("NSStrikethroughColor")
|
|
38
|
+
public static let kern = NSAttributedString.Key("NSKern")
|
|
39
|
+
public static let baselineOffset = NSAttributedString.Key("NSBaselineOffset")
|
|
40
|
+
public static let paragraphStyle = NSAttributedString.Key("NSParagraphStyle")
|
|
41
|
+
public static let attachment = NSAttributedString.Key("NSAttachment")
|
|
42
|
+
public static let shadow = NSAttributedString.Key("NSShadow")
|
|
43
|
+
public static let obliqueness = NSAttributedString.Key("NSObliqueness")
|
|
44
|
+
public static let inlinePresentationIntent = NSAttributedString.Key("NSInlinePresentationIntent")
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/// How text is underlined or struck through.
|
|
48
|
+
public struct NSUnderlineStyle: OptionSet, Hashable, Sendable {
|
|
49
|
+
public let rawValue: Int
|
|
50
|
+
public init(rawValue: Int) { self.rawValue = rawValue }
|
|
51
|
+
public static let single = NSUnderlineStyle(rawValue: 0x01)
|
|
52
|
+
public static let thick = NSUnderlineStyle(rawValue: 0x02)
|
|
53
|
+
public static let double = NSUnderlineStyle(rawValue: 0x09)
|
|
54
|
+
public static let patternDot = NSUnderlineStyle(rawValue: 0x0100)
|
|
55
|
+
public static let patternDash = NSUnderlineStyle(rawValue: 0x0200)
|
|
56
|
+
public static let byWord = NSUnderlineStyle(rawValue: 0x8000)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public enum NSTextAlignment: Int, Sendable {
|
|
60
|
+
case left = 0, center = 1, right = 2, justified = 3, natural = 4
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
public enum NSLineBreakMode: Int, Sendable {
|
|
64
|
+
case byWordWrapping = 0, byCharWrapping, byClipping, byTruncatingHead, byTruncatingTail, byTruncatingMiddle
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
extension NSAttributedString {
|
|
68
|
+
/// The runs of an `AttributedString`: their links and inline
|
|
69
|
+
/// presentation intents (Markdown's emphasis), the attributes the browser
|
|
70
|
+
/// build's `AttributedString` carries.
|
|
71
|
+
public convenience init(_ attributedString: AttributedString) {
|
|
72
|
+
self.init(attributedString: _nsAttributedString(attributedString))
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
extension AttributedString {
|
|
77
|
+
/// The string's runs with their links and inline presentation intents;
|
|
78
|
+
/// UIKit's attributes (font, color) have no place in it here and are dropped.
|
|
79
|
+
public init(_ nsAttributedString: NSAttributedString) {
|
|
80
|
+
var result = AttributedString()
|
|
81
|
+
nsAttributedString.enumerateAttributes(in: NSRange(location: 0, length: nsAttributedString.length)) { attributes, range, _ in
|
|
82
|
+
var piece = AttributedString(nsAttributedString.attributedSubstring(from: range).string)
|
|
83
|
+
if let link = _url(attributes[.link]) { piece.link = link }
|
|
84
|
+
if let intent = attributes[.inlinePresentationIntent] as? InlinePresentationIntent { piece.inlinePresentationIntent = intent }
|
|
85
|
+
result += piece
|
|
86
|
+
}
|
|
87
|
+
self = result
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private func _nsAttributedString(_ attributedString: AttributedString) -> NSAttributedString {
|
|
92
|
+
let result = NSMutableAttributedString(string: "")
|
|
93
|
+
for run in attributedString.runs {
|
|
94
|
+
var attributes: [NSAttributedString.Key: Any] = [:]
|
|
95
|
+
if let link = run.link { attributes[.link] = link }
|
|
96
|
+
if let intent = run.inlinePresentationIntent { attributes[.inlinePresentationIntent] = intent }
|
|
97
|
+
result.append(NSAttributedString(string: String(attributedString[run.range].characters), attributes: attributes))
|
|
98
|
+
}
|
|
99
|
+
return result
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/// A link attribute's URL: a `URL` or a string.
|
|
103
|
+
func _url(_ value: Any?) -> URL? {
|
|
104
|
+
if let url = value as? URL { return url }
|
|
105
|
+
if let string = value as? String { return URL(string: string) }
|
|
106
|
+
return nil
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
extension Text {
|
|
110
|
+
/// An attributed string as styled text: each run's font, color,
|
|
111
|
+
/// underline, strikethrough, kerning, baseline offset, link and inline
|
|
112
|
+
/// presentation intent. Runs without a font or color take the view's.
|
|
113
|
+
init(_uiKit attributed: NSAttributedString) {
|
|
114
|
+
var result: Text?
|
|
115
|
+
attributed.enumerateAttributes(in: NSRange(location: 0, length: attributed.length)) { attributes, range, _ in
|
|
116
|
+
var piece = Text(verbatim: attributed.attributedSubstring(from: range).string)
|
|
117
|
+
if let font = attributes[.font] as? UIFont { piece = piece.font(Font(font)) }
|
|
118
|
+
if let color = attributes[.foregroundColor] as? UIColor { piece = piece.foregroundColor(Color(uiColor: color)) }
|
|
119
|
+
if let style = attributes[.underlineStyle] as? Int, style != 0 {
|
|
120
|
+
piece = piece.underline(true, color: (attributes[.underlineColor] as? UIColor).map { Color(uiColor: $0) })
|
|
121
|
+
}
|
|
122
|
+
if let style = attributes[.strikethroughStyle] as? Int, style != 0 {
|
|
123
|
+
piece = piece.strikethrough(true, color: (attributes[.strikethroughColor] as? UIColor).map { Color(uiColor: $0) })
|
|
124
|
+
}
|
|
125
|
+
if let kern = attributes[.kern] as? CGFloat { piece = piece.kerning(kern) }
|
|
126
|
+
if let kern = attributes[.kern] as? Double { piece = piece.kerning(CGFloat(kern)) }
|
|
127
|
+
if let offset = attributes[.baselineOffset] as? CGFloat { piece = piece.baselineOffset(offset) }
|
|
128
|
+
if let intent = attributes[.inlinePresentationIntent] as? InlinePresentationIntent {
|
|
129
|
+
if intent.contains(.stronglyEmphasized) { piece = piece.bold() }
|
|
130
|
+
if intent.contains(.emphasized) { piece = piece.italic() }
|
|
131
|
+
if intent.contains(.code) { piece = piece.monospaced() }
|
|
132
|
+
if intent.contains(.strikethrough) { piece = piece.strikethrough() }
|
|
133
|
+
}
|
|
134
|
+
if let link = _url(attributes[.link]) { piece = piece._link(link.absoluteString) }
|
|
135
|
+
result = result.map { $0 + piece } ?? piece
|
|
136
|
+
}
|
|
137
|
+
self = result ?? Text(verbatim: "")
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// MARK: - Size estimates
|
|
142
|
+
|
|
143
|
+
/// Text sizes from a font's point size alone: an average character is a
|
|
144
|
+
/// little over half the size wide, and paragraphs wrap at `width`.
|
|
145
|
+
enum _TextEstimate {
|
|
146
|
+
static func size(of string: String, font: UIFont, width: CGFloat?, maximumLines: Int = 0) -> CGSize {
|
|
147
|
+
let characterWidth = font.pointSize * 0.52
|
|
148
|
+
let paragraphs = string.split(separator: "\n", omittingEmptySubsequences: false)
|
|
149
|
+
var lines = 0
|
|
150
|
+
var widest: CGFloat = 0
|
|
151
|
+
for paragraph in paragraphs {
|
|
152
|
+
let natural = CGFloat(paragraph.count) * characterWidth
|
|
153
|
+
widest = max(widest, natural)
|
|
154
|
+
if let width, width > 0, width.isFinite {
|
|
155
|
+
lines += max(1, Int((natural / width).rounded(.up)))
|
|
156
|
+
} else {
|
|
157
|
+
lines += 1
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if maximumLines > 0 { lines = min(lines, maximumLines) }
|
|
161
|
+
let resolvedWidth = width.flatMap { $0 > 0 && $0.isFinite ? min($0, widest) : nil } ?? widest
|
|
162
|
+
return CGSize(width: resolvedWidth.rounded(.up), height: CGFloat(max(1, lines)) * font.lineHeight)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// MARK: - Labels
|
|
167
|
+
|
|
168
|
+
/// A label: drawn as a `text` with its font, color, alignment and line limit.
|
|
169
|
+
@MainActor
|
|
170
|
+
open class UILabel: UIView {
|
|
171
|
+
private var plainText: String?
|
|
172
|
+
private var attributed: NSAttributedString?
|
|
173
|
+
|
|
174
|
+
open var text: String? {
|
|
175
|
+
get { attributed?.string ?? plainText }
|
|
176
|
+
set {
|
|
177
|
+
plainText = newValue
|
|
178
|
+
attributed = nil
|
|
179
|
+
_setNeedsTranslation()
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
open var attributedText: NSAttributedString? {
|
|
183
|
+
get {
|
|
184
|
+
if let attributed { return attributed }
|
|
185
|
+
guard let plainText else { return nil }
|
|
186
|
+
var attributes: [NSAttributedString.Key: Any] = [:]
|
|
187
|
+
if let font { attributes[.font] = font }
|
|
188
|
+
if let textColor { attributes[.foregroundColor] = textColor }
|
|
189
|
+
return NSAttributedString(string: plainText, attributes: attributes)
|
|
190
|
+
}
|
|
191
|
+
set {
|
|
192
|
+
attributed = newValue.map { NSAttributedString(attributedString: $0) }
|
|
193
|
+
plainText = newValue?.string
|
|
194
|
+
_setNeedsTranslation()
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/// Whether the text was set as an attributed string (drawn with its runs).
|
|
198
|
+
var _hasAttributedText: Bool { attributed != nil }
|
|
199
|
+
open var font: UIFont! = .systemFont(ofSize: 17) { didSet { _setNeedsTranslation() } }
|
|
200
|
+
open var textColor: UIColor! = .label { didSet { _setNeedsTranslation() } }
|
|
201
|
+
open var textAlignment: NSTextAlignment = .natural { didSet { _setNeedsTranslation() } }
|
|
202
|
+
open var numberOfLines = 1 { didSet { _setNeedsTranslation() } }
|
|
203
|
+
open var lineBreakMode: NSLineBreakMode = .byTruncatingTail
|
|
204
|
+
open var isEnabled = true { didSet { _setNeedsTranslation() } }
|
|
205
|
+
open var isHighlighted = false
|
|
206
|
+
open var highlightedTextColor: UIColor?
|
|
207
|
+
open var adjustsFontSizeToFitWidth = false
|
|
208
|
+
open var adjustsFontForContentSizeCategory = false
|
|
209
|
+
open var minimumScaleFactor: CGFloat = 0
|
|
210
|
+
open var allowsDefaultTighteningForTruncation = false
|
|
211
|
+
open var preferredMaxLayoutWidth: CGFloat = 0
|
|
212
|
+
|
|
213
|
+
public override init(frame: CGRect) { super.init(frame: frame) }
|
|
214
|
+
public required init?(coder: NSCoder) { super.init(coder: coder) }
|
|
215
|
+
|
|
216
|
+
open override var intrinsicContentSize: CGSize {
|
|
217
|
+
_TextEstimate.size(of: text ?? "", font: font, width: preferredMaxLayoutWidth > 0 ? preferredMaxLayoutWidth : nil,
|
|
218
|
+
maximumLines: numberOfLines)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
open override func sizeThatFits(_ size: CGSize) -> CGSize {
|
|
222
|
+
_TextEstimate.size(of: text ?? "", font: font, width: size.width > 0 ? size.width : nil, maximumLines: numberOfLines)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// MARK: - Text views
|
|
227
|
+
|
|
228
|
+
/// A text container's settings, kept (the page lays the text out).
|
|
229
|
+
@MainActor
|
|
230
|
+
open class NSTextContainer {
|
|
231
|
+
open var size: CGSize
|
|
232
|
+
open var lineFragmentPadding: CGFloat = 5
|
|
233
|
+
open var maximumNumberOfLines = 0
|
|
234
|
+
open var lineBreakMode: NSLineBreakMode = .byWordWrapping
|
|
235
|
+
open var widthTracksTextView = true
|
|
236
|
+
open var heightTracksTextView = false
|
|
237
|
+
|
|
238
|
+
public init(size: CGSize = .zero) { self.size = size }
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/// A position in a text input's text, as a UTF-16 offset.
|
|
242
|
+
@MainActor
|
|
243
|
+
open class UITextPosition {
|
|
244
|
+
let offset: Int
|
|
245
|
+
init(offset: Int) { self.offset = offset }
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/// A range of a text input's text.
|
|
249
|
+
@MainActor
|
|
250
|
+
open class UITextRange {
|
|
251
|
+
public let start: UITextPosition
|
|
252
|
+
public let end: UITextPosition
|
|
253
|
+
open var isEmpty: Bool { start.offset == end.offset }
|
|
254
|
+
init(start: UITextPosition, end: UITextPosition) {
|
|
255
|
+
self.start = start
|
|
256
|
+
self.end = end
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
public enum UITextAutocapitalizationType: Int, Sendable { case none, words, sentences, allCharacters }
|
|
261
|
+
public enum UITextAutocorrectionType: Int, Sendable { case `default`, no, yes }
|
|
262
|
+
public enum UITextSpellCheckingType: Int, Sendable { case `default`, no, yes }
|
|
263
|
+
public enum UITextSmartQuotesType: Int, Sendable { case `default`, no, yes }
|
|
264
|
+
public enum UITextSmartDashesType: Int, Sendable { case `default`, no, yes }
|
|
265
|
+
public enum UITextInlinePredictionType: Int, Sendable { case `default`, no, yes }
|
|
266
|
+
public enum UIReturnKeyType: Int, Sendable {
|
|
267
|
+
case `default`, go, google, join, next, route, search, send, yahoo, done, emergencyCall, `continue`
|
|
268
|
+
}
|
|
269
|
+
public enum UITextItemInteraction: Int, Sendable { case invokeDefaultAction, presentActions, preview }
|
|
270
|
+
|
|
271
|
+
public struct UIDataDetectorTypes: OptionSet, Sendable {
|
|
272
|
+
public let rawValue: UInt
|
|
273
|
+
public init(rawValue: UInt) { self.rawValue = rawValue }
|
|
274
|
+
public static let phoneNumber = UIDataDetectorTypes(rawValue: 1 << 0)
|
|
275
|
+
public static let link = UIDataDetectorTypes(rawValue: 1 << 1)
|
|
276
|
+
public static let address = UIDataDetectorTypes(rawValue: 1 << 2)
|
|
277
|
+
public static let calendarEvent = UIDataDetectorTypes(rawValue: 1 << 3)
|
|
278
|
+
public static let all = UIDataDetectorTypes(rawValue: UInt.max)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/// What a text view tells its delegate. Every method has a default (they are
|
|
282
|
+
/// `@objc optional` on iOS); `delegate?.textViewDidChange?(textView)`, the
|
|
283
|
+
/// Objective-C way to call one, works through the properties below.
|
|
284
|
+
@MainActor
|
|
285
|
+
public protocol UITextViewDelegate: UIScrollViewDelegate {
|
|
286
|
+
func textViewShouldBeginEditing(_ textView: UITextView) -> Bool
|
|
287
|
+
func textViewShouldEndEditing(_ textView: UITextView) -> Bool
|
|
288
|
+
func textViewDidBeginEditing(_ textView: UITextView)
|
|
289
|
+
func textViewDidEndEditing(_ textView: UITextView)
|
|
290
|
+
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool
|
|
291
|
+
func textViewDidChange(_ textView: UITextView)
|
|
292
|
+
func textViewDidChangeSelection(_ textView: UITextView)
|
|
293
|
+
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange,
|
|
294
|
+
interaction: UITextItemInteraction) -> Bool
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
extension UITextViewDelegate {
|
|
298
|
+
public func textViewShouldBeginEditing(_ textView: UITextView) -> Bool { true }
|
|
299
|
+
public func textViewShouldEndEditing(_ textView: UITextView) -> Bool { true }
|
|
300
|
+
public func textViewDidBeginEditing(_ textView: UITextView) {}
|
|
301
|
+
public func textViewDidEndEditing(_ textView: UITextView) {}
|
|
302
|
+
public func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { true }
|
|
303
|
+
public func textViewDidChange(_ textView: UITextView) {}
|
|
304
|
+
public func textViewDidChangeSelection(_ textView: UITextView) {}
|
|
305
|
+
public func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange,
|
|
306
|
+
interaction: UITextItemInteraction) -> Bool { true }
|
|
307
|
+
|
|
308
|
+
// `delegate?.textViewDidChange?(textView)`: an optional requirement's
|
|
309
|
+
// call on iOS. A property of optional function type with the method's
|
|
310
|
+
// base name lets the same expression call the (always present) method.
|
|
311
|
+
public var textViewDidChange: ((UITextView) -> Void)? { { self.textViewDidChange($0) } }
|
|
312
|
+
public var textViewDidChangeSelection: ((UITextView) -> Void)? { { self.textViewDidChangeSelection($0) } }
|
|
313
|
+
public var textViewDidBeginEditing: ((UITextView) -> Void)? { { self.textViewDidBeginEditing($0) } }
|
|
314
|
+
public var textViewDidEndEditing: ((UITextView) -> Void)? { { self.textViewDidEndEditing($0) } }
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/// A multi-line text view: editable, drawn as a `texteditor`; read-only, as
|
|
318
|
+
/// a `text` (see the top of the file).
|
|
319
|
+
@MainActor
|
|
320
|
+
open class UITextView: UIScrollView {
|
|
321
|
+
private var storage = NSMutableAttributedString(string: "")
|
|
322
|
+
|
|
323
|
+
/// The text; setting it gives all of it the view's font and color.
|
|
324
|
+
open var text: String! {
|
|
325
|
+
get { storage.string }
|
|
326
|
+
set {
|
|
327
|
+
storage = NSMutableAttributedString(string: newValue ?? "", attributes: baseAttributes)
|
|
328
|
+
clampSelection()
|
|
329
|
+
_setNeedsTranslation()
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
open var attributedText: NSAttributedString! {
|
|
333
|
+
get { NSAttributedString(attributedString: storage) }
|
|
334
|
+
set {
|
|
335
|
+
storage = NSMutableAttributedString(attributedString: newValue ?? NSAttributedString(string: ""))
|
|
336
|
+
clampSelection()
|
|
337
|
+
_setNeedsTranslation()
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
/// The font of all the text (setting it restyles the text, as on iOS).
|
|
341
|
+
open var font: UIFont? {
|
|
342
|
+
didSet {
|
|
343
|
+
if let font { storage.addAttribute(.font, value: font, range: NSRange(location: 0, length: storage.length)) }
|
|
344
|
+
_setNeedsTranslation()
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
open var textColor: UIColor? {
|
|
348
|
+
didSet {
|
|
349
|
+
if let textColor { storage.addAttribute(.foregroundColor, value: textColor, range: NSRange(location: 0, length: storage.length)) }
|
|
350
|
+
_setNeedsTranslation()
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
open var textAlignment: NSTextAlignment = .natural { didSet { _setNeedsTranslation() } }
|
|
354
|
+
open var isEditable = true { didSet { _setNeedsTranslation() } }
|
|
355
|
+
open var isSelectable = true { didSet { _setNeedsTranslation() } }
|
|
356
|
+
/// The selection: where the app put it, else the end of the last edit.
|
|
357
|
+
open var selectedRange = NSRange(location: 0, length: 0)
|
|
358
|
+
open var typingAttributes: [NSAttributedString.Key: Any] = [:]
|
|
359
|
+
open var linkTextAttributes: [NSAttributedString.Key: Any] = [:]
|
|
360
|
+
open var textContainerInset = UIEdgeInsets(top: 8, left: 0, bottom: 8, right: 0) { didSet { _setNeedsTranslation() } }
|
|
361
|
+
public let textContainer = NSTextContainer()
|
|
362
|
+
open var keyboardType: UIKeyboardType = .default
|
|
363
|
+
open var autocapitalizationType: UITextAutocapitalizationType = .sentences
|
|
364
|
+
open var autocorrectionType: UITextAutocorrectionType = .default
|
|
365
|
+
open var spellCheckingType: UITextSpellCheckingType = .default
|
|
366
|
+
open var smartQuotesType: UITextSmartQuotesType = .default
|
|
367
|
+
open var smartDashesType: UITextSmartDashesType = .default
|
|
368
|
+
open var inlinePredictionType: UITextInlinePredictionType = .default
|
|
369
|
+
open var returnKeyType: UIReturnKeyType = .default
|
|
370
|
+
open var dataDetectorTypes: UIDataDetectorTypes = []
|
|
371
|
+
open var allowsEditingTextAttributes = false
|
|
372
|
+
open var adjustsFontForContentSizeCategory = false
|
|
373
|
+
open var clearsOnInsertion = false
|
|
374
|
+
open var isFindInteractionEnabled = false
|
|
375
|
+
open var inputAccessoryView: UIView?
|
|
376
|
+
open var inputView: UIView?
|
|
377
|
+
/// No marked (composing) text reaches Swift: the page commits it first.
|
|
378
|
+
open var markedTextRange: UITextRange? { nil }
|
|
379
|
+
/// Also the scroll view's delegate (`UITextViewDelegate` refines it).
|
|
380
|
+
open weak var delegate: UITextViewDelegate? { didSet { _scrollDelegate = delegate } }
|
|
381
|
+
/// Asked to transform pasted items (TextPaste.swift). The page pastes
|
|
382
|
+
/// text into the text area itself, so it is not consulted yet.
|
|
383
|
+
open weak var pasteDelegate: (any UITextPasteDelegate)?
|
|
384
|
+
/// What the view accepts when pasted or dropped into; kept.
|
|
385
|
+
open var pasteConfiguration: UIPasteConfiguration?
|
|
386
|
+
public private(set) var isFirstResponder = false
|
|
387
|
+
|
|
388
|
+
public override init(frame: CGRect) {
|
|
389
|
+
super.init(frame: frame)
|
|
390
|
+
isScrollEnabled = true
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
public convenience init(frame: CGRect, textContainer: NSTextContainer?) {
|
|
394
|
+
self.init(frame: frame)
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
public required init?(coder: NSCoder) { super.init(coder: coder) }
|
|
398
|
+
|
|
399
|
+
private var baseAttributes: [NSAttributedString.Key: Any] {
|
|
400
|
+
var attributes = typingAttributes
|
|
401
|
+
if let font { attributes[.font] = font }
|
|
402
|
+
if let textColor { attributes[.foregroundColor] = textColor }
|
|
403
|
+
return attributes
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
open var hasText: Bool { storage.length > 0 }
|
|
407
|
+
|
|
408
|
+
/// Takes the keyboard focus: the page focuses the text area, and the
|
|
409
|
+
/// delegate hears that editing began.
|
|
410
|
+
@discardableResult open func becomeFirstResponder() -> Bool {
|
|
411
|
+
guard !isFirstResponder else { return true }
|
|
412
|
+
guard isEditable, delegate?.textViewShouldBeginEditing(self) ?? true else { return false }
|
|
413
|
+
isFirstResponder = true
|
|
414
|
+
_setNeedsTranslation()
|
|
415
|
+
delegate?.textViewDidBeginEditing(self)
|
|
416
|
+
return true
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
@discardableResult open func resignFirstResponder() -> Bool {
|
|
420
|
+
guard isFirstResponder else { return true }
|
|
421
|
+
guard delegate?.textViewShouldEndEditing(self) ?? true else { return false }
|
|
422
|
+
isFirstResponder = false
|
|
423
|
+
_setNeedsTranslation()
|
|
424
|
+
delegate?.textViewDidEndEditing(self)
|
|
425
|
+
return true
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
open func select(_ sender: Any?) {}
|
|
429
|
+
open func selectAll(_ sender: Any?) { selectedRange = NSRange(location: 0, length: storage.length) }
|
|
430
|
+
open func scrollRangeToVisible(_ range: NSRange) {}
|
|
431
|
+
|
|
432
|
+
/// Typing: replaces the selection with `text` (as the keyboard would).
|
|
433
|
+
open func insertText(_ text: String) {
|
|
434
|
+
_replace(selectedRange, with: text)
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
open func deleteBackward() {
|
|
438
|
+
let range = selectedRange.length > 0 ? selectedRange
|
|
439
|
+
: NSRange(location: max(0, selectedRange.location - 1), length: selectedRange.location > 0 ? 1 : 0)
|
|
440
|
+
guard range.length > 0 else { return }
|
|
441
|
+
_replace(range, with: "")
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// MARK: Positions and ranges
|
|
445
|
+
|
|
446
|
+
public var beginningOfDocument: UITextPosition { UITextPosition(offset: 0) }
|
|
447
|
+
public var endOfDocument: UITextPosition { UITextPosition(offset: storage.length) }
|
|
448
|
+
open var selectedTextRange: UITextRange? {
|
|
449
|
+
get {
|
|
450
|
+
UITextRange(start: UITextPosition(offset: selectedRange.location),
|
|
451
|
+
end: UITextPosition(offset: selectedRange.location + selectedRange.length))
|
|
452
|
+
}
|
|
453
|
+
set {
|
|
454
|
+
guard let newValue else { return }
|
|
455
|
+
selectedRange = NSRange(location: newValue.start.offset, length: max(0, newValue.end.offset - newValue.start.offset))
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
public func position(from position: UITextPosition, offset: Int) -> UITextPosition? {
|
|
459
|
+
let target = position.offset + offset
|
|
460
|
+
return (0...storage.length).contains(target) ? UITextPosition(offset: target) : nil
|
|
461
|
+
}
|
|
462
|
+
public func offset(from: UITextPosition, to toPosition: UITextPosition) -> Int { toPosition.offset - from.offset }
|
|
463
|
+
public func textRange(from fromPosition: UITextPosition, to toPosition: UITextPosition) -> UITextRange? {
|
|
464
|
+
UITextRange(start: fromPosition, end: toPosition)
|
|
465
|
+
}
|
|
466
|
+
public func text(in range: UITextRange) -> String? {
|
|
467
|
+
let nsRange = NSRange(location: range.start.offset, length: range.end.offset - range.start.offset)
|
|
468
|
+
guard nsRange.location >= 0, nsRange.length >= 0, nsRange.location + nsRange.length <= storage.length else { return nil }
|
|
469
|
+
return storage.attributedSubstring(from: nsRange).string
|
|
470
|
+
}
|
|
471
|
+
public func replace(_ range: UITextRange, withText text: String) {
|
|
472
|
+
_replace(NSRange(location: range.start.offset, length: range.end.offset - range.start.offset), with: text)
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// MARK: Sizes
|
|
476
|
+
|
|
477
|
+
/// An estimate: the text wrapped at `size.width` less the insets, at
|
|
478
|
+
/// the font's line height (Swift has no text metrics).
|
|
479
|
+
open override func sizeThatFits(_ size: CGSize) -> CGSize {
|
|
480
|
+
let insets = textContainerInset
|
|
481
|
+
let padding = textContainer.lineFragmentPadding * 2 + insets.left + insets.right
|
|
482
|
+
let width = size.width.isFinite && size.width > 0 ? max(1, size.width - padding) : nil
|
|
483
|
+
let font = self.font ?? .preferredFont(forTextStyle: .body)
|
|
484
|
+
let text = _TextEstimate.size(of: storage.string, font: font, width: width)
|
|
485
|
+
return CGSize(width: width.map { $0 + padding } ?? text.width + padding,
|
|
486
|
+
height: text.height + insets.top + insets.bottom)
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
open override var intrinsicContentSize: CGSize {
|
|
490
|
+
isScrollEnabled ? CGSize(width: UIView.noIntrinsicMetric, height: UIView.noIntrinsicMetric)
|
|
491
|
+
: sizeThatFits(CGSize(width: bounds.width, height: .greatestFiniteMagnitude))
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// MARK: Editing
|
|
495
|
+
|
|
496
|
+
/// The page's text area now shows `newText`: works out the edit, asks
|
|
497
|
+
/// the delegate, applies it and reports it (see the top of the file).
|
|
498
|
+
func _userEdited(_ newText: String) {
|
|
499
|
+
let old = Array(storage.string)
|
|
500
|
+
let new = Array(newText)
|
|
501
|
+
guard old != new else { return }
|
|
502
|
+
var prefix = 0
|
|
503
|
+
while prefix < old.count, prefix < new.count, old[prefix] == new[prefix] { prefix += 1 }
|
|
504
|
+
var suffix = 0
|
|
505
|
+
while suffix < old.count - prefix, suffix < new.count - prefix,
|
|
506
|
+
old[old.count - 1 - suffix] == new[new.count - 1 - suffix] { suffix += 1 }
|
|
507
|
+
let location = String(old[..<prefix]).utf16.count
|
|
508
|
+
let length = String(old[prefix..<(old.count - suffix)]).utf16.count
|
|
509
|
+
let replacement = String(new[prefix..<(new.count - suffix)])
|
|
510
|
+
let range = NSRange(location: location, length: length)
|
|
511
|
+
if !isFirstResponder { becomeFirstResponder() }
|
|
512
|
+
// A refused edit stays on the page until the text next changes: the
|
|
513
|
+
// text area is only told about text that differs from what it was sent.
|
|
514
|
+
guard delegate?.textView(self, shouldChangeTextIn: range, replacementText: replacement) ?? true else { return }
|
|
515
|
+
_replace(range, with: replacement)
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
private func _replace(_ range: NSRange, with replacement: String) {
|
|
519
|
+
let clamped = NSRange(location: min(range.location, storage.length),
|
|
520
|
+
length: max(0, min(range.length, storage.length - min(range.location, storage.length))))
|
|
521
|
+
var attributes = baseAttributes
|
|
522
|
+
if clamped.location > 0, typingAttributes.isEmpty {
|
|
523
|
+
// Typed text continues the attributes before it, as on iOS.
|
|
524
|
+
attributes.merge(storage.attributes(at: clamped.location - 1, effectiveRange: nil)) { _, previous in previous }
|
|
525
|
+
}
|
|
526
|
+
storage.replaceCharacters(in: clamped, with: NSAttributedString(string: replacement, attributes: attributes))
|
|
527
|
+
selectedRange = NSRange(location: clamped.location + replacement.utf16.count, length: 0)
|
|
528
|
+
_setNeedsTranslation()
|
|
529
|
+
delegate?.textViewDidChange(self)
|
|
530
|
+
delegate?.textViewDidChangeSelection(self)
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
private func clampSelection() {
|
|
534
|
+
let length = storage.length
|
|
535
|
+
let location = min(selectedRange.location, length)
|
|
536
|
+
selectedRange = NSRange(location: location, length: min(selectedRange.length, length - location))
|
|
537
|
+
}
|
|
538
|
+
}
|