OSDN Git Service

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