OSDN Git Service

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