Skip to content

Commit 2053149

Browse files
authored
Add initial Text API support (#619)
1 parent a07f255 commit 2053149

16 files changed

+1990
-88
lines changed

Sources/OpenSwiftUICore/Accessibility/AccessibilityCore.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,15 @@ extension _ViewInputs {
3131
needsAccessibility && self[WithinAccessibilityRotor.self]
3232
}
3333
}
34+
35+
// TODO
36+
37+
extension AccessibilityCore {
38+
package static func description(
39+
for symbolName: String,
40+
in environment: EnvironmentValues
41+
) -> String? {
42+
_openSwiftUIUnimplementedWarning()
43+
return nil
44+
}
45+
}

Sources/OpenSwiftUICore/Render/DisplayList/DisplayList.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,7 @@ extension GraphicsImage: ProtobufMessage {
529529
}
530530
package struct ResolvedShadowStyle {}
531531

532-
package struct StyledTextContentView {}
532+
package struct StyledTextContentView: PrimitiveView {}
533533
package struct RasterizationOptions {}
534534
package protocol RBDisplayListContents {} // RenderBox.RBDisplayListContents
535535
public struct PlatformDrawableOptions {}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
//
2+
// AccessibilityHeadingLevel.swift
3+
// OpenSwiftUICore
4+
//
5+
// Audited for 6.5.4
6+
// Status: Complete
7+
8+
/// The hierarchy of a heading in relation other headings.
9+
///
10+
/// Assistive technologies can use this to improve a users navigation
11+
/// through multiple headings. When users navigate through top level
12+
/// headings they expect the content for each heading to be unrelated.
13+
///
14+
/// For example, you can categorize a list of available products into sections,
15+
/// like Fruits and Vegetables. With only top level headings, this list requires no
16+
/// heading hierarchy, and you use the ``unspecified`` heading level. On the other hand, if sections
17+
/// contain subsections, like if the Fruits section has subsections for varieties of Apples,
18+
/// Pears, and so on, you apply the ``h1`` level to Fruits and Vegetables, and the ``h2``
19+
/// level to Apples and Pears.
20+
///
21+
/// Except for ``h1``, be sure to precede all leveled headings by another heading with a level
22+
/// that's one less.
23+
@available(OpenSwiftUI_v3_0, *)
24+
@frozen
25+
public enum AccessibilityHeadingLevel: UInt {
26+
27+
/// A heading without a hierarchy.
28+
case unspecified
29+
30+
/// Level 1 heading.
31+
case h1
32+
33+
/// Level 2 heading.
34+
case h2
35+
36+
/// Level 3 heading.
37+
case h3
38+
39+
/// Level 4 heading.
40+
case h4
41+
42+
/// Level 5 heading.
43+
case h5
44+
45+
/// Level 6 heading.
46+
case h6
47+
}
48+
49+
extension AccessibilityHeadingLevel: CodableByProxy {
50+
package var codingProxy: RawValue {
51+
rawValue
52+
}
53+
54+
package static func unwrap(
55+
codingProxy rawValue: RawValue
56+
) -> AccessibilityHeadingLevel {
57+
.init(rawValue: rawValue) ?? .unspecified
58+
}
59+
}
60+
61+
extension AccessibilityHeadingLevel: ProtobufEnum {
62+
package var protobufValue: UInt {
63+
rawValue
64+
}
65+
66+
package init?(protobufValue v: UInt) {
67+
self.init(rawValue: v)
68+
}
69+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//
2+
// AccessibilityTextContentType.swift
3+
// OpenSwiftUICore
4+
//
5+
// Audited for 6.5.4
6+
// Status: Complete
7+
8+
/// Textual context that assistive technologies can use to improve the
9+
/// presentation of spoken text.
10+
///
11+
/// Use an `AccessibilityTextContentType` value when setting the accessibility text content
12+
/// type of a view using the ``View/accessibilityTextContentType(_:)`` modifier.
13+
///
14+
@available(OpenSwiftUI_v3_0, *)
15+
public struct AccessibilityTextContentType: Sendable {
16+
package enum RawValue: UInt, Codable {
17+
case plain
18+
case console
19+
case fileSystem
20+
case messaging
21+
case narrative
22+
case sourceCode
23+
case spreadsheet
24+
case wordProcessing
25+
}
26+
27+
package var rawValue: RawValue
28+
29+
package init(_ rawValue: RawValue) {
30+
self.rawValue = rawValue
31+
}
32+
33+
/// A type that represents generic text that has no specific type.
34+
public static let plain: AccessibilityTextContentType = .init(.plain)
35+
36+
/// A type that represents text used for input, like in the Terminal app.
37+
public static let console: AccessibilityTextContentType = .init(.console)
38+
39+
/// A type that represents text used by a file browser, like in the Finder app in macOS.
40+
public static let fileSystem: AccessibilityTextContentType = .init(.fileSystem)
41+
42+
/// A type that represents text used in a message, like in the Messages app.
43+
public static let messaging: AccessibilityTextContentType = .init(.messaging)
44+
45+
/// A type that represents text used in a story or poem, like in the Books app.
46+
public static let narrative: AccessibilityTextContentType = .init(.narrative)
47+
48+
/// A type that represents text used in source code, like in Swift Playgrounds.
49+
public static let sourceCode: AccessibilityTextContentType = .init(.sourceCode)
50+
51+
/// A type that represents text used in a grid of data, like in the Numbers app.
52+
public static let spreadsheet: AccessibilityTextContentType = .init(.spreadsheet)
53+
54+
/// A type that represents text used in a document, like in the Pages app.
55+
public static let wordProcessing: AccessibilityTextContentType = .init(.wordProcessing)
56+
}
57+
58+
extension AccessibilityTextContentType: CodableByProxy {
59+
package var codingProxy: RawValue {
60+
rawValue
61+
}
62+
63+
package static func unwrap(codingProxy rawValue: RawValue) -> AccessibilityTextContentType {
64+
.init(rawValue)
65+
}
66+
}
67+
68+
extension AccessibilityTextContentType: ProtobufEnum {
69+
package var protobufValue: UInt {
70+
rawValue.rawValue
71+
}
72+
73+
package init?(protobufValue v: UInt) {
74+
guard let rawValue = RawValue(rawValue: v) else {
75+
return nil
76+
}
77+
self.rawValue = rawValue
78+
}
79+
}
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
//
2+
// NSAttributedString+Accessibility.swift
3+
// OpenSwiftUICore
4+
//
5+
// Audited for 6.5.4
6+
// Status: WIP
7+
// ID: 2BDB458C6E5F105189088F0894A913CC (SwiftUICore)
8+
9+
package import Foundation
10+
11+
// MARK: - Accessibility + Text resolve [WIP]
12+
13+
extension AccessibilityCore {
14+
package static func textResolvesToEmpty(_ text: Text, in environment: EnvironmentValues) -> Bool {
15+
_openSwiftUIUnimplementedFailure()
16+
}
17+
18+
package static func textsResolveToEmpty(_ texts: [Text], in environment: EnvironmentValues) -> Bool {
19+
_openSwiftUIUnimplementedFailure()
20+
}
21+
22+
package static func textResolvedToPlainText(
23+
_ text: Text,
24+
in environment: EnvironmentValues,
25+
updateResolvableAttributes: Bool = false,
26+
idiom: AnyInterfaceIdiom? = nil
27+
) -> String {
28+
_openSwiftUIUnimplementedFailure()
29+
}
30+
31+
package static func textsResolvedToPlainText(
32+
_ texts: [Text],
33+
in environment: EnvironmentValues,
34+
updateResolvableAttributes: Bool = false,
35+
idiom: AnyInterfaceIdiom? = nil,
36+
separator: String = ", "
37+
) -> String? {
38+
_openSwiftUIUnimplementedFailure()
39+
}
40+
41+
package static func textResolvedToAttributedText(
42+
_ text: Text,
43+
in environment: EnvironmentValues,
44+
includeResolvableAttributes: Bool = false,
45+
includeDefaultAttributes: Bool = true,
46+
updateResolvableAttributes: Bool = true,
47+
idiom: AnyInterfaceIdiom? = nil
48+
) -> NSAttributedString? {
49+
_openSwiftUIUnimplementedFailure()
50+
}
51+
52+
@discardableResult
53+
package static func resolveAttributedTextAttributes(
54+
in string: inout NSAttributedString,
55+
environment: EnvironmentValues,
56+
includeResolvableAttributes: Bool = false
57+
) -> Bool {
58+
_openSwiftUIUnimplementedFailure()
59+
}
60+
61+
package static func resolveAttributedTextAttributes(
62+
_ attributes: inout [NSAttributedString.Key: Any],
63+
environment: EnvironmentValues
64+
) {
65+
_openSwiftUIUnimplementedFailure()
66+
}
67+
68+
package static func resolveAccessibilitySpeechAttributes(
69+
into attributes: inout [NSAttributedString.Key: Any],
70+
speechAttr: AccessibilitySpeechAttributes,
71+
environment: EnvironmentValues,
72+
includeDefaultAttributes: Bool = true
73+
) {
74+
_openSwiftUIUnimplementedFailure()
75+
}
76+
77+
package static func textsResolvedToAttributedText(
78+
_ texts: [Text],
79+
in environment: EnvironmentValues,
80+
includeResolvableAttributes: Bool = false,
81+
includeDefaultAttributes: Bool = true,
82+
updateResolvableAttributes: Bool = true,
83+
resolveSuffix: Bool = false,
84+
idiom: AnyInterfaceIdiom? = nil,
85+
separator: String = ", "
86+
) -> NSAttributedString? {
87+
_openSwiftUIUnimplementedFailure()
88+
}
89+
}
90+
91+
extension Text {
92+
package func accessibilityResolvedText(
93+
in environment: EnvironmentValues,
94+
idiom: AnyInterfaceIdiom? = nil
95+
) -> AccessibilityText? {
96+
_openSwiftUIUnimplementedFailure()
97+
}
98+
}
99+
100+
// MARK: - AccessibilityTextAttributeResolver
101+
102+
package protocol AccessibilityTextAttributeResolver {
103+
func resolveDefaultAttributes(_: inout [NSAttributedString.Key: Any])
104+
105+
func resolveTextStyleAttributes(
106+
_: inout [NSAttributedString.Key: Any],
107+
textStyle: Text.Style,
108+
environment: EnvironmentValues
109+
)
110+
111+
func resolveAccessibilitySpeechAttributes(
112+
into attributes: inout [NSAttributedString.Key: Any],
113+
speechAttr: AccessibilitySpeechAttributes,
114+
environment: EnvironmentValues,
115+
includeDefaultAttributes: Bool
116+
)
117+
}
118+
119+
extension EnvironmentValues {
120+
private struct AccessibilityTextAttributeResolverKey: EnvironmentKey {
121+
static let defaultValue: (any AccessibilityTextAttributeResolver)? = nil
122+
}
123+
124+
package var accessibilityTextAttributeResolver: (any AccessibilityTextAttributeResolver)? {
125+
get { self[AccessibilityTextAttributeResolverKey.self] }
126+
set { self[AccessibilityTextAttributeResolverKey.self] = newValue }
127+
}
128+
}
129+
130+
// MARK: - NSAttributedString + AX [WIP]
131+
132+
extension NSAttributedString {
133+
convenience package init(axAttributedString: String) {
134+
_openSwiftUIUnimplementedFailure()
135+
}
136+
}
137+
138+
// MARK: - AX AttributedString Keys
139+
140+
extension NSAttributedString.Key {
141+
package static var coreAXForegroundColor: NSAttributedString.Key {
142+
isUIKitBased() ? .init("UIAccessibilityTokenForegroundColor") : .init("AXForegroundColor")
143+
}
144+
145+
package static var coreAXFontName: NSAttributedString.Key {
146+
.init("UIAccessibilityTokenFontName")
147+
}
148+
149+
package static var coreAXFontSize: NSAttributedString.Key {
150+
.init("UIAccessibilityTokenFontSize")
151+
}
152+
153+
package static var coreAXFontFamily: NSAttributedString.Key {
154+
.init("UIAccessibilityTokenFontFamily")
155+
}
156+
157+
package static var coreAXStrikethrough: NSAttributedString.Key {
158+
isUIKitBased() ? .init("UIAccessibilityTokenStrikethrough") : .init("AXStrikethrough")
159+
}
160+
161+
package static var coreAXUnderline: NSAttributedString.Key {
162+
isUIKitBased() ? .init("UIAccessibilityTokenUnderline") : .init("AXUnderline")
163+
}
164+
165+
package static var coreAXAlignment: NSAttributedString.Key {
166+
isUIKitBased() ? .init("UIAccessibilityTokenParagraphAlignment") : .init("AXATextAlignmentValue")
167+
}
168+
169+
package static var coreAXAttachment: NSAttributedString.Key {
170+
isUIKitBased() ? .init("UIAccessibilityTokenAttachment") : .init("AXAttachment")
171+
}
172+
173+
package static var coreAXLink: NSAttributedString.Key {
174+
isUIKitBased() ? .init("UIAccessibilityTokenLink") : .init("AXLink")
175+
}
176+
177+
package static var coreAXTextHeadingLevel: NSAttributedString.Key {
178+
isUIKitBased() ? .init("UIAccessibilityTextAttributeHeadingLevel") : .init("AXHeadingLevel")
179+
}
180+
181+
package static var coreAXSpeechPitch: NSAttributedString.Key {
182+
isUIKitBased() ? .init("UIAccessibilitySpeechAttributePitch") : .init("AXPitch")
183+
}
184+
185+
package static var coreAXSpeechPunctuation: NSAttributedString.Key {
186+
isUIKitBased() ? .init("UIAccessibilitySpeechAttributePunctuation") : .init("AXPunctuation")
187+
}
188+
189+
package static var coreAXSpeechSpellOut: NSAttributedString.Key {
190+
isUIKitBased() ? .init("UIAccessibilitySpeechAttributeSpellOut") : .init("AXSpellOut")
191+
}
192+
193+
package static var coreAXSpeechIPANotation: NSAttributedString.Key {
194+
isUIKitBased() ? .init("UIAccessibilitySpeechAttributeIPANotation") : .init("AXIPANotation")
195+
}
196+
197+
package static var coreAXTextualContext: NSAttributedString.Key {
198+
isUIKitBased() ? .init("UIAccessibilityTextAttributeContext") : .init("AXTextualContext")
199+
}
200+
201+
package static var coreAXSpeechAnnouncementPriority: NSAttributedString.Key {
202+
isUIKitBased() ? .init("UIAccessibilitySpeechAttributeAnnouncementPriority") : .init("AXAnnouncementPriority")
203+
}
204+
205+
package static var coreAXSpeechLanguage: NSAttributedString.Key {
206+
isUIKitBased() ? .init("UIAccessibilitySpeechAttributeLanguage") : .init("AXLanguage")
207+
}
208+
209+
package static let coreAXLabel: NSAttributedString.Key = .init("OpenSwiftUI.accessibilityLabel")
210+
211+
package static var coreAXDurationTimeMMSS: NSAttributedString.Key {
212+
isUIKitBased() ? .init("UIAccessibilityTokenDurationTimeMMSS") : .init("AXTokenDurationTimeMMSS")
213+
}
214+
}
215+
216+
extension NSAttributedString.Key {
217+
package var isAccessibilityAttribute: Bool {
218+
rawValue.hasPrefix(isUIKitBased() ? "UIAccessibility" : "AX")
219+
}
220+
}

0 commit comments

Comments
 (0)