Fix segmented Text

This commit is contained in:
Carson Katri 2020-08-03 18:27:27 -04:00
parent cb6359431d
commit c0a580653a
2 changed files with 50 additions and 23 deletions

View File

@ -33,14 +33,11 @@ public struct Text: View {
let storage: _Storage let storage: _Storage
let modifiers: [_Modifier] let modifiers: [_Modifier]
@Environment(\.self) public var environment @Environment(\.self) var environment
@Environment(\.font) var font
@Environment(\.foregroundColor) var foregroundColor
@Environment(\.redactionReasons) var redactionReasons
public enum _Storage { public enum _Storage {
case verbatim(String) case verbatim(String)
case segmentedText([Text]) case segmentedText([(_Storage, [_Modifier])])
} }
public enum _Modifier: Equatable { public enum _Modifier: Equatable {
@ -59,8 +56,7 @@ public struct Text: View {
init(storage: _Storage, modifiers: [_Modifier] = []) { init(storage: _Storage, modifiers: [_Modifier] = []) {
if case let .segmentedText(segments) = storage { if case let .segmentedText(segments) = storage {
self.storage = .segmentedText(segments.map { self.storage = .segmentedText(segments.map {
Self(storage: $0.storage, ($0.0, modifiers + $0.1)
modifiers: modifiers + $0.modifiers)
}) })
} else { } else {
self.storage = storage self.storage = storage
@ -81,6 +77,19 @@ public struct Text: View {
} }
} }
extension Text._Storage {
public var rawText: String {
switch self {
case let .segmentedText(segments):
return segments
.map { $0.0.rawText }
.reduce("", +)
case let .verbatim(text):
return text
}
}
}
/// This is a helper class that works around absence of "package private" access control in Swift /// This is a helper class that works around absence of "package private" access control in Swift
public struct _TextProxy { public struct _TextProxy {
public let subject: Text public let subject: Text
@ -89,24 +98,17 @@ public struct _TextProxy {
public var storage: Text._Storage { subject.storage } public var storage: Text._Storage { subject.storage }
public var rawText: String { public var rawText: String {
switch subject.storage { subject.storage.rawText
case let .segmentedText(segments):
return segments
.map { Self($0).rawText }
.reduce("", +)
case let .verbatim(text):
return text
}
} }
public var modifiers: [Text._Modifier] { public var modifiers: [Text._Modifier] {
[ [
.font(subject.font), .font(subject.environment.font),
.color(subject.foregroundColor), .color(subject.environment.foregroundColor),
] + subject.modifiers ] + subject.modifiers
} }
public var redactionReasons: RedactionReasons { subject.redactionReasons } public var environment: EnvironmentValues { subject.environment }
} }
public extension Text { public extension Text {
@ -149,6 +151,9 @@ public extension Text {
extension Text { extension Text {
public static func _concatenating(lhs: Self, rhs: Self) -> Self { public static func _concatenating(lhs: Self, rhs: Self) -> Self {
.init(storage: .segmentedText([lhs, rhs])) .init(storage: .segmentedText([
(lhs.storage, lhs.modifiers),
(rhs.storage, rhs.modifiers),
]))
} }
} }

View File

@ -89,14 +89,28 @@ extension Font: StylesConvertible {
} }
} }
private struct TextSpan: AnyHTML {
let content: String
let attributes: [String: String]
var innerHTML: String? { content }
var tag: String { "span" }
}
extension Text: AnyHTML { extension Text: AnyHTML {
public var innerHTML: String? { public var innerHTML: String? {
switch _TextProxy(self).storage { let proxy = _TextProxy(self)
switch proxy.storage {
case let .verbatim(text): case let .verbatim(text):
return text return text
case let .segmentedText(segments): case let .segmentedText(segments):
return segments return segments
.map(\.outerHTML) .map {
TextSpan(content: $0.0.rawText,
attributes: Self.attributes(from: $0.1,
environment: proxy.environment))
.outerHTML
}
.reduce("", +) .reduce("", +)
} }
} }
@ -104,7 +118,15 @@ extension Text: AnyHTML {
public var tag: String { "span" } public var tag: String { "span" }
public var attributes: [String: String] { public var attributes: [String: String] {
let proxy = _TextProxy(self) let proxy = _TextProxy(self)
let isRedacted = proxy.redactionReasons.contains(.placeholder) return Self.attributes(from: proxy.modifiers,
environment: proxy.environment)
}
}
extension Text {
static func attributes(from modifiers: [_Modifier],
environment: EnvironmentValues) -> [String: String] {
let isRedacted = environment.redactionReasons.contains(.placeholder)
var font: Font? var font: Font?
var color: Color? var color: Color?
@ -114,7 +136,7 @@ extension Text: AnyHTML {
var baseline: CGFloat? var baseline: CGFloat?
var strikethrough: (Bool, Color?)? var strikethrough: (Bool, Color?)?
var underline: (Bool, Color?)? var underline: (Bool, Color?)?
for modifier in proxy.modifiers { for modifier in modifiers {
switch modifier { switch modifier {
case let .color(_color): case let .color(_color):
color = _color color = _color