Sources/Orgstar/BlockEditorView.swift
65 lines · 2456 bytes
5 symbols in this file
1import AppKit
2import OrgCore
3import OrgDocument
4import OrgEditorAppKit
5import SwiftUI
6
7/// `org-edit-special` for a block: its text in an editor of its own, highlighted as its
8/// language. ⌘Return or C-c ' puts it back; Escape or C-c C-k leaves it.
9struct BlockEditorView: View {
10 let edit: BlockEdit
11 let finish: (String?) -> Void
12 @State private var box = EditorBox()
13
14 var body: some View {
15 VStack(alignment: .leading, spacing: 8) {
16 Text(edit.title)
17 .font(.headline)
18 CodeEditor(text: edit.content, language: edit.language, box: box) { save in finish(save ? box.text : nil) }
19 .frame(minWidth: 560, minHeight: 320)
20 .border(.separator)
21 HStack {
22 Text("C-c ' or ⌘Return saves, Escape or C-c C-k leaves").font(.caption).foregroundStyle(.secondary)
23 Spacer()
24 Button("Cancel", role: .cancel) { finish(nil) }
25 .keyboardShortcut(.cancelAction)
26 Button(edit.kind == "field" ? "Save Field" : "Save Block") { finish(box.text) }
27 .keyboardShortcut(.return, modifiers: .command)
28 }
29 }
30 .padding(16)
31 }
32}
33
34/// Holds the editor so the view can read its text when saving.
35@MainActor
36final class EditorBox {
37 var editor: OrgEditor?
38 var text: String { editor?.document.text ?? "" }
39}
40
41/// An `OrgEditor` in plain mode for the block's language.
42struct CodeEditor: NSViewRepresentable {
43 let text: String
44 let language: String?
45 let box: EditorBox
46 let done: (Bool) -> Void
47
48 func makeNSView(context: Context) -> NSScrollView {
49 let editor = OrgEditor(document: DocumentState(bytes: [UInt8](text.utf8)), highlighter: EditorView.highlighter, mode: .plain(language: language))
50 editor.keymap = Keymap([
51 KeyBinding(keys: KeySequence.parse("C-c '")!, command: "app.block.save"),
52 KeyBinding(keys: KeySequence.parse("C-c C-k")!, command: "app.block.cancel"),
53 ])
54 editor.onAppCommand = { id in
55 if id == "app.block.save" { done(true) }
56 if id == "app.block.cancel" { done(false) }
57 }
58 box.editor = editor
59 let scroll = editor.makeScrollView()
60 DispatchQueue.main.async { scroll.window?.makeFirstResponder(editor.textView) }
61 return scroll
62 }
63
64 func updateNSView(_ view: NSScrollView, context: Context) {}
65}