OSDN Git Service

洋上補給の補強増設用のショートネームをつけた
[kcd/KCD.git] / KCD / ScreenshotListViewController.swift
index 92b45d8..14325dc 100644 (file)
 
 import Cocoa
 
-fileprivate struct CacheVersionInfo {
-    let url: URL
+extension NSUserInterfaceItemIdentifier {
     
-    init(url: URL, version: Int = 0) {
-        self.url = url
-        self.version = version
-    }
-    
-    private(set) var version: Int
-    
-    mutating func incrementVersion() { version = version + 1 }
+    static let item = NSUserInterfaceItemIdentifier("item")
 }
 
-class ScreenshotListViewController: NSViewController {
-    private static let def = 800.0
+final class ScreenshotListViewController: NSViewController {
+    
+    private static let maxImageSize = 800.0
     private static let leftMergin = 8.0 + 1.0
     private static let rightMergin = 8.0 + 1.0
     
     var screenshots: ScreenshotModel = ScreenshotModel()
     
-    @IBOutlet var screenshotsController: NSArrayController!
-    @IBOutlet weak var collectionView: NSCollectionView!
-    @IBOutlet var contextMenu: NSMenu!
-    @IBOutlet weak var shareButton: NSButton!
-    @IBOutlet weak var standardView: NSView!
-    @IBOutlet weak var editorView: NSView!
+    @IBOutlet private var screenshotsController: NSArrayController!
+    @IBOutlet private weak var collectionView: NSCollectionView!
+    
+    private var selectionObservation: NSKeyValueObservation?
     
-    dynamic var zoom: Double = UserDefaults.standard.screenshotPreviewZoomValue {
+    @objc dynamic var zoom: Double = UserDefaults.standard[.screenshotPreviewZoomValue] {
+        
         didSet {
             collectionView.reloadData()
-            UserDefaults.standard.screenshotPreviewZoomValue = zoom
+            UserDefaults.standard[.screenshotPreviewZoomValue] = zoom
         }
     }
-    dynamic var maxZoom: Double = 1.0
-    
-    fileprivate var collectionVisibleDidChangeHandler: ((Set<IndexPath>) -> Void)? = nil
-    fileprivate var reloadHandler: (() -> Void)? = nil
-    fileprivate var collectionSelectionDidChangeHandler: ((Int) -> Void)? = nil
-    fileprivate(set) var inLiveScrolling = false
-    fileprivate var arrangedInformations: [ScreenshotInformation]? {
-        return screenshotsController.arrangedObjects as? [ScreenshotInformation]
+    @objc dynamic var maxZoom: Double = 1.0
+    
+    private var collectionVisibleDidChangeHandler: ((Set<IndexPath>) -> Void)?
+    private var reloadHandler: (() -> Void)?
+    private var collectionSelectionDidChangeHandler: ((Int) -> Void)?
+    private(set) var inLiveScrolling = false
+    private var arrangedInformations: [ScreenshotInformation] {
+        
+        return screenshotsController.arrangedObjects as? [ScreenshotInformation] ?? []
     }
-    fileprivate var selectionInformations : [ScreenshotInformation] {
+    private var selectionInformations: [ScreenshotInformation] {
+        
         return screenshotsController.selectedObjects as? [ScreenshotInformation] ?? []
     }
     
-    private var deletedPaths: [CacheVersionInfo] = []
     private var dirName: String {
+        
         guard let name = Bundle.main.localizedInfoDictionary?["CFBundleName"] as? String,
-            !name.isEmpty
-            else { return "KCD" }
+            !name.isEmpty else {
+                
+                return "KCD"
+        }
+        
         return name
     }
     private var screenshotSaveDirectoryURL: URL {
+        
         let parentURL = URL(fileURLWithPath: AppDelegate.shared.screenShotSaveDirectory)
         let url = parentURL.appendingPathComponent(dirName)
         let fm = FileManager.default
         var isDir: ObjCBool = false
+        
         do {
+            
             if !fm.fileExists(atPath: url.path, isDirectory: &isDir) {
+                
                 try fm.createDirectory(at: url, withIntermediateDirectories: false)
+                
             } else if !isDir.boolValue {
+                
                 print("\(url) is regular file, not direcory.")
                 return parentURL
             }
-        }
-        catch {
+            
+        } catch {
+            
             print("Can not create screenshot save directory.")
             return parentURL
         }
+        
         return url
     }
-    private var cachURL: URL {
-        return screenshotSaveDirectoryURL.appendingPathComponent("Cache.db")
-    }
+    
+    var indexPathsOfItemsBeingDragged: Set<IndexPath>?
     
     // MARK: - Function
     override func viewDidLoad() {
-        super.viewDidLoad()
         
-        screenshots.screenshots = loadCache()
+        super.viewDidLoad()
         
-        let nib = NSNib(nibNamed: "ScreenshotCollectionViewItem", bundle: nil)
-        collectionView.register(NSCollectionView.self, forItemWithIdentifier: "item")
-        collectionView.register(nib, forItemWithIdentifier: "item")
+        let nib = NSNib(nibNamed: ScreenshotCollectionViewItem.nibName, bundle: nil)
+        collectionView.register(nib, forItemWithIdentifier: .item)
         
-        screenshots.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
-        collectionView.addObserver(self, forKeyPath: "selectionIndexPaths", context: nil)
+        screenshots.sortDescriptors = [NSSortDescriptor(key: #keyPath(ScreenshotInformation.creationDate), ascending: false)]
+        selectionObservation = collectionView.observe(\NSCollectionView.selectionIndexPaths) { [weak self] (_, _) in
+            
+            guard let `self` = self else { return }
+            
+            let selections = self.collectionView.selectionIndexPaths
+            let selectionIndexes = selections.reduce(into: IndexSet()) { $0.insert($1.item) }
+            self.screenshots.selectedIndexes = selectionIndexes
+            selectionIndexes.first.map { self.collectionSelectionDidChangeHandler?($0) }
+        }
         collectionView.postsFrameChangedNotifications = true
         
         let nc = NotificationCenter.default
         let scrollView = collectionView.enclosingScrollView
         
-        nc.addObserver(forName: .NSViewFrameDidChange, object: collectionView, queue: nil, using: viewFrameDidChange)
-        nc.addObserver(forName: .NSScrollViewDidLiveScroll, object: collectionView.enclosingScrollView, queue: nil) { _ in
+        nc.addObserver(forName: NSView.frameDidChangeNotification, object: collectionView, queue: nil, using: viewFrameDidChange)
+        nc.addObserver(forName: NSScrollView.didLiveScrollNotification,
+                       object: collectionView.enclosingScrollView, queue: nil) { _ in
+                        
             let visibleItems = self.collectionView.indexPathsForVisibleItems()
             self.collectionVisibleDidChangeHandler?(visibleItems)
         }
-        nc.addObserver(forName: .NSScrollViewWillStartLiveScroll, object: scrollView, queue: nil) { _ in
+        nc.addObserver(forName: NSScrollView.willStartLiveScrollNotification, object: scrollView, queue: nil) { _ in
+            
             self.inLiveScrolling = true
         }
-        nc.addObserver(forName: .NSScrollViewDidEndLiveScroll, object: scrollView, queue: nil) { _ in
+        nc.addObserver(forName: NSScrollView.didEndLiveScrollNotification, object: scrollView, queue: nil) { _ in
+            
             self.inLiveScrolling = false
         }
+        nc.addObserver(forName: .didRegisterScreenshot,
+                       object: nil,
+                       queue: .main) { notification in
+                        
+                        guard let url = notification.userInfo?[ScreenshotRegister.screenshotURLKey] as? URL else { return }
+                        
+                        let info = ScreenshotInformation(url: url)
+                        
+                        self.screenshotsController.insert(info, atArrangedObjectIndex: 0)
+                        let set: Set<IndexPath> = [NSIndexPath(forItem: 0, inSection: 0) as IndexPath]
+                        self.collectionView.selectionIndexPaths = set
+                        
+                        self.collectionView.scrollToItems(at: set, scrollPosition: .nearestHorizontalEdge)
+                        if UserDefaults.standard[.showsListWindowAtScreenshot] {
+                            
+                            self.view.window?.makeKeyAndOrderFront(nil)
+                        }
+        }
         
-        viewFrameDidChange(nil)
+        collectionView.setDraggingSourceOperationMask([.move, .copy, .delete], forLocal: false)
         
-        DispatchQueue.main
-            .asyncAfter(deadline: .now() + 0.0001 ) { self.reloadData() }
-    }
-    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
-        if let object = object as? NSCollectionView,
-            object == collectionView {
-            let selections = collectionView.selectionIndexPaths
-            var selectionIndexes = IndexSet()
-            selections.forEach { selectionIndexes.insert($0.item) }
-            screenshots.selectedIndexes = selectionIndexes
-            selectionIndexes.first.map { collectionSelectionDidChangeHandler?($0) }
-            return
-        }
+        viewFrameDidChange(nil)
         
-        super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
+        DispatchQueue.main.asyncAfter(deadline: .now() + 0.0001, execute: self.reloadData)
     }
+    
     override func prepare(for segue: NSStoryboardSegue, sender: Any?) {
+        
         guard let vc = segue.destinationController as? NSViewController else { return }
+        
         vc.representedObject = screenshots
     }
     
-    func registerImage(_ image: NSImage?) {
-        image?.tiffRepresentation
-            .flatMap { NSBitmapImageRep(data: $0) }
-            .map { registerScreenshot($0, fromOnScreen: .zero) }
-    }
-    func registerScreenshot(_ image: NSBitmapImageRep, fromOnScreen: NSRect) {
-        DispatchQueue(label: "Screenshot queue")
-            .async {
-                guard let data = image.representation(using: .JPEG, properties: [:])
-                    else { return }
-                let url = self.screenshotSaveDirectoryURL
-                    .appendingPathComponent(self.dirName)
-                    .appendingPathExtension("jpg")
-                let pathURL = FileManager.default.uniqueFileURL(url)
-                do {
-                    try data.write(to: pathURL)
-                }
-                catch {
-                    print("Can not write image")
-                    return
-                }
-                
-                DispatchQueue.main.async {
-                    let info = ScreenshotInformation(url: pathURL, version: self.cacheVersion(forUrl: pathURL))
-                    
-                    self.screenshotsController.insert(info, atArrangedObjectIndex: 0)
-                    let set: Set<IndexPath> = [NSIndexPath(forItem: 0, inSection: 0) as IndexPath]
-                    self.collectionView.selectionIndexPaths = set
-                    
-                    self.collectionView.scrollToItems(at: set, scrollPosition: .nearestHorizontalEdge)
-                    if UserDefaults.standard.showsListWindowAtScreenshot {
-                        self.view.window?.makeKeyAndOrderFront(nil)
-                    }
-                    self.saveCache()
-                }
-        }
-    }
     func viewFrameDidChange(_ notification: Notification?) {
-        maxZoom = self.maxZoom(width: collectionView.frame.size.width)
+        
+        maxZoom = calcMaxZoom()
         if zoom > maxZoom { zoom = maxZoom }
     }
     
-    fileprivate func realFromZoom(zoom: Double) -> CGFloat {
-        if zoom < 0.5 { return CGFloat(ScreenshotListViewController.def * zoom * 0.6) }
-        return CGFloat(ScreenshotListViewController.def * (0.8 * zoom * zoom * zoom  + 0.2))
-    }
-    private func maxZoom(width: CGFloat) -> Double {
-        let w = Double(width) - ScreenshotListViewController.leftMergin - ScreenshotListViewController.rightMergin
-        if w < 240 { return w / ScreenshotListViewController.def / 0.6 }
-        if w > 800 { return 1.0 }
-        return pow((w / ScreenshotListViewController.def - 0.2) / 0.8, 1.0 / 3.0)
-    }
-    
-    private func reloadData() {
-        guard let f = try? FileManager.default.contentsOfDirectory(at: screenshotSaveDirectoryURL, includingPropertiesForKeys: nil) else {
-            print("can not read list of screenshot directory")
-            return
-        }
-        let imageTypes = NSImage.imageTypes()
-        let ws = NSWorkspace.shared()
-        var current = screenshots.screenshots
-        let newFiles: [URL] = f.flatMap {
-            guard let type = try? ws.type(ofFile: $0.path) else { return nil }
-            if imageTypes.contains(type) {
-                return $0
-            }
-            return nil
-        }
-        
-        // なくなっているものを削除
-        current = current.filter { newFiles.contains($0.url) }
+    /// 画像の大きさの変化が自然になるようにzoom値から画像サイズを計算
+    private func sizeFrom(zoom: Double) -> CGFloat {
         
-        // 新しいものを追加
-        let new: [ScreenshotInformation] = newFiles.flatMap { (url) in
-            let index = current.index(where: { (info) -> Bool in
-                url == info.url
-            })
-            if index == nil {
-                return ScreenshotInformation(url: url)
-            }
-            return nil
-        }
+        if zoom < 0.5 { return CGFloat(type(of: self).maxImageSize * zoom * 0.6) }
         
-        screenshots.screenshots = current + new
+        return CGFloat(type(of: self).maxImageSize * (0.8 * zoom * zoom * zoom  + 0.2))
+    }
+    
+    /// ビューの幅に合わせたzoomの最大値を計算
+    private func calcMaxZoom() -> Double {
         
-        collectionView.selectionIndexPaths = [NSIndexPath(forItem: 0, inSection: 0) as IndexPath]
+        let effectiveWidth = Double(collectionView.frame.size.width) - type(of: self).leftMergin - type(of: self).rightMergin
         
-        reloadHandler?()
-        saveCache()
+        if effectiveWidth < 240 { return effectiveWidth / type(of: self).maxImageSize / 0.6 }
+        if effectiveWidth > 800 { return 1.0 }
         
+        return pow((effectiveWidth / type(of: self).maxImageSize - 0.2) / 0.8, 1.0 / 3.0)
     }
     
-    private func saveCache() {
-        let data = NSKeyedArchiver.archivedData(withRootObject: screenshots.screenshots)
-        do {
-            try data.write(to: cachURL)
-        }
-        catch let e {
-            print("Can not write cache: \(e)")
-        }
-    }
-    private func loadCache() -> [ScreenshotInformation] {
-        guard let data = try? Data(contentsOf: cachURL)
-            else {
-                print("can not load cach \(cachURL)")
-                return []
-        }
-        guard let l = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data as NSData),
-            let loaded = l as? [ScreenshotInformation]
-            else {
-                print("Can not decode \(cachURL)")
-                return []
-        }
+    private func reloadData() {
         
-        return loaded
-    }
-    
-    private func incrementCacheVersion(forUrl url: URL) {
-        let infos = deletedPaths.filter { $0.url == url }
-        if var info = infos.first {
-            info.incrementVersion()
-        }
-        else {
-            deletedPaths.append(CacheVersionInfo(url: url))
+        Promise<[ScreenshotInformation]>()
+            .complete {
+                Result(ScreenshotLoader(self.screenshotSaveDirectoryURL).merge(screenshots: []))
+            }
+            .future
+            .onSuccess { screenshots in
+                
+                DispatchQueue.main.async {
+                    self.screenshots.screenshots = screenshots
+                    
+                    self.collectionView.selectionIndexPaths = [NSIndexPath(forItem: 0, inSection: 0) as IndexPath]
+                    
+                    self.reloadHandler?()
+                }
         }
     }
     
-    private func cacheVersion(forUrl url: URL) -> Int {
-        return deletedPaths
-            .filter { $0.url == url }
-            .first?
-            .version ?? 0
+}
+
+// MARK: - IBAction
+extension ScreenshotListViewController {
+    
+    @IBAction func reloadContent(_ sender: AnyObject?) {
+        
+        reloadData()
     }
     
-    // MARK: - IBAction
     @IBAction func reloadData(_ sender: AnyObject?) {
+        
         reloadData()
     }
-    @IBAction func delete(_ sender: AnyObject?) {
-        let posixPaths = selectionInformations
-            .map { $0.url.path }
+    
+    private func moveToTrash(_ urls: [URL]) {
+        
+        let list = urls.map { $0.path }
             .map { "(\"\($0)\" as POSIX file)" }
-        let list = posixPaths.joined(separator: " , ")
+            .joined(separator: " , ")
         let script = "tell application \"Finder\"\n"
-        + "    delete { \(list) }\n"
-        + "end tell"
+            + "    delete { \(list) }\n"
+            + "end tell"
+        
         guard let aps = NSAppleScript(source: script) else { return }
+        
         aps.executeAndReturnError(nil)
+    }
+    
+    @IBAction func delete(_ sender: AnyObject?) {
+        
+        let selectionURLs = selectionInformations.map { $0.url }
         
         let selectionIndexes = screenshotsController.selectionIndexes
         screenshotsController.remove(atArrangedObjectIndexes: selectionIndexes)
-        selectionInformations.forEach { incrementCacheVersion(forUrl: $0.url) }
-        saveCache()
         reloadHandler?()
         
-        guard var index = selectionIndexes.first,
-            let newInfos = arrangedInformations
-            else { return }
-        if newInfos.count <= index { index = newInfos.count - 1 }
+        guard var index = selectionIndexes.first else { return }
+        
+        if arrangedInformations.count <= index {
+            
+            index = arrangedInformations.count - 1
+        }
         collectionView.selectionIndexPaths = [NSIndexPath(forItem: index, inSection: 0) as IndexPath]
+        
+        moveToTrash(selectionURLs)
     }
+    
     @IBAction func revealInFinder(_ sender: AnyObject?) {
-        guard let infos = arrangedInformations else { return }
-        let urls = infos.map { $0.url }
-        NSWorkspace.shared().activateFileViewerSelecting(urls)
+        
+        let urls = selectionInformations.map { $0.url }
+        NSWorkspace.shared.activateFileViewerSelecting(urls)
     }
 }
 
 extension ScreenshotListViewController: NSCollectionViewDelegateFlowLayout {
-    func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> NSSize {
-        let f = realFromZoom(zoom: zoom)
-        return NSMakeSize(f, f)
+    
+    func collectionView(_ collectionView: NSCollectionView,
+                        layout collectionViewLayout: NSCollectionViewLayout,
+                        sizeForItemAt indexPath: IndexPath) -> NSSize {
+        
+        let size = sizeFrom(zoom: zoom)
+        
+        return NSSize(width: size, height: size)
+    }
+    
+    // Drag and Drop
+    func collectionView(_ collectionView: NSCollectionView, canDragItemsAt indexPaths: Set<IndexPath>, with event: NSEvent) -> Bool {
+        
+        return true
+    }
+    
+    func collectionView(_ collectionView: NSCollectionView, pasteboardWriterForItemAt indexPath: IndexPath) -> NSPasteboardWriting? {
+        
+        return arrangedInformations[indexPath.item].url.absoluteURL as NSURL
+    }
+    
+    func collectionView(_ collectionView: NSCollectionView, draggingSession session: NSDraggingSession, willBeginAt screenPoint: NSPoint, forItemsAt indexPaths: Set<IndexPath>) {
+        
+        indexPathsOfItemsBeingDragged = indexPaths
+    }
+    
+    func collectionView(_ collectionView: NSCollectionView, draggingSession session: NSDraggingSession, endedAt screenPoint: NSPoint, dragOperation operation: NSDragOperation) {
+        
+        defer { indexPathsOfItemsBeingDragged = nil }
+        
+        guard let dragged = indexPathsOfItemsBeingDragged else { return }
+        guard operation.contains(.move) || operation.contains(.delete) else { return }
+        
+        var indexes = IndexSet()
+        dragged.forEach { indexes.insert($0.item) }
+        
+        screenshotsController.remove(atArrangedObjectIndexes: indexes)
     }
+    
 }
 
 @available(OSX 10.12.2, *)
-fileprivate var kTouchBars:[Int: NSTouchBar] = [:]
+private var kTouchBars: [Int: NSTouchBar] = [:]
 @available(OSX 10.12.2, *)
-fileprivate var kScrubbers:[Int: NSScrubber] = [:]
+private var kScrubbers: [Int: NSScrubber] = [:]
 @available(OSX 10.12.2, *)
-fileprivate var kPickers:[Int: NSSharingServicePickerTouchBarItem] = [:]
+private var kPickers: [Int: NSSharingServicePickerTouchBarItem] = [:]
 
 @available(OSX 10.12.2, *)
 extension ScreenshotListViewController: NSTouchBarDelegate {
-    static let ServicesItemIdentifier: NSTouchBarItemIdentifier = NSTouchBarItemIdentifier(rawValue: "com.masakih.sharingTouchBarItem")
     
-    @IBOutlet var screenshotTouchBar: NSTouchBar! {
+    static let ServicesItemIdentifier: NSTouchBarItem.Identifier
+        = NSTouchBarItem.Identifier(rawValue: "com.masakih.sharingTouchBarItem")
+    
+    @IBOutlet private var screenshotTouchBar: NSTouchBar! {
+        
         get { return kTouchBars[hashValue] }
         set { kTouchBars[hashValue] = newValue }
     }
-    @IBOutlet var scrubber: NSScrubber! {
+    
+    @IBOutlet private var scrubber: NSScrubber! {
+        
         get { return kScrubbers[hashValue] }
         set { kScrubbers[hashValue] = newValue }
     }
-    @IBOutlet var sharingItem: NSSharingServicePickerTouchBarItem! {
+    
+    @IBOutlet private var sharingItem: NSSharingServicePickerTouchBarItem! {
+        
         get { return kPickers[hashValue] }
         set { kPickers[hashValue] = newValue }
     }
     
     override func makeTouchBar() -> NSTouchBar? {
-        var array: NSArray = []
-        Bundle.main.loadNibNamed("ScreenshotTouchBar", owner: self, topLevelObjects: &array)
-        let identifiers = self.screenshotTouchBar.defaultItemIdentifiers + [ScreenshotListViewController.ServicesItemIdentifier]
+        
+        Bundle.main.loadNibNamed(NSNib.Name("ScreenshotTouchBar"), owner: self, topLevelObjects: nil)
+        let identifiers = self.screenshotTouchBar.defaultItemIdentifiers
+            + [type(of: self).ServicesItemIdentifier]
         screenshotTouchBar.defaultItemIdentifiers = identifiers
         
         if collectionVisibleDidChangeHandler == nil {
-            collectionVisibleDidChangeHandler = { [unowned self] (visible) in
-                guard let objects = self.arrangedInformations else { return }
-                guard let index = visible.first else { return }
-                let middle = index.item + visible.count / 2
-                if middle < objects.count - 1 {
+            
+            collectionVisibleDidChangeHandler = { [weak self] in
+                
+                guard let `self` = self else { return }
+                guard let index = $0.first else { return }
+                
+                let middle = index.item + $0.count / 2
+                
+                if middle < self.arrangedInformations.count - 1 {
+                    
                     self.scrubber.scrollItem(at: middle, to: .none)
                 }
             }
         }
+        
         if collectionSelectionDidChangeHandler == nil {
-            collectionSelectionDidChangeHandler = { [unowned self] (index) in
-                self.scrubber.selectedIndex = index
+            
+            collectionSelectionDidChangeHandler = { [weak self] in
+                
+                self?.scrubber.selectedIndex = $0
             }
         }
+        
         if reloadHandler == nil {
-            reloadHandler = { [unowned self] _ in
-                self.scrubber.reloadData()
+            
+            reloadHandler = { [weak self] in
+                
+                self?.scrubber.reloadData()
             }
         }
         
         return screenshotTouchBar
     }
     
-    func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItemIdentifier) -> NSTouchBarItem? {
-        guard identifier == ScreenshotListViewController.ServicesItemIdentifier
-            else { return nil }
+    func touchBar(_ touchBar: NSTouchBar,
+                  makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? {
+        
+        guard identifier == type(of: self).ServicesItemIdentifier else { return nil }
+        
         if sharingItem == nil {
+            
             sharingItem = NSSharingServicePickerTouchBarItem(identifier: identifier)
+            
             if let w = view.window?.windowController as? NSSharingServicePickerTouchBarItemDelegate {
+                
                 sharingItem.delegate = w
             }
         }
@@ -380,15 +393,21 @@ extension ScreenshotListViewController: NSTouchBarDelegate {
 
 @available(OSX 10.12.2, *)
 extension ScreenshotListViewController: NSScrubberDataSource, NSScrubberDelegate {
+    
     func numberOfItems(for scrubber: NSScrubber) -> Int {
-        return arrangedInformations?.count ?? 0
+        
+        return arrangedInformations.count
     }
+    
     func scrubber(_ scrubber: NSScrubber, viewForItemAt index: Int) -> NSScrubberItemView {
-        guard let objects = arrangedInformations else { return NSScrubberImageItemView() }
-        guard objects.count > index else { return NSScrubberImageItemView() }
-        let info = objects[index]
+        
+        guard case 0..<arrangedInformations.count = index else { return NSScrubberImageItemView() }
+        
+        let info = arrangedInformations[index]
         let itemView = NSScrubberImageItemView()
+        
         if let image = NSImage(contentsOf: info.url) {
+            
             itemView.image = image
         }
         
@@ -396,11 +415,16 @@ extension ScreenshotListViewController: NSScrubberDataSource, NSScrubberDelegate
     }
     
     func scrubber(_ scrubber: NSScrubber, didSelectItemAt selectedIndex: Int) {
+        
         let p = NSIndexPath(forItem: selectedIndex, inSection: 0) as IndexPath
+        
         collectionView.selectionIndexPaths = [p]
     }
+    
     func scrubber(_ scrubber: NSScrubber, didChangeVisibleRange visibleRange: NSRange) {
+        
         if inLiveScrolling { return }
+        
         let center = visibleRange.location + visibleRange.length / 2
         let p = NSIndexPath(forItem: center, inSection: 0) as IndexPath
         collectionView.scrollToItems(at: [p], scrollPosition: [.centeredVertically])