OSDN Git Service

Swift4.0にコンバートした
[kcd/KCD.git] / KCD / BroserWindowController.swift
1 //
2 //  BroserWindowController.swift
3 //  KCD
4 //
5 //  Created by Hori,Masaki on 2016/12/31.
6 //  Copyright © 2016年 Hori,Masaki. All rights reserved.
7 //
8
9 import Cocoa
10
11
12 private extension Selector {
13     
14     static let reloadContent = #selector(BroserWindowController.reloadContent(_:))
15     static let deleteCacheAndReload = #selector(BroserWindowController.deleteCacheAndReload(_:))
16     static let clearQuestList = #selector(BroserWindowController.clearQuestList(_:))
17     static let selectView = #selector(BroserWindowController.selectView(_:))
18     static let changeMainTab = #selector(BroserWindowController.changeMainTab(_:))
19     static let screenShot = #selector(BroserWindowController.screenShot(_:))
20     static let toggleAnchorageSize = #selector(BroserWindowController.toggleAnchorageSize(_:))
21     static let showHideCombinedView = #selector(BroserWindowController.showHideCombinedView(_:))
22     static let fleetListAbove = #selector(BroserWindowController.fleetListAbove(_:))
23     static let fleetListBelow = #selector(BroserWindowController.fleetListBelow(_:))
24     static let fleetListDivide = #selector(BroserWindowController.fleetListDivide(_:))
25     static let fleetListSimple = #selector(BroserWindowController.fleetListSimple(_:))
26     static let reorderToDoubleLine = #selector(BroserWindowController.reorderToDoubleLine(_:))
27     static let reorderToLeftToRight = #selector(BroserWindowController.reorderToLeftToRight(_:))
28     static let selectNextFleet = #selector(BroserWindowController.selectNextFleet(_:))
29     static let selectPreviousFleet = #selector(BroserWindowController.selectPreviousFleet(_:))
30     static let changeSakutekiCalculator = #selector(BroserWindowController.changeSakutekiCalculator(_:))
31 }
32
33 final class BroserWindowController: NSWindowController {
34     
35     enum FleetViewPosition: Int {
36         
37         case above = 0
38         case below = 1
39         case divided = 2
40         case oldStyle = 0xffffffff
41     }
42     
43     @objc class func keyPathsForValuesAffectingFlagShipName() -> Set<String> {
44         
45         return [#keyPath(flagShipID)]
46     }
47     
48     @objc let managedObjectContext = ServerDataStore.default.context
49     
50     deinit {
51         
52         NotificationCenter.default.removeObserver(self)
53     }
54     
55     @IBOutlet weak var placeholder: NSView!
56     @IBOutlet weak var combinedViewPlaceholder: NSView!
57     @IBOutlet weak var deckPlaceholder: NSView!
58     @IBOutlet weak var resourcePlaceholder: NSView!
59     @IBOutlet weak var ancherageRepariTimerPlaceholder: NSView!
60     @IBOutlet weak var informations: NSTabView!
61     @IBOutlet var deckContoller: NSArrayController!
62     
63     override var windowNibName: NSNib.Name {
64         
65         return .nibName(instanceOf: self)
66     }
67     
68     @objc var flagShipID: Int = 0
69     @objc var flagShipName: String? {
70         return ServerDataStore.default.ship(by: flagShipID)?.name
71     }
72     var changeMainTabHandler: ((Int) -> Void)?
73     @objc dynamic var selectedMainTabIndex: Int = 0 {
74         
75         didSet {
76             changeMainTabHandler?(selectedMainTabIndex)
77         }
78     }
79     
80     private var gameViewController: GameViewController!
81     private var fleetViewController: FleetViewController!
82     private var tabViewItemViewControllers: [MainTabVIewItemViewController] = []
83     private var ancherageRepariTimerViewController: AncherageRepairTimerViewController!
84     private var resourceViewController: ResourceViewController!
85     private var docksViewController: DocksViewController!
86     private var shipViewController: ShipViewController!
87     private var powerUpViewController: PowerUpSupportViewController!
88     private var strengthedListViewController: StrengthenListViewController!
89     private var repairListViewController: RepairListViewController!
90     private var combinedViewController: CombileViewController!
91     
92     private var fleetViewPosition: FleetViewPosition = .above
93     private var isCombinedMode = false
94     
95     // MARK: - Function
96     override func windowDidLoad() {
97         
98         super.windowDidLoad()
99     
100         gameViewController = GameViewController()
101         replace(placeholder, with: gameViewController)
102         
103         resourceViewController = ResourceViewController()
104         replace(resourcePlaceholder, with: resourceViewController)
105         
106         ancherageRepariTimerViewController = AncherageRepairTimerViewController()
107         replace(ancherageRepariTimerPlaceholder, with: ancherageRepariTimerViewController)
108         if UserDefaults.standard[.screenshotButtonSize] == .small { toggleAnchorageSize(nil) }
109         
110         tabViewItemViewControllers = [
111             DocksViewController(),
112             ShipViewController(),
113             PowerUpSupportViewController(),
114             StrengthenListViewController(),
115             RepairListViewController()
116         ]
117         tabViewItemViewControllers.enumerated().forEach {
118             
119             _ = $0.element.view
120             let item = informations.tabViewItem(at: $0.offset)
121             item.viewController = $0.element
122         }
123         
124         fleetViewController = FleetViewController(viewType: .detailViewType)
125         replace(deckPlaceholder, with: fleetViewController)
126         setFleetView(position: UserDefaults.standard[.fleetViewPosition], animate: false)
127         fleetViewController.enableAnimation = false
128         fleetViewController.shipOrder = UserDefaults.standard[.fleetViewShipOrder]
129         fleetViewController.enableAnimation = true
130         
131         bind(NSBindingName(rawValue: #keyPath(flagShipID)), to: deckContoller, withKeyPath: "selection.ship_0", options: nil)
132         
133         NotificationCenter.default
134             .addObserver(forName: .CombinedDidCange, object: nil, queue: nil) {
135                 
136                 guard UserDefaults.standard[.autoCombinedView],
137                     let type = $0.userInfo?[CombinedCommand.userInfoKey] as? CombineType
138                     else { return }
139                 
140                 if !Thread.isMainThread { Thread.sleep(forTimeInterval: 0.1) }
141                 
142                 DispatchQueue.main.async {
143                     
144                     switch type {
145                     case .cancel:
146                         self.hideCombinedView()
147                         
148                     case .maneuver, .water, .transportation:
149                         self.showCombinedView()
150                     }
151                 }
152         }
153         
154         if UserDefaults.standard[.lastHasCombinedView] { showCombinedView() }
155     }
156     
157     override func swipe(with event: NSEvent) {
158         
159         guard UserDefaults.standard[.useSwipeChangeCombinedView]
160             else { return }
161         
162         if event.deltaX > 0 {
163             
164             showCombinedView()
165             
166         }
167         
168         if event.deltaX < 0 {
169             
170             hideCombinedView()
171             
172         }
173     }
174     
175     @objc func windowWillClose(_ notification: Notification) {
176         
177         UserDefaults.standard[.lastHasCombinedView] = isCombinedMode
178     }
179     
180     private func replace(_ view: NSView, with viewController: NSViewController) {
181         
182         viewController.view.frame = view.frame
183         viewController.view.autoresizingMask = view.autoresizingMask
184         self.window?.contentView?.replaceSubview(view, with: viewController.view)
185     }
186     
187     private func showCombinedView() {
188         
189         if isCombinedMode { return }
190         
191         if fleetViewPosition == .oldStyle { return }
192         
193         isCombinedMode = true
194         
195         if combinedViewController == nil {
196             
197             combinedViewController = CombileViewController()
198             combinedViewController.view.isHidden = true
199             replace(combinedViewPlaceholder, with: combinedViewController)
200         }
201         
202         var winFrame = window!.frame
203         let incWid = combinedViewController.view.frame.maxX
204         winFrame.size.width += incWid
205         winFrame.origin.x -= incWid
206         combinedViewController.view.isHidden = false
207         window?.setFrame(winFrame, display: true, animate: true)
208     }
209     
210     private func hideCombinedView() {
211         
212         if !isCombinedMode { return }
213         
214         isCombinedMode = false
215         
216         var winFrame = window!.frame
217         let decWid = combinedViewController.view.frame.maxX
218         winFrame.size.width -= decWid
219         winFrame.origin.x += decWid
220         window?.setFrame(winFrame, display: true, animate: true)
221         combinedViewController.view.isHidden = true
222     }
223 }
224
225 // MARK: - IBAction
226 extension BroserWindowController {
227     
228     private func showView(number: Int) {
229         
230         informations.selectTabViewItem(at: number)
231     }
232     
233     @IBAction func reloadContent(_ sender: AnyObject?) {
234         
235         gameViewController.reloadContent(sender)
236     }
237     
238     @IBAction func deleteCacheAndReload(_ sender: AnyObject?) {
239         
240         gameViewController.deleteCacheAndReload(sender)
241     }
242     
243     @IBAction func clearQuestList(_ sender: AnyObject?) {
244         
245         let store = ServerDataStore.oneTimeEditor()
246         store.quests().forEach { store.delete($0) }
247     }
248     
249     @IBAction func selectView(_ sender: AnyObject?) {
250         
251         guard let tag = sender?.tag else { return }
252         
253         showView(number: tag)
254     }
255     
256     @IBAction func changeMainTab(_ sender: AnyObject?) {
257         
258         guard let segment = sender?.selectedSegment else { return }
259         
260         showView(number: segment)
261     }
262     
263     @IBAction func screenShot(_ sender: AnyObject?) {
264         
265         gameViewController.screenShot(sender)
266     }
267     
268     @IBAction func toggleAnchorageSize(_ sender: AnyObject?) {
269         
270         let current = ancherageRepariTimerViewController.controlSize
271         var diff = AncherageRepairTimerViewController.regularHeight - AncherageRepairTimerViewController.smallHeight
272         let newSize: NSControl.ControlSize = {
273             
274             if current == .regular {
275                 
276                 diff *= -1
277                 
278                 return .small
279             }
280             
281             return .regular
282         }()
283         ancherageRepariTimerViewController.controlSize = newSize
284         
285         var frame = informations.frame
286         frame.size.height -= diff
287         frame.origin.y += diff
288         informations.frame = frame
289         
290         UserDefaults.standard[.screenshotButtonSize] = newSize
291     }
292     
293     @IBAction func showHideCombinedView(_ sender: AnyObject?) {
294         
295          isCombinedMode ? hideCombinedView() : showCombinedView()
296     }
297     
298     @IBAction func fleetListAbove(_ sender: AnyObject?) {
299         
300         setFleetView(position: .above, animate: true)
301     }
302     
303     @IBAction func fleetListBelow(_ sender: AnyObject?) {
304         
305         setFleetView(position: .below, animate: true)
306     }
307     
308     @IBAction func fleetListDivide(_ sender: AnyObject?) {
309         
310         setFleetView(position: .divided, animate: true)
311     }
312     
313     @IBAction func fleetListSimple(_ sender: AnyObject?) {
314         
315         setFleetView(position: .oldStyle, animate: true)
316     }
317     
318     @IBAction func reorderToDoubleLine(_ sender: AnyObject?) {
319         
320         fleetViewController.shipOrder = .doubleLine
321         UserDefaults.standard[.fleetViewShipOrder] = .doubleLine
322     }
323     
324     @IBAction func reorderToLeftToRight(_ sender: AnyObject?) {
325         
326         fleetViewController.shipOrder = .leftToRight
327         UserDefaults.standard[.fleetViewShipOrder] = .leftToRight
328     }
329     
330     @IBAction func selectNextFleet(_ sender: AnyObject?) {
331         
332         fleetViewController.selectNextFleet(sender)
333     }
334     
335     @IBAction func selectPreviousFleet(_ sender: AnyObject?) {
336         
337         fleetViewController.selectPreviousFleet(sender)
338     }
339     
340     @IBAction func changeSakutekiCalculator(_ sender: Any?) {
341         
342         fleetViewController.changeSakutekiCalculator(sender)
343     }
344     
345     override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
346         
347         guard let action: Selector = menuItem.action else { return false }
348         
349         switch action {
350         case Selector.reloadContent, Selector.screenShot, Selector.deleteCacheAndReload:
351             return gameViewController.validateMenuItem(menuItem)
352             
353         case Selector.selectView, Selector.selectNextFleet, Selector.selectPreviousFleet:
354             return true
355             
356         case Selector.fleetListAbove:
357             menuItem.state = (fleetViewPosition == .above ? .on : .off)
358             return true
359             
360         case Selector.fleetListBelow:
361             menuItem.state = (fleetViewPosition == .below ? .on : .off)
362             return true
363             
364         case Selector.fleetListDivide:
365             menuItem.state = (fleetViewPosition == .divided ? .on : .off)
366             return true
367             
368         case Selector.fleetListSimple:
369             menuItem.state = (fleetViewPosition == .oldStyle ? .on : .off)
370             return true
371             
372         case Selector.reorderToDoubleLine:
373             menuItem.state = (fleetViewController.shipOrder == .doubleLine ? .on : .off)
374             return true
375             
376         case Selector.reorderToLeftToRight:
377             menuItem.state = (fleetViewController.shipOrder == .leftToRight ? .on: .off)
378             return true
379             
380         case Selector.clearQuestList:
381             return true
382             
383         case Selector.showHideCombinedView:
384             if isCombinedMode {
385                 
386                 menuItem.title = NSLocalizedString("Hide Combined View", comment: "View menu, hide combined view")
387                 
388             } else {
389                 
390                 menuItem.title = NSLocalizedString("Show Combined View", comment: "View menu, show combined view")
391                 
392             }
393             if fleetViewPosition == .oldStyle { return false }
394             
395             return true
396             
397         case Selector.toggleAnchorageSize:
398             return true
399             
400         case Selector.changeSakutekiCalculator:
401             return fleetViewController.validateMenuItem(menuItem)
402             
403         default:
404             return false
405         }
406     }
407 }
408
409 extension BroserWindowController {
410     
411     private static let margin: CGFloat = 1.0
412     private static let flashTopMargin: CGFloat = 4.0
413     
414     private func changeFleetViewForFleetViewPositionIfNeeded(position newPosition: FleetViewPosition) {
415         
416         if fleetViewPosition == newPosition { return }
417         if fleetViewPosition != .oldStyle && newPosition != .oldStyle { return }
418         if newPosition == .oldStyle && isCombinedMode { hideCombinedView() }
419         
420         let type: FleetViewType = (newPosition == .oldStyle) ? .minimumViewType : .detailViewType
421         
422         guard let newController = FleetViewController(viewType: type)
423             else { return }
424         
425         newController.enableAnimation = true
426         newController.shipOrder = fleetViewController.shipOrder
427         replace(fleetViewController.view, with: newController)
428         fleetViewController = newController
429     }
430     
431     private func windowHeightForFleetViewPosition(position newPosition: FleetViewPosition) -> CGFloat {
432         
433         guard var contentHeight = window!.contentView?.frame.size.height
434             else { return 0.0 }
435         
436         if fleetViewPosition == newPosition { return contentHeight }
437         if fleetViewPosition == .oldStyle {
438             
439             contentHeight += FleetViewController.heightDifference
440         }
441         if newPosition == .oldStyle {
442             
443             contentHeight -= FleetViewController.heightDifference
444         }
445         
446         return contentHeight
447     }
448     
449     private func windowFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
450         
451         var contentRect = window!.frame
452         
453         if fleetViewPosition == newPosition { return contentRect }
454         if fleetViewPosition == .oldStyle {
455             
456             contentRect.size.height += FleetViewController.heightDifference
457             contentRect.origin.y -= FleetViewController.heightDifference
458         }
459         if newPosition == .oldStyle {
460             
461             contentRect.size.height -= FleetViewController.heightDifference
462             contentRect.origin.y += FleetViewController.heightDifference
463         }
464         
465         return contentRect
466     }
467     
468     private func flashFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
469         
470         let contentHeight = windowHeightForFleetViewPosition(position: newPosition)
471         
472         var flashRect = gameViewController.view.frame
473         var flashY: CGFloat
474         switch newPosition {
475         case .above:
476             flashY = contentHeight - flashRect.height - fleetViewController.normalHeight
477             
478         case .below:
479             flashY = contentHeight - flashRect.height
480             
481         case .divided:
482             flashY = contentHeight - flashRect.height - fleetViewController.upsideHeight - BroserWindowController.margin
483             
484         case .oldStyle:
485             flashY = contentHeight - flashRect.height - BroserWindowController.flashTopMargin
486         }
487         
488         flashRect.origin.y = flashY
489         
490         return flashRect
491     }
492     
493     private func fleetViewFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
494         
495         let contentHeight = windowHeightForFleetViewPosition(position: newPosition)
496         let flashRect = gameViewController.view.frame
497         var fleetListRect = fleetViewController.view.frame
498         
499         var fleetViewHeight: CGFloat
500         var fleetViewY: CGFloat
501         
502         switch newPosition {
503         case .above:
504             fleetViewHeight = fleetViewController.normalHeight
505             fleetViewY = contentHeight - fleetViewHeight
506             
507         case .below:
508             fleetViewHeight = fleetViewController.normalHeight
509             fleetViewY = contentHeight - fleetViewHeight - flashRect.height - BroserWindowController.margin
510             
511         case .divided:
512             fleetViewHeight = fleetViewController.normalHeight + flashRect.height + BroserWindowController.margin + BroserWindowController.margin
513             fleetViewY = contentHeight - fleetViewHeight
514             
515         case .oldStyle:
516             fleetViewHeight = FleetViewController.oldStyleFleetViewHeight
517             fleetViewY = contentHeight - fleetViewHeight - flashRect.height - BroserWindowController.margin - BroserWindowController.flashTopMargin
518         }
519         
520         fleetListRect.size.height = fleetViewHeight
521         fleetListRect.origin.y = fleetViewY
522         
523         return fleetListRect
524     }
525     
526     private func setFleetView(position newPosition: FleetViewPosition, animate: Bool) {
527         
528         guard let window = window else { return }
529         
530         changeFleetViewForFleetViewPositionIfNeeded(position: newPosition)
531         let winFrame = windowFrameForFleetViewPosition(position: newPosition)
532         let flashRect = flashFrameForFleetViewPosition(position: newPosition)
533         let fleetListRect = fleetViewFrameForFleetViewPosition(position: newPosition)
534         
535         fleetViewPosition = newPosition
536         UserDefaults.standard[.fleetViewPosition] = newPosition
537         
538         if animate {
539             
540             let winAnime: [NSViewAnimation.Key: Any]  = [.target: window,
541                                                          .endFrame: NSValue(rect: winFrame) ]
542             let flashAnime: [NSViewAnimation.Key: Any] = [.target: gameViewController.view,
543                                                           .endFrame: NSValue(rect: flashRect) ]
544             let fleetAnime: [NSViewAnimation.Key: Any] = [.target: fleetViewController.view,
545                                                           .endFrame: NSValue(rect: fleetListRect) ]
546             
547             let anime = NSViewAnimation(viewAnimations: [winAnime, flashAnime, fleetAnime])
548             
549             anime.start()
550             
551         } else {
552             
553             window.setFrame(winFrame, display: false)
554             gameViewController.view.frame = flashRect
555             fleetViewController.view.frame = fleetListRect
556         }
557     }
558     
559 }
560
561 @available(OSX 10.12.2, *)
562 private var mainTouchBars: [Int: NSTouchBar] = [:]
563 @available(OSX 10.12.2, *)
564 private var shipTypeButtons: [Int: NSPopoverTouchBarItem] = [:]
565 @available(OSX 10.12.2, *)
566 private var shipTypeSegments: [Int: NSSegmentedControl] = [:]
567
568 @available(OSX 10.12.2, *)
569 extension BroserWindowController {
570     
571     @IBOutlet var mainTouchBar: NSTouchBar! {
572         
573         get { return mainTouchBars[hashValue] }
574         set { mainTouchBars[hashValue] = newValue }
575     }
576     
577     @IBOutlet var shipTypeButton: NSPopoverTouchBarItem! {
578         
579         get { return shipTypeButtons[hashValue] }
580         set { shipTypeButtons[hashValue] = newValue }
581     }
582     
583     @IBOutlet var shipTypeSegment: NSSegmentedControl! {
584         
585         get { return shipTypeSegments[hashValue] }
586         set { shipTypeSegments[hashValue] = newValue }
587     }
588     
589     override func makeTouchBar() -> NSTouchBar? {
590         
591         if let mainTouchBar = mainTouchBar { return mainTouchBar }
592         
593         var array: NSArray?
594         Bundle.main.loadNibNamed(NSNib.Name("BroswerTouchBar"), owner: self, topLevelObjects: &array)
595         
596         shipTypeSegment.bind(.selectedIndex,
597                              to: tabViewItemViewControllers[0],
598                              withKeyPath: #keyPath(MainTabVIewItemViewController.selectedShipType),
599                              options: nil)
600         let o = selectedMainTabIndex
601         selectedMainTabIndex = o
602         
603         changeMainTabHandler = { [weak self] in
604             
605             guard let `self` = self else { return }
606             
607             self.shipTypeButton.dismissPopover(nil)
608             self.shipTypeSegment.unbind(.selectedIndex)
609             
610             guard let button = self.shipTypeButton.view as? NSButton
611                 else { return }
612             
613             let vc = self.tabViewItemViewControllers[$0]
614             button.isHidden = !vc.hasShipTypeSelector
615             self.shipTypeSegment.bind(.selectedIndex,
616                                       to: vc,
617                                       withKeyPath: #keyPath(MainTabVIewItemViewController.selectedShipType),
618                                       options: nil)
619         }
620         
621         return mainTouchBar
622     }
623 }