OSDN Git Service

可能な限り#keyPathを使うように変更
[kcd/KCD.git] / KCD / ShipDetailViewController.swift
1 //
2 //  ShipDetailViewController.swift
3 //  KCD
4 //
5 //  Created by Hori,Masaki on 2016/12/25.
6 //  Copyright © 2016年 Hori,Masaki. All rights reserved.
7 //
8
9 import Cocoa
10
11 enum ShipDetailViewType {
12     
13     case full
14     case medium
15     case minimum
16 }
17
18 private func nibNameFor(_ type: ShipDetailViewType) -> NSNib.Name {
19     
20     switch type {
21     case .full: return ShipDetailViewController.nibName
22     case .medium: return NSNib.Name("MediumShipViewController")
23     case .minimum: return NSNib.Name("MediumShipViewController")
24     }
25 }
26
27 private var shipContext: Int = 0
28 private var equippedItem0Context: Int = 0
29 private var equippedItem1Context: Int = 0
30 private var equippedItem2Context: Int = 0
31 private var equippedItem3Context: Int = 0
32 private var equippedItem4Context: Int = 0
33
34 final class ShipDetailViewController: NSViewController {
35     
36     let type: ShipDetailViewType
37     @objc let managedObjectContext = ServerDataStore.default.context
38     
39     init?(type: ShipDetailViewType) {
40         
41         self.type = type
42         
43         super.init(nibName: nibNameFor(type), bundle: nil)
44         
45         NotificationCenter
46             .default
47             .addObserver(forName: .DidUpdateGuardEscape, object: nil, queue: nil) { [weak self] _ in
48                 
49                 guard let `self` = self else { return }
50                 
51                 self.guardEscaped = self.ship?.guardEscaped ?? false
52         }
53     }
54     
55     required init?(coder: NSCoder) {
56         
57         fatalError("not implemented")
58     }
59     
60     deinit {
61         
62         NotificationCenter.default.removeObserver(self)
63         damageView.unbind(NSBindingName(#keyPath(DamageView.damageType)))
64         supply.unbind(NSBindingName(#keyPath(SuppliesView.shipStatus)))
65         [slot00Field, slot01Field, slot02Field, slot03Field]
66             .forEach { $0?.unbind(NSBindingName(#keyPath(SlotItemLevelView.slotItemID))) }
67         
68         shipController.removeObject(self)
69     }
70     
71     
72     @IBOutlet weak var supply: SuppliesView!
73     @IBOutlet weak var guardEscapedView: GuardEscapedView!
74     @IBOutlet weak var damageView: DamageView!
75     @IBOutlet weak var slot00Field: SlotItemLevelView!
76     @IBOutlet weak var slot01Field: SlotItemLevelView!
77     @IBOutlet weak var slot02Field: SlotItemLevelView!
78     @IBOutlet weak var slot03Field: SlotItemLevelView!
79     @IBOutlet var shipController: NSObjectController!
80     
81     @objc dynamic var guardEscaped: Bool = false {
82         
83         didSet {
84             guardEscapedView.isHidden = !guardEscaped
85         }
86     }
87     
88     @objc dynamic var ship: Ship? {
89         
90         get { return shipController.content as? Ship }
91         set {
92             
93             shipController.fetchPredicate = NSPredicate(#keyPath(Ship.id), equal: newValue?.id ?? 0)
94         }
95     }
96     
97     override func viewDidLoad() {
98         
99         super.viewDidLoad()
100         
101         damageView.setFrameOrigin(.zero)
102         view.addSubview(damageView)
103         damageView.bind(NSBindingName(#keyPath(DamageView.damageType)),
104                         to: shipController,
105                         withKeyPath: "selection.status", options: nil)
106         
107         supply.bind(NSBindingName(#keyPath(SuppliesView.shipStatus)),
108                     to: shipController,
109                     withKeyPath: "selection.self", options: nil)
110         
111         guardEscapedView.setFrameOrigin(.zero)
112         view.addSubview(guardEscapedView)
113         switch type {
114         case .medium, .minimum:
115             guardEscapedView.controlSize = .mini
116             
117         default: break
118         }
119         
120         let fields = [slot00Field, slot01Field, slot02Field, slot03Field]
121         let keypath = ["selection.slot_0", "selection.slot_1", "selection.slot_2", "selection.slot_3"]
122         zip(fields, keypath).forEach {
123             
124             $0.0?.bind(NSBindingName(#keyPath(SlotItemLevelView.slotItemID)), to: shipController, withKeyPath: $0.1, options: nil)
125         }
126         
127         // observe slotitems count
128         shipController.addObserver(self, forKeyPath: "selection", context: &shipContext)
129         
130         shipController.addObserver(self, forKeyPath: "selection.slot_0", context: &equippedItem0Context)
131         shipController.addObserver(self, forKeyPath: "selection.onslot_0", context: &equippedItem0Context)
132         shipController.addObserver(self, forKeyPath: "selection.master_ship.maxeq_0", context: &equippedItem0Context)
133         
134         shipController.addObserver(self, forKeyPath: "selection.slot_1", context: &equippedItem1Context)
135         shipController.addObserver(self, forKeyPath: "selection.onslot_1", context: &equippedItem1Context)
136         shipController.addObserver(self, forKeyPath: "selection.master_ship.maxeq_1", context: &equippedItem1Context)
137         
138         shipController.addObserver(self, forKeyPath: "selection.slot_2", context: &equippedItem2Context)
139         shipController.addObserver(self, forKeyPath: "selection.onslot_2", context: &equippedItem2Context)
140         shipController.addObserver(self, forKeyPath: "selection.master_ship.maxeq_2", context: &equippedItem2Context)
141         
142         shipController.addObserver(self, forKeyPath: "selection.slot_3", context: &equippedItem3Context)
143         shipController.addObserver(self, forKeyPath: "selection.onslot_3", context: &equippedItem3Context)
144         shipController.addObserver(self, forKeyPath: "selection.master_ship.maxeq_3", context: &equippedItem3Context)
145         
146         shipController.addObserver(self, forKeyPath: "selection.slot_4", context: &equippedItem4Context)
147         shipController.addObserver(self, forKeyPath: "selection.onslot_4", context: &equippedItem4Context)
148         shipController.addObserver(self, forKeyPath: "selection.master_ship.maxeq_4", context: &equippedItem4Context)
149         
150     }
151     
152     override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
153         
154         if context == &shipContext {
155             
156             notifyChangeValue(forKey: #keyPath(planeString0))
157             notifyChangeValue(forKey: #keyPath(planeString0Color))
158             notifyChangeValue(forKey: #keyPath(planeString1))
159             notifyChangeValue(forKey: #keyPath(planeString1Color))
160             notifyChangeValue(forKey: #keyPath(planeString2))
161             notifyChangeValue(forKey: #keyPath(planeString2Color))
162             notifyChangeValue(forKey: #keyPath(planeString3))
163             notifyChangeValue(forKey: #keyPath(planeString3Color))
164             notifyChangeValue(forKey: #keyPath(planeString4))
165             notifyChangeValue(forKey: #keyPath(planeString4Color))
166             
167             return
168         }
169         if context == &equippedItem0Context {
170             
171             notifyChangeValue(forKey: #keyPath(planeString0))
172             notifyChangeValue(forKey: #keyPath(planeString0Color))
173             
174             return
175         }
176         if context == &equippedItem1Context {
177             
178             notifyChangeValue(forKey: #keyPath(planeString1))
179             notifyChangeValue(forKey: #keyPath(planeString1Color))
180             
181             return
182         }
183         if context == &equippedItem2Context {
184             
185             notifyChangeValue(forKey: #keyPath(planeString2))
186             notifyChangeValue(forKey: #keyPath(planeString2Color))
187             
188             return
189         }
190         if context == &equippedItem3Context {
191             
192             notifyChangeValue(forKey: #keyPath(planeString3))
193             notifyChangeValue(forKey: #keyPath(planeString3Color))
194             
195             return
196         }
197         if context == &equippedItem4Context {
198             
199             notifyChangeValue(forKey: #keyPath(planeString4))
200             notifyChangeValue(forKey: #keyPath(planeString4Color))
201             
202             return
203         }
204         
205         super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
206     }
207 }
208
209
210 private let allPlaneTypes: [Int] = [6, 7, 8, 9, 10, 11, 25, 26, 41, 45, 56, 57, 58, 59]
211
212
213 extension ShipDetailViewController {
214     
215     // MARK: - Plane count strings
216     private enum PlaneState {
217         
218         case cannotEquip
219         case notEquip(Int)
220         case equiped(Int, Int)
221     }
222     
223     private func planState(_ index: Int) -> PlaneState {
224         
225         guard let ship = ship else { return .cannotEquip }
226         
227         let itemId = ship.slotItemId(index)
228         let maxCount = ship.slotItemMax(index)
229         
230         if maxCount == 0 { return .cannotEquip }
231         if itemId == -1 { return .notEquip(maxCount) }
232         
233         if let item = ship.slotItem(index),
234             allPlaneTypes.contains(item.master_slotItem.type_2) {
235             
236             return .equiped(ship.slotItemCount(index), maxCount)
237         }
238         
239         return .notEquip(maxCount)
240     }
241     
242     private func planeString(_ index: Int) -> String? {
243         
244         switch planState(index) {
245         case .cannotEquip:
246             return nil
247         case .notEquip(let max):
248             return "\(max)"
249         case .equiped(let count, let max):
250             return "\(count)/\(max)"
251         }
252     }
253     
254     @objc dynamic var planeString0: String? { return planeString(0) }
255     
256     @objc dynamic var planeString1: String? { return planeString(1) }
257     
258     @objc dynamic var planeString2: String? { return planeString(2) }
259     
260     @objc dynamic var planeString3: String? { return planeString(3) }
261     
262     @objc dynamic var planeString4: String? { return planeString(4) }
263     
264     // MARK: - Plane count string color
265     private func planeStringColor(_ index: Int) -> NSColor {
266         
267         switch planState(index) {
268         case .cannotEquip: return NSColor.controlTextColor
269         case .notEquip: return NSColor.disabledControlTextColor
270         case .equiped: return NSColor.controlTextColor
271         }
272     }
273     
274     @objc dynamic var planeString0Color: NSColor { return planeStringColor(0) }
275     
276     @objc dynamic var planeString1Color: NSColor { return planeStringColor(1) }
277     
278     @objc dynamic var planeString2Color: NSColor { return planeStringColor(2) }
279     
280     @objc dynamic var planeString3Color: NSColor { return planeStringColor(3) }
281     
282     @objc dynamic var planeString4Color: NSColor { return planeStringColor(4) }
283 }
284
285 extension ShipDetailViewController: NibLoadable {}