| 1 | import Foundation |
| 2 | |
| 3 | /// Only read formatting from a selection whose projection has caught up with |
| 4 | /// native text input. While it catches up, the toolbar keeps its last valid state. |
| 5 | struct DocumentToolbarSelection: Equatable { |
| 6 | var marks: Set<String> = [] |
| 7 | var blocks: Set<String> = [] |
| 8 | var style = "paragraph" |
| 9 | var font = "sans" |
| 10 | var size = 12 |
| 11 | var alignment = "left" |
| 12 | var canUndo = false |
| 13 | var canRedo = false |
| 14 | var hasSelection = false |
| 15 | |
| 16 | var styleLabel: String { |
| 17 | if style.hasPrefix("heading"), let level = Int(style.dropFirst(7)) { |
| 18 | return String(format: docsMacString("Heading %d"), level) |
| 19 | } |
| 20 | return String(localized: "docs.editor.toolbar.block.paragraph", bundle: DocsResources.bundle) |
| 21 | } |
| 22 | init() {} |
| 23 | |
| 24 | @MainActor init?(commands: DocumentCommands) { |
| 25 | guard commands.isEnabled else { return nil } |
| 26 | marks = Set(["strong", "em", "code"].filter { commands.hasMark($0) }) |
| 27 | blocks = Set(["bullet_list", "ordered_list", "blockquote"].filter { commands.hasBlock($0) }) |
| 28 | if let block = commands.selectedBlock { |
| 29 | style = block.type == "heading" ? "heading" + (block.attrs?["level"] ?? "1") : "paragraph" |
| 30 | alignment = block.attrs?["align"] ?? "left" |
| 31 | } |
| 32 | font = ["serif", "mono"].first { commands.hasMark("font_" + $0) } ?? "sans" |
| 33 | size = [8,9,10,11,12,14,18,24,36,48,72].first { commands.hasMark("size_\($0)") } ?? (commands.hasMark("size_sm") ? 10 : commands.hasMark("size_lg") ? 14 : commands.hasMark("size_xl") ? 18 : 12) |
| 34 | canUndo = commands.model.projection?.canUndo == true |
| 35 | canRedo = commands.model.projection?.canRedo == true |
| 36 | hasSelection = commands.model.selection.length > 0 |
| 37 | } |
| 38 | } |
| 39 | |