├── .gitignore ├── Apps └── DiffableTextAppXUIKit │ ├── App │ ├── App.swift │ ├── Assets │ │ ├── Assets.xcassets │ │ │ ├── AccentColor.colorset │ │ │ │ └── Contents.json │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ └── Preview Content │ │ │ └── Preview Assets.xcassets │ │ │ └── Contents.json │ ├── Helpers │ │ ├── Bounds.swift │ │ ├── Random.swift │ │ ├── Range.swift │ │ └── Twins.swift │ ├── Models │ │ └── Constants.swift │ ├── Screens │ │ ├── Number │ │ │ ├── NumberScreen.swift │ │ │ ├── NumberScreenContext.swift │ │ │ ├── NumberScreenExample.swift │ │ │ ├── NumberScreenPrecision.swift │ │ │ └── NumberScreenWheels.swift │ │ ├── Pattern │ │ │ ├── PatternScreen.swift │ │ │ ├── PatternScreenContext.swift │ │ │ └── PatternScreenExample.swift │ │ └── Tabs.swift │ ├── Utilities │ │ ├── Observable.swift │ │ └── Observer.swift │ └── Views │ │ ├── Action.swift │ │ ├── Example.swift │ │ ├── Interval.swift │ │ ├── Screen.swift │ │ ├── Scroller.swift │ │ └── Selector.swift │ ├── DiffableTextAppXUIKit.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── DiffableTextAppXUIKit.xcscheme │ ├── Entitlements.entitlements │ └── Modules │ └── IntervalSliderViews │ ├── .gitignore │ ├── Package.swift │ └── Sources │ └── IntervalSliderViews │ ├── IntervalSlider.swift │ ├── Models │ ├── Context.swift │ ├── Layout.swift │ └── Values.swift │ └── Views │ ├── Beam.swift │ ├── Handle.swift │ └── Slider.swift ├── Assets ├── AppXNumber.png ├── AppXPattern.png ├── ViewXNumber.gif └── ViewXPattern.gif ├── LICENSE ├── Package.swift ├── README.md ├── Sources ├── DiffableTextKit │ ├── DiffableTextStyle+Nullable.swift │ ├── DiffableTextStyle+Wrapper.swift │ ├── DiffableTextStyle.swift │ ├── Models │ │ ├── Attribute.swift │ │ ├── Caret.swift │ │ ├── Changes.swift │ │ ├── Commit.swift │ │ ├── Context.swift │ │ ├── Direction.swift │ │ ├── Focus.swift │ │ ├── Index.swift │ │ ├── Jump.swift │ │ ├── Layout.swift │ │ ├── Mismatches.swift │ │ ├── Offset.swift │ │ ├── Proposal.swift │ │ ├── Resolve.swift │ │ ├── Selection.swift │ │ ├── Snapshot.swift │ │ ├── Status.swift │ │ ├── Symbol.swift │ │ ├── Synchronize.swift │ │ └── Update.swift │ ├── Styles │ │ ├── Constant.swift │ │ ├── Equals.swift │ │ ├── Normal.swift │ │ ├── Prefix.swift │ │ ├── Standalone.swift │ │ └── Suffix.swift │ └── Utilities │ │ ├── Brrr.swift │ │ ├── Count.swift │ │ ├── Info.swift │ │ ├── Locale.swift │ │ ├── Lock.swift │ │ ├── Options.swift │ │ ├── Slice.swift │ │ ├── Trigger.swift │ │ └── Void.swift ├── DiffableTextKitXNumber │ ├── Aliases.swift │ ├── Cache.swift │ ├── Default.swift │ ├── Exports.swift │ ├── Format.swift │ ├── Graph.swift │ ├── Graphs │ │ ├── Graph+Decimal.swift │ │ ├── Graph+Floats.swift │ │ ├── Graph+Integers.swift │ │ └── Graph+Optional.swift │ ├── Models │ │ ├── Attributes.swift │ │ ├── Bounds.swift │ │ ├── Clamp.swift │ │ ├── Components.swift │ │ ├── Count.swift │ │ ├── Formatter.swift │ │ ├── Interpreter.swift │ │ ├── Label.swift │ │ ├── Links.swift │ │ ├── Parser.swift │ │ ├── Precision.swift │ │ ├── Preferences.swift │ │ └── Translator.swift │ ├── Style.swift │ ├── Styles │ │ ├── Style+Currency.swift │ │ ├── Style+Optional.swift │ │ └── Style+Standard.swift │ ├── Tokens │ │ ├── Digit.swift │ │ ├── Digits.swift │ │ ├── Number.swift │ │ ├── Separator.swift │ │ ├── Sign.swift │ │ └── Token.swift │ ├── Traits.swift │ └── Value.swift ├── DiffableTextKitXPattern │ ├── Exports.swift │ ├── Placeholders.swift │ └── Style.swift ├── DiffableTextKitXUIKit │ ├── DiffableTextField.swift │ ├── Environment+AutocorrectionDisabled.swift │ ├── Environment+Font.swift │ ├── Environment+ForegroundColor.swift │ ├── Environment+KeyboardType.swift │ ├── Environment+MultilineTextAlignment.swift │ ├── Environment+OnSubmit.swift │ ├── Environment+SubmitLabel.swift │ ├── Environment+TextContentType.swift │ ├── Environment+TextFieldStyle.swift │ ├── Environment+TextInputAutocapitalization.swift │ ├── Environment+Tint.swift │ ├── Environment+ToolbarDoneButton.swift │ ├── Exports.swift │ ├── Models │ │ ├── Alignment.swift │ │ ├── Field.swift │ │ ├── Font.swift │ │ └── Intent.swift │ └── Streams │ │ ├── Downstream.swift │ │ ├── Sidestream.swift │ │ └── Upstream.swift └── DiffableTextViews │ └── Exports.swift └── Tests ├── DiffableTextKitTests ├── Aliases.swift ├── Constants.swift ├── Mock.swift ├── Models │ ├── Attribute.swift │ ├── Changes.swift │ ├── Commit.swift │ ├── Direction.swift │ ├── Focus.swift │ ├── Index.swift │ ├── Offset.swift │ ├── Proposal.swift │ ├── Resolve.swift │ ├── Selection.swift │ ├── Snapshot.swift │ ├── Status.swift │ ├── Symbol.swift │ ├── Synchronize.swift │ └── Update.swift ├── Styles │ ├── Constant.swift │ ├── Equals.swift │ ├── Normal.swift │ ├── Prefix.swift │ └── Suffix.swift └── Utilities │ ├── Count.swift │ ├── Info.swift │ ├── Lock.swift │ ├── Options.swift │ ├── Slice.swift │ ├── Trigger.swift │ └── Void.swift ├── DiffableTextKitXNumberTests ├── Comments+Currency.swift ├── Comments+Inaccurate.swift ├── Comments+Percent.swift ├── Comments.swift ├── Constants.swift ├── Graphs │ ├── Graph+Floats.swift │ ├── Graph+Integers.swift │ └── Graph.swift ├── Models │ ├── Bounds.swift │ ├── Links.swift │ └── Precision.swift ├── Styles │ ├── Style+Currency.swift │ ├── Style+Number.swift │ ├── Style+Percent.swift │ └── Style.swift └── Tokens │ ├── Digit.swift │ ├── Separator.swift │ ├── Sign.swift │ └── Token.swift ├── DiffableTextKitXPatternTests ├── Placeholders.swift ├── Style+Hidden.swift ├── Style+Visible.swift └── Style.swift └── DiffableTextKitXUIKitTests ├── Field.swift └── Font.swift /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | DerivedData/ 7 | .swiftpm/xcode/ -------------------------------------------------------------------------------- /Apps/DiffableTextAppXUIKit/App/App.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | import SwiftUI 11 | 12 | //*============================================================================* 13 | // MARK: * App 14 | //*============================================================================* 15 | 16 | @main struct App: SwiftUI.App { 17 | 18 | //=------------------------------------------------------------------------= 19 | // MARK: Body 20 | //=------------------------------------------------------------------------= 21 | 22 | var body: some Scene { 23 | WindowGroup { 24 | Tabs().preferredColorScheme(.dark) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Apps/DiffableTextAppXUIKit/App/Assets/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.708", 9 | "green" : "0.389", 10 | "red" : "0.206" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Apps/DiffableTextAppXUIKit/App/Assets/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Apps/DiffableTextAppXUIKit/App/Assets/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Apps/DiffableTextAppXUIKit/App/Assets/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Apps/DiffableTextAppXUIKit/App/Helpers/Bounds.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | import Foundation 11 | 12 | //*============================================================================* 13 | // MARK: * Bounds 14 | //*============================================================================* 15 | 16 | struct Bounds { 17 | 18 | //=------------------------------------------------------------------------= 19 | // MARK: State 20 | //=------------------------------------------------------------------------= 21 | 22 | private var _unordered: (Int, Int) 23 | private(set) var closed: ClosedRange 24 | 25 | //=------------------------------------------------------------------------= 26 | // MARK: Initializers 27 | //=------------------------------------------------------------------------= 28 | 29 | init(_ unordered: (Int, Int)) { 30 | let closed = ClosedRange(unordered); self._unordered = unordered; self.closed = 31 | Decimal(string: Self.number(nines: closed.lowerBound))! ... 32 | Decimal(string: Self.number(nines: closed.upperBound))! 33 | } 34 | 35 | //=------------------------------------------------------------------------= 36 | // MARK: Accessors 37 | //=------------------------------------------------------------------------= 38 | 39 | var unordered: (Int, Int) { 40 | get { _unordered } set { self = Self(newValue) } 41 | } 42 | 43 | //=------------------------------------------------------------------------= 44 | // MARK: Utilities 45 | //=------------------------------------------------------------------------= 46 | 47 | private static func number(nines: Int) -> String { 48 | guard nines != 0 else { return "0" } 49 | var description = nines < 0 ? "-" : "" 50 | description += String(repeating: "9", count: abs(nines)) 51 | return description 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Apps/DiffableTextAppXUIKit/App/Helpers/Random.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | //*============================================================================* 11 | // MARK: * Random 12 | //*============================================================================* 13 | 14 | extension Character { 15 | 16 | //=------------------------------------------------------------------------= 17 | // MARK: Utilitis 18 | //=------------------------------------------------------------------------= 19 | 20 | static func random(in bounds: ClosedRange) -> Character? { 21 | let bounds = ClosedRange(uncheckedBounds: 22 | (bounds.lowerBound.value, bounds.upperBound.value)) 23 | return UnicodeScalar(UInt32.random(in: bounds)).map(Character.init) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Apps/DiffableTextAppXUIKit/App/Helpers/Range.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | //*============================================================================* 11 | // MARK: * Range 12 | //*============================================================================* 13 | 14 | extension ClosedRange where Bound: Comparable { 15 | 16 | //=------------------------------------------------------------------------= 17 | // MARK: Initializers 18 | //=------------------------------------------------------------------------= 19 | 20 | init(_ unordered: (Bound, Bound)) { 21 | switch unordered.0 <= unordered.1 { 22 | case true: self.init(uncheckedBounds: (unordered.0, unordered.1)) 23 | case false: self.init(uncheckedBounds: (unordered.1, unordered.0)) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Apps/DiffableTextAppXUIKit/App/Helpers/Twins.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | import DiffableTextViews 11 | 12 | //*============================================================================* 13 | // MARK: * Twins 14 | //*============================================================================* 15 | 16 | struct Twins { 17 | 18 | //=------------------------------------------------------------------------= 19 | // MARK: State 20 | //=------------------------------------------------------------------------= 21 | 22 | private var _standard: T 23 | private var _optional: T? 24 | 25 | //=------------------------------------------------------------------------= 26 | // MARK: Initializers 27 | //=------------------------------------------------------------------------= 28 | 29 | init(_ standard: T = .zero) { 30 | self._standard = standard 31 | self._optional = standard 32 | } 33 | 34 | init(_ optional: T? = .zero) { 35 | self._standard = optional ?? .zero 36 | self._optional = optional 37 | } 38 | 39 | //=------------------------------------------------------------------------= 40 | // MARK: Accessors 41 | //=------------------------------------------------------------------------= 42 | 43 | var standard: T { 44 | get { _standard } set { self = Self(newValue) } 45 | } 46 | 47 | var optional: T? { 48 | get { _optional } set { self = Self(newValue) } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Apps/DiffableTextAppXUIKit/App/Models/Constants.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | import Foundation 11 | 12 | //*============================================================================* 13 | // MARK: * Constants 14 | //*============================================================================* 15 | 16 | enum Constants { 17 | 18 | //=------------------------------------------------------------------------= 19 | // MARK: State 20 | //=------------------------------------------------------------------------= 21 | 22 | static let currencies: [String] = Locale.Currency 23 | .isoCurrencies 24 | .map(\.identifier) 25 | 26 | static let locales: [Locale] = Locale 27 | .availableIdentifiers 28 | .lazy.map(Locale.init) 29 | .reduce(into: Set()) { $0.insert($1) } 30 | .sorted(by: { $0.identifier < $1.identifier }) 31 | } 32 | -------------------------------------------------------------------------------- /Apps/DiffableTextAppXUIKit/App/Screens/Number/NumberScreenContext.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | import SwiftUI 11 | 12 | //*============================================================================* 13 | // MARK: * Screen x Number x Context 14 | //*============================================================================* 15 | 16 | final class NumberScreenContext: ObservableObject { 17 | 18 | //=------------------------------------------------------------------------= 19 | // MARK: State 20 | //=------------------------------------------------------------------------= 21 | 22 | @Observable var kind = KindID.standard 23 | @Observable var format = FormatID.currency 24 | @Observable var precision = PrecisionID.sides 25 | 26 | @Observable var locale = Locale(identifier: "en_US") 27 | @Observable var currency = "USD" 28 | 29 | @Observable var bounds = Bounds((0, p)) 30 | @Observable var digits = (1, p) 31 | @Observable var integer = (1, p) 32 | @Observable var fraction = (2, 2) 33 | 34 | @Observable var decimals = Twins(Decimal(string: "1234567.89")!) 35 | 36 | let boundsLimits = -p ... p 37 | let digitsLimits = 1 ... p 38 | let integerLimits = 1 ... p 39 | let fractionLimits = 0 ... p 40 | 41 | //=------------------------------------------------------------------------= 42 | // MARK: Accessors 43 | //=------------------------------------------------------------------------= 44 | 45 | static var p: Int { Decimal._NumberTextGraph.precision } 46 | 47 | //*========================================================================* 48 | // MARK: * ID(s) 49 | //*========================================================================* 50 | 51 | enum KindID: String, CaseIterable { 52 | case standard, optional 53 | } 54 | 55 | enum FormatID: String, CaseIterable { 56 | case number, currency, percent 57 | } 58 | 59 | enum PrecisionID: String, CaseIterable { 60 | case total = "total" 61 | case sides = "integer & fraction" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Apps/DiffableTextAppXUIKit/App/Screens/Pattern/PatternScreenContext.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | import DiffableTextViews 11 | import SwiftUI 12 | 13 | //*============================================================================* 14 | // MARK: * Screen x Pattern x Context 15 | //*============================================================================* 16 | 17 | final class PatternScreenContext: ObservableObject { 18 | 19 | //=------------------------------------------------------------------------= 20 | // MARK: State 21 | //=------------------------------------------------------------------------= 22 | 23 | @Observable var pattern = PatternID.phone 24 | @Observable var visibility = VisibilityID.visible 25 | @Observable var value = "123456789" 26 | 27 | let phone = pattern("+## (###) ###-##-##") 28 | let card = pattern("#### #### #### ####") 29 | 30 | //=------------------------------------------------------------------------= 31 | // MARK: Transformations 32 | //=------------------------------------------------------------------------= 33 | 34 | func popLast() { 35 | _ = value.popLast() 36 | } 37 | 38 | func appendDigit() { 39 | value.append(Character.random(in: "0" ... "9")!) 40 | } 41 | 42 | func appendLetter() { 43 | value.append(Character.random(in: "A" ... "Z")!) 44 | } 45 | 46 | //=------------------------------------------------------------------------= 47 | // MARK: Utilities 48 | //=------------------------------------------------------------------------= 49 | 50 | static func pattern(_ pattern: String) -> PatternTextStyle { 51 | .pattern(pattern).placeholders("#") { $0.isASCII && $0.isNumber } 52 | } 53 | 54 | //*========================================================================* 55 | // MARK: * ID(s) 56 | //*========================================================================* 57 | 58 | enum PatternID: String, CaseIterable { 59 | case phone, card 60 | } 61 | 62 | enum VisibilityID: String, CaseIterable { 63 | case visible, hidden 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Apps/DiffableTextAppXUIKit/App/Screens/Pattern/PatternScreenExample.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | import DiffableTextViews 11 | import SwiftUI 12 | 13 | //*============================================================================* 14 | // MARK: * Screen x Pattern x Example 15 | //*============================================================================* 16 | 17 | struct PatternScreenExample: View { 18 | typealias Context = PatternScreenContext 19 | typealias PatternID = Context.PatternID 20 | typealias VisibilityID = Context.VisibilityID 21 | 22 | //=------------------------------------------------------------------------= 23 | // MARK: State 24 | //=------------------------------------------------------------------------= 25 | 26 | let context: Context 27 | 28 | @ObservedObject var pattern: Observable 29 | @ObservedObject var visibility: Observable 30 | 31 | //=------------------------------------------------------------------------= 32 | // MARK: Initializers 33 | //=------------------------------------------------------------------------= 34 | 35 | init(_ context: Context) { 36 | self.context = context 37 | self.pattern = context.$pattern 38 | self.visibility = context.$visibility 39 | } 40 | 41 | //=------------------------------------------------------------------------= 42 | // MARK: Accessors 43 | //=------------------------------------------------------------------------= 44 | 45 | var style: PatternTextStyle { 46 | let base = (pattern == .phone) ? context.phone : context.card 47 | let hidden = (visibility == .hidden); return base.hidden(hidden) 48 | } 49 | 50 | //=------------------------------------------------------------------------= 51 | // MARK: Body 52 | //=------------------------------------------------------------------------= 53 | 54 | var body: some View { 55 | Observer(context.$value, cache: style) { 56 | Example(value: $0.value, style: $1) 57 | } 58 | .diffableTextViews_keyboardType(.numberPad) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Apps/DiffableTextAppXUIKit/App/Utilities/Observable.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | import SwiftUI 11 | 12 | //*============================================================================* 13 | // MARK: * Observable 14 | //*============================================================================* 15 | 16 | @propertyWrapper final class Observable: ObservableObject { 17 | typealias KeyPath = ReferenceWritableKeyPath 18 | 19 | //=------------------------------------------------------------------------= 20 | // MARK: State 21 | //=------------------------------------------------------------------------= 22 | 23 | @Published var value: Value 24 | 25 | //=------------------------------------------------------------------------= 26 | // MARK: Initializers 27 | //=------------------------------------------------------------------------= 28 | 29 | init(_ value: Value) { 30 | self.value = value 31 | } 32 | 33 | //=------------------------------------------------------------------------= 34 | // MARK: Wrapper 35 | //=------------------------------------------------------------------------= 36 | 37 | init(wrappedValue: Value) { 38 | self.value = wrappedValue 39 | } 40 | 41 | var wrappedValue: Value { 42 | get { value } 43 | set { value = newValue } 44 | } 45 | 46 | var projectedValue: Observable { self } 47 | 48 | //=------------------------------------------------------------------------= 49 | // MARK: Utilities 50 | //=------------------------------------------------------------------------= 51 | 52 | static func == (lhs: Observable, rhs: Value) -> Bool where Value: Equatable { 53 | lhs.value == rhs 54 | } 55 | 56 | static func == (lhs: Value, rhs: Observable) -> Bool where Value: Equatable { 57 | lhs == rhs.value 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Apps/DiffableTextAppXUIKit/App/Utilities/Observer.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | import SwiftUI 11 | 12 | //*============================================================================* 13 | // MARK: * Observer 14 | //*============================================================================* 15 | 16 | struct Observer: View { 17 | typealias Wrapper = ObservedObject.Wrapper 18 | 19 | //=------------------------------------------------------------------------= 20 | // MARK: State 21 | //=------------------------------------------------------------------------= 22 | 23 | let content: (Wrapper) -> Content 24 | @ObservedObject var observable: Observable 25 | 26 | //=------------------------------------------------------------------------= 27 | // MARK: Initializers 28 | //=------------------------------------------------------------------------= 29 | 30 | init(_ observable: Observable, @ViewBuilder content: @escaping (Wrapper) -> Content) { 31 | self.observable = observable; self.content = content 32 | } 33 | 34 | init(_ observable: Observable, cache: T, @ViewBuilder content: @escaping (Wrapper, T) -> Content) { 35 | self.observable = observable; self.content = { content($0, cache) } 36 | } 37 | 38 | //=------------------------------------------------------------------------= 39 | // MARK: Body 40 | //=------------------------------------------------------------------------= 41 | 42 | var body: some View { 43 | content($observable) 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Apps/DiffableTextAppXUIKit/App/Views/Action.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | import SwiftUI 11 | 12 | //*============================================================================* 13 | // MARK: * Action 14 | //*============================================================================* 15 | 16 | struct Action: View { 17 | 18 | //=------------------------------------------------------------------------= 19 | // MARK: State 20 | //=------------------------------------------------------------------------= 21 | 22 | let title: String 23 | let action: () -> Void 24 | 25 | //=------------------------------------------------------------------------= 26 | // MARK: Initializers 27 | //=------------------------------------------------------------------------= 28 | 29 | init(_ title: String, action: @escaping () -> Void) { 30 | self.title = title 31 | self.action = action 32 | } 33 | 34 | //=------------------------------------------------------------------------= 35 | // MARK: Body 36 | //=------------------------------------------------------------------------= 37 | 38 | var body: some View { 39 | Button(action: action) { 40 | Text(title) 41 | .font(.subheadline) 42 | .frame(height: 16) 43 | .frame(maxWidth: .infinity) 44 | } 45 | .tint(Color.gray.opacity(2/3)) 46 | .buttonStyle(.borderedProminent) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Apps/DiffableTextAppXUIKit/App/Views/Interval.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | import IntervalSliderViews 11 | import SwiftUI 12 | 13 | //*============================================================================* 14 | // MARK: * Interval 15 | //*============================================================================* 16 | 17 | struct Interval: View { 18 | 19 | //=------------------------------------------------------------------------= 20 | // MARK: State 21 | //=------------------------------------------------------------------------= 22 | 23 | let title: String 24 | let limits: ClosedRange 25 | @Binding var unordered: (Int, Int) 26 | 27 | //=------------------------------------------------------------------------= 28 | // MARK: Initializers 29 | //=------------------------------------------------------------------------= 30 | 31 | init(_ title: String, unordered: Binding<(Int, Int)>, in limits: ClosedRange) { 32 | self.title = title; self._unordered = unordered; self.limits = limits 33 | } 34 | 35 | //=------------------------------------------------------------------------= 36 | // MARK: Body 37 | //=------------------------------------------------------------------------= 38 | 39 | var body: some View { 40 | VStack(alignment: .leading) { 41 | label 42 | IntervalSlider($unordered, in: limits) 43 | Spacer(minLength: 16).fixedSize() 44 | } 45 | } 46 | 47 | var label: some View { 48 | let interval = ClosedRange(unordered) 49 | let text = Text("\(title): \(interval.lowerBound) to \(interval.upperBound)") 50 | return text.font(.subheadline.weight(.light)).animation(nil) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Apps/DiffableTextAppXUIKit/App/Views/Screen.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | import SwiftUI 11 | 12 | //*============================================================================* 13 | // MARK: * Screen 14 | //*============================================================================* 15 | 16 | struct Screen: View { 17 | 18 | //=------------------------------------------------------------------------= 19 | // MARK: State 20 | //=------------------------------------------------------------------------= 21 | 22 | @ViewBuilder let content: () -> Content 23 | 24 | //=------------------------------------------------------------------------= 25 | // MARK: Body 26 | //=------------------------------------------------------------------------= 27 | 28 | var body: some View { 29 | ZStack { 30 | Color(uiColor: .secondarySystemBackground).ignoresSafeArea() 31 | VStack(spacing: 0, content: content) 32 | } 33 | } 34 | } 35 | 36 | //=----------------------------------------------------------------------------= 37 | // MARK: + Previews 38 | //=----------------------------------------------------------------------------= 39 | 40 | struct Screen_Previews: PreviewProvider { 41 | 42 | //=------------------------------------------------------------------------= 43 | // MARK: Previews 44 | //=------------------------------------------------------------------------= 45 | 46 | static var previews: some View { 47 | Screen { Color.gray }.preferredColorScheme(.dark) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Apps/DiffableTextAppXUIKit/App/Views/Scroller.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | import SwiftUI 11 | 12 | //*============================================================================* 13 | // MARK: * Scroller 14 | //*============================================================================* 15 | 16 | struct Scroller: View { 17 | 18 | //=------------------------------------------------------------------------= 19 | // MARK: State 20 | //=------------------------------------------------------------------------= 21 | 22 | @ViewBuilder let content: () -> Content 23 | 24 | //=------------------------------------------------------------------------= 25 | // MARK: Body 26 | //=------------------------------------------------------------------------= 27 | 28 | var body: some View { 29 | ScrollView(showsIndicators: false) { 30 | content().padding() 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Apps/DiffableTextAppXUIKit/DiffableTextAppXUIKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Apps/DiffableTextAppXUIKit/DiffableTextAppXUIKit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Apps/DiffableTextAppXUIKit/Entitlements.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.network.client 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Apps/DiffableTextAppXUIKit/Modules/IntervalSliderViews/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | DerivedData/ 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | -------------------------------------------------------------------------------- /Apps/DiffableTextAppXUIKit/Modules/IntervalSliderViews/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.5 2 | //=----------------------------------------------------------------------------= 3 | // This source file is part of the DiffableTextViews open source project. 4 | // 5 | // Copyright (c) 2022 Oscar Byström Ericsson 6 | // Licensed under Apache License, Version 2.0 7 | // 8 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 9 | //=----------------------------------------------------------------------------= 10 | 11 | import PackageDescription 12 | 13 | //*============================================================================* 14 | // MARK: IntervalSliderViews 15 | //*============================================================================* 16 | 17 | let package = Package( 18 | name: "IntervalSliderViews", 19 | platforms: [ 20 | .iOS(.v15), 21 | ], 22 | products: [ 23 | //=--------------------------------------= 24 | // IntervalSliderViews 25 | //=--------------------------------------= 26 | .library( 27 | name: "IntervalSliderViews", 28 | targets: ["IntervalSliderViews"]) 29 | ], 30 | targets: [ 31 | //=--------------------------------------= 32 | // IntervalSliderViews 33 | //=--------------------------------------= 34 | .target(name: "IntervalSliderViews"), 35 | ] 36 | ) 37 | -------------------------------------------------------------------------------- /Apps/DiffableTextAppXUIKit/Modules/IntervalSliderViews/Sources/IntervalSliderViews/Models/Context.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | import SwiftUI 11 | 12 | //*============================================================================* 13 | // MARK: Declaration 14 | //*============================================================================* 15 | 16 | final class Context { 17 | typealias Tuple = (CGFloat, CGFloat) 18 | typealias Limits = ClosedRange 19 | 20 | //=------------------------------------------------------------------------= 21 | // MARK: State 22 | //=------------------------------------------------------------------------= 23 | 24 | let values: Values 25 | let layout: Layout 26 | 27 | //=------------------------------------------------------------------------= 28 | // MARK: Initializers 29 | //=------------------------------------------------------------------------= 30 | 31 | init(_ values: Values, _ layout: Layout) { 32 | self.values = values 33 | self.layout = layout 34 | } 35 | 36 | //=------------------------------------------------------------------------= 37 | // MARK: Accessors 38 | //=------------------------------------------------------------------------= 39 | 40 | var coordinates: UInt8 { 0 } 41 | 42 | var animation: Animation { 43 | .linear(duration: 0.125) 44 | } 45 | 46 | //=------------------------------------------------------------------------= 47 | // MARK: Utilities 48 | //=------------------------------------------------------------------------= 49 | 50 | static func map(_ value: CGFloat, from start: Limits, to end: Limits) -> CGFloat { 51 | if start.lowerBound == start.upperBound { return end.lowerBound } 52 | let ratio = (end.upperBound - end.lowerBound) / (start.upperBound - start.lowerBound) 53 | let proposal = end.lowerBound + ratio * (value - start.lowerBound) 54 | return min(max(end.lowerBound, proposal), end.upperBound) 55 | } 56 | 57 | static func map(_ value: Tuple, from start: Limits, to end: Limits) -> Tuple {( 58 | self.map(value.0, from: start, to: end), 59 | self.map(value.1, from: start, to: end) 60 | )} 61 | } 62 | -------------------------------------------------------------------------------- /Apps/DiffableTextAppXUIKit/Modules/IntervalSliderViews/Sources/IntervalSliderViews/Models/Layout.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | import SwiftUI 11 | 12 | //*============================================================================* 13 | // MARK: Declaration 14 | //*============================================================================* 15 | 16 | struct Layout { 17 | 18 | //=------------------------------------------------------------------------= 19 | // MARK: State 20 | //=------------------------------------------------------------------------= 21 | 22 | let bounds: CGRect 23 | let limits: ClosedRange 24 | var positions: (CGFloat, CGFloat) 25 | 26 | //=------------------------------------------------------------------------= 27 | // MARK: Initializers 28 | //=------------------------------------------------------------------------= 29 | 30 | init(_ values: Values, in bounds: CGRect) { 31 | self.bounds = bounds 32 | self.limits = ClosedRange(uncheckedBounds: (0, bounds.maxX)) 33 | self.positions = Context.map(values.remote, from: values.limits, to: limits) 34 | } 35 | 36 | //=------------------------------------------------------------------------= 37 | // MARK: Accessors 38 | //=------------------------------------------------------------------------= 39 | 40 | func position(_ item: WritableKeyPath<(CGFloat, CGFloat), CGFloat>) -> CGPoint { 41 | CGPoint(x: positions[keyPath: item], y: bounds.midY) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Apps/DiffableTextAppXUIKit/Modules/IntervalSliderViews/Sources/IntervalSliderViews/Models/Values.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | import SwiftUI 11 | 12 | //*============================================================================* 13 | // MARK: Declaration 14 | //*============================================================================* 15 | 16 | struct Values { 17 | 18 | //=------------------------------------------------------------------------= 19 | // MARK: State 20 | //=------------------------------------------------------------------------= 21 | 22 | let limits: ClosedRange 23 | @Binding var remote: (CGFloat, CGFloat) 24 | 25 | //=------------------------------------------------------------------------= 26 | // MARK: Initializers 27 | //=------------------------------------------------------------------------= 28 | 29 | init(_ remote: Binding<(CGFloat, CGFloat)>, in limits: ClosedRange) { 30 | self.limits = limits; self._remote = remote 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Assets/AppXNumber.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oscbyspro/DiffableTextViews/3d81dd1f92d43a1978311b5ad4b529c5d6e396f0/Assets/AppXNumber.png -------------------------------------------------------------------------------- /Assets/AppXPattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oscbyspro/DiffableTextViews/3d81dd1f92d43a1978311b5ad4b529c5d6e396f0/Assets/AppXPattern.png -------------------------------------------------------------------------------- /Assets/ViewXNumber.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oscbyspro/DiffableTextViews/3d81dd1f92d43a1978311b5ad4b529c5d6e396f0/Assets/ViewXNumber.gif -------------------------------------------------------------------------------- /Assets/ViewXPattern.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oscbyspro/DiffableTextViews/3d81dd1f92d43a1978311b5ad4b529c5d6e396f0/Assets/ViewXPattern.gif -------------------------------------------------------------------------------- /Sources/DiffableTextKit/Models/Caret.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | //*============================================================================* 11 | // MARK: * Caret 12 | //*============================================================================* 13 | 14 | public struct Caret: Comparable { 15 | 16 | //=------------------------------------------------------------------------= 17 | // MARK: State 18 | //=------------------------------------------------------------------------= 19 | 20 | public var position: Position 21 | public var affinity: Direction 22 | public var momentum: Direction? 23 | 24 | //=------------------------------------------------------------------------= 25 | // MARK: Initilizers 26 | //=------------------------------------------------------------------------= 27 | 28 | @inlinable init( _ position: Position, affinity: Direction) { 29 | self.position = position 30 | self.affinity = affinity 31 | } 32 | 33 | //=------------------------------------------------------------------------= 34 | // MARK: Initializers 35 | //=------------------------------------------------------------------------= 36 | 37 | @inlinable static func lower(_ position: Position) -> Self { 38 | Self(position, affinity: .forwards) 39 | } 40 | 41 | @inlinable static func upper(_ position: Position) -> Self { 42 | Self(position, affinity: .backwards) 43 | } 44 | 45 | //=------------------------------------------------------------------------= 46 | // MARK: Utilities x Comparable (!) 47 | //=------------------------------------------------------------------------= 48 | 49 | @inlinable public static func == (lhs: Self, rhs: Self) -> Bool { 50 | lhs.position == rhs.position 51 | } 52 | 53 | @inlinable public static func < (lhs: Self, rhs: Self) -> Bool { 54 | lhs.position < rhs.position 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Sources/DiffableTextKit/Models/Changes.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | //*============================================================================* 11 | // MARK: * Changes [...] 12 | //*============================================================================* 13 | 14 | /// A model used to capture comparison results. 15 | @frozen @usableFromInline struct Changes: OptionSet { 16 | 17 | public static let style = Self(rawValue: 1 << 0) 18 | public static let value = Self(rawValue: 1 << 1) 19 | public static let focus = Self(rawValue: 1 << 2) 20 | 21 | //=------------------------------------------------------------------------= 22 | 23 | public var rawValue: UInt8 24 | 25 | //=------------------------------------------------------------------------= 26 | 27 | @inlinable init(rawValue: UInt8) { self.rawValue = rawValue } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/DiffableTextKit/Models/Direction.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | //*============================================================================* 11 | // MARK: * Direction 12 | //*============================================================================* 13 | 14 | /// A forwards/backwards model. 15 | @frozen public enum Direction { 16 | 17 | //=------------------------------------------------------------------------= 18 | // MARK: Instances 19 | //=------------------------------------------------------------------------= 20 | 21 | case forwards 22 | case backwards 23 | 24 | //=------------------------------------------------------------------------= 25 | // MARK: Initializers 26 | //=------------------------------------------------------------------------= 27 | 28 | @inlinable public init?(from start: T, to end: T) where T: Comparable { 29 | if start < end { self = .forwards } 30 | else if start > end { self = .backwards } 31 | else { return nil } 32 | } 33 | 34 | //=------------------------------------------------------------------------= 35 | // MARK: Transformations 36 | //=------------------------------------------------------------------------= 37 | 38 | @inlinable mutating public func reverse() { 39 | self = reversed() 40 | } 41 | 42 | @inlinable public func reversed() -> Self { 43 | self == .forwards ? .backwards : .forwards 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/DiffableTextKit/Models/Focus.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | //*============================================================================* 11 | // MARK: * Focus [...] 12 | //*============================================================================* 13 | 14 | @frozen public struct Focus: Equatable, ExpressibleByBooleanLiteral { 15 | 16 | //=------------------------------------------------------------------------= 17 | 18 | public let value: Bool 19 | 20 | //=------------------------------------------------------------------------= 21 | 22 | @inlinable public init(_ value: Bool) { self.value = value } 23 | 24 | @inlinable public init(booleanLiteral value: Bool) { self.value = value } 25 | } 26 | -------------------------------------------------------------------------------- /Sources/DiffableTextKit/Models/Index.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | //*============================================================================* 11 | // MARK: * Index [...] 12 | //*============================================================================* 13 | 14 | /// A character encoded index and offset. 15 | public struct Index: Comparable, CustomStringConvertible { 16 | 17 | //=------------------------------------------------------------------------= 18 | 19 | @usableFromInline let character: String.Index 20 | @usableFromInline let attribute: Int 21 | 22 | //=------------------------------------------------------------------------= 23 | 24 | /// Creates an instance describing a character's position. 25 | /// 26 | /// - Parameters: 27 | /// - character: The character encoded index. 28 | /// - attribute: The character encoded offset. 29 | /// 30 | @inlinable @inline(__always) init(_ character: String.Index, as attribute: Int) { 31 | self.character = character 32 | self.attribute = attribute 33 | } 34 | 35 | public var description: String { 36 | String(describing: attribute) 37 | } 38 | 39 | @inlinable @inline(__always) public static func == (lhs: Self, rhs: Self) -> Bool { 40 | lhs.attribute == rhs.attribute 41 | } 42 | 43 | @inlinable @inline(__always) public static func < (lhs: Self, rhs: Self) -> Bool { 44 | lhs.attribute < rhs.attribute 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/DiffableTextKit/Models/Resolve.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | //*========================================================================* 11 | // MARK: * Resolve [...] 12 | //*========================================================================* 13 | 14 | /// A message describing selection behavior. 15 | @frozen public struct Resolve: OptionSet { 16 | 17 | /// Resolve max selection. 18 | public static let max = Self(rawValue: 1 << 0) 19 | 20 | /// Resolve selection by using caret momentums. 21 | public static let momentums = Self(rawValue: 1 << 1) 22 | 23 | //=--------------------------------------------------------------------= 24 | 25 | public var rawValue: UInt8 26 | 27 | //=--------------------------------------------------------------------= 28 | 29 | @inlinable public init(rawValue: UInt8) { self.rawValue = rawValue } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/DiffableTextKit/Models/Synchronize.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | //*============================================================================* 11 | // MARK: * Synchronize [...] 12 | //*============================================================================* 13 | 14 | /// A message describing synchronization behavior. 15 | @frozen public struct Synchronize: OptionSet { 16 | 17 | /// Requires that input and output values are equal. 18 | public static let acyclical = Self(rawValue: 1 << 0) 19 | 20 | //=------------------------------------------------------------------------= 21 | 22 | public let rawValue: UInt8 23 | 24 | //=------------------------------------------------------------------------= 25 | 26 | @inlinable public init(rawValue: UInt8) { self.rawValue = rawValue } 27 | } 28 | -------------------------------------------------------------------------------- /Sources/DiffableTextKit/Models/Update.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | //*============================================================================* 11 | // MARK: * Update [...] 12 | //*============================================================================* 13 | 14 | /// A message describing which properties should be updated. 15 | /// 16 | /// It is recommended to update the value first, then the text before selection. 17 | /// 18 | /// - Implement reentrant updates by only updating the value until it stabilizes. 19 | /// 20 | @frozen public struct Update: OptionSet { 21 | 22 | public static let value = Self(rawValue: 1 << 0) 23 | public static let text = Self(rawValue: 1 << 1) 24 | public static let selection = Self(rawValue: 1 << 2) 25 | 26 | //=------------------------------------------------------------------------= 27 | 28 | public let rawValue: UInt8 29 | 30 | //=------------------------------------------------------------------------= 31 | 32 | @inlinable public init(rawValue: UInt8) { self.rawValue = rawValue } 33 | } 34 | -------------------------------------------------------------------------------- /Sources/DiffableTextKit/Styles/Constant.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | import Foundation 11 | 12 | //*============================================================================* 13 | // MARK: * Constant 14 | //*============================================================================* 15 | 16 | /// Prevents style transformations. 17 | /// 18 | /// Use this modifier to ignore the environment. 19 | /// 20 | public struct ConstantTextStyle: WrapperTextStyle { 21 | 22 | public typealias Cache = Base.Cache 23 | public typealias Value = Base.Value 24 | 25 | //=------------------------------------------------------------------------= 26 | // MARK: State 27 | //=------------------------------------------------------------------------= 28 | 29 | public var base: Base 30 | 31 | //=------------------------------------------------------------------------= 32 | // MARK: Initializers 33 | //=------------------------------------------------------------------------= 34 | 35 | @inlinable public init(_ base: Base) { self.base = base } 36 | 37 | //=------------------------------------------------------------------------= 38 | // MARK: Transformations 39 | //=------------------------------------------------------------------------= 40 | 41 | @inlinable public func locale(_ locale: Locale) -> Self { self } 42 | } 43 | 44 | //*============================================================================* 45 | // MARK: * Constant x Style 46 | //*============================================================================* 47 | 48 | public extension DiffableTextStyle { 49 | 50 | typealias Constant = ConstantTextStyle 51 | 52 | //=------------------------------------------------------------------------= 53 | // MARK: Transformations 54 | //=------------------------------------------------------------------------= 55 | 56 | /// Prevents style transformations. 57 | /// 58 | /// Use this modifier to ignore the environment. 59 | /// 60 | @inlinable func constant() -> Constant { 61 | Constant(self) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Sources/DiffableTextKit/Styles/Normal.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | //*============================================================================* 11 | // MARK: * Normal 12 | //*============================================================================* 13 | 14 | /// A normal text style, without bells and whistles. 15 | public struct NormalTextStyle: DiffableTextStyle { 16 | 17 | //=------------------------------------------------------------------------= 18 | // MARK: Initializers 19 | //=------------------------------------------------------------------------= 20 | 21 | @inlinable @inline(__always) public init() { } 22 | 23 | //=------------------------------------------------------------------------= 24 | // MARK: Utilities 25 | //=------------------------------------------------------------------------= 26 | 27 | @inlinable @inline(__always) public func format(_ value: String, with cache: inout Void) -> String { 28 | value 29 | } 30 | 31 | @inlinable public func interpret(_ value: String, with cache: inout Void) -> Commit { 32 | Commit(value, Snapshot(value)) 33 | } 34 | 35 | @inlinable public func resolve(_ proposal: Proposal, with cache: inout Void) throws -> Commit { 36 | let S0 = Snapshot(proposal.lazy.merged().nonvirtuals()); return Commit(S0.characters, S0) 37 | } 38 | } 39 | 40 | //*============================================================================* 41 | // MARK: * Normal x Init 42 | //*============================================================================* 43 | 44 | extension DiffableTextStyle where Self == NormalTextStyle { 45 | 46 | //=------------------------------------------------------------------------= 47 | // MARK: Initializers 48 | //=------------------------------------------------------------------------= 49 | 50 | /// A normal text style, without bells and whistles. 51 | @inlinable @inline(__always) public static var normal: Self { .init() } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/DiffableTextKit/Utilities/Brrr.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | //*============================================================================* 11 | // MARK: * Brrr [...] 12 | //*============================================================================* 13 | 14 | /// A model that prints messages in DEBUG mode. 15 | /// 16 | /// It uses conditional compilation such that it has no size or cost in RELEASE mode. 17 | /// 18 | public struct Brrr: Equatable { 19 | 20 | public static let cancellation = Self("cancellation") 21 | public static let autocorrection = Self("autocorrection") 22 | public static let unsynchronizable = Self("unsynchronizable") 23 | 24 | //=------------------------------------------------------------------------= 25 | 26 | #if DEBUG 27 | @usableFromInline let context: String 28 | #endif 29 | 30 | //=------------------------------------------------------------------------= 31 | 32 | @inlinable @inline(__always) public init(_ context: @autoclosure () -> String) { 33 | #if DEBUG 34 | self.context = context() 35 | #endif 36 | } 37 | 38 | @inlinable @inline(__always) public static func << (brrr: Self, message: @autoclosure () -> Any) { 39 | #if DEBUG 40 | Swift.print("[DiffableTextViews] \(brrr.context): \(message())") 41 | #endif 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Sources/DiffableTextKit/Utilities/Count.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | //*============================================================================* 11 | // MARK: * Count 12 | //*============================================================================* 13 | 14 | public extension Sequence { 15 | 16 | //=------------------------------------------------------------------------= 17 | // MARK: Predicate 18 | //=------------------------------------------------------------------------= 19 | 20 | @inlinable func count(where predicate: (Element) throws -> Bool) rethrows -> Int { 21 | var S0 = 0; for S1 in self { if try predicate(S1) { S0 += 1 } }; return S0 22 | } 23 | 24 | @inlinable func count(while predicate: (Element) throws -> Bool) rethrows -> Int { 25 | var S0 = 0; for S1 in self { if try predicate(S1) { S0 += 1 } else { break } }; return S0 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Sources/DiffableTextKit/Utilities/Locale.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | import struct Foundation.Locale 11 | 12 | //*============================================================================* 13 | // MARK: * Locale [...] 14 | //*============================================================================* 15 | 16 | public extension Locale { static let en_US_POSIX = Locale(identifier: "en_US_POSIX") } 17 | -------------------------------------------------------------------------------- /Sources/DiffableTextKit/Utilities/Lock.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | //*============================================================================* 11 | // MARK: * Lock 12 | //*============================================================================* 13 | 14 | @MainActor public final class Lock { 15 | 16 | //=------------------------------------------------------------------------= 17 | // MARK: State 18 | //=------------------------------------------------------------------------= 19 | 20 | @usableFromInline private(set) var count: UInt = 0 21 | 22 | //=------------------------------------------------------------------------= 23 | // MARK: Initializers 24 | //=------------------------------------------------------------------------= 25 | 26 | @inlinable public init() { } 27 | 28 | //=------------------------------------------------------------------------= 29 | // MARK: Accessors 30 | //=------------------------------------------------------------------------= 31 | 32 | @inlinable @inline(__always) 33 | public var isLocked: Bool { 34 | self.count != 0 35 | } 36 | 37 | //=------------------------------------------------------------------------= 38 | // MARK: Transformations 39 | //=------------------------------------------------------------------------= 40 | 41 | @inlinable @inline(__always) func lock() { 42 | self.count += 1 43 | } 44 | 45 | @inlinable @inline(__always) func open() { 46 | self.count -= 1 47 | } 48 | 49 | //=------------------------------------------------------------------------= 50 | // MARK: Utilities 51 | //=------------------------------------------------------------------------= 52 | 53 | @inlinable public func perform(action: () throws -> Void) { 54 | self.lock(); try? action(); self.open() 55 | } 56 | 57 | @inlinable public func task(operation: @escaping () async throws -> Void) { 58 | _ = asynchronous(operation: operation) 59 | } 60 | 61 | @inlinable public func task(operation: @escaping () async throws -> Void) async { 62 | await asynchronous(operation: operation).value 63 | } 64 | 65 | @inlinable @inline(__always) @discardableResult func asynchronous( 66 | operation: @escaping () async throws -> Void) -> Task { 67 | self.lock(); return Task { try? await operation(); self.open() } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Sources/DiffableTextKit/Utilities/Options.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | //*============================================================================* 11 | // MARK: * Options 12 | //*============================================================================* 13 | 14 | public extension OptionSet { 15 | 16 | //=------------------------------------------------------------------------= 17 | // MARK: Transformations 18 | //=------------------------------------------------------------------------= 19 | 20 | @inlinable @inline(__always) func callAsFunction(_ mask: Bool) -> Self { 21 | mask ? self : Self() 22 | } 23 | 24 | @inlinable @inline(__always) static prefix func !(options: Self) -> Bool { 25 | options.isEmpty 26 | } 27 | 28 | @inlinable @inline(__always) static func + (lhs: Self, rhs: Self) -> Self { 29 | lhs.union(rhs) 30 | } 31 | 32 | @inlinable @inline(__always) static func += (lhs: inout Self, rhs: Self) { 33 | lhs.formUnion(rhs) 34 | } 35 | 36 | @inlinable @inline(__always) static func - (lhs: Self, rhs: Self) -> Self { 37 | lhs.subtracting(rhs) 38 | } 39 | 40 | @inlinable @inline(__always) static func -= (lhs: inout Self, rhs: Self) { 41 | lhs.subtract(rhs) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Sources/DiffableTextKit/Utilities/Slice.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | //*============================================================================* 11 | // MARK: * Slice 12 | //*============================================================================* 13 | 14 | extension BidirectionalCollection { 15 | 16 | //=------------------------------------------------------------------------= 17 | // MARK: Suffix 18 | //=------------------------------------------------------------------------= 19 | 20 | @inlinable public func suffix(while predicate: (Element) throws -> Bool) rethrows -> SubSequence { 21 | try self[startOfSuffix(while: predicate)...] 22 | } 23 | 24 | @inlinable func startOfSuffix(while predicate: (Element) throws -> Bool) rethrows -> Index { 25 | var position = endIndex 26 | 27 | backwards: while position != startIndex { 28 | let after = position; formIndex(before: &position) 29 | if try !predicate(self[position]) { return after } 30 | } 31 | 32 | return position 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/DiffableTextKit/Utilities/Trigger.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | //*============================================================================* 11 | // MARK: * Trigger [...] 12 | //*============================================================================* 13 | 14 | public struct Trigger { 15 | 16 | //=------------------------------------------------------------------------= 17 | 18 | @usableFromInline var action: () -> Void 19 | 20 | //=------------------------------------------------------------------------= 21 | 22 | @inlinable public init(_ action: @escaping () -> Void) { 23 | self.action = action 24 | } 25 | 26 | @inlinable public func callAsFunction() { 27 | self.action() 28 | } 29 | 30 | @inlinable public static func += (lhs: inout Self, rhs: Self) { 31 | lhs.action = { [lhs] in lhs(); rhs() } 32 | } 33 | } 34 | 35 | //*============================================================================* 36 | // MARK: * Trigger x Optional [...] 37 | //*============================================================================* 38 | 39 | extension Optional where Wrapped == Trigger { 40 | 41 | @inlinable public init(_ action: @escaping () -> Void) { 42 | self = Trigger(action) 43 | } 44 | 45 | @inlinable public func callAsFunction() { 46 | if case let .some(wrapped) = self { wrapped() } 47 | } 48 | 49 | @inlinable public static func += (lhs: inout Self, rhs: Self) { 50 | (lhs != nil && rhs != nil) ? (lhs! += rhs!) : (lhs = rhs) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/DiffableTextKit/Utilities/Void.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | //*============================================================================* 11 | // MARK: * Void [...] 12 | //*============================================================================* 13 | 14 | public struct _Void: Hashable { @inlinable @inline(__always) public init() { } } 15 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXNumber/Aliases.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | import Foundation 11 | 12 | //*============================================================================* 13 | // MARK: * Aliases 14 | //*============================================================================* 15 | 16 | public typealias NumberTextStyle = 17 | Value.NumberTextGraph.Number where 18 | Value.NumberTextGraph: _Numberable 19 | 20 | public typealias NumberTextBounds = _Bounds 21 | public typealias NumberTextPrecision = _Precision 22 | 23 | //*============================================================================* 24 | // MARK: * Aliases x Internal 25 | //*============================================================================* 26 | 27 | public typealias _FPRR = FloatingPointRoundingRule 28 | 29 | public typealias _NFSC = NumberFormatStyleConfiguration 30 | public typealias _CFSC = CurrencyFormatStyleConfiguration 31 | 32 | public typealias _NFSC_SignDS = _NFSC.SignDisplayStrategy 33 | public typealias _CFSC_SignDS = _CFSC.SignDisplayStrategy 34 | 35 | public typealias _NFSC_SeparatorDS = _NFSC.DecimalSeparatorDisplayStrategy 36 | public typealias _CFSC_SeparatorDS = _CFSC.DecimalSeparatorDisplayStrategy 37 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXNumber/Exports.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | //*============================================================================* 11 | // MARK: * Exports 12 | //*============================================================================* 13 | 14 | @_exported import protocol DiffableTextKit.DiffableTextStyle 15 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXNumber/Graph.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | import Foundation 11 | 12 | //*============================================================================* 13 | // MARK: * Graph 14 | //*============================================================================* 15 | 16 | public protocol _Graph { 17 | 18 | associatedtype Value: _Value // where Value.NumberTextGraph == Self 19 | 20 | associatedtype Input: _Input 21 | 22 | //=------------------------------------------------------------------------= 23 | // MARK: State 24 | //=------------------------------------------------------------------------= 25 | 26 | @inlinable var min: Input { get } 27 | 28 | @inlinable var max: Input { get } 29 | 30 | @inlinable var zero: Input { get } 31 | 32 | @inlinable var precision: Int { get } 33 | 34 | @inlinable var optional: Bool { get } 35 | 36 | @inlinable var unsigned: Bool { get } 37 | 38 | @inlinable var integer: Bool { get } 39 | } 40 | 41 | //*============================================================================* 42 | // MARK: * Graph x Numberable 43 | //*============================================================================* 44 | 45 | public protocol _Numberable: _Graph { 46 | associatedtype Number: _Style where 47 | Number: _Standard, 48 | Number.Value == Value, 49 | Number.Input == Input 50 | } 51 | 52 | //*============================================================================* 53 | // MARK: * Graph x Percentable 54 | //*============================================================================* 55 | 56 | public protocol _Percentable: _Graph { 57 | associatedtype Percent: _Style where 58 | Percent: _Standard, 59 | Percent.Value == Value, 60 | Percent.Input == Input 61 | } 62 | 63 | public extension _Style where Graph: _Percentable, Graph: _Numberable, Graph.Number == Self { 64 | typealias Percent = Graph.Percent 65 | } 66 | 67 | //*============================================================================* 68 | // MARK: * Graph x Currency 69 | //*============================================================================* 70 | 71 | public protocol _Currencyable: _Graph { 72 | associatedtype Currency: _Style where 73 | Currency: _Currency, 74 | Currency.Value == Value, 75 | Currency.Input == Input 76 | } 77 | 78 | public extension _Style where Graph: _Currencyable, Graph: _Numberable, Graph.Number == Self { 79 | typealias Currency = Graph.Currency 80 | } 81 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXNumber/Graphs/Graph+Decimal.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | import Foundation 11 | 12 | //*============================================================================* 13 | // MARK: * Graph x Decimal 14 | //*============================================================================* 15 | 16 | public final class _DecimalGraph: _Graph, _Numberable, _Percentable, _Currencyable { 17 | 18 | public typealias Value = Decimal 19 | public typealias Input = Decimal 20 | 21 | //=------------------------------------------------------------------------= 22 | // MARK: Nodes 23 | //=------------------------------------------------------------------------= 24 | 25 | public typealias Number = _StandardStyle 26 | public typealias Percent = _StandardStyle 27 | public typealias Currency = _CurrencyStyle 28 | 29 | //=------------------------------------------------------------------------= 30 | // MARK: State 31 | //=------------------------------------------------------------------------= 32 | 33 | public let min: Value 34 | public let max: Value 35 | public let precision: Int 36 | 37 | //=------------------------------------------------------------------------= 38 | // MARK: Initializers 39 | //=------------------------------------------------------------------------= 40 | 41 | fileprivate init() { 42 | self.precision = 38 43 | self.max = Value(string: String(repeating: "9", count: precision))! 44 | self.min = -(max) 45 | } 46 | 47 | //=------------------------------------------------------------------------= 48 | // MARK: Accessors 49 | //=------------------------------------------------------------------------= 50 | 51 | @inlinable @inline(__always) public var zero: Value { .zero } 52 | 53 | @inlinable @inline(__always) public var optional: Bool { false } 54 | 55 | @inlinable @inline(__always) public var unsigned: Bool { false } 56 | 57 | @inlinable @inline(__always) public var integer: Bool { false } 58 | } 59 | 60 | //=----------------------------------------------------------------------------= 61 | // MARK: + Decimal [...] 62 | //=----------------------------------------------------------------------------= 63 | 64 | extension Decimal: _Input { public static let _NumberTextGraph = _DecimalGraph() } 65 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXNumber/Models/Attributes.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | import DiffableTextKit 11 | 12 | //*============================================================================* 13 | // MARK: * Attributes 14 | //*============================================================================* 15 | 16 | @usableFromInline struct Attributes { 17 | 18 | //=------------------------------------------------------------------------= 19 | // MARK: State 20 | //=------------------------------------------------------------------------= 21 | 22 | @usableFromInline private(set) var map = [Character: Attribute]() 23 | 24 | //=------------------------------------------------------------------------= 25 | // MARK: Initializers 26 | //=------------------------------------------------------------------------= 27 | 28 | @inlinable init(_ components: Components) { 29 | self.map[components.separators[.fraction/*------*/]] = .removable 30 | components.digits.tokens.keys.forEach { self.map[$0] = .content } 31 | components.signs .tokens.keys.forEach { self.map[$0] = .phantom - .virtual } 32 | } 33 | 34 | //=------------------------------------------------------------------------= 35 | // MARK: Accessors 36 | //=------------------------------------------------------------------------= 37 | 38 | @inlinable subscript(character: Character) -> Attribute { 39 | map[character] ?? .phantom 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXNumber/Models/Clamp.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | //*============================================================================* 11 | // MARK: * Clamp 12 | //*============================================================================* 13 | //=----------------------------------------------------------------------------= 14 | // MARK: + Comparable 15 | //=----------------------------------------------------------------------------= 16 | 17 | extension Comparable { 18 | 19 | //=------------------------------------------------------------------------= 20 | // MARK: Transformations 21 | //=------------------------------------------------------------------------= 22 | 23 | @inlinable func clamped() -> Self where Self: _Input { 24 | Swift.min(Swift.max(Self.min, self), Self.max) 25 | } 26 | 27 | @inlinable func clamped(to bounds: ClosedRange) -> Self { 28 | Swift.min(Swift.max(bounds.lowerBound, self), bounds.upperBound) 29 | } 30 | } 31 | 32 | //=----------------------------------------------------------------------------= 33 | // MARK: + Expression 34 | //=----------------------------------------------------------------------------= 35 | 36 | extension RangeExpression where Bound: FixedWidthInteger { 37 | 38 | //=------------------------------------------------------------------------= 39 | // MARK: Transformations 40 | //=------------------------------------------------------------------------= 41 | 42 | @inlinable func clamped(to bounds: Range) -> Range { 43 | relative(to: Range(uncheckedBounds: (Bound.min, Bound.max))).clamped(to: bounds) 44 | } 45 | 46 | @inlinable func clamped(to bounds: ClosedRange) -> ClosedRange { 47 | ClosedRange(relative(to: Range(uncheckedBounds: (Bound.min, Bound.max)))).clamped(to: bounds) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXNumber/Models/Count.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | //*============================================================================* 11 | // MARK: * Count [...] 12 | //*============================================================================* 13 | 14 | /// A count of a number's components. 15 | /// 16 | /// - It SHOULD NOT include leading integer zeros. 17 | /// 18 | @usableFromInline struct Count: CustomStringConvertible, Equatable { 19 | 20 | //=------------------------------------------------------------------------= 21 | 22 | @usableFromInline let digits: Int 23 | @usableFromInline let integer: Int 24 | @usableFromInline let fraction: Int 25 | 26 | //=------------------------------------------------------------------------= 27 | 28 | @inlinable init(digits: Int, integer: Int, fraction: Int) { 29 | self.digits = digits; self.integer = integer; self.fraction = fraction 30 | } 31 | 32 | @inlinable init( _ number: Number) { 33 | self.integer = number.integer .count - number.integer.count(prefix:{$0 == .zero}) 34 | self.fraction = number.fraction.count; self.digits = self.integer + self.fraction 35 | } 36 | 37 | public var description: String { 38 | "\((digits, integer, fraction))" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXNumber/Models/Label.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | import DiffableTextKit 11 | import Foundation 12 | 13 | //*============================================================================* 14 | // MARK: * Label 15 | //*============================================================================* 16 | 17 | @usableFromInline struct Label { 18 | 19 | //=------------------------------------------------------------------------= 20 | // MARK: State 21 | //=------------------------------------------------------------------------= 22 | 23 | @usableFromInline let text: String 24 | @usableFromInline let direction: Direction 25 | @usableFromInline var virtual: Bool = false 26 | 27 | //=------------------------------------------------------------------------= 28 | // MARK: Initializers 29 | //=------------------------------------------------------------------------= 30 | 31 | @inlinable init(_ text: String, search direction: Direction) { 32 | self.text = text; self.direction = direction 33 | } 34 | 35 | @inlinable init(_ text: String, context: String) { 36 | let match = context.range(of: text)! 37 | 38 | let lhs = (match.lowerBound == context.startIndex) 39 | let rhs = (match.upperBound == context .endIndex) 40 | 41 | self.init(text, search: (lhs || !rhs) ? .forwards : .backwards) 42 | } 43 | 44 | //=------------------------------------------------------------------------= 45 | // MARK: Initializers 46 | //=------------------------------------------------------------------------= 47 | 48 | /// - Test by parsing each combination. 49 | @inlinable static func currency(_ formatter: NumberFormatter) -> Self { 50 | assert(formatter.numberStyle == .currency) 51 | assert(formatter.maximumFractionDigits == 0) 52 | 53 | let text = formatter.currencySymbol! 54 | let body = formatter.string(from:0)! 55 | 56 | return Self(text, context: String(body)) 57 | } 58 | 59 | //=------------------------------------------------------------------------= 60 | // MARK: Utilities 61 | //=------------------------------------------------------------------------= 62 | 63 | @inlinable func autocorrect(_ snapshot: inout Snapshot) { 64 | if !virtual, let label = snapshot.range(of: text, first: direction) { 65 | snapshot.transform(attributes: label, with: { $0 = Attribute.phantom }) 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXNumber/Models/Parser.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | //*============================================================================* 11 | // MARK: * Parser [...] 12 | //*============================================================================* 13 | 14 | @usableFromInline struct Parser { 15 | 16 | //=------------------------------------------------------------------------= 17 | 18 | @usableFromInline let base: Format.Strategy 19 | 20 | //=------------------------------------------------------------------------= 21 | 22 | @inlinable init(initial: Format) { 23 | self.base = initial.locale(.en_US_POSIX).parseStrategy 24 | } 25 | 26 | @inlinable func parse(_ number: Number) throws -> Format.FormatInput { 27 | try base.parse(String.init(bytes: number.ascii, encoding: .ascii)!) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXNumber/Models/Preferences.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | import Foundation 11 | 12 | //*============================================================================* 13 | // MARK: * Preferences [...] 14 | //*============================================================================* 15 | 16 | @usableFromInline struct Preferences { 17 | 18 | @usableFromInline typealias Bounds = _Bounds 19 | @usableFromInline typealias Precision = _Precision 20 | 21 | //=------------------------------------------------------------------------= 22 | 23 | @usableFromInline let bounds: Bounds 24 | @usableFromInline let precision: Precision 25 | 26 | //=------------------------------------------------------------------------= 27 | 28 | @inlinable init(bounds: Bounds, precision: Precision) { 29 | self.bounds = bounds; self.precision = precision 30 | } 31 | 32 | @inlinable static func standard() -> Self { 33 | Self(bounds: Bounds(), precision: Precision()) 34 | } 35 | 36 | /// - Requires that formatter.maximumFractionDigits == default. 37 | @inlinable static func currency(_ formatter: NumberFormatter) -> Self { 38 | assert(formatter.numberStyle == .currency) 39 | 40 | let precision = Precision(fraction: 41 | formatter.minimumFractionDigits ... 42 | formatter.maximumFractionDigits) 43 | 44 | return Self(bounds: _Bounds(), precision: precision) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXNumber/Tokens/Digit.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | import Foundation 11 | 12 | //*============================================================================* 13 | // MARK: * Digit 14 | //*============================================================================* 15 | 16 | @usableFromInline struct Digit: Token { 17 | 18 | //=------------------------------------------------------------------------= 19 | // MARK: Instances 20 | //=------------------------------------------------------------------------= 21 | 22 | @usableFromInline static let zero = Self(ascii: "0") 23 | @usableFromInline static let one = Self(ascii: "1") 24 | @usableFromInline static let two = Self(ascii: "2") 25 | @usableFromInline static let three = Self(ascii: "3") 26 | @usableFromInline static let four = Self(ascii: "4") 27 | @usableFromInline static let five = Self(ascii: "5") 28 | @usableFromInline static let six = Self(ascii: "6") 29 | @usableFromInline static let seven = Self(ascii: "7") 30 | @usableFromInline static let eight = Self(ascii: "8") 31 | @usableFromInline static let nine = Self(ascii: "9") 32 | 33 | @usableFromInline static let allCases = [ 34 | zero, one, two, three, four, 35 | five, six, seven, eight, nine] 36 | 37 | //=------------------------------------------------------------------------= 38 | // MARK: State 39 | //=------------------------------------------------------------------------= 40 | 41 | @usableFromInline let ascii: UInt8 42 | 43 | //=------------------------------------------------------------------------= 44 | // MARK: Initializers 45 | //=------------------------------------------------------------------------= 46 | 47 | private init(ascii: Unicode.Scalar) { 48 | self.ascii = UInt8(ascii:ascii) 49 | } 50 | 51 | //=------------------------------------------------------------------------= 52 | // MARK: Utilities 53 | //=------------------------------------------------------------------------= 54 | 55 | @inlinable func standard(_ formatter: NumberFormatter) -> Character! { 56 | formatter.string(from: ascii - Self.zero.ascii as NSNumber)!.first 57 | } 58 | 59 | @inlinable func currency(_ formatter: NumberFormatter) -> Character! { 60 | self.standard(formatter) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXNumber/Tokens/Separator.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | import Foundation 11 | 12 | //*============================================================================* 13 | // MARK: * Separator 14 | //*============================================================================* 15 | 16 | @usableFromInline struct Separator: Token { 17 | 18 | //=------------------------------------------------------------------------= 19 | // MARK: Instances 20 | //=------------------------------------------------------------------------= 21 | 22 | @usableFromInline static let grouping = Self(ascii: ",") 23 | @usableFromInline static let fraction = Self(ascii: ".") 24 | 25 | @usableFromInline static let allCases = [grouping, fraction] 26 | 27 | //=------------------------------------------------------------------------= 28 | // MARK: State 29 | //=------------------------------------------------------------------------= 30 | 31 | @usableFromInline let ascii: UInt8 32 | 33 | //=------------------------------------------------------------------------= 34 | // MARK: Initializers 35 | //=------------------------------------------------------------------------= 36 | 37 | private init(ascii: Unicode.Scalar) { 38 | self.ascii = UInt8(ascii:ascii) 39 | } 40 | 41 | //=------------------------------------------------------------------------= 42 | // MARK: Utilities 43 | //=------------------------------------------------------------------------= 44 | 45 | @inlinable func standard(_ formatter: NumberFormatter) -> Character! { switch self { 46 | case .grouping: return formatter.groupingSeparator.first 47 | default: return formatter .decimalSeparator.first } 48 | } 49 | 50 | @inlinable func currency(_ formatter: NumberFormatter) -> Character! { switch self { 51 | case .grouping: return formatter.currencyGroupingSeparator.first 52 | default: return formatter .currencyDecimalSeparator.first } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXNumber/Tokens/Sign.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | import Foundation 11 | 12 | //*============================================================================* 13 | // MARK: * Sign 14 | //*============================================================================* 15 | 16 | @usableFromInline struct Sign: Token { 17 | 18 | //=------------------------------------------------------------------------= 19 | // MARK: Instances 20 | //=------------------------------------------------------------------------= 21 | 22 | @usableFromInline static let positive = Self(ascii: "+") 23 | @usableFromInline static let negative = Self(ascii: "-") 24 | 25 | @usableFromInline static let allCases = [positive, negative] 26 | 27 | //=------------------------------------------------------------------------= 28 | // MARK: State 29 | //=------------------------------------------------------------------------= 30 | 31 | @usableFromInline let ascii: UInt8 32 | 33 | //=------------------------------------------------------------------------= 34 | // MARK: Initializers 35 | //=------------------------------------------------------------------------= 36 | 37 | private init(ascii: Unicode.Scalar) { 38 | self.ascii = UInt8(ascii:ascii) 39 | } 40 | 41 | //=------------------------------------------------------------------------= 42 | // MARK: Transformations 43 | //=------------------------------------------------------------------------= 44 | 45 | @inlinable mutating func toggle() { 46 | self = toggled() 47 | } 48 | 49 | @inlinable func toggled() -> Self { 50 | self == .positive ? .negative : .positive 51 | } 52 | 53 | //=------------------------------------------------------------------------= 54 | // MARK: Utilities 55 | //=------------------------------------------------------------------------= 56 | 57 | @inlinable func standard(_ formatter: NumberFormatter) -> Character! { switch self { 58 | case .positive: return formatter .plusSign.first { $0.isPunctuation || $0.isMathSymbol } 59 | default: return formatter.minusSign.first { $0.isPunctuation || $0.isMathSymbol } } 60 | } 61 | 62 | @inlinable func currency(_ formatter: NumberFormatter) -> Character! { 63 | self.standard(formatter) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXNumber/Traits.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | import Foundation 11 | 12 | //*============================================================================* 13 | // MARK: * Traits x Standard 14 | //*============================================================================* 15 | 16 | public protocol _Standard { 17 | 18 | //=------------------------------------------------------------------------= 19 | // MARK: State 20 | //=------------------------------------------------------------------------= 21 | 22 | @inlinable var locale: Locale { get set } 23 | 24 | //=------------------------------------------------------------------------= 25 | // MARK: Initializers 26 | //=------------------------------------------------------------------------= 27 | 28 | @inlinable init(locale: Locale) 29 | } 30 | 31 | //*============================================================================* 32 | // MARK: * Traits x Currency 33 | //*============================================================================* 34 | 35 | public protocol _Currency { 36 | 37 | //=------------------------------------------------------------------------= 38 | // MARK: State 39 | //=------------------------------------------------------------------------= 40 | 41 | @inlinable var locale: Locale { get set } 42 | 43 | @inlinable var currencyCode: String { get } 44 | 45 | //=------------------------------------------------------------------------= 46 | // MARK: Initializers 47 | //=------------------------------------------------------------------------= 48 | 49 | @inlinable init(code: String, locale: Locale) 50 | } 51 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXNumber/Value.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | //*============================================================================* 11 | // MARK: * Value 12 | //*============================================================================* 13 | 14 | public protocol _Value: Equatable where NumberTextGraph.Value == Self { 15 | 16 | associatedtype NumberTextGraph: _Graph 17 | 18 | //=------------------------------------------------------------------------= 19 | // MARK: State 20 | //=------------------------------------------------------------------------= 21 | 22 | @inlinable static var _NumberTextGraph: NumberTextGraph { get } 23 | } 24 | 25 | //=----------------------------------------------------------------------------= 26 | // MARK: + Details 27 | //=----------------------------------------------------------------------------= 28 | 29 | extension _Value { 30 | 31 | //=------------------------------------------------------------------------= 32 | // MARK: Accessors 33 | //=------------------------------------------------------------------------= 34 | 35 | @inlinable @inline(__always) static var min: NumberTextGraph.Input { 36 | _NumberTextGraph.min 37 | } 38 | 39 | @inlinable @inline(__always) static var max: NumberTextGraph.Input { 40 | _NumberTextGraph.max 41 | } 42 | 43 | @inlinable @inline(__always) static var zero: NumberTextGraph.Input { 44 | _NumberTextGraph.zero 45 | } 46 | 47 | @inlinable @inline(__always) static var precision: Int { 48 | _NumberTextGraph.precision 49 | } 50 | 51 | @inlinable @inline(__always) static var optional: Bool { 52 | _NumberTextGraph.optional 53 | } 54 | 55 | @inlinable @inline(__always) static var unsigned: Bool { 56 | _NumberTextGraph.unsigned 57 | } 58 | 59 | @inlinable @inline(__always) static var integer: Bool { 60 | _NumberTextGraph.integer 61 | } 62 | } 63 | 64 | //*============================================================================* 65 | // MARK: * Input 66 | //*============================================================================* 67 | 68 | public protocol _Input: _Value, Comparable where NumberTextGraph.Input == Self { } 69 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXPattern/Exports.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | //*============================================================================* 11 | // MARK: * Exports 12 | //*============================================================================* 13 | 14 | @_exported import protocol DiffableTextKit.DiffableTextStyle 15 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXUIKit/Environment+AutocorrectionDisabled.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | #if canImport(UIKit) 11 | 12 | import SwiftUI 13 | 14 | //*============================================================================* 15 | // MARK: * Environment x Autocorrection Disabled 16 | //*============================================================================* 17 | //=----------------------------------------------------------------------------= 18 | // MARK: + Keys 19 | //=----------------------------------------------------------------------------= 20 | 21 | @usableFromInline enum DiffableTextViews_AutocorrectionDisabled: EnvironmentKey { 22 | @usableFromInline static let defaultValue: Bool = false 23 | } 24 | 25 | //=----------------------------------------------------------------------------= 26 | // MARK: + Values 27 | //=----------------------------------------------------------------------------= 28 | 29 | extension EnvironmentValues { 30 | @inlinable var diffableTextViews_autocorrectionDisabled: Bool { 31 | get { self[DiffableTextViews_AutocorrectionDisabled.self] } 32 | set { self[DiffableTextViews_AutocorrectionDisabled.self] = newValue } 33 | } 34 | } 35 | 36 | //=----------------------------------------------------------------------------= 37 | // MARK: + View 38 | //=----------------------------------------------------------------------------= 39 | 40 | public extension View { 41 | 42 | /// Optionally disables autocorrection in diffable text views. 43 | /// 44 | /// It is similar to `View/autocorrectionDisabled(_:)`. 45 | /// 46 | /// ``` 47 | /// DiffableTextField("Text...", value: $text, style: .normal) 48 | /// .diffableTextViews_autocorrectionDisabled(true) 49 | /// ``` 50 | /// 51 | /// **Notes** 52 | /// 53 | /// - The default value is `false`. 54 | /// - This method was added for consistency. 55 | /// - This method does not affect style autocorrection behavior. 56 | /// 57 | @inlinable func diffableTextViews_autocorrectionDisabled(_ disabled: Bool = true) -> some View { 58 | self.environment(\.diffableTextViews_autocorrectionDisabled, disabled) 59 | } 60 | } 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXUIKit/Environment+Font.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | #if canImport(UIKit) 11 | 12 | import SwiftUI 13 | 14 | //*============================================================================* 15 | // MARK: * Environment x Font 16 | //*============================================================================* 17 | //=----------------------------------------------------------------------------= 18 | // MARK: + Keys 19 | //=----------------------------------------------------------------------------= 20 | 21 | @usableFromInline enum DiffableTextViews_Font: EnvironmentKey { 22 | @usableFromInline static let defaultValue: DiffableTextFont? = nil 23 | } 24 | 25 | //=----------------------------------------------------------------------------= 26 | // MARK: + Values 27 | //=----------------------------------------------------------------------------= 28 | 29 | extension EnvironmentValues { 30 | @inlinable var diffableTextViews_font: DiffableTextFont? { 31 | get { self[DiffableTextViews_Font.self] } 32 | set { self[DiffableTextViews_Font.self] = newValue } 33 | } 34 | } 35 | 36 | //=----------------------------------------------------------------------------= 37 | // MARK: + View 38 | //=----------------------------------------------------------------------------= 39 | 40 | public extension View { 41 | 42 | /// Sets the font for diffable text views. 43 | /// 44 | /// It is similar to `View/font(_:)` but uses a SwiftUI-esque system font type. 45 | /// 46 | /// ``` 47 | /// DiffableTextField("Amount", value: $amount, style: .number) 48 | /// .diffableTextViews_font(.body.monospaced()) 49 | /// ``` 50 | /// 51 | /// Monospaced fonts are recommended because they make as-you-type formatting 52 | /// more visually predictable. Sometimes it does not matter much, however. As- 53 | /// you-type formatting with trailing text alignment works fine with any font, 54 | /// because the caret and/or selection does not jump around as much. 55 | /// 56 | /// **Notes** 57 | /// 58 | /// - The default value is `DiffableTextFont.body`. 59 | /// - It is not yet possible to convert `SwiftUI.Font` to `UIKit.UIFont`. 60 | /// 61 | @inlinable func diffableTextViews_font(_ font: DiffableTextFont?) -> some View { 62 | self.environment(\.diffableTextViews_font, font) 63 | } 64 | } 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXUIKit/Environment+ForegroundColor.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | #if canImport(UIKit) 11 | 12 | import SwiftUI 13 | 14 | //*============================================================================* 15 | // MARK: * Environment x Foreground Color 16 | //*============================================================================* 17 | //=----------------------------------------------------------------------------= 18 | // MARK: + Keys 19 | //=----------------------------------------------------------------------------= 20 | 21 | @usableFromInline enum DiffableTextViews_ForegroundColor: EnvironmentKey { 22 | @usableFromInline static let defaultValue: Color? = nil 23 | } 24 | 25 | //=----------------------------------------------------------------------------= 26 | // MARK: + Values 27 | //=----------------------------------------------------------------------------= 28 | 29 | extension EnvironmentValues { 30 | @inlinable var diffableTextViews_foregroundColor: Color? { 31 | get { self[DiffableTextViews_ForegroundColor.self] } 32 | set { self[DiffableTextViews_ForegroundColor.self] = newValue } 33 | } 34 | } 35 | 36 | //=----------------------------------------------------------------------------= 37 | // MARK: + View 38 | //=----------------------------------------------------------------------------= 39 | 40 | public extension View { 41 | 42 | /// Sets the text color of diffable text views. 43 | /// 44 | /// It is similar to `View/foregroundColor(_:)`. 45 | /// 46 | /// ``` 47 | /// DiffableTextField("Amount", value: $amount, style: .number) 48 | /// .diffableTextViews_foregroundColor(amount > 100 ? .red : nil) 49 | /// ``` 50 | /// 51 | /// **Notes** 52 | /// 53 | /// - The default value is `Color.primary`. 54 | /// - The `View/foregroundColor(_:)` environment value is inaccessible. 55 | /// 56 | @inlinable func diffableTextViews_foregroundColor(_ color: Color?) -> some View { 57 | self.environment(\.diffableTextViews_foregroundColor, color) 58 | } 59 | } 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXUIKit/Environment+KeyboardType.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | #if canImport(UIKit) 11 | 12 | import SwiftUI 13 | 14 | //*============================================================================* 15 | // MARK: * Environment x Keyboard Type 16 | //*============================================================================* 17 | //=----------------------------------------------------------------------------= 18 | // MARK: + Keys 19 | //=----------------------------------------------------------------------------= 20 | 21 | @usableFromInline enum DiffableTextViews_KeyboardType: EnvironmentKey { 22 | @usableFromInline static let defaultValue: UIKeyboardType = .default 23 | } 24 | 25 | //=----------------------------------------------------------------------------= 26 | // MARK: + Values 27 | //=----------------------------------------------------------------------------= 28 | 29 | extension EnvironmentValues { 30 | @inlinable var diffableTextViews_keyboardType: UIKeyboardType { 31 | get { self[DiffableTextViews_KeyboardType.self] } 32 | set { self[DiffableTextViews_KeyboardType.self] = newValue } 33 | } 34 | } 35 | 36 | //=----------------------------------------------------------------------------= 37 | // MARK: + View 38 | //=----------------------------------------------------------------------------= 39 | 40 | public extension View { 41 | 42 | /// Sets the keyboard type for diffable text views. 43 | /// 44 | /// It is similar to `View/keyboardType(_:)`. 45 | /// 46 | /// ``` 47 | /// DiffableTextField("Amount", value: $amount, style: .number) 48 | /// .diffableTextViews_keyboardType(.decimalPad) 49 | /// ``` 50 | /// 51 | /// **Notes** 52 | /// 53 | /// - The default value is `UIKeyboardType.default`. 54 | /// - The `View/keyboardType(_:)` environment value is inaccessible. 55 | /// 56 | @inlinable func diffableTextViews_keyboardType(_ type: UIKeyboardType) -> some View { 57 | self.environment(\.diffableTextViews_keyboardType, type) 58 | } 59 | } 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXUIKit/Environment+MultilineTextAlignment.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | #if canImport(UIKit) 11 | 12 | import SwiftUI 13 | 14 | //*============================================================================* 15 | // MARK: * Environment x Multiline Text Alignment 16 | //*============================================================================* 17 | //=----------------------------------------------------------------------------= 18 | // MARK: + Keys 19 | //=----------------------------------------------------------------------------= 20 | 21 | @usableFromInline enum DiffableTextViews_MultilineTextAlignment: EnvironmentKey { 22 | @usableFromInline static let defaultValue: TextAlignment = .leading 23 | } 24 | 25 | //=----------------------------------------------------------------------------= 26 | // MARK: + Values 27 | //=----------------------------------------------------------------------------= 28 | 29 | extension EnvironmentValues { 30 | @inlinable var diffableTextViews_multilineTextAlignment: TextAlignment { 31 | get { self[DiffableTextViews_MultilineTextAlignment.self] } 32 | set { self[DiffableTextViews_MultilineTextAlignment.self] = newValue } 33 | } 34 | } 35 | 36 | //=----------------------------------------------------------------------------= 37 | // MARK: + View 38 | //=----------------------------------------------------------------------------= 39 | 40 | public extension View { 41 | 42 | /// Sets the alignment of text in diffable text views. 43 | /// 44 | /// It is similar to `View/multilineTextAlignment(_:)`. 45 | /// 46 | /// ``` 47 | /// DiffableTextField("Amount", value: $amount, style: .number) 48 | /// .diffableTextViews_multilineTextAlignment(.trailing) 49 | /// ``` 50 | /// 51 | /// **Notes** 52 | /// 53 | /// - The default value is `TextAlignment.leading`. 54 | /// - This method was added for consistency. 55 | /// 56 | @inlinable func diffableTextViews_multilineTextAlignment(_ alignment: TextAlignment) -> some View { 57 | self.environment(\.diffableTextViews_multilineTextAlignment, alignment) 58 | } 59 | } 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXUIKit/Environment+OnSubmit.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | #if canImport(UIKit) 11 | 12 | import DiffableTextKit 13 | import SwiftUI 14 | 15 | //*============================================================================* 16 | // MARK: * Environment x On Submit [...] 17 | //*============================================================================* 18 | 19 | @usableFromInline enum DiffableTextViews_OnSubmit: EnvironmentKey { 20 | @usableFromInline static let defaultValue: Trigger? = nil 21 | } 22 | 23 | extension EnvironmentValues { 24 | @inlinable var diffableTextViews_onSubmit: Trigger? { 25 | get { self[DiffableTextViews_OnSubmit.self] } 26 | set { self[DiffableTextViews_OnSubmit.self] += newValue } 27 | } 28 | } 29 | 30 | public extension View { 31 | 32 | /// Adds an action to perform when the user submits a value to this view. 33 | /// 34 | /// It is similar to `View/onSubmit(_:)`. 35 | /// 36 | /// ``` 37 | /// DiffableTextField("Username", text: $username, style: .normal) 38 | /// .diffableTextViews_onSubmit { 39 | /// print("Validate and try to login with \(username)...") 40 | /// } 41 | /// ``` 42 | /// 43 | /// **Notes** 44 | /// 45 | /// - The action triggers when the user hits the return key. 46 | /// - The `View/onSubmit(_:)` environment value is inaccessible. 47 | /// 48 | @inlinable func diffableTextViews_onSubmit(_ action: (() -> Void)?) -> some View { 49 | self.environment(\.diffableTextViews_onSubmit, action.map(Trigger.init)) 50 | } 51 | } 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXUIKit/Environment+SubmitLabel.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | #if canImport(UIKit) 11 | 12 | import SwiftUI 13 | 14 | //*============================================================================* 15 | // MARK: * Environment x Submit Label 16 | //*============================================================================* 17 | //=----------------------------------------------------------------------------= 18 | // MARK: + Keys 19 | //=----------------------------------------------------------------------------= 20 | 21 | @usableFromInline enum DiffableTextViews_SubmitLabel: EnvironmentKey { 22 | @usableFromInline static let defaultValue: UIReturnKeyType = .default 23 | } 24 | 25 | //=----------------------------------------------------------------------------= 26 | // MARK: + Values 27 | //=----------------------------------------------------------------------------= 28 | 29 | extension EnvironmentValues { 30 | @inlinable var diffableTextViews_submitLabel: UIReturnKeyType { 31 | get { self[DiffableTextViews_SubmitLabel.self] } 32 | set { self[DiffableTextViews_SubmitLabel.self] = newValue } 33 | } 34 | } 35 | 36 | //=----------------------------------------------------------------------------= 37 | // MARK: + View 38 | //=----------------------------------------------------------------------------= 39 | 40 | public extension View { 41 | 42 | /// Sets the submit label for diffable text views. 43 | /// 44 | /// It is similar to `View/submitLabel(_:)`. 45 | /// 46 | /// ``` 47 | /// DiffableTextField("Search", value: $text, style: .normal) 48 | /// .diffableTextViews_submitLabel(.search) 49 | /// ``` 50 | /// 51 | /// **Notes** 52 | /// 53 | /// - The default value is `UIReturnKeyType.default`. 54 | /// - The `View/submitLabel(_:)` environment value is inaccessible. 55 | /// 56 | @inlinable func diffableTextViews_submitLabel(_ label: UIReturnKeyType) -> some View { 57 | self.environment(\.diffableTextViews_submitLabel, label) 58 | } 59 | } 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXUIKit/Environment+TextContentType.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | #if canImport(UIKit) 11 | 12 | import SwiftUI 13 | 14 | //*============================================================================* 15 | // MARK: * Environment x Text Content Type 16 | //*============================================================================* 17 | //=----------------------------------------------------------------------------= 18 | // MARK: + Keys 19 | //=----------------------------------------------------------------------------= 20 | 21 | @usableFromInline enum DiffableTextViews_TextContentType: EnvironmentKey { 22 | @usableFromInline static let defaultValue: UITextContentType? = nil 23 | } 24 | 25 | //=----------------------------------------------------------------------------= 26 | // MARK: + Values 27 | //=----------------------------------------------------------------------------= 28 | 29 | extension EnvironmentValues { 30 | @inlinable var diffableTextViews_textContentType: UITextContentType? { 31 | get { self[DiffableTextViews_TextContentType.self] } 32 | set { self[DiffableTextViews_TextContentType.self] = newValue } 33 | } 34 | } 35 | 36 | //=----------------------------------------------------------------------------= 37 | // MARK: + View 38 | //=----------------------------------------------------------------------------= 39 | 40 | public extension View { 41 | 42 | /// Sets the text content type for diffable text views, which the system uses 43 | /// to offer suggestions while the user enters text on an iOS or tvOS device. 44 | /// 45 | /// It is similar to `View/textContentType(_:)`. 46 | /// 47 | /// ``` 48 | /// DiffableTextField("Phone", value: $address) { 49 | /// .pattern("+## (###) ###-##-##") 50 | /// .placeholders("#") { $0.isASCII && $0.isNumber } 51 | /// .equals(()) 52 | /// } 53 | /// .diffableTextViews_textContentType(.telephoneNumber) 54 | /// ``` 55 | /// 56 | /// **Notes** 57 | /// 58 | /// - The default value is `UITextContentType?.none`. 59 | /// - The `View/textContentType(_:)` environment value is inaccessible. 60 | /// 61 | @inlinable func diffableTextViews_textContentType(_ type: UITextContentType?) -> some View { 62 | self.environment(\.diffableTextViews_textContentType, type) 63 | } 64 | } 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXUIKit/Environment+TextFieldStyle.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | #if canImport(UIKit) 11 | 12 | import SwiftUI 13 | 14 | //*============================================================================* 15 | // MARK: * Environment x Text Field Style 16 | //*============================================================================* 17 | //=----------------------------------------------------------------------------= 18 | // MARK: + Keys 19 | //=----------------------------------------------------------------------------= 20 | 21 | @usableFromInline enum DiffableTextViews_TextFieldStyle: EnvironmentKey { 22 | @usableFromInline static let defaultValue: UITextField.BorderStyle = .none 23 | } 24 | 25 | //=----------------------------------------------------------------------------= 26 | // MARK: + Values 27 | //=----------------------------------------------------------------------------= 28 | 29 | extension EnvironmentValues { 30 | @inlinable var diffableTextViews_textFieldStyle: UITextField.BorderStyle { 31 | get { self[DiffableTextViews_TextFieldStyle.self] } 32 | set { self[DiffableTextViews_TextFieldStyle.self] = newValue } 33 | } 34 | } 35 | 36 | //=----------------------------------------------------------------------------= 37 | // MARK: + View 38 | //=----------------------------------------------------------------------------= 39 | 40 | public extension View { 41 | 42 | /// Sets the text field style for diffable text views. 43 | /// 44 | /// It is similar to `View/textFieldStyle(_:), but based on UIKit. 45 | /// 46 | /// ``` 47 | /// DiffableTextField("Bordered", value: $value, style: style) 48 | /// .diffableTextViews_textFieldStyle(.roundedRect) 49 | /// ``` 50 | /// 51 | /// **Notes** 52 | /// 53 | /// - The value is read when the view is set up. 54 | /// - The default value is `UITextField.BorderStyle.none`. 55 | /// - The `View/textFieldStyle(_:)` environment value is inaccessible. 56 | /// 57 | @inlinable func diffableTextViews_textFieldStyle(_ style: UITextField.BorderStyle) -> some View { 58 | self.environment(\.diffableTextViews_textFieldStyle, style) 59 | } 60 | } 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXUIKit/Environment+TextInputAutocapitalization.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | #if canImport(UIKit) 11 | 12 | import SwiftUI 13 | 14 | //*============================================================================* 15 | // MARK: * Environment x Text Input Autocapitalization 16 | //*============================================================================* 17 | //=----------------------------------------------------------------------------= 18 | // MARK: + Keys 19 | //=----------------------------------------------------------------------------= 20 | 21 | @usableFromInline enum DiffableTextViews_TextInputAutocapitalization: EnvironmentKey { 22 | @usableFromInline static let defaultValue: UITextAutocapitalizationType? = nil 23 | } 24 | 25 | //=----------------------------------------------------------------------------= 26 | // MARK: + Values 27 | //=----------------------------------------------------------------------------= 28 | 29 | extension EnvironmentValues { 30 | @inlinable var diffableTextViews_textInputAutocapitalization: UITextAutocapitalizationType? { 31 | get { self[DiffableTextViews_TextInputAutocapitalization.self] } 32 | set { self[DiffableTextViews_TextInputAutocapitalization.self] = newValue } 33 | } 34 | } 35 | 36 | //=----------------------------------------------------------------------------= 37 | // MARK: + View 38 | //=----------------------------------------------------------------------------= 39 | 40 | public extension View { 41 | 42 | /// Sets how often the shift key is automatically enabled in diffable text views. 43 | /// 44 | /// It is similar to `View/textInputAutocapitalization(_:)`. 45 | /// 46 | /// ``` 47 | /// DiffableTextField("Username", value: $username, style: .normal) 48 | /// .diffableTextViews_textInputAutocapitalization(.never) 49 | /// ``` 50 | /// 51 | /// **Notes** 52 | /// 53 | /// - The default is `TextInputAutocapitalization.sentences`. 54 | /// - The `View/textInputAutocapitalization(_:)` environment value is inaccessible. 55 | /// 56 | @inlinable func diffableTextViews_textInputAutocapitalization( 57 | _ autocapitalization: UITextAutocapitalizationType?) -> some View { 58 | self.environment(\.diffableTextViews_textInputAutocapitalization, autocapitalization) 59 | } 60 | } 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXUIKit/Environment+Tint.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | #if canImport(UIKit) 11 | 12 | import SwiftUI 13 | 14 | //*============================================================================* 15 | // MARK: * Environment x Tint 16 | //*============================================================================* 17 | //=----------------------------------------------------------------------------= 18 | // MARK: + Keys 19 | //=----------------------------------------------------------------------------= 20 | 21 | @usableFromInline enum DiffableTextViews_Tint: EnvironmentKey { 22 | @usableFromInline static let defaultValue: Color? = nil 23 | } 24 | 25 | //=----------------------------------------------------------------------------= 26 | // MARK: + Values 27 | //=----------------------------------------------------------------------------= 28 | 29 | extension EnvironmentValues { 30 | @inlinable var diffableTextViews_tint: Color? { 31 | get { self[DiffableTextViews_Tint.self] } 32 | set { self[DiffableTextViews_Tint.self] = newValue } 33 | } 34 | } 35 | 36 | //=----------------------------------------------------------------------------= 37 | // MARK: + View 38 | //=----------------------------------------------------------------------------= 39 | 40 | public extension View { 41 | 42 | /// Sets the tint color for diffable text views. 43 | /// 44 | /// It is similar to `View/tint(_:)` and affects text selection. 45 | /// 46 | /// ``` 47 | /// DiffableTextField("Tinted", value: $value, style: style) 48 | /// .diffableTextViews_tint(.gray) 49 | /// ``` 50 | /// 51 | /// **Notes** 52 | /// 53 | /// - The default value is `Color.accentColor`. 54 | /// - The `View/tint(_:)` environment value is inaccessible. 55 | /// 56 | @inlinable func diffableTextViews_tint(_ color: Color?) -> some View { 57 | self.environment(\.diffableTextViews_tint, color) 58 | } 59 | } 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXUIKit/Environment+ToolbarDoneButton.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | #if canImport(UIKit) 11 | 12 | import SwiftUI 13 | import UIKit 14 | 15 | //*============================================================================* 16 | // MARK: * Environment x Toolbar Done Button 17 | //*============================================================================* 18 | //=----------------------------------------------------------------------------= 19 | // MARK: + Keys 20 | //=----------------------------------------------------------------------------= 21 | 22 | @usableFromInline enum DiffableTextViews_ToolbarDoneButton: EnvironmentKey { 23 | @usableFromInline static let defaultValue: UIBarButtonItem.Style? = nil 24 | } 25 | 26 | //=----------------------------------------------------------------------------= 27 | // MARK: + Values 28 | //=----------------------------------------------------------------------------= 29 | 30 | extension EnvironmentValues { 31 | @inlinable var diffableTextViews_toolbarDoneButton: UIBarButtonItem.Style? { 32 | get { self[DiffableTextViews_ToolbarDoneButton.self] } 33 | set { self[DiffableTextViews_ToolbarDoneButton.self] = newValue } 34 | } 35 | } 36 | 37 | //=----------------------------------------------------------------------------= 38 | // MARK: + View 39 | //=----------------------------------------------------------------------------= 40 | 41 | public extension View { 42 | 43 | /// Adds a toolbar with a done button for convenient dismissal. 44 | /// 45 | /// Instructs the view to install a toolbar similar to SwiftUI's 46 | /// 47 | /// ``` 48 | /// .toolbar { 49 | /// ToolbarItemGroup(placement: .keyboard) { 50 | /// Spacer() 51 | /// Button("Done") { 52 | /// // UIResponder/resignFirstResponder() 53 | /// } 54 | /// } 55 | /// } 56 | /// ``` 57 | /// 58 | /// **Notes** 59 | /// 60 | /// - The value is read when the view is set up. 61 | /// - The default value is `nil` (no toolbar installed). 62 | /// - The `View/toolbar(_:)` environment value is inaccessible. 63 | /// 64 | @inlinable func diffableTextViews_toolbarDoneButton(_ style: UIBarButtonItem.Style = .plain) -> some View { 65 | self.environment(\.diffableTextViews_toolbarDoneButton, style) 66 | } 67 | } 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXUIKit/Exports.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | //*============================================================================* 11 | // MARK: * Exports 12 | //*============================================================================* 13 | 14 | @_exported import protocol DiffableTextKit.DiffableTextStyle 15 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXUIKit/Models/Alignment.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | #if canImport(UIKit) 11 | 12 | import SwiftUI 13 | import UIKit 14 | 15 | //*============================================================================* 16 | // MARK: * Alignment x UIKit 17 | //*============================================================================* 18 | 19 | extension NSTextAlignment { 20 | 21 | //=------------------------------------------------------------------------= 22 | // MARK: Initializers 23 | //=------------------------------------------------------------------------= 24 | 25 | @inlinable init(_ alignment: TextAlignment, relativeTo layout: LayoutDirection) { 26 | switch alignment { 27 | case .leading: self.init(layout, leftToRight: .left, rightToLeft: .right) 28 | case .trailing: self.init(layout, leftToRight: .right, rightToLeft: .left) 29 | case .center: self = .center } 30 | } 31 | 32 | @inlinable init(_ layout: LayoutDirection, leftToRight: Self, rightToLeft: Self) { 33 | switch layout { 34 | case .rightToLeft: self = rightToLeft 35 | case .leftToRight: self = leftToRight 36 | @unknown default: self = leftToRight } 37 | } 38 | } 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXUIKit/Models/Intent.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | #if canImport(UIKit) 11 | 12 | import UIKit 13 | 14 | //*============================================================================* 15 | // MARK: * Intent [...] 16 | //*============================================================================* 17 | 18 | @usableFromInline struct Intent { 19 | 20 | @usableFromInline typealias Key = UIKeyboardHIDUsage 21 | 22 | //=------------------------------------------------------------------------= 23 | 24 | @usableFromInline private(set) var latest: Key? 25 | 26 | //=------------------------------------------------------------------------= 27 | 28 | @inlinable mutating func insert(_ presses: Set) { 29 | parse(presses).map({ latest = $0 }) 30 | } 31 | 32 | @inlinable mutating func remove(_ presses: Set) { 33 | parse(presses).map({ latest == $0 ? latest = nil : () }) 34 | } 35 | 36 | @inlinable func parse(_ presses: Set) -> Key? { 37 | if let key = presses.first?.key?.keyCode, 38 | key == .keyboardLeftArrow || 39 | key == .keyboardRightArrow { return key }; return nil 40 | } 41 | } 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXUIKit/Streams/Sidestream.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | #if canImport(UIKit) 11 | 12 | import DiffableTextKit 13 | import SwiftUI 14 | 15 | //*============================================================================* 16 | // MARK: * Sidestream [...] 17 | //*============================================================================* 18 | 19 | @usableFromInline struct Sidestream { 20 | 21 | //=------------------------------------------------------------------------= 22 | 23 | @usableFromInline var onSubmit: Trigger? 24 | 25 | //=------------------------------------------------------------------------= 26 | 27 | @inlinable init() { } 28 | 29 | @inlinable init(_ environment: EnvironmentValues) { 30 | self.onSubmit = environment.diffableTextViews_onSubmit 31 | } 32 | } 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /Sources/DiffableTextKitXUIKit/Streams/Upstream.swift: -------------------------------------------------------------------------------- 1 | //=----------------------------------------------------------------------------= 2 | // This source file is part of the DiffableTextViews open source project. 3 | // 4 | // Copyright (c) 2022 Oscar Byström Ericsson 5 | // Licensed under Apache License, Version 2.0 6 | // 7 | // See http://www.apache.org/licenses/LICENSE-2.0 for license information. 8 | //=----------------------------------------------------------------------------= 9 | 10 | #if canImport(UIKit) 11 | 12 | import DiffableTextKit 13 | import SwiftUI 14 | 15 | //*============================================================================* 16 | // MARK: * Upstream 17 | //*============================================================================* 18 | 19 | @usableFromInline struct Upstream { 20 | 21 | //=------------------------------------------------------------------------= 22 | // MARK: State 23 | //=------------------------------------------------------------------------= 24 | 25 | @usableFromInline let style: Style 26 | @usableFromInline let proxy: Binding 27 | 28 | //=------------------------------------------------------------------------= 29 | // MARK: Initializers 30 | //=------------------------------------------------------------------------= 31 | 32 | @inlinable init(_ view: DiffableTextField