-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathAttributes.swift
More file actions
179 lines (167 loc) · 6.33 KB
/
Attributes.swift
File metadata and controls
179 lines (167 loc) · 6.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
//
// Attributes.swift
//
//
// Created by Juul Spee on 08/07/2022.
import AppKit
/// Attributes for `NSAttributedString`, wrapped in a struct for convenience.
public struct Attributes {
public init(
family: String = "Helvetica",
size: CGFloat = 14,
bold: Bool = false,
italic: Bool = false,
monospace: Bool = false,
textColor: NSColor = .textColor,
backgroundColor: NSColor? = nil,
kern: CGFloat = 0,
firstlineHeadIndent: CGFloat = 0,
headIndent: CGFloat = 0,
tailIndent: CGFloat = 0,
tabStops: [NSTextTab] = (1..<10).map {
NSTextTab(textAlignment: .left,
location: CGFloat($0) * 2 * 16)
},
alignment: NSTextAlignment = .left,
lineHeightMultiple: CGFloat = 1.3,
minimumLineHeight: CGFloat? = nil,
maximumLineHeight: CGFloat? = nil,
paragraphSpacing: CGFloat = 14,
paragraphSpacingBefore: CGFloat = 0,
link: URL? = nil,
cursor: NSCursor? = nil,
underlineColor: NSColor? = nil,
underlineStyle: NSUnderlineStyle? = nil,
suppressHeader: Bool = false) {
self.family = family
self.size = size
self.bold = bold
self.italic = italic
self.monospace = monospace
self.textColor = textColor
self.backgroundColor = backgroundColor
self.kern = kern
self.firstlineHeadIndent = firstlineHeadIndent
self.headIndent = headIndent
self.tailIndent = tailIndent
self.tabStops = tabStops
self.alignment = alignment
self.lineHeightMultiple = lineHeightMultiple
self.minimumLineHeight = minimumLineHeight
self.maximumLineHeight = maximumLineHeight
self.paragraphSpacing = paragraphSpacing
self.paragraphSpacingBefore = paragraphSpacingBefore
self.link = link
self.cursor = cursor
self.underlineColor = underlineColor
self.underlineStyle = underlineStyle
self.suppressHeader = suppressHeader
}
public var family: String
public var size: CGFloat
public var bold: Bool = false
public var italic: Bool = false
public var monospace: Bool = false
public var textColor: NSColor = .textColor
public var backgroundColor: NSColor? = nil
public var kern: CGFloat = 0
public var firstlineHeadIndent: CGFloat = 0
public var headIndent: CGFloat = 0
public var tailIndent: CGFloat = 0
public var tabStops: [NSTextTab] = (1..<10).map { NSTextTab(textAlignment: .left, location: CGFloat($0) * 2 * 16) }
public var alignment: NSTextAlignment = .left
public var lineHeightMultiple: CGFloat = 1.3
public var minimumLineHeight: CGFloat? = nil
public var maximumLineHeight: CGFloat? = nil
public var paragraphSpacing: CGFloat = 0
public var paragraphSpacingBefore: CGFloat = 0
public var link: URL? = nil
public var cursor: NSCursor? = nil
public var underlineColor: NSColor?
public var underlineStyle: NSUnderlineStyle?
// public var suppressHeading: Bool?
public var customAttributes: [String: Any] = [:]
}
extension Attributes {
public mutating func setIndent(_ value: CGFloat) {
firstlineHeadIndent = value
headIndent = value
}
public var computedFont: NSFont {
font
}
fileprivate var font: NSFont {
var fontDescriptor = NSFontDescriptor(name: family, size: size)
var traits = NSFontDescriptor.SymbolicTraits()
if monospace { traits.formUnion(.monoSpace) }
if bold { traits.formUnion(.bold) }
if italic { traits.formUnion(.italic )}
if !traits.isEmpty { fontDescriptor = fontDescriptor.withSymbolicTraits(traits) }
guard let font = NSFont(descriptor: fontDescriptor, size: size) else {
print("Font creation with traits failed: \(traits). Fallback to named font.")
return NSFont(name: family, size: size) ?? .systemFont(ofSize: size)
}
return font
}
public var paragraphStyle: NSParagraphStyle {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.firstLineHeadIndent = firstlineHeadIndent
paragraphStyle.headIndent = headIndent
paragraphStyle.tailIndent = tailIndent
paragraphStyle.tabStops = tabStops
paragraphStyle.alignment = alignment
paragraphStyle.lineHeightMultiple = lineHeightMultiple
paragraphStyle.minimumLineHeight = minimumLineHeight ?? 0
paragraphStyle.maximumLineHeight = maximumLineHeight ?? 0 // 0 is the default
paragraphStyle.paragraphSpacing = paragraphSpacing
paragraphStyle.paragraphSpacingBefore = paragraphSpacingBefore
return paragraphStyle
}
fileprivate var attachmentParagraphStyle: NSParagraphStyle {
let ps = NSMutableParagraphStyle()
ps.firstLineHeadIndent = firstlineHeadIndent
ps.headIndent = headIndent
ps.tailIndent = tailIndent
ps.alignment = alignment
ps.paragraphSpacing = paragraphSpacing
ps.paragraphSpacingBefore = paragraphSpacingBefore
return ps
}
/// Outputs a dictionary of the attributes that can be passed into an attributed string.
public var atts: [NSAttributedString.Key:Any] {
var result: [NSAttributedString.Key: Any] = [
.font: font,
.foregroundColor: textColor,
.kern: kern,
.paragraphStyle: paragraphStyle,
]
if let bg = backgroundColor {
result[.backgroundColor] = bg
}
if let url = link {
result[.link] = url
}
if let cursor {
result[.cursor] = cursor
}
if let underlineColor {
result[.underlineColor] = underlineColor
}
if let underlineStyle {
result[.underlineStyle] = underlineStyle.rawValue
}
result[.suppressHeader] = suppressHeader
for (key, value) in customAttributes {
result[NSAttributedString.Key(key)] = value
}
return result
}
public var attachmentAtts: [NSAttributedString.Key: Any] {
[.paragraphStyle: attachmentParagraphStyle]
}
}
extension NSAttributedString {
public convenience init(string: String, attributes: Attributes) {
self.init(string: string, attributes: attributes.atts)
}
}