Add main window and toolbar
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
import SwiftUI
|
||||
|
||||
struct ContentView: View {
|
||||
@Bindable var model: ScanViewModel
|
||||
|
||||
@State private var isConfirmingTrash = false
|
||||
|
||||
var body: some View {
|
||||
NavigationSplitView {
|
||||
GroupListView(model: model)
|
||||
.navigationSplitViewColumnWidth(min: 270, ideal: 310, max: 420)
|
||||
} detail: {
|
||||
if let group = model.selectedGroup {
|
||||
GroupDetailView(model: model, group: group)
|
||||
} else {
|
||||
placeholder
|
||||
}
|
||||
}
|
||||
.toolbar { ScanToolbar(model: model, isConfirmingTrash: $isConfirmingTrash) }
|
||||
.overlay {
|
||||
if let progress = model.progress {
|
||||
Color.black.opacity(0.12).ignoresSafeArea()
|
||||
ScanProgressView(progress: progress) { model.cancelScan() }
|
||||
}
|
||||
}
|
||||
.confirmationDialog(
|
||||
"\(model.markedCount) 枚をゴミ箱へ移動します",
|
||||
isPresented: $isConfirmingTrash,
|
||||
titleVisibility: .visible
|
||||
) {
|
||||
Button("ゴミ箱へ移動(\(model.markedBytesText))", role: .destructive) {
|
||||
Task { await model.moveMarkedToTrash() }
|
||||
}
|
||||
Button("キャンセル", role: .cancel) {}
|
||||
} message: {
|
||||
Text("削除ではなくゴミ箱への移動です。Finder のゴミ箱からいつでも元に戻せます。")
|
||||
}
|
||||
.alert(
|
||||
"エラー",
|
||||
isPresented: Binding(
|
||||
get: { model.errorMessage != nil },
|
||||
set: { if !$0 { model.errorMessage = nil } }
|
||||
)
|
||||
) {
|
||||
Button("OK") { model.errorMessage = nil }
|
||||
} message: {
|
||||
Text(model.errorMessage ?? "")
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var placeholder: some View {
|
||||
if model.folder == nil {
|
||||
ContentUnavailableView {
|
||||
Label("フォルダを選んでください", systemImage: "photo.stack")
|
||||
} description: {
|
||||
Text("選んだフォルダの中から、内容が同じ写真とよく似た写真を探します。")
|
||||
} actions: {
|
||||
Button("フォルダを選ぶ…") { model.chooseFolder() }
|
||||
.buttonStyle(.borderedProminent)
|
||||
}
|
||||
} else if model.hasScanned && model.groups.isEmpty {
|
||||
ContentUnavailableView {
|
||||
Label("似た写真は見つかりませんでした", systemImage: "checkmark.circle")
|
||||
} description: {
|
||||
Text("判定をゆるくすると、より広く似た写真を拾えます。")
|
||||
}
|
||||
} else {
|
||||
ContentUnavailableView(
|
||||
"グループを選択してください",
|
||||
systemImage: "square.grid.2x2"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ScanToolbar: ToolbarContent {
|
||||
@Bindable var model: ScanViewModel
|
||||
@Binding var isConfirmingTrash: Bool
|
||||
|
||||
var body: some ToolbarContent {
|
||||
ToolbarItem(placement: .navigation) {
|
||||
Button {
|
||||
model.chooseFolder()
|
||||
} label: {
|
||||
Label("フォルダを選ぶ", systemImage: "folder")
|
||||
}
|
||||
.help(model.folder?.path ?? "調べるフォルダを選びます")
|
||||
}
|
||||
|
||||
ToolbarItem {
|
||||
Picker("類似度", selection: $model.settings.sensitivity) {
|
||||
ForEach(Sensitivity.allCases) { level in
|
||||
Text(level.label).tag(level)
|
||||
}
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
.frame(width: 220)
|
||||
.help(model.settings.sensitivity.help)
|
||||
.disabled(model.isScanning)
|
||||
}
|
||||
|
||||
ToolbarItem {
|
||||
Toggle(isOn: $model.settings.includesSubfolders) {
|
||||
Label("サブフォルダも含む", systemImage: "folder.badge.plus")
|
||||
}
|
||||
.help("サブフォルダの中まで再帰的に調べます")
|
||||
.disabled(model.isScanning)
|
||||
}
|
||||
|
||||
ToolbarItem {
|
||||
Menu {
|
||||
ForEach(KeepRule.allCases) { rule in
|
||||
Button(rule.label) { model.applyKeepRule(rule) }
|
||||
}
|
||||
Divider()
|
||||
Button("選択をすべて解除") { model.clearMarks() }
|
||||
} label: {
|
||||
Label("一括選択", systemImage: "checklist")
|
||||
}
|
||||
.disabled(model.groups.isEmpty)
|
||||
}
|
||||
|
||||
ToolbarItem {
|
||||
Button {
|
||||
isConfirmingTrash = true
|
||||
} label: {
|
||||
Label(
|
||||
model.markedCount > 0 ? "\(model.markedCount) 枚をゴミ箱へ" : "ゴミ箱へ",
|
||||
systemImage: "trash"
|
||||
)
|
||||
}
|
||||
.disabled(model.markedCount == 0)
|
||||
}
|
||||
|
||||
ToolbarItem {
|
||||
Button {
|
||||
model.rescan()
|
||||
} label: {
|
||||
Label("再スキャン", systemImage: "arrow.clockwise")
|
||||
}
|
||||
.disabled(model.folder == nil || model.isScanning)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user