167 lines
6.5 KiB
Swift
167 lines
6.5 KiB
Swift
import AppKit
|
|
import SwiftUI
|
|
|
|
struct NewTaskView: View {
|
|
@Binding var isPresented: Bool
|
|
@State private var path = ""
|
|
@State private var errorMessage: String?
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading, spacing: 18) {
|
|
HStack(spacing: 14) {
|
|
Image(systemName: "plus.square.dashed")
|
|
.font(.system(size: 32))
|
|
.foregroundStyle(.tint)
|
|
VStack(alignment: .leading, spacing: 3) {
|
|
Text("Run new task").font(.title2.weight(.semibold))
|
|
Text("Open an application, document, or executable.").foregroundStyle(.secondary)
|
|
}
|
|
}
|
|
|
|
HStack {
|
|
TextField("Application or executable path", text: $path)
|
|
.textFieldStyle(.roundedBorder)
|
|
.onSubmit { launch() }
|
|
Button("Browse…") { browse() }
|
|
}
|
|
|
|
if let errorMessage {
|
|
Label(errorMessage, systemImage: "exclamationmark.triangle.fill")
|
|
.font(.callout)
|
|
.foregroundStyle(.red)
|
|
}
|
|
|
|
HStack {
|
|
Text("Tip: drag an app or file from Finder into the path field.")
|
|
.font(.caption)
|
|
.foregroundStyle(.secondary)
|
|
Spacer()
|
|
Button("Cancel") { isPresented = false }
|
|
.keyboardShortcut(.cancelAction)
|
|
Button("Run") { launch() }
|
|
.keyboardShortcut(.defaultAction)
|
|
.buttonStyle(.borderedProminent)
|
|
.disabled(path.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty)
|
|
}
|
|
}
|
|
.padding(24)
|
|
.frame(width: 560)
|
|
}
|
|
|
|
private func browse() {
|
|
let panel = NSOpenPanel()
|
|
panel.title = "Choose an application, executable, or document"
|
|
panel.canChooseDirectories = false
|
|
panel.canChooseFiles = true
|
|
panel.allowsMultipleSelection = false
|
|
if panel.runModal() == .OK, let url = panel.url { path = url.path }
|
|
}
|
|
|
|
private func launch() {
|
|
let cleaned = (path as NSString).expandingTildeInPath.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
let url = URL(fileURLWithPath: cleaned)
|
|
guard FileManager.default.fileExists(atPath: cleaned) else {
|
|
errorMessage = "That file could not be found."
|
|
return
|
|
}
|
|
if NSWorkspace.shared.open(url) {
|
|
isPresented = false
|
|
} else {
|
|
errorMessage = "macOS could not open this item."
|
|
}
|
|
}
|
|
}
|
|
|
|
struct SettingsView: View {
|
|
@Environment(SystemMonitor.self) private var monitor
|
|
@AppStorage("alwaysOnTop") private var alwaysOnTop = false
|
|
@AppStorage("defaultStartPage") private var defaultStartPage: TaskSection = .processes
|
|
@AppStorage("diagnosticReportDuration") private var diagnosticDuration = 5
|
|
@AppStorage("diagnosticAskEveryTime") private var diagnosticAskEveryTime = true
|
|
@AppStorage("diagnosticReportDirectory") private var diagnosticDirectory = ""
|
|
|
|
var body: some View {
|
|
@Bindable var monitor = monitor
|
|
VStack(spacing: 0) {
|
|
PageHeader(title: "Settings", subtitle: "Customize Task Manager")
|
|
Form {
|
|
Section("General") {
|
|
Picker("Default start page", selection: $defaultStartPage) {
|
|
ForEach(TaskSection.allCases.filter { $0 != .settings }) { section in
|
|
Text(section.rawValue).tag(section)
|
|
}
|
|
}
|
|
Picker("Real-time update speed", selection: $monitor.updateSpeed) {
|
|
ForEach(UpdateSpeed.allCases) { speed in
|
|
Text(speed.rawValue).tag(speed)
|
|
}
|
|
}
|
|
Toggle("Always on top", isOn: $alwaysOnTop)
|
|
.onChange(of: alwaysOnTop) { _, enabled in
|
|
NSApp.keyWindow?.level = enabled ? .floating : .normal
|
|
}
|
|
}
|
|
|
|
Section("Process data") {
|
|
LabeledContent("Refresh interval", value: intervalLabel)
|
|
LabeledContent("Process source", value: "macOS process table")
|
|
LabeledContent("Memory units", value: "Automatic")
|
|
}
|
|
|
|
Section("Diagnostic reports") {
|
|
Picker("Sample duration", selection: $diagnosticDuration) {
|
|
Text("1 second").tag(1)
|
|
Text("5 seconds").tag(5)
|
|
Text("10 seconds").tag(10)
|
|
}
|
|
Toggle("Ask where to save each report", isOn: $diagnosticAskEveryTime)
|
|
LabeledContent("Output folder") {
|
|
HStack(spacing: 10) {
|
|
Text(diagnosticDirectory.isEmpty ? "Not selected" : URL(fileURLWithPath: diagnosticDirectory).lastPathComponent)
|
|
.foregroundStyle(.secondary)
|
|
Button("Choose…") { chooseDiagnosticDirectory() }
|
|
}
|
|
}
|
|
}
|
|
|
|
Section("About") {
|
|
LabeledContent("Application", value: "Task Manager for macOS")
|
|
LabeledContent("Version", value: "1.0")
|
|
LabeledContent("Framework", value: "Native SwiftUI")
|
|
}
|
|
}
|
|
.formStyle(.grouped)
|
|
.scrollContentBackground(.hidden)
|
|
.frame(maxWidth: 720)
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
}
|
|
.task {
|
|
NSApp.keyWindow?.level = alwaysOnTop ? .floating : .normal
|
|
}
|
|
}
|
|
|
|
private var intervalLabel: String {
|
|
switch monitor.updateSpeed {
|
|
case .high: "Every second"
|
|
case .normal: "Every 2 seconds"
|
|
case .low: "Every 5 seconds"
|
|
case .paused: "Paused"
|
|
}
|
|
}
|
|
|
|
private func chooseDiagnosticDirectory() {
|
|
let panel = NSOpenPanel()
|
|
panel.title = "Choose diagnostic report folder"
|
|
panel.prompt = "Choose"
|
|
panel.canChooseFiles = false
|
|
panel.canChooseDirectories = true
|
|
panel.allowsMultipleSelection = false
|
|
if !diagnosticDirectory.isEmpty {
|
|
panel.directoryURL = URL(fileURLWithPath: diagnosticDirectory, isDirectory: true)
|
|
}
|
|
if panel.runModal() == .OK, let url = panel.url {
|
|
diagnosticDirectory = url.path
|
|
}
|
|
}
|
|
}
|