its whats on the tin; culls raw photos
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: add undo redo

+24 -6
+20 -6
cull/Models/CullSession.swift
··· 12 12 var importProgress: Double = 0 13 13 var importStatus: String = "" 14 14 15 + var undoManager: UndoManager? 16 + 15 17 // Remember cursor position per group 16 18 private var groupCursorPositions: [UUID: Int] = [:] 17 19 ··· 237 239 238 240 // MARK: - Culling Actions 239 241 242 + private func applyPhotoState(_ photo: Photo, rating: Int, flag: PhotoFlag, actionName: String) { 243 + let oldRating = photo.rating 244 + let oldFlag = photo.flag 245 + photo.rating = rating 246 + photo.flag = flag 247 + undoManager?.registerUndo(withTarget: self) { session in 248 + session.applyPhotoState(photo, rating: oldRating, flag: oldFlag, actionName: actionName) 249 + } 250 + undoManager?.setActionName(actionName) 251 + } 252 + 240 253 func setRating(_ rating: Int) { 241 - guard (1...5).contains(rating) else { return } 242 - selectedPhoto?.rating = rating 254 + guard (1...5).contains(rating), let photo = selectedPhoto else { return } 255 + applyPhotoState(photo, rating: rating, flag: photo.flag, actionName: "Set Rating \(rating)") 243 256 ensureVisibleSelection() 244 257 } 245 258 246 259 func togglePick() { 247 260 guard let photo = selectedPhoto else { return } 248 - photo.flag = photo.flag == .pick ? .none : .pick 261 + let newFlag: PhotoFlag = photo.flag == .pick ? .none : .pick 262 + applyPhotoState(photo, rating: photo.rating, flag: newFlag, actionName: newFlag == .pick ? "Pick" : "Remove Pick") 249 263 ensureVisibleSelection() 250 264 } 251 265 252 266 func toggleReject() { 253 267 guard let photo = selectedPhoto else { return } 254 - photo.flag = photo.flag == .reject ? .none : .reject 268 + let newFlag: PhotoFlag = photo.flag == .reject ? .none : .reject 269 + applyPhotoState(photo, rating: photo.rating, flag: newFlag, actionName: newFlag == .reject ? "Reject" : "Remove Reject") 255 270 ensureVisibleSelection() 256 271 } 257 272 258 273 func clearRatingAndFlag() { 259 274 guard let photo = selectedPhoto else { return } 260 - photo.rating = 0 261 - photo.flag = .none 275 + applyPhotoState(photo, rating: 0, flag: .none, actionName: "Clear Rating & Flag") 262 276 } 263 277 }
+4
cull/Views/ContentView.swift
··· 3 3 struct ContentView: View { 4 4 @Environment(CullSession.self) private var session 5 5 @Environment(ThumbnailCache.self) private var cache 6 + @Environment(\.undoManager) private var windowUndoManager 6 7 @State private var showExportSheet = false 7 8 @FocusState private var isViewerFocused: Bool 8 9 ··· 35 36 } 36 37 .onReceive(NotificationCenter.default.publisher(for: .showExport)) { _ in 37 38 showExportSheet = true 39 + } 40 + .onAppear { 41 + session.undoManager = windowUndoManager 38 42 } 39 43 } 40 44