OSDN Git Service

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