2020-06-04 09:46:06 -04:00
|
|
|
import SwiftUI
|
|
|
|
|
|
|
|
extension NSMutableAttributedString {
|
2020-11-18 08:18:26 -05:00
|
|
|
func with(font: NSFont) -> NSMutableAttributedString {
|
2020-06-04 09:46:06 -04:00
|
|
|
enumerateAttribute(NSAttributedString.Key.font, in: NSMakeRange(0, length), options: .longestEffectiveRangeNotRequired, using: { (value, range, stop) in
|
2020-11-18 08:18:26 -05:00
|
|
|
if let originalFont = value as? NSFont, let newFont = applyTraitsFromFont(originalFont, to: font) {
|
2020-06-04 09:46:06 -04:00
|
|
|
addAttribute(NSAttributedString.Key.font, value: newFont, range: range)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
return self
|
|
|
|
}
|
|
|
|
|
2020-11-18 08:18:26 -05:00
|
|
|
func applyTraitsFromFont(_ originalFont: NSFont, to newFont: NSFont) -> NSFont? {
|
2020-06-04 09:46:06 -04:00
|
|
|
let originalTrait = originalFont.fontDescriptor.symbolicTraits
|
|
|
|
|
2020-11-18 08:18:26 -05:00
|
|
|
if(originalTrait.contains(NSFontDescriptor.SymbolicTraits.bold)) {
|
2020-06-04 09:46:06 -04:00
|
|
|
var traits = newFont.fontDescriptor.symbolicTraits
|
2020-11-18 08:18:26 -05:00
|
|
|
traits.insert(.bold)
|
2020-06-04 09:46:06 -04:00
|
|
|
|
2020-11-18 08:18:26 -05:00
|
|
|
let fontDescriptor = newFont.fontDescriptor.withSymbolicTraits(traits)
|
|
|
|
return NSFont.init(descriptor: fontDescriptor, size: 0)
|
2020-06-04 09:46:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return newFont
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-18 08:18:26 -05:00
|
|
|
final class AttributedTextComponent: NSViewRepresentable {
|
2020-06-04 09:46:06 -04:00
|
|
|
let string: NSMutableAttributedString
|
|
|
|
|
|
|
|
init(_ string: NSMutableAttributedString) {
|
|
|
|
self.string = string
|
|
|
|
}
|
|
|
|
|
2020-11-18 08:18:26 -05:00
|
|
|
public func makeNSView(context: NSViewRepresentableContext<AttributedTextComponent>) -> NSTextField {
|
|
|
|
NSTextField()
|
2020-06-04 09:46:06 -04:00
|
|
|
}
|
|
|
|
|
2020-11-18 08:18:26 -05:00
|
|
|
func updateNSView(_ uiView: NSTextField, context: Context) {
|
2020-06-04 09:46:06 -04:00
|
|
|
uiView.backgroundColor = .clear
|
2020-11-18 08:18:26 -05:00
|
|
|
//uiView.numberOfLines = 0
|
2020-06-04 09:46:06 -04:00
|
|
|
uiView.lineBreakMode = .byWordWrapping
|
|
|
|
uiView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
2020-11-18 08:18:26 -05:00
|
|
|
uiView.attributedStringValue = string
|
2020-06-04 09:46:06 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct SizeKey: PreferenceKey {
|
|
|
|
static var defaultValue: CGSize = .zero
|
|
|
|
|
|
|
|
static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
|
|
|
|
value = nextValue()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct AttributedText: View {
|
|
|
|
let html: NSMutableAttributedString
|
|
|
|
let component: AttributedTextComponent
|
|
|
|
|
|
|
|
@State var height: CGFloat = 0.0
|
|
|
|
@State var lastSize: CGSize = .zero
|
|
|
|
|
|
|
|
init(_ html: NSMutableAttributedString) {
|
2020-11-18 08:18:26 -05:00
|
|
|
self.html = html.with(font: NSFont.systemFont(ofSize: 15))
|
2020-06-04 09:46:06 -04:00
|
|
|
self.component = AttributedTextComponent(html)
|
|
|
|
}
|
|
|
|
|
|
|
|
func calculateHeight(size: CGSize) {
|
2020-11-18 08:18:26 -05:00
|
|
|
let label = NSTextField(frame: NSRect(x: 0, y: 0, width: size.width, height: .greatestFiniteMagnitude))
|
2020-06-04 09:46:06 -04:00
|
|
|
|
2020-11-18 08:18:26 -05:00
|
|
|
//label.numberOfLines = 0
|
2020-06-04 09:46:06 -04:00
|
|
|
label.lineBreakMode = .byWordWrapping
|
|
|
|
|
2020-11-18 08:18:26 -05:00
|
|
|
//label.attributedText = self.html
|
|
|
|
label.attributedStringValue = self.html
|
2020-06-04 09:46:06 -04:00
|
|
|
|
|
|
|
label.sizeToFit()
|
|
|
|
|
|
|
|
self.height = label.frame.height
|
|
|
|
self.lastSize = size
|
|
|
|
}
|
|
|
|
|
|
|
|
var body: some View {
|
|
|
|
ZStack {
|
|
|
|
GeometryReader { geometry in
|
|
|
|
Rectangle().fill(Color.clear).preference(key: SizeKey.self, value: geometry.size)
|
|
|
|
}.onPreferenceChange(SizeKey.self, perform: { size in
|
|
|
|
self.calculateHeight(size: size)
|
|
|
|
})
|
|
|
|
|
|
|
|
component
|
|
|
|
}
|
|
|
|
.frame(minWidth: 0.0, maxWidth: .infinity, minHeight: self.height, maxHeight: self.height)
|
|
|
|
.onAppear {
|
|
|
|
if self.lastSize != .zero {
|
|
|
|
self.calculateHeight(size: self.lastSize)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|