krz/orgstar

A native macOS editor for org-mode files. editor org-mode swift

Sources/OrgEditorAppKit/Theme.swift

0271e942ff3d2f72a407b3d9a6463fab8e4b48cf
orgstar/Sources/OrgEditorAppKit/Theme.swift history · blame · raw

74 lines · 3592 bytes

 1#if os(macOS)
 2import AppKit
 3import OrgPresentation
 4
 5/// Attributes for each style role. A fixed theme for now; the app's theme settings replace it.
 6@MainActor
 7struct Theme {
 8    let font = NSFont.monospacedSystemFont(ofSize: 13, weight: .regular)
 9
10    var base: [NSAttributedString.Key: Any] {
11        [.font: font, .foregroundColor: NSColor.textColor]
12    }
13
14    /// Width of one column of the monospaced font.
15    var column: CGFloat {
16        ("0" as NSString).size(withAttributes: [.font: font]).width
17    }
18
19    func paragraphStyle(firstLine: Int, wrapped: Int) -> NSParagraphStyle {
20        let style = NSMutableParagraphStyle()
21        style.firstLineHeadIndent = CGFloat(firstLine) * column
22        style.headIndent = CGFloat(wrapped) * column
23        return style
24    }
25
26    func apply(_ role: StyleRole, to storage: NSTextStorage, range: NSRange) {
27        switch role {
28        case .heading(let level):
29            let size = font.pointSize + CGFloat(max(0, 4 - level))
30            storage.addAttribute(.font, value: NSFont.monospacedSystemFont(ofSize: size, weight: .semibold), range: range)
31        case .leadingStars:
32            storage.addAttribute(.foregroundColor, value: NSColor.clear, range: range)
33        case .todo:
34            storage.addAttributes([.foregroundColor: NSColor.systemRed, .font: bold(storage, range)], range: range)
35        case .done:
36            storage.addAttributes([.foregroundColor: NSColor.systemGreen, .font: bold(storage, range)], range: range)
37        case .priority:
38            storage.addAttribute(.foregroundColor, value: NSColor.systemOrange, range: range)
39        case .tags, .comment, .keyword, .metadata:
40            storage.addAttribute(.foregroundColor, value: NSColor.secondaryLabelColor, range: range)
41        case .bold:
42            storage.addAttribute(.font, value: bold(storage, range), range: range)
43        case .italic:
44            storage.addAttribute(.font, value: NSFontManager.shared.convert(currentFont(storage, range), toHaveTrait: .italicFontMask), range: range)
45        case .underline:
46            storage.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: range)
47        case .strikeThrough:
48            storage.addAttribute(.strikethroughStyle, value: NSUnderlineStyle.single.rawValue, range: range)
49        case .verbatim, .code, .inlineSource:
50            storage.addAttribute(.backgroundColor, value: NSColor.quaternarySystemFill, range: range)
51        case .markup, .linkMarkup, .blockDelimiter:
52            storage.addAttribute(.foregroundColor, value: NSColor.tertiaryLabelColor, range: range)
53        case .link:
54            storage.addAttributes([.foregroundColor: NSColor.linkColor, .underlineStyle: NSUnderlineStyle.single.rawValue], range: range)
55        case .timestamp:
56            storage.addAttribute(.foregroundColor, value: NSColor.systemPurple, range: range)
57        case .footnote, .cookie, .target, .macro, .latex:
58            storage.addAttribute(.foregroundColor, value: NSColor.systemTeal, range: range)
59        case .block:
60            storage.addAttribute(.backgroundColor, value: NSColor.quaternarySystemFill, range: range)
61        case .table, .rule:
62            break
63        }
64    }
65
66    private func currentFont(_ storage: NSTextStorage, _ range: NSRange) -> NSFont {
67        storage.attribute(.font, at: range.location, effectiveRange: nil) as? NSFont ?? font
68    }
69
70    private func bold(_ storage: NSTextStorage, _ range: NSRange) -> NSFont {
71        NSFontManager.shared.convert(currentFont(storage, range), toHaveTrait: .boldFontMask)
72    }
73}
74#endif