OSDN Git Service

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