| 1 | #if os(macOS) |
| 2 | import AppKit |
| 3 | import SwiftUI |
| 4 | |
| 5 | public struct SheetsEditorCommands: Commands { |
| 6 | @FocusedValue(\.sheetEditor) private var editor |
| 7 | public init() {} |
| 8 | private var available: Bool { editor?.model.page != nil } |
| 9 | private var writable: Bool { |
| 10 | guard let model = editor?.model else { return false } |
| 11 | return model.mayWrite && !model.busy && !model.hasUnconfirmedChanges |
| 12 | } |
| 13 | private func route(_ selector: Selector, grid: (SheetModel) -> Void) { |
| 14 | if NSApp.keyWindow?.firstResponder is NSTextView { |
| 15 | NSApp.sendAction(selector, to: nil, from: nil) |
| 16 | } else if let model = editor?.model, model.gridFocused { grid(model) } |
| 17 | else { NSApp.sendAction(selector, to: nil, from: nil) } |
| 18 | } |
| 19 | public var body: some Commands { |
| 20 | |
| 21 | CommandGroup(replacing: .undoRedo) { |
| 22 | Button(sheetsMacString("Undo")) { route(Selector(("undo:"))) { model in Task { await model.command("/undo") } } }.keyboardShortcut("z") |
| 23 | Button(sheetsMacString("Redo")) { NSApp.sendAction(Selector(("redo:")), to: nil, from: nil) }.keyboardShortcut("z", modifiers: [.command, .shift]) |
| 24 | } |
| 25 | CommandGroup(replacing: .pasteboard) { |
| 26 | Button(sheetsMacString("Cut")) { route(#selector(NSText.cut(_:))) { model in |
| 27 | guard model.mayWrite, !model.busy, !model.hasUnconfirmedChanges else { return } |
| 28 | model.copy(); Task { await model.clear() } |
| 29 | } }.keyboardShortcut("x") |
| 30 | Button(sheetsMacString("Copy")) { route(#selector(NSText.copy(_:))) { $0.copy() } }.keyboardShortcut("c") |
| 31 | Button(sheetsMacString("Paste")) { route(#selector(NSText.paste(_:))) { model in Task { await model.paste() } } }.keyboardShortcut("v") |
| 32 | Button(sheetsMacString("Select all")) { route(#selector(NSText.selectAll(_:))) { model in |
| 33 | model.rangeText = "A1:" + Coordinate(r: model.rows - 1, c: model.cols - 1).ref |
| 34 | model.selectRange() |
| 35 | } }.keyboardShortcut("a") |
| 36 | } |
| 37 | CommandGroup(after: .pasteboard) { |
| 38 | Button(sheetsMacString("Clear contents")) { if let model = editor?.model { Task { await model.clear() } } }.disabled(!writable) |
| 39 | Button(sheetsMacString("Fill down")) { if let model = editor?.model { Task { await model.fill(down: true) } } }.keyboardShortcut("d").disabled(!writable) |
| 40 | Button(sheetsMacString("Fill right")) { if let model = editor?.model { Task { await model.fill(down: false) } } }.disabled(!writable) |
| 41 | } |
| 42 | CommandMenu(sheetsMacString("Format")) { |
| 43 | if let model = editor?.model { |
| 44 | SheetFormatMenu(model: model) |
| 45 | Button(sheetsMacString("Row and column sizes…")) { editor?.panel.wrappedValue = "Sizes" }.disabled(!writable) |
| 46 | Button(sheetsMacString("Fit column to contents")) { Task { await model.fitColumn(model.selection.start.c) } }.disabled(!writable) |
| 47 | } |
| 48 | } |
| 49 | CommandMenu(sheetsMacString("Insert")) { |
| 50 | ForEach(["SUM", "AVERAGE", "COUNT", "MIN", "MAX"], id: \.self) { name in |
| 51 | Button(name) { if let model = editor?.model { Task { await model.insertFunction(name) } } }.disabled(!writable) |
| 52 | } |
| 53 | } |
| 54 | CommandMenu(sheetsMacString("Sheet")) { |
| 55 | Button(sheetsMacString("Sort and filter…")) { editor?.panel.wrappedValue = "Data" }.disabled(!available) |
| 56 | Button(sheetsMacString("Comments…")) { editor?.panel.wrappedValue = "Comments" }.disabled(!available) |
| 57 | Button(sheetsMacString("Refresh")) { if let model = editor?.model { Task { await model.refresh() } } }.disabled(!available) |
| 58 | } |
| 59 | } |
| 60 | } |
| 61 | #endif |
| 62 | |