OSDN Git Service

洋上補給の補強増設用のショートネームをつけた
[kcd/KCD.git] / KCD / BroserWindowController.swift
1 //
2 //  BroserWindowController.swift
3 //  KCD
4 //
5 //  Created by Hori,Masaki on 2016/12/31.
6 //  Copyright © 2016年 Hori,Masaki. All rights reserved.
7 //
8
9 import Cocoa
10
11 final class BroserWindowController: NSWindowController {
12     
13     enum FleetViewPosition: Int {
14         
15         case above = 0
16         case below = 1
17         case divided = 2
18         case oldStyle = 0xffffffff
19     }
20     
21     @objc override class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String> {
22         
23         switch key {
24             
25         case #keyPath(flagShipName): return [#keyPath(flagShipID)]
26             
27         default: return []
28         }
29     }
30     
31     @objc let managedObjectContext = ServerDataStore.default.context
32     
33     deinit {
34         
35         NotificationCenter.default.removeObserver(self)
36     }
37     
38     @IBOutlet 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     // call from menu item
201     @IBAction func selectView(_ sender: AnyObject?) {
202         
203         guard let tag = sender?.tag else { return }
204         
205         showView(number: tag)
206     }
207     
208     // call from touch bar
209     @IBAction func changeMainTab(_ sender: AnyObject?) {
210         
211         guard let segment = sender?.selectedSegment else { return }
212         
213         showView(number: segment)
214     }
215     
216     @IBAction func screenShot(_ sender: AnyObject?) {
217         
218         gameViewController.screenShot(sender)
219     }
220     
221     @IBAction func toggleAnchorageSize(_ sender: AnyObject?) {
222         
223         let current = ancherageRepariTimerViewController.controlSize
224         var diff = AncherageRepairTimerViewController.regularHeight - AncherageRepairTimerViewController.smallHeight
225         let newSize: NSControl.ControlSize = {
226             
227             if current == .regular {
228                 
229                 diff *= -1
230                 
231                 return .small
232             }
233             
234             return .regular
235         }()
236         ancherageRepariTimerViewController.controlSize = newSize
237         
238         var frame = informantionViewController.view.frame
239         frame.size.height -= diff
240         frame.origin.y += diff
241         informantionViewController.view.frame = frame
242         
243         UserDefaults.standard[.screenshotButtonSize] = newSize
244     }
245     
246     @IBAction func showHideCombinedView(_ sender: AnyObject?) {
247         
248          isCombinedMode ? hideCombinedView() : showCombinedView()
249     }
250     
251     @IBAction func fleetListAbove(_ sender: AnyObject?) {
252         
253         setFleetView(position: .above, animate: true)
254     }
255     
256     @IBAction func fleetListBelow(_ sender: AnyObject?) {
257         
258         setFleetView(position: .below, animate: true)
259     }
260     
261     @IBAction func fleetListDivide(_ sender: AnyObject?) {
262         
263         setFleetView(position: .divided, animate: true)
264     }
265     
266     @IBAction func fleetListSimple(_ sender: AnyObject?) {
267         
268         setFleetView(position: .oldStyle, animate: true)
269     }
270     
271     @IBAction func reorderToDoubleLine(_ sender: AnyObject?) {
272         
273         fleetViewController.shipOrder = .doubleLine
274         UserDefaults.standard[.fleetViewShipOrder] = .doubleLine
275     }
276     
277     @IBAction func reorderToLeftToRight(_ sender: AnyObject?) {
278         
279         fleetViewController.shipOrder = .leftToRight
280         UserDefaults.standard[.fleetViewShipOrder] = .leftToRight
281     }
282     
283     @IBAction func selectNextFleet(_ sender: AnyObject?) {
284         
285         fleetViewController.selectNextFleet(sender)
286     }
287     
288     @IBAction func selectPreviousFleet(_ sender: AnyObject?) {
289         
290         fleetViewController.selectPreviousFleet(sender)
291     }
292     
293     @IBAction func changeSakutekiCalculator(_ sender: Any?) {
294         
295         fleetViewController.changeSakutekiCalculator(sender)
296     }
297     
298     override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
299         
300         guard let action: Selector = menuItem.action else { return false }
301         
302         switch action {
303             
304         case #selector(BroserWindowController.reloadContent(_:)),
305              #selector(BroserWindowController.screenShot(_:)),
306              #selector(BroserWindowController.deleteCacheAndReload(_:)):
307             
308             return gameViewController.validateMenuItem(menuItem)
309             
310         case #selector(BroserWindowController.selectView(_:)),
311              #selector(BroserWindowController.selectNextFleet(_:)),
312              #selector(BroserWindowController.selectPreviousFleet(_:)):
313             
314             return true
315             
316         case #selector(BroserWindowController.fleetListAbove(_:)):
317             menuItem.state = (fleetViewPosition == .above ? .on : .off)
318             return true
319             
320         case #selector(BroserWindowController.fleetListBelow(_:)):
321             menuItem.state = (fleetViewPosition == .below ? .on : .off)
322             return true
323             
324         case #selector(BroserWindowController.fleetListDivide(_:)):
325             menuItem.state = (fleetViewPosition == .divided ? .on : .off)
326             return true
327             
328         case #selector(BroserWindowController.fleetListSimple(_:)):
329             menuItem.state = (fleetViewPosition == .oldStyle ? .on : .off)
330             return true
331             
332         case #selector(BroserWindowController.reorderToDoubleLine(_:)):
333             menuItem.state = (fleetViewController.shipOrder == .doubleLine ? .on : .off)
334             return true
335             
336         case #selector(BroserWindowController.reorderToLeftToRight(_:)):
337             menuItem.state = (fleetViewController.shipOrder == .leftToRight ? .on: .off)
338             return true
339             
340         case #selector(BroserWindowController.clearQuestList(_:)):
341             return true
342             
343         case #selector(BroserWindowController.showHideCombinedView(_:)):
344             if isCombinedMode {
345                 
346                 menuItem.title = LocalizedStrings.hideCombinedView.string
347                 
348             } else {
349                 
350                 menuItem.title = LocalizedStrings.showCombinedView.string
351                 
352             }
353             if fleetViewPosition == .oldStyle { return false }
354             
355             return true
356             
357         case #selector(BroserWindowController.toggleAnchorageSize(_:)):
358             return true
359             
360         case #selector(BroserWindowController.changeSakutekiCalculator(_:)):
361             return fleetViewController.validateMenuItem(menuItem)
362             
363         default:
364             return false
365         }
366     }
367 }
368
369 extension BroserWindowController {
370     
371     private static let margin: CGFloat = 1.0
372     private static let flashTopMargin: CGFloat = 4.0
373     
374     private func changeFleetViewForFleetViewPositionIfNeeded(position newPosition: FleetViewPosition) {
375         
376         if fleetViewPosition == newPosition { return }
377         if fleetViewPosition != .oldStyle && newPosition != .oldStyle { return }
378         if newPosition == .oldStyle && isCombinedMode { hideCombinedView() }
379         
380         let type: FleetViewType = (newPosition == .oldStyle) ? .minimumViewType : .detailViewType
381         
382         guard let newController = FleetViewController(viewType: type) else { return }
383         
384         newController.enableAnimation = true
385         newController.shipOrder = fleetViewController.shipOrder
386         replace(view: fleetViewController.view, with: newController)
387         fleetViewController = newController
388         fleetViewController.delegate = self
389     }
390     
391     private func windowHeightForFleetViewPosition(position newPosition: FleetViewPosition) -> CGFloat {
392         
393         guard var contentHeight = window!.contentView?.frame.size.height else { return 0.0 }
394         
395         if fleetViewPosition == newPosition { return contentHeight }
396         if fleetViewPosition == .oldStyle {
397             
398             contentHeight += FleetViewController.heightDifference
399         }
400         if newPosition == .oldStyle {
401             
402             contentHeight -= FleetViewController.heightDifference
403         }
404         
405         return contentHeight
406     }
407     
408     private func windowFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
409         
410         var contentRect = window!.frame
411         
412         if fleetViewPosition == newPosition { return contentRect }
413         if fleetViewPosition == .oldStyle {
414             
415             contentRect.size.height += FleetViewController.heightDifference
416             contentRect.origin.y -= FleetViewController.heightDifference
417         }
418         if newPosition == .oldStyle {
419             
420             contentRect.size.height -= FleetViewController.heightDifference
421             contentRect.origin.y += FleetViewController.heightDifference
422         }
423         
424         return contentRect
425     }
426     
427     private func flashFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
428         
429         let contentHeight = windowHeightForFleetViewPosition(position: newPosition)
430         
431         var flashRect = gameViewController.view.frame
432         var flashY: CGFloat
433         switch newPosition {
434         case .above:
435             flashY = contentHeight - flashRect.height - fleetViewController.normalHeight
436             
437         case .below:
438             flashY = contentHeight - flashRect.height
439             
440         case .divided:
441             flashY = contentHeight - flashRect.height - fleetViewController.upsideHeight - BroserWindowController.margin
442             
443         case .oldStyle:
444             flashY = contentHeight - flashRect.height - BroserWindowController.flashTopMargin
445         }
446         
447         flashRect.origin.y = flashY
448         
449         return flashRect
450     }
451     
452     private func fleetViewFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
453         
454         let contentHeight = windowHeightForFleetViewPosition(position: newPosition)
455         let flashRect = gameViewController.view.frame
456         var fleetListRect = fleetViewController.view.frame
457         
458         var fleetViewHeight: CGFloat
459         var fleetViewY: CGFloat
460         
461         switch newPosition {
462         case .above:
463             fleetViewHeight = fleetViewController.normalHeight
464             fleetViewY = contentHeight - fleetViewHeight
465             
466         case .below:
467             fleetViewHeight = fleetViewController.normalHeight
468             fleetViewY = contentHeight - fleetViewHeight - flashRect.height - BroserWindowController.margin
469             
470         case .divided:
471             fleetViewHeight = fleetViewController.normalHeight + flashRect.height + BroserWindowController.margin + BroserWindowController.margin
472             fleetViewY = contentHeight - fleetViewHeight
473             
474         case .oldStyle:
475             fleetViewHeight = FleetViewController.oldStyleFleetViewHeight
476             fleetViewY = contentHeight - fleetViewHeight - flashRect.height - BroserWindowController.margin - BroserWindowController.flashTopMargin
477         }
478         
479         fleetListRect.size.height = fleetViewHeight
480         fleetListRect.origin.y = fleetViewY
481         
482         return fleetListRect
483     }
484     
485     private func setFleetView(position newPosition: FleetViewPosition, animate: Bool) {
486         
487         guard let window = window else { return }
488         
489         changeFleetViewForFleetViewPositionIfNeeded(position: newPosition)
490         let winFrame = windowFrameForFleetViewPosition(position: newPosition)
491         let flashRect = flashFrameForFleetViewPosition(position: newPosition)
492         let fleetListRect = fleetViewFrameForFleetViewPosition(position: newPosition)
493         
494         fleetViewPosition = newPosition
495         UserDefaults.standard[.fleetViewPosition] = newPosition
496         
497         if animate {
498             
499             let winAnime = ViewAnimationAttributes(target: window, endFrame: winFrame)
500             let flashAnime = ViewAnimationAttributes(target: gameViewController.view, endFrame: flashRect)
501             let fleetAnime = ViewAnimationAttributes(target: fleetViewController.view, endFrame: fleetListRect)
502             
503             let anime = ViewAnimation(viewAnimations: [winAnime, flashAnime, fleetAnime])
504             
505             anime.start()
506             
507         } else {
508             
509             window.setFrame(winFrame, display: false)
510             gameViewController.view.frame = flashRect
511             fleetViewController.view.frame = fleetListRect
512         }
513     }
514     
515 }
516
517 extension BroserWindowController: FleetViewControllerDelegate {
518     
519     func changeShowsExtShip(_ fleetViewController: FleetViewController, showsExtShip: Bool) {
520         
521         guard self.fleetViewController == fleetViewController else { return }
522         
523         if isExtShpMode && !showsExtShip {
524             
525             // hide
526             let diffHeight = fleetViewController.shipViewSize.height
527             
528             var iFrame = informantionViewController.view.frame
529             iFrame.origin.y -= diffHeight
530             iFrame.size.height += diffHeight
531             informantionViewController.view.animator().frame = iFrame
532             
533             var sFrame = stokerContainer.frame
534             sFrame.origin.y -= diffHeight
535             stokerContainer.animator().frame = sFrame
536             
537             isExtShpMode = false
538             
539         } else if !isExtShpMode && showsExtShip {
540             
541             //show
542             let diffHeight = fleetViewController.shipViewSize.height
543             
544             var iFrame = informantionViewController.view.frame
545             iFrame.origin.y += diffHeight
546             iFrame.size.height -= diffHeight
547             informantionViewController.view.animator().frame = iFrame
548             
549             var sFrame = stokerContainer.frame
550             sFrame.origin.y += diffHeight
551             stokerContainer.animator().frame = sFrame
552
553             isExtShpMode = true
554         }
555     }
556 }
557
558 @available(OSX 10.12.2, *)
559 private var mainTouchBars: [Int: NSTouchBar] = [:]
560 @available(OSX 10.12.2, *)
561 private var shipTypeButtons: [Int: NSPopoverTouchBarItem] = [:]
562 @available(OSX 10.12.2, *)
563 private var shipTypeSegments: [Int: NSSegmentedControl] = [:]
564
565 @available(OSX 10.12.2, *)
566 extension BroserWindowController {
567     
568     @IBOutlet private var mainTouchBar: NSTouchBar! {
569         
570         get { return mainTouchBars[hashValue] }
571         set { mainTouchBars[hashValue] = newValue }
572     }
573     
574     @IBOutlet private var shipTypeButton: NSPopoverTouchBarItem! {
575         
576         get { return shipTypeButtons[hashValue] }
577         set { shipTypeButtons[hashValue] = newValue }
578     }
579     
580     @IBOutlet private var shipTypeSegment: NSSegmentedControl! {
581         
582         get { return shipTypeSegments[hashValue] }
583         set { shipTypeSegments[hashValue] = newValue }
584     }
585     
586     override func makeTouchBar() -> NSTouchBar? {
587         
588         if let mainTouchBar = mainTouchBar { return mainTouchBar }
589         
590         Bundle.main.loadNibNamed(NSNib.Name("BroswerTouchBar"), owner: self, topLevelObjects: nil)
591         
592         shipTypeSegment.bind(.selectedIndex,
593                              to: informantionViewController,
594                              withKeyPath: #keyPath(InformationTabViewController.selectedShipType),
595                              options: nil)
596         
597         informantionViewController.selectionDidChangeHandler = { [weak self] in
598             
599             guard let `self` = self else { return }
600             
601             self.shipTypeButton.dismissPopover(nil)
602             
603             guard let button = self.shipTypeButton.view as? NSButton else { return }
604             button.isHidden = !self.informantionViewController.hasShipTypeSelector
605             
606             self.shipTypeSegment.bind(.selectedIndex,
607                                       to: self.informantionViewController,
608                                       withKeyPath: #keyPath(InformationTabViewController.selectedShipType),
609                                       options: nil)
610         }
611         
612         return mainTouchBar
613     }
614 }