OSDN Git Service

guard の書き方を統一した
[kcd/KCD.git] / KCD / ScreenshotEditorViewController.swift
1 //
2 //  ScreenshotEditorViewController.swift
3 //  KCD
4 //
5 //  Created by Hori,Masaki on 2016/12/29.
6 //  Copyright © 2016年 Hori,Masaki. All rights reserved.
7 //
8
9 import Cocoa
10
11 private struct EditedImage {
12     
13     var editedImage: NSImage
14     var url: URL
15     
16     init(image: NSImage, url: URL) {
17         
18         editedImage = image
19         self.url = url
20     }
21 }
22
23 final class TrimRectInformation: NSObject {
24     
25     @objc private(set) var name: String
26     private(set) var rect: NSRect
27     
28     fileprivate init(name: String, rect: NSRect) {
29         
30         self.name = name
31         self.rect = rect
32     }
33 }
34
35 private extension Selector {
36     
37     static let done = #selector(ScreenshotEditorViewController.done(_:))
38     static let changeToDetail = #selector(ScreenshotListWindowController.changeToDetail(_:))
39     static let registerImage = #selector(ScreenshotListViewController.registerImage(_:))
40 }
41
42 final class ScreenshotEditorViewController: BridgeViewController {
43     
44     @objc let trimInfo: [TrimRectInformation]
45     
46     override init(nibName: NSNib.Name?, bundle: Bundle?) {
47         
48         trimInfo = [
49             TrimRectInformation(name: "Status", rect: NSRect(x: 328, y: 13, width: 470, height: 365)),
50             TrimRectInformation(name: "List", rect: NSRect(x: 362, y: 15, width: 438, height: 368)),
51             TrimRectInformation(name: "AirplaneBase", rect: NSRect(x: 575, y: 13, width: 225, height: 358))
52         ]
53         currentTrimInfo = trimInfo[0]
54         
55         super.init(nibName: ScreenshotEditorViewController.nibName, bundle: nil)
56     }
57     
58     required init?(coder: NSCoder) {
59         
60         fatalError("init(coder:) has not been implemented")
61     }
62     
63     deinit {
64         
65         arrayController.removeObserver(self, forKeyPath: NSBindingName.selectionIndexes.rawValue)
66     }
67     
68     @IBOutlet weak var tiledImageView: TiledImageView!
69     @IBOutlet weak var doneButton: NSButton!
70     
71     @objc var columnCount: Int {
72         
73         get { return tiledImageView.columnCount }
74         set {
75             tiledImageView.columnCount = newValue
76             UserDefaults.standard[.screenshotEditorColumnCount] = newValue
77         }
78     }
79     
80     var image: NSImage? {
81         
82         return tiledImageView.image
83     }
84     
85     @objc dynamic var currentTrimInfoIndex: Int {
86         
87         get { return realiesCurrentTrimInforIndex }
88         set {
89             guard 0..<trimInfo.count ~= newValue else { return }
90             
91             realiesCurrentTrimInforIndex = newValue
92             currentTrimInfo = trimInfo[newValue]
93         }
94     }
95     
96     private var editedImage: NSImage?
97     private var currentSelection: [ScreenshotInformation] = []
98     private var editedImages: [EditedImage] = []
99     private var realiesCurrentTrimInforIndex = UserDefaults.standard[.scrennshotEditorType]
100     private var currentTrimInfo: TrimRectInformation {
101         
102         didSet {
103             makeEditedImage()
104             trimInfo
105                 .index {
106                     
107                     if $0.name != currentTrimInfo.name { return false }
108                     return $0.rect == currentTrimInfo.rect
109                 }
110                 .map { UserDefaults.standard[.scrennshotEditorType] = $0 }
111         }
112     }
113     
114     override func viewDidLoad() {
115         
116         super.viewDidLoad()
117         
118         arrayController.addObserver(self, forKeyPath: NSBindingName.selectionIndexes.rawValue, context: nil)
119         currentTrimInfoIndex = UserDefaults.standard[.scrennshotEditorType]
120         updateSelections()
121     }
122     
123     override func viewWillAppear() {
124         
125         doneButton.action = .done
126     }
127     
128     override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
129         
130         if keyPath == NSBindingName.selectionIndexes.rawValue {
131             
132             updateSelections()
133             
134             return
135         }
136         
137         super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
138     }
139     
140     private func updateSelections() {
141         
142         guard let selection = arrayController.selectedObjects as? [ScreenshotInformation] else { return }
143         
144         if selection == currentSelection { return }
145         
146         let removed: [ScreenshotInformation] = currentSelection.flatMap {
147             
148             selection.contains($0) ? nil : $0
149         }
150         
151         let appended: [ScreenshotInformation] = selection.flatMap {
152             
153             currentSelection.contains($0) ? nil : $0
154         }
155         
156         removed.forEach {
157             
158             removeEditedImage(url: $0.url)
159         }
160         
161         appended.forEach {
162             
163             appendEditedImage(url: $0.url)
164         }
165         
166         currentSelection = selection
167         makeEditedImage()
168     }
169     
170     private func removeEditedImage(url: URL) {
171         
172         _ = editedImages
173             .index { $0.url == url }
174             .map { editedImages.remove(at: $0) }
175     }
176     
177     private func appendEditedImage(url: URL) {
178         
179         NSImage(contentsOf: url)
180             .flatMap { EditedImage(image: $0, url: url) }
181             .map { editedImages.append($0) }
182     }
183     
184     private func makeEditedImage() {
185         
186         guard !editedImages.isEmpty else {
187             
188             tiledImageView.images = []
189             return
190         }
191         
192         DispatchQueue(label: "makeTrimedImage queue").async {
193             
194             let images: [NSImage] = self.editedImages.flatMap {
195                 
196                 guard let originalImage = NSImage(contentsOf: $0.url) else { return nil }
197                 
198                 let trimedImage = NSImage(size: self.currentTrimInfo.rect.size)
199                 
200                 trimedImage.lockFocus()
201                 originalImage.draw(at: .zero,
202                                    from: self.currentTrimInfo.rect,
203                                    operation: .copy,
204                                    fraction: 1.0)
205                 trimedImage.unlockFocus()
206                 
207                 return trimedImage
208             }
209             
210             DispatchQueue.main.async {
211                 
212                 self.tiledImageView.images = images
213             }
214         }
215     }
216     
217     // TODO: 外部から End Handlerを登録できるようにして依存をなくす
218     @IBAction func done(_ sender: AnyObject?) {
219         
220         NSApplication.shared.sendAction(.registerImage, to: nil, from: self.image)
221         NSApplication.shared.sendAction(.changeToDetail, to: nil, from: sender)
222     }
223 }
224
225 extension ScreenshotEditorViewController: NibLoadable {}