OSDN Git Service

swift.yml を修正
[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 private weak var placeholder: NSView!
39     @IBOutlet private weak var combinedViewPlaceholder: NSView!
40     @IBOutlet private weak var deckPlaceholder: NSView!
41     @IBOutlet private weak var stokerContainer: NSView!
42     @IBOutlet private weak var resourcePlaceholder: NSView!
43     @IBOutlet private weak var ancherageRepariTimerPlaceholder: NSView!
44     @IBOutlet private weak var informations: NSTabView!
45     @IBOutlet private 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(view: placeholder, with: gameViewController)
87         
88         resourceViewController = ResourceViewController()
89         replace(view: resourcePlaceholder, with: resourceViewController)
90         
91         ancherageRepariTimerViewController = AncherageRepairTimerViewController()
92         replace(view: 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(view: 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 showCombinedView() {
163         
164         if isCombinedMode { return }
165         
166         if fleetViewPosition == .oldStyle { return }
167         
168         isCombinedMode = true
169         
170         if combinedViewController == nil {
171             
172             combinedViewController = CombileViewController()
173             combinedViewController.view.isHidden = true
174             replace(view: combinedViewPlaceholder, with: combinedViewController)
175         }
176         
177         var winFrame = window!.frame
178         let incWid = combinedViewController.view.frame.maxX
179         winFrame.size.width += incWid
180         winFrame.origin.x -= incWid
181         combinedViewController.view.isHidden = false
182         window?.setFrame(winFrame, display: true, animate: true)
183     }
184     
185     private func hideCombinedView() {
186         
187         if !isCombinedMode { return }
188         
189         isCombinedMode = false
190         
191         var winFrame = window!.frame
192         let decWid = combinedViewController.view.frame.maxX
193         winFrame.size.width -= decWid
194         winFrame.origin.x += decWid
195         window?.setFrame(winFrame, display: true, animate: true)
196         combinedViewController.view.isHidden = true
197     }
198 }
199
200 // MARK: - IBAction
201 extension BroserWindowController {
202     
203     private func showView(number: Int) {
204         
205         informations.selectTabViewItem(at: number)
206     }
207     
208     @IBAction func reloadContent(_ sender: AnyObject?) {
209         
210         gameViewController.reloadContent(sender)
211     }
212     
213     @IBAction func deleteCacheAndReload(_ sender: AnyObject?) {
214         
215         gameViewController.deleteCacheAndReload(sender)
216     }
217     
218     @IBAction func clearQuestList(_ sender: AnyObject?) {
219         
220         let store = ServerDataStore.oneTimeEditor()
221         store.quests().forEach(store.delete)
222     }
223     
224     @IBAction func selectView(_ sender: AnyObject?) {
225         
226         guard let tag = sender?.tag else { return }
227         
228         showView(number: tag)
229     }
230     
231     @IBAction func changeMainTab(_ sender: AnyObject?) {
232         
233         guard let segment = sender?.selectedSegment else { return }
234         
235         showView(number: segment)
236     }
237     
238     @IBAction func screenShot(_ sender: AnyObject?) {
239         
240         gameViewController.screenShot(sender)
241     }
242     
243     @IBAction func toggleAnchorageSize(_ sender: AnyObject?) {
244         
245         let current = ancherageRepariTimerViewController.controlSize
246         var diff = AncherageRepairTimerViewController.regularHeight - AncherageRepairTimerViewController.smallHeight
247         let newSize: NSControl.ControlSize = {
248             
249             if current == .regular {
250                 
251                 diff *= -1
252                 
253                 return .small
254             }
255             
256             return .regular
257         }()
258         ancherageRepariTimerViewController.controlSize = newSize
259         
260         var frame = informations.frame
261         frame.size.height -= diff
262         frame.origin.y += diff
263         informations.frame = frame
264         
265         UserDefaults.standard[.screenshotButtonSize] = newSize
266     }
267     
268     @IBAction func showHideCombinedView(_ sender: AnyObject?) {
269         
270          isCombinedMode ? hideCombinedView() : showCombinedView()
271     }
272     
273     @IBAction func fleetListAbove(_ sender: AnyObject?) {
274         
275         setFleetView(position: .above, animate: true)
276     }
277     
278     @IBAction func fleetListBelow(_ sender: AnyObject?) {
279         
280         setFleetView(position: .below, animate: true)
281     }
282     
283     @IBAction func fleetListDivide(_ sender: AnyObject?) {
284         
285         setFleetView(position: .divided, animate: true)
286     }
287     
288     @IBAction func fleetListSimple(_ sender: AnyObject?) {
289         
290         setFleetView(position: .oldStyle, animate: true)
291     }
292     
293     @IBAction func reorderToDoubleLine(_ sender: AnyObject?) {
294         
295         fleetViewController.shipOrder = .doubleLine
296         UserDefaults.standard[.fleetViewShipOrder] = .doubleLine
297     }
298     
299     @IBAction func reorderToLeftToRight(_ sender: AnyObject?) {
300         
301         fleetViewController.shipOrder = .leftToRight
302         UserDefaults.standard[.fleetViewShipOrder] = .leftToRight
303     }
304     
305     @IBAction func selectNextFleet(_ sender: AnyObject?) {
306         
307         fleetViewController.selectNextFleet(sender)
308     }
309     
310     @IBAction func selectPreviousFleet(_ sender: AnyObject?) {
311         
312         fleetViewController.selectPreviousFleet(sender)
313     }
314     
315     @IBAction func changeSakutekiCalculator(_ sender: Any?) {
316         
317         fleetViewController.changeSakutekiCalculator(sender)
318     }
319     
320     override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
321         
322         guard let action: Selector = menuItem.action else { return false }
323         
324         switch action {
325             
326         case #selector(BroserWindowController.reloadContent(_:)),
327              #selector(BroserWindowController.screenShot(_:)),
328              #selector(BroserWindowController.deleteCacheAndReload(_:)):
329             
330             return gameViewController.validateMenuItem(menuItem)
331             
332         case #selector(BroserWindowController.selectView(_:)),
333              #selector(BroserWindowController.selectNextFleet(_:)),
334              #selector(BroserWindowController.selectPreviousFleet(_:)):
335             
336             return true
337             
338         case #selector(BroserWindowController.fleetListAbove(_:)):
339             menuItem.state = (fleetViewPosition == .above ? .on : .off)
340             return true
341             
342         case #selector(BroserWindowController.fleetListBelow(_:)):
343             menuItem.state = (fleetViewPosition == .below ? .on : .off)
344             return true
345             
346         case #selector(BroserWindowController.fleetListDivide(_:)):
347             menuItem.state = (fleetViewPosition == .divided ? .on : .off)
348             return true
349             
350         case #selector(BroserWindowController.fleetListSimple(_:)):
351             menuItem.state = (fleetViewPosition == .oldStyle ? .on : .off)
352             return true
353             
354         case #selector(BroserWindowController.reorderToDoubleLine(_:)):
355             menuItem.state = (fleetViewController.shipOrder == .doubleLine ? .on : .off)
356             return true
357             
358         case #selector(BroserWindowController.reorderToLeftToRight(_:)):
359             menuItem.state = (fleetViewController.shipOrder == .leftToRight ? .on: .off)
360             return true
361             
362         case #selector(BroserWindowController.clearQuestList(_:)):
363             return true
364             
365         case #selector(BroserWindowController.showHideCombinedView(_:)):
366             if isCombinedMode {
367                 
368                 menuItem.title = LocalizedStrings.hideCombinedView.string
369                 
370             } else {
371                 
372                 menuItem.title = LocalizedStrings.showCombinedView.string
373                 
374             }
375             if fleetViewPosition == .oldStyle { return false }
376             
377             return true
378             
379         case #selector(BroserWindowController.toggleAnchorageSize(_:)):
380             return true
381             
382         case #selector(BroserWindowController.changeSakutekiCalculator(_:)):
383             return fleetViewController.validateMenuItem(menuItem)
384             
385         default:
386             return false
387         }
388     }
389 }
390
391 extension BroserWindowController {
392     
393     private static let margin: CGFloat = 1.0
394     private static let flashTopMargin: CGFloat = 4.0
395     
396     private func changeFleetViewForFleetViewPositionIfNeeded(position newPosition: FleetViewPosition) {
397         
398         if fleetViewPosition == newPosition { return }
399         if fleetViewPosition != .oldStyle && newPosition != .oldStyle { return }
400         if newPosition == .oldStyle && isCombinedMode { hideCombinedView() }
401         
402         let type: FleetViewType = (newPosition == .oldStyle) ? .minimumViewType : .detailViewType
403         
404         guard let newController = FleetViewController(viewType: type) else { return }
405         
406         newController.enableAnimation = true
407         newController.shipOrder = fleetViewController.shipOrder
408         replace(view: fleetViewController.view, with: newController)
409         fleetViewController = newController
410         fleetViewController.delegate = self
411     }
412     
413     private func windowHeightForFleetViewPosition(position newPosition: FleetViewPosition) -> CGFloat {
414         
415         guard var contentHeight = window!.contentView?.frame.size.height else { return 0.0 }
416         
417         if fleetViewPosition == newPosition { return contentHeight }
418         if fleetViewPosition == .oldStyle {
419             
420             contentHeight += FleetViewController.heightDifference
421         }
422         if newPosition == .oldStyle {
423             
424             contentHeight -= FleetViewController.heightDifference
425         }
426         
427         return contentHeight
428     }
429     
430     private func windowFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
431         
432         var contentRect = window!.frame
433         
434         if fleetViewPosition == newPosition { return contentRect }
435         if fleetViewPosition == .oldStyle {
436             
437             contentRect.size.height += FleetViewController.heightDifference
438             contentRect.origin.y -= FleetViewController.heightDifference
439         }
440         if newPosition == .oldStyle {
441             
442             contentRect.size.height -= FleetViewController.heightDifference
443             contentRect.origin.y += FleetViewController.heightDifference
444         }
445         
446         return contentRect
447     }
448     
449     private func flashFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
450         
451         let contentHeight = windowHeightForFleetViewPosition(position: newPosition)
452         
453         var flashRect = gameViewController.view.frame
454         var flashY: CGFloat
455         switch newPosition {
456         case .above:
457             flashY = contentHeight - flashRect.height - fleetViewController.normalHeight
458             
459         case .below:
460             flashY = contentHeight - flashRect.height
461             
462         case .divided:
463             flashY = contentHeight - flashRect.height - fleetViewController.upsideHeight - BroserWindowController.margin
464             
465         case .oldStyle:
466             flashY = contentHeight - flashRect.height - BroserWindowController.flashTopMargin
467         }
468         
469         flashRect.origin.y = flashY
470         
471         return flashRect
472     }
473     
474     private func fleetViewFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
475         
476         let contentHeight = windowHeightForFleetViewPosition(position: newPosition)
477         let flashRect = gameViewController.view.frame
478         var fleetListRect = fleetViewController.view.frame
479         
480         var fleetViewHeight: CGFloat
481         var fleetViewY: CGFloat
482         
483         switch newPosition {
484         case .above:
485             fleetViewHeight = fleetViewController.normalHeight
486             fleetViewY = contentHeight - fleetViewHeight
487             
488         case .below:
489             fleetViewHeight = fleetViewController.normalHeight
490             fleetViewY = contentHeight - fleetViewHeight - flashRect.height - BroserWindowController.margin
491             
492         case .divided:
493             fleetViewHeight = fleetViewController.normalHeight + flashRect.height + BroserWindowController.margin + BroserWindowController.margin
494             fleetViewY = contentHeight - fleetViewHeight
495             
496         case .oldStyle:
497             fleetViewHeight = FleetViewController.oldStyleFleetViewHeight
498             fleetViewY = contentHeight - fleetViewHeight - flashRect.height - BroserWindowController.margin - BroserWindowController.flashTopMargin
499         }
500         
501         fleetListRect.size.height = fleetViewHeight
502         fleetListRect.origin.y = fleetViewY
503         
504         return fleetListRect
505     }
506     
507     private func setFleetView(position newPosition: FleetViewPosition, animate: Bool) {
508         
509         guard let window = window else { return }
510         
511         changeFleetViewForFleetViewPositionIfNeeded(position: newPosition)
512         let winFrame = windowFrameForFleetViewPosition(position: newPosition)
513         let flashRect = flashFrameForFleetViewPosition(position: newPosition)
514         let fleetListRect = fleetViewFrameForFleetViewPosition(position: newPosition)
515         
516         fleetViewPosition = newPosition
517         UserDefaults.standard[.fleetViewPosition] = newPosition
518         
519         if animate {
520             
521             let winAnime = ViewAnimationAttributes(target: window, endFrame: winFrame)
522             let flashAnime = ViewAnimationAttributes(target: gameViewController.view, endFrame: flashRect)
523             let fleetAnime = ViewAnimationAttributes(target: fleetViewController.view, endFrame: fleetListRect)
524             
525             let anime = ViewAnimation(viewAnimations: [winAnime, flashAnime, fleetAnime])
526             
527             anime.start()
528             
529         } else {
530             
531             window.setFrame(winFrame, display: false)
532             gameViewController.view.frame = flashRect
533             fleetViewController.view.frame = fleetListRect
534         }
535     }
536     
537 }
538
539 extension BroserWindowController: FleetViewControllerDelegate {
540     
541     func changeShowsExtShip(_ fleetViewController: FleetViewController, showsExtShip: Bool) {
542         
543         guard self.fleetViewController == fleetViewController else { return }
544         
545         if isExtShpMode && !showsExtShip {
546             
547             // hide
548             let diffHeight = fleetViewController.shipViewSize.height
549             
550             var iFrame = informations.frame
551             iFrame.origin.y -= diffHeight
552             iFrame.size.height += diffHeight
553             informations.animator().frame = iFrame
554             
555             var sFrame = stokerContainer.frame
556             sFrame.origin.y -= diffHeight
557             stokerContainer.animator().frame = sFrame
558             
559             isExtShpMode = false
560             
561         } else if !isExtShpMode && showsExtShip {
562             
563             //show
564             let diffHeight = fleetViewController.shipViewSize.height
565             
566             var iFrame = informations.frame
567             iFrame.origin.y += diffHeight
568             iFrame.size.height -= diffHeight
569             informations.animator().frame = iFrame
570             
571             var sFrame = stokerContainer.frame
572             sFrame.origin.y += diffHeight
573             stokerContainer.animator().frame = sFrame
574
575             isExtShpMode = true
576         }
577     }
578 }
579
580 @available(OSX 10.12.2, *)
581 private var mainTouchBars: [Int: NSTouchBar] = [:]
582 @available(OSX 10.12.2, *)
583 private var shipTypeButtons: [Int: NSPopoverTouchBarItem] = [:]
584 @available(OSX 10.12.2, *)
585 private var shipTypeSegments: [Int: NSSegmentedControl] = [:]
586
587 @available(OSX 10.12.2, *)
588 extension BroserWindowController {
589     
590     @IBOutlet private var mainTouchBar: NSTouchBar! {
591         
592         get { return mainTouchBars[hashValue] }
593         set { mainTouchBars[hashValue] = newValue }
594     }
595     
596     @IBOutlet private var shipTypeButton: NSPopoverTouchBarItem! {
597         
598         get { return shipTypeButtons[hashValue] }
599         set { shipTypeButtons[hashValue] = newValue }
600     }
601     
602     @IBOutlet private var shipTypeSegment: NSSegmentedControl! {
603         
604         get { return shipTypeSegments[hashValue] }
605         set { shipTypeSegments[hashValue] = newValue }
606     }
607     
608     override func makeTouchBar() -> NSTouchBar? {
609         
610         if let mainTouchBar = mainTouchBar { return mainTouchBar }
611         
612         Bundle.main.loadNibNamed(NSNib.Name("BroswerTouchBar"), owner: self, topLevelObjects: nil)
613         
614         shipTypeSegment.bind(.selectedIndex,
615                              to: tabViewItemViewControllers[0],
616                              withKeyPath: #keyPath(MainTabVIewItemViewController.selectedShipType),
617                              options: nil)
618         let o = selectedMainTabIndex
619         selectedMainTabIndex = o
620         
621         changeMainTabHandler = { [weak self] in
622             
623             guard let `self` = self else { return }
624             
625             self.shipTypeButton.dismissPopover(nil)
626             self.shipTypeSegment.unbind(.selectedIndex)
627             
628             guard let button = self.shipTypeButton.view as? NSButton else { return }
629             
630             let vc = self.tabViewItemViewControllers[$0]
631             button.isHidden = !vc.hasShipTypeSelector
632             self.shipTypeSegment.bind(.selectedIndex,
633                                       to: vc,
634                                       withKeyPath: #keyPath(MainTabVIewItemViewController.selectedShipType),
635                                       options: nil)
636         }
637         
638         return mainTouchBar
639     }
640 }