139 lines
4.7 KiB
Swift
139 lines
4.7 KiB
Swift
import AppKit
|
|
import SwiftUI
|
|
import ShotdeckCore
|
|
|
|
struct SettingsView: View {
|
|
@Environment(AppModel.self) private var model
|
|
|
|
private let labelWidth: CGFloat = 104
|
|
|
|
var body: some View {
|
|
Grid(alignment: .leading, horizontalSpacing: 12, verticalSpacing: 10) {
|
|
GridRow {
|
|
Text("Hotkey")
|
|
.font(.headline)
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
.gridCellColumns(2)
|
|
}
|
|
|
|
GridRow(alignment: .firstTextBaseline) {
|
|
fieldLabel("Capture")
|
|
Text("⌥⇧2 — fixed in this version")
|
|
.foregroundStyle(.secondary)
|
|
.lineLimit(1)
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
.frame(minHeight: 22, alignment: .leading)
|
|
}
|
|
|
|
GridRow {
|
|
Text("Folders")
|
|
.font(.headline)
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
.gridCellColumns(2)
|
|
.padding(.top, 6)
|
|
}
|
|
|
|
GridRow(alignment: .center) {
|
|
fieldLabel("Watch folder")
|
|
folderValue(path: model.watchFolderURL.path) {
|
|
model.chooseWatchFolder()
|
|
}
|
|
}
|
|
|
|
GridRow(alignment: .center) {
|
|
fieldLabel("Output folder")
|
|
folderValue(path: model.outboxURL.path) {
|
|
model.chooseOutboxFolder()
|
|
}
|
|
}
|
|
|
|
GridRow {
|
|
Button("Reveal spool folder") { model.openSpoolFolder() }
|
|
.gridCellColumns(2)
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
.padding(.top, 4)
|
|
}
|
|
}
|
|
.padding(16)
|
|
.frame(minWidth: 320, idealWidth: 360, maxWidth: 360, alignment: .leading)
|
|
.controlSize(.small)
|
|
}
|
|
|
|
private func fieldLabel(_ title: String) -> some View {
|
|
Text(title)
|
|
.lineLimit(1)
|
|
.frame(width: labelWidth, alignment: .trailing)
|
|
.gridColumnAlignment(.trailing)
|
|
.frame(minHeight: 22, alignment: .trailing)
|
|
}
|
|
|
|
private func folderValue(path: String, choose: @escaping () -> Void) -> some View {
|
|
HStack(spacing: 8) {
|
|
Text(path)
|
|
.lineLimit(1)
|
|
.truncationMode(.middle)
|
|
.foregroundStyle(.secondary)
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
Button("Choose…") { choose() }
|
|
}
|
|
.frame(minHeight: 22)
|
|
}
|
|
}
|
|
|
|
extension AppModel: SettingsWindowPresenting {
|
|
private static var settingsWindowController: NSWindowController?
|
|
|
|
public func presentSettingsWindow() {
|
|
if let existing = Self.settingsWindowController {
|
|
existing.window?.makeKeyAndOrderFront(nil)
|
|
NSApp.activate()
|
|
return
|
|
}
|
|
let hosting = NSHostingController(rootView: SettingsView().environment(self))
|
|
let window = NSWindow(contentViewController: hosting)
|
|
window.title = "Redline Settings"
|
|
window.styleMask = [.titled, .closable]
|
|
window.isReleasedWhenClosed = false
|
|
window.center()
|
|
let controller = NSWindowController(window: window)
|
|
Self.settingsWindowController = controller
|
|
controller.showWindow(nil)
|
|
NSApp.activate()
|
|
}
|
|
|
|
func chooseOutboxFolder() {
|
|
guard let url = chooseDirectory(startingAt: outboxURL) else { return }
|
|
FolderSettings.setOutbox(url)
|
|
setFolderURLs(outbox: url, watch: watchFolderURL)
|
|
setStatus("Output folder set to \(url.lastPathComponent).")
|
|
}
|
|
|
|
func chooseWatchFolder() {
|
|
guard let url = chooseDirectory(startingAt: watchFolderURL) else { return }
|
|
FolderSettings.setWatchFolder(url)
|
|
setFolderURLs(outbox: outboxURL, watch: url)
|
|
Task {
|
|
do {
|
|
try await watcher.updateWatchFolder(url)
|
|
setStatus("Watch folder set to \(url.lastPathComponent).")
|
|
} catch {
|
|
setStatus(
|
|
(error as? ShotdeckError)?.errorDescription ?? "Could not switch the watch folder."
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
private func chooseDirectory(startingAt directory: URL) -> URL? {
|
|
let panel = NSOpenPanel()
|
|
panel.canChooseDirectories = true
|
|
panel.canChooseFiles = false
|
|
panel.allowsMultipleSelection = false
|
|
panel.canCreateDirectories = true
|
|
panel.prompt = "Choose"
|
|
panel.directoryURL = directory
|
|
guard panel.runModal() == .OK else { return nil }
|
|
return panel.url
|
|
}
|
|
}
|