OSDN Git Service

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