OSDN Git Service

判定式33に対応
[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
12 fileprivate extension Selector {
13     static let reloadContent = #selector(BroserWindowController.reloadContent(_:))
14     static let deleteCacheAndReload = #selector(BroserWindowController.deleteCacheAndReload(_:))
15     static let clearQuestList = #selector(BroserWindowController.clearQuestList(_:))
16     static let selectView = #selector(BroserWindowController.selectView(_:))
17     static let changeMainTab = #selector(BroserWindowController.changeMainTab(_:))
18     static let screenShot = #selector(BroserWindowController.screenShot(_:))
19     static let toggleAnchorageSize = #selector(BroserWindowController.toggleAnchorageSize(_:))
20     static let showHideCombinedView = #selector(BroserWindowController.showHideCombinedView(_:))
21     static let fleetListAbove = #selector(BroserWindowController.fleetListAbove(_:))
22     static let fleetListBelow = #selector(BroserWindowController.fleetListBelow(_:))
23     static let fleetListDivide = #selector(BroserWindowController.fleetListDivide(_:))
24     static let fleetListSimple = #selector(BroserWindowController.fleetListSimple(_:))
25     static let reorderToDoubleLine = #selector(BroserWindowController.reorderToDoubleLine(_:))
26     static let reorderToLeftToRight = #selector(BroserWindowController.reorderToLeftToRight(_:))
27     static let selectNextFleet = #selector(BroserWindowController.selectNextFleet(_:))
28     static let selectPreviousFleet = #selector(BroserWindowController.selectPreviousFleet(_:))
29     static let changeSakutekiCalculator = #selector(BroserWindowController.changeSakutekiCalculator(_:))
30 }
31
32 class BroserWindowController: NSWindowController {
33     enum FleetViewPosition: Int {
34         case above = 0
35         case below = 1
36         case divided = 2
37         case oldStyle = 0xffffffff
38     }
39     
40     class func keyPathsForValuesAffectingFlagShipName() -> Set<String> {
41         return [#keyPath(flagShipID)]
42     }
43     
44     let managedObjectContext = ServerDataStore.default.context
45     
46     deinit {
47         NotificationCenter.default.removeObserver(self)
48     }
49     
50     @IBOutlet weak var placeholder: NSView!
51     @IBOutlet weak var combinedViewPlaceholder: NSView!
52     @IBOutlet weak var deckPlaceholder: NSView!
53     @IBOutlet weak var resourcePlaceholder: NSView!
54     @IBOutlet weak var ancherageRepariTimerPlaceholder: NSView!
55     @IBOutlet weak var informations: NSTabView!
56     @IBOutlet var deckContoller: NSArrayController!
57     
58     override var windowNibName: String! {
59         return "BroserWindowController"
60     }
61     var flagShipID: Int = 0
62     var flagShipName: String? {
63         return ServerDataStore.default.ship(by: flagShipID)?.name
64     }
65     var changeMainTabHandler: ((Int) -> Void)?
66     dynamic var selectedMainTabIndex: Int = 0 {
67         didSet {
68             changeMainTabHandler?(selectedMainTabIndex)
69         }
70     }
71     
72     fileprivate var gameViewController: GameViewController!
73     fileprivate var fleetViewController: FleetViewController!
74     fileprivate var tabViewItemViewControllers: [MainTabVIewItemViewController] = []
75     fileprivate var ancherageRepariTimerViewController: AncherageRepairTimerViewController!
76     private var resourceViewController: ResourceViewController!
77     private var docksViewController: DocksViewController!
78     private var shipViewController: ShipViewController!
79     private var powerUpViewController: PowerUpSupportViewController!
80     private var strengthedListViewController: StrengthenListViewController!
81     private var repairListViewController: RepairListViewController!
82     private var combinedViewController: CombileViewController!
83     
84     fileprivate var fleetViewPosition: FleetViewPosition = .above
85     fileprivate var isCombinedMode = false
86     
87     // MARK: - Function
88     override func windowDidLoad() {
89         super.windowDidLoad()
90     
91         gameViewController = GameViewController()
92         replace(placeholder, with: gameViewController)
93         
94         resourceViewController = ResourceViewController()
95         replace(resourcePlaceholder, with: resourceViewController)
96         
97         ancherageRepariTimerViewController = AncherageRepairTimerViewController()
98         replace(ancherageRepariTimerPlaceholder, with: ancherageRepariTimerViewController)
99         if UserDefaults.standard.screenshotButtonSize == .small { toggleAnchorageSize(nil) }
100         
101         tabViewItemViewControllers = [
102             DocksViewController(),
103             ShipViewController(),
104             PowerUpSupportViewController(),
105             StrengthenListViewController(),
106             RepairListViewController()
107         ]
108         tabViewItemViewControllers.enumerated().forEach {
109             let _ = $0.element.view
110             let item = informations.tabViewItem(at: $0.offset)
111             item.viewController = $0.element
112         }
113         
114         fleetViewController = FleetViewController(viewType: .detailViewType)
115         replace(deckPlaceholder, with: fleetViewController)
116         setFleetView(position: UserDefaults.standard.fleetViewPosition, animate: false)
117         fleetViewController.enableAnimation = false
118         fleetViewController.shipOrder = UserDefaults.standard.fleetViewShipOrder
119         fleetViewController.enableAnimation = true
120         
121         bind(#keyPath(flagShipID), to: deckContoller, withKeyPath: "selection.ship_0", options: nil)
122         
123         NotificationCenter.default
124             .addObserver(forName: .CombinedDidCange, object: nil, queue: nil) {
125                 guard UserDefaults.standard.autoCombinedView,
126                     let type = $0.userInfo?[CombinedCommand.userInfoKey] as? CombineType
127                     else { return }
128                 if !Thread.isMainThread { Thread.sleep(forTimeInterval: 0.1) }
129                 DispatchQueue.main.async {
130                     switch type {
131                     case .cancel:
132                         self.hideCombinedView()
133                     case .maneuver, .water, .transportation:
134                         self.showCombinedView()
135                     }
136                 }
137         }
138         
139         if UserDefaults.standard.lastHasCombinedView { showCombinedView() }
140     }
141     override func swipe(with event: NSEvent) {
142         guard UserDefaults.standard.useSwipeChangeCombinedView else { return }
143         if event.deltaX > 0 {
144             showCombinedView()
145         }
146         if event.deltaX < 0 {
147             hideCombinedView()
148         }
149     }
150     
151     func windowWillClose(_ notification: Notification) {
152         UserDefaults.standard.lastHasCombinedView = isCombinedMode
153     }
154     
155     fileprivate func replace(_ view: NSView, with viewController: NSViewController) {
156         viewController.view.frame = view.frame
157         viewController.view.autoresizingMask = view.autoresizingMask
158         self.window?.contentView?.replaceSubview(view, with: viewController.view)
159     }
160     fileprivate func showCombinedView() {
161         if isCombinedMode { return }
162         if fleetViewPosition == .oldStyle { return }
163         isCombinedMode = true
164         if combinedViewController == nil {
165             combinedViewController = CombileViewController()
166             combinedViewController.view.isHidden = true
167             replace(combinedViewPlaceholder, with: combinedViewController)
168         }
169         
170         var winFrame = window!.frame
171         let incWid = combinedViewController.view.frame.maxX
172         winFrame.size.width += incWid
173         winFrame.origin.x -= incWid
174         combinedViewController.view.isHidden = false
175         window?.setFrame(winFrame, display: true, animate: true)
176     }
177     fileprivate func hideCombinedView() {
178         if !isCombinedMode { return }
179         isCombinedMode = false
180         var winFrame = window!.frame
181         let decWid = combinedViewController.view.frame.maxX
182         winFrame.size.width -= decWid
183         winFrame.origin.x += decWid
184         window?.setFrame(winFrame, display: true, animate: true)
185         combinedViewController.view.isHidden = true
186     }
187 }
188 // MARK: - IBAction
189 extension BroserWindowController {
190     private func showView(number: Int) {
191         informations.selectTabViewItem(at: number)
192     }
193     @IBAction func reloadContent(_ sender: AnyObject?) {
194         gameViewController.reloadContent(sender)
195     }
196     @IBAction func deleteCacheAndReload(_ sender: AnyObject?) {
197         gameViewController.deleteCacheAndReload(sender)
198     }
199     @IBAction func clearQuestList(_ sender: AnyObject?) {
200         let store = ServerDataStore.oneTimeEditor()
201         store.quests().forEach { store.delete($0) }
202     }
203     @IBAction func selectView(_ sender: AnyObject?) {
204         guard let tag = sender?.tag else { return }
205         showView(number: tag)
206     }
207     @IBAction func changeMainTab(_ sender: AnyObject?) {
208         guard let segment = sender?.selectedSegment else { return }
209         showView(number: segment)
210     }
211     @IBAction func screenShot(_ sender: AnyObject?) {
212         gameViewController.screenShot(sender)
213     }
214     @IBAction func toggleAnchorageSize(_ sender: AnyObject?) {
215         let current = ancherageRepariTimerViewController.controlSize
216         var diff = AncherageRepairTimerViewController.regularHeight - AncherageRepairTimerViewController.smallHeight
217         let newSize: NSControlSize = {
218             if current == .regular {
219                 diff *= -1
220                 return .small
221             }
222             return .regular
223         }()
224         ancherageRepariTimerViewController.controlSize = newSize
225         
226         var frame = informations.frame
227         frame.size.height -= diff
228         frame.origin.y += diff
229         informations.frame = frame
230         
231         UserDefaults.standard.screenshotButtonSize = newSize
232     }
233     @IBAction func showHideCombinedView(_ sender: AnyObject?) {
234          isCombinedMode ? hideCombinedView() : showCombinedView()
235     }
236     
237     @IBAction func fleetListAbove(_ sender: AnyObject?) {
238         setFleetView(position: .above, animate: true)
239     }
240     @IBAction func fleetListBelow(_ sender: AnyObject?) {
241         setFleetView(position: .below, animate: true)
242     }
243     @IBAction func fleetListDivide(_ sender: AnyObject?) {
244         setFleetView(position: .divided, animate: true)
245     }
246     @IBAction func fleetListSimple(_ sender: AnyObject?) {
247         setFleetView(position: .oldStyle, animate: true)
248     }
249     @IBAction func reorderToDoubleLine(_ sender: AnyObject?) {
250         fleetViewController.shipOrder = .doubleLine
251         UserDefaults.standard.fleetViewShipOrder = .doubleLine
252     }
253     @IBAction func reorderToLeftToRight(_ sender: AnyObject?) {
254         fleetViewController.shipOrder = .leftToRight
255         UserDefaults.standard.fleetViewShipOrder = .leftToRight
256     }
257     @IBAction func selectNextFleet(_ sender: AnyObject?) {
258         fleetViewController.selectNextFleet(sender)
259     }
260     @IBAction func selectPreviousFleet(_ sender: AnyObject?) {
261         fleetViewController.selectPreviousFleet(sender)
262     }
263     @IBAction func changeSakutekiCalculator(_ sender: Any?) {
264         fleetViewController.changeSakutekiCalculator(sender)
265     }
266     
267     override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
268         guard let action: Selector = menuItem.action else { return false }
269         switch action {
270         case Selector.reloadContent, Selector.screenShot, Selector.deleteCacheAndReload:
271             return gameViewController.validateMenuItem(menuItem)
272         case Selector.selectView, Selector.selectNextFleet, Selector.selectPreviousFleet:
273             return true
274         case Selector.fleetListAbove:
275             menuItem.state = fleetViewPosition == .above ? NSOnState : NSOffState
276             return true
277         case Selector.fleetListBelow:
278             menuItem.state = fleetViewPosition == .below ? NSOnState : NSOffState
279             return true
280         case Selector.fleetListDivide:
281             menuItem.state = fleetViewPosition == .divided ? NSOnState : NSOffState
282             return true
283         case Selector.fleetListSimple:
284             menuItem.state = fleetViewPosition == .oldStyle ? NSOnState : NSOffState
285             return true
286         case Selector.reorderToDoubleLine:
287             menuItem.state = fleetViewController.shipOrder == .doubleLine ? NSOnState : NSOffState
288             return true
289         case Selector.reorderToLeftToRight:
290             menuItem.state = fleetViewController.shipOrder == .leftToRight ? NSOnState: NSOffState
291             return true
292         case Selector.clearQuestList:
293             return true
294         case Selector.showHideCombinedView:
295             if isCombinedMode {
296                 menuItem.title = NSLocalizedString("Hide Combined View", comment: "View menu, hide combined view")
297             } else {
298                 menuItem.title = NSLocalizedString("Show Combined View", comment: "View menu, show combined view")
299             }
300             if fleetViewPosition == .oldStyle { return false }
301             return true
302         case Selector.toggleAnchorageSize:
303             return true
304         case Selector.changeSakutekiCalculator:
305             return fleetViewController.validateMenuItem(menuItem)
306         default:
307             return false
308         }
309     }
310 }
311
312 extension BroserWindowController {
313     private static let margin: CGFloat = 1.0
314     private static let flashTopMargin: CGFloat = 4.0
315     
316     private func changeFleetViewForFleetViewPositionIfNeeded(position newPosition: FleetViewPosition) {
317         if fleetViewPosition == newPosition { return }
318         if fleetViewPosition != .oldStyle && newPosition != .oldStyle { return }
319         if newPosition == .oldStyle && isCombinedMode { hideCombinedView() }
320         let type: FleetViewType = (newPosition == .oldStyle) ? .minimumViewType : .detailViewType
321         guard let newController = FleetViewController(viewType: type) else { return }
322         newController.enableAnimation = true
323         newController.shipOrder = fleetViewController.shipOrder
324         replace(fleetViewController.view, with: newController)
325         fleetViewController = newController
326     }
327     private func windowHeightForFleetViewPosition(position newPosition: FleetViewPosition) -> CGFloat {
328         guard var contentHeight = window!.contentView?.frame.size.height else { return 0.0 }
329         if fleetViewPosition == newPosition { return contentHeight }
330         if fleetViewPosition == .oldStyle {
331             contentHeight += FleetViewController.heightDifference
332         }
333         if newPosition == .oldStyle {
334             contentHeight -= FleetViewController.heightDifference
335         }
336         return contentHeight
337     }
338     private func windowFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
339         var contentRect = window!.frame
340         if fleetViewPosition == newPosition { return contentRect }
341         if fleetViewPosition == .oldStyle {
342             contentRect.size.height += FleetViewController.heightDifference
343             contentRect.origin.y -= FleetViewController.heightDifference
344         }
345         if newPosition == .oldStyle {
346             contentRect.size.height -= FleetViewController.heightDifference
347             contentRect.origin.y += FleetViewController.heightDifference
348         }
349         return contentRect
350     }
351     private func flashFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
352         let contentHeight = windowHeightForFleetViewPosition(position: newPosition)
353         var flashRect = gameViewController.view.frame
354         var flashY: CGFloat
355         switch newPosition {
356         case .above:
357             flashY = contentHeight - flashRect.height - fleetViewController.normalHeight
358         case .below:
359             flashY = contentHeight - flashRect.height
360         case .divided:
361             flashY = contentHeight
362                 - flashRect.height
363                 - fleetViewController.upsideHeight
364                 - BroserWindowController.margin
365         case .oldStyle:
366             flashY = contentHeight - flashRect.height - BroserWindowController.flashTopMargin
367         }
368         flashRect.origin.y = flashY
369         return flashRect
370     }
371     private func fleetViewFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
372         let contentHeight = windowHeightForFleetViewPosition(position: newPosition)
373         let flashRect = gameViewController.view.frame
374         var fleetListRect = fleetViewController.view.frame
375         
376         var fleetViewHeight: CGFloat
377         var fleetViewY: CGFloat
378         switch newPosition {
379         case .above:
380             fleetViewHeight = fleetViewController.normalHeight
381             fleetViewY = contentHeight - fleetViewHeight
382         case .below:
383             fleetViewHeight = fleetViewController.normalHeight
384             fleetViewY = contentHeight
385                 - fleetViewHeight
386                 - flashRect.height
387                 - BroserWindowController.margin
388         case .divided:
389             fleetViewHeight = fleetViewController.normalHeight
390                 + flashRect.height
391                 + BroserWindowController.margin
392                 + BroserWindowController.margin
393             fleetViewY = contentHeight - fleetViewHeight
394         case .oldStyle:
395             fleetViewHeight = FleetViewController.oldStyleFleetViewHeight
396             fleetViewY = contentHeight
397                 - fleetViewHeight
398                 - flashRect.height
399                 - BroserWindowController.margin
400                 - BroserWindowController.flashTopMargin
401         }
402         fleetListRect.size.height = fleetViewHeight
403         fleetListRect.origin.y = fleetViewY
404         return fleetListRect
405     }
406     fileprivate func setFleetView(position newPosition: FleetViewPosition, animate: Bool) {
407         guard let window = window else { return }
408         changeFleetViewForFleetViewPositionIfNeeded(position: newPosition)
409         let winFrame = windowFrameForFleetViewPosition(position: newPosition)
410         let flashRect = flashFrameForFleetViewPosition(position: newPosition)
411         let fleetListRect = fleetViewFrameForFleetViewPosition(position: newPosition)
412         
413         fleetViewPosition = newPosition
414         UserDefaults.standard.fleetViewPosition = newPosition
415         
416         if animate {
417             let winAnime: [String: Any]  = [ NSViewAnimationTargetKey: window,
418                              NSViewAnimationEndFrameKey: NSValue(rect: winFrame) ]
419             let flashAnime: [String: Any] = [ NSViewAnimationTargetKey: gameViewController.view,
420                                NSViewAnimationEndFrameKey: NSValue(rect: flashRect) ]
421             let fleetAnime: [String: Any] = [ NSViewAnimationTargetKey: fleetViewController.view,
422                                NSViewAnimationEndFrameKey: NSValue(rect: fleetListRect) ]
423             let anime = NSViewAnimation(viewAnimations: [winAnime, flashAnime, fleetAnime])
424             anime.start()
425         } else {
426             window.setFrame(winFrame, display: false)
427             gameViewController.view.frame = flashRect
428             fleetViewController.view.frame = fleetListRect
429         }
430     }
431     
432 }
433
434 @available(OSX 10.12.2, *)
435 fileprivate var mainTouchBars: [Int: NSTouchBar] = [:]
436 @available(OSX 10.12.2, *)
437 fileprivate var shipTypeButtons: [Int: NSPopoverTouchBarItem] = [:]
438 @available(OSX 10.12.2, *)
439 fileprivate var shipTypeSegments: [Int: NSSegmentedControl] = [:]
440
441 @available(OSX 10.12.2, *)
442 extension BroserWindowController {
443     @IBOutlet var mainTouchBar: NSTouchBar! {
444         get { return mainTouchBars[hashValue] }
445         set { mainTouchBars[hashValue] = newValue }
446     }
447     @IBOutlet var shipTypeButton: NSPopoverTouchBarItem! {
448         get { return shipTypeButtons[hashValue] }
449         set { shipTypeButtons[hashValue] = newValue }
450     }
451     @IBOutlet var shipTypeSegment: NSSegmentedControl! {
452         get { return shipTypeSegments[hashValue] }
453         set { shipTypeSegments[hashValue] = newValue }
454     }
455     
456     override func makeTouchBar() -> NSTouchBar? {
457         if let mainTouchBar = mainTouchBar { return mainTouchBar }
458         var array: NSArray = []
459         Bundle.main.loadNibNamed("BroswerTouchBar", owner: self, topLevelObjects: &array)
460         
461         shipTypeSegment.bind(NSSelectedIndexBinding,
462                              to: tabViewItemViewControllers[0],
463                              withKeyPath: #keyPath(MainTabVIewItemViewController.selectedShipType),
464                              options: nil)
465         let o = selectedMainTabIndex
466         selectedMainTabIndex = o
467         
468         changeMainTabHandler = { [weak self] in
469             guard let `self` = self else { return }
470             self.shipTypeButton.dismissPopover(nil)
471             self.shipTypeSegment.unbind(NSSelectedIndexBinding)
472             guard let button = self.shipTypeButton.view as? NSButton else { return }
473             let vc = self.tabViewItemViewControllers[$0]
474             button.isHidden = !vc.hasShipTypeSelector
475             self.shipTypeSegment.bind(NSSelectedIndexBinding,
476                                       to: vc,
477                                       withKeyPath: #keyPath(MainTabVIewItemViewController.selectedShipType),
478                                       options: nil)
479         }
480         return mainTouchBar
481     }
482 }