2 // BroserWindowController.swift
5 // Created by Hori,Masaki on 2016/12/31.
6 // Copyright © 2016年 Hori,Masaki. All rights reserved.
12 private extension Selector {
14 static let reloadContent = #selector(BroserWindowController.reloadContent(_:))
15 static let deleteCacheAndReload = #selector(BroserWindowController.deleteCacheAndReload(_:))
16 static let clearQuestList = #selector(BroserWindowController.clearQuestList(_:))
17 static let selectView = #selector(BroserWindowController.selectView(_:))
18 static let changeMainTab = #selector(BroserWindowController.changeMainTab(_:))
19 static let screenShot = #selector(BroserWindowController.screenShot(_:))
20 static let toggleAnchorageSize = #selector(BroserWindowController.toggleAnchorageSize(_:))
21 static let showHideCombinedView = #selector(BroserWindowController.showHideCombinedView(_:))
22 static let fleetListAbove = #selector(BroserWindowController.fleetListAbove(_:))
23 static let fleetListBelow = #selector(BroserWindowController.fleetListBelow(_:))
24 static let fleetListDivide = #selector(BroserWindowController.fleetListDivide(_:))
25 static let fleetListSimple = #selector(BroserWindowController.fleetListSimple(_:))
26 static let reorderToDoubleLine = #selector(BroserWindowController.reorderToDoubleLine(_:))
27 static let reorderToLeftToRight = #selector(BroserWindowController.reorderToLeftToRight(_:))
28 static let selectNextFleet = #selector(BroserWindowController.selectNextFleet(_:))
29 static let selectPreviousFleet = #selector(BroserWindowController.selectPreviousFleet(_:))
30 static let changeSakutekiCalculator = #selector(BroserWindowController.changeSakutekiCalculator(_:))
33 final class BroserWindowController: NSWindowController {
35 enum FleetViewPosition: Int {
40 case oldStyle = 0xffffffff
43 @objc class func keyPathsForValuesAffectingFlagShipName() -> Set<String> {
45 return [#keyPath(flagShipID)]
48 @objc let managedObjectContext = ServerDataStore.default.context
52 NotificationCenter.default.removeObserver(self)
55 @IBOutlet weak var placeholder: NSView!
56 @IBOutlet weak var combinedViewPlaceholder: NSView!
57 @IBOutlet weak var deckPlaceholder: NSView!
58 @IBOutlet weak var resourcePlaceholder: NSView!
59 @IBOutlet weak var ancherageRepariTimerPlaceholder: NSView!
60 @IBOutlet weak var informations: NSTabView!
61 @IBOutlet var deckContoller: NSArrayController!
63 override var windowNibName: NSNib.Name {
65 return .nibName(instanceOf: self)
68 @objc var flagShipID: Int = 0
69 @objc var flagShipName: String? {
70 return ServerDataStore.default.ship(by: flagShipID)?.name
72 var changeMainTabHandler: ((Int) -> Void)?
73 @objc dynamic var selectedMainTabIndex: Int = 0 {
76 changeMainTabHandler?(selectedMainTabIndex)
80 private var gameViewController: GameViewController!
81 private var fleetViewController: FleetViewController!
82 private var tabViewItemViewControllers: [MainTabVIewItemViewController] = []
83 private var ancherageRepariTimerViewController: AncherageRepairTimerViewController!
84 private var resourceViewController: ResourceViewController!
85 private var docksViewController: DocksViewController!
86 private var shipViewController: ShipViewController!
87 private var powerUpViewController: PowerUpSupportViewController!
88 private var strengthedListViewController: StrengthenListViewController!
89 private var repairListViewController: RepairListViewController!
90 private var combinedViewController: CombileViewController!
92 private var fleetViewPosition: FleetViewPosition = .above
93 private var isCombinedMode = false
96 override func windowDidLoad() {
100 gameViewController = GameViewController()
101 replace(placeholder, with: gameViewController)
103 resourceViewController = ResourceViewController()
104 replace(resourcePlaceholder, with: resourceViewController)
106 ancherageRepariTimerViewController = AncherageRepairTimerViewController()
107 replace(ancherageRepariTimerPlaceholder, with: ancherageRepariTimerViewController)
108 if UserDefaults.standard[.screenshotButtonSize] == .small { toggleAnchorageSize(nil) }
110 tabViewItemViewControllers = [
111 DocksViewController(),
112 ShipViewController(),
113 PowerUpSupportViewController(),
114 StrengthenListViewController(),
115 RepairListViewController()
117 tabViewItemViewControllers.enumerated().forEach {
120 let item = informations.tabViewItem(at: $0.offset)
121 item.viewController = $0.element
124 fleetViewController = FleetViewController(viewType: .detailViewType)
125 replace(deckPlaceholder, with: fleetViewController)
126 setFleetView(position: UserDefaults.standard[.fleetViewPosition], animate: false)
127 fleetViewController.enableAnimation = false
128 fleetViewController.shipOrder = UserDefaults.standard[.fleetViewShipOrder]
129 fleetViewController.enableAnimation = true
131 bind(NSBindingName(rawValue: #keyPath(flagShipID)), to: deckContoller, withKeyPath: "selection.ship_0", options: nil)
133 NotificationCenter.default
134 .addObserver(forName: .CombinedDidCange, object: nil, queue: nil) {
136 guard UserDefaults.standard[.autoCombinedView],
137 let type = $0.userInfo?[CombinedCommand.userInfoKey] as? CombineType
140 if !Thread.isMainThread { Thread.sleep(forTimeInterval: 0.1) }
142 DispatchQueue.main.async {
146 self.hideCombinedView()
148 case .maneuver, .water, .transportation:
149 self.showCombinedView()
154 if UserDefaults.standard[.lastHasCombinedView] { showCombinedView() }
157 override func swipe(with event: NSEvent) {
159 guard UserDefaults.standard[.useSwipeChangeCombinedView]
162 if event.deltaX > 0 {
168 if event.deltaX < 0 {
175 @objc func windowWillClose(_ notification: Notification) {
177 UserDefaults.standard[.lastHasCombinedView] = isCombinedMode
180 private func replace(_ view: NSView, with viewController: NSViewController) {
182 viewController.view.frame = view.frame
183 viewController.view.autoresizingMask = view.autoresizingMask
184 self.window?.contentView?.replaceSubview(view, with: viewController.view)
187 private func showCombinedView() {
189 if isCombinedMode { return }
191 if fleetViewPosition == .oldStyle { return }
193 isCombinedMode = true
195 if combinedViewController == nil {
197 combinedViewController = CombileViewController()
198 combinedViewController.view.isHidden = true
199 replace(combinedViewPlaceholder, with: combinedViewController)
202 var winFrame = window!.frame
203 let incWid = combinedViewController.view.frame.maxX
204 winFrame.size.width += incWid
205 winFrame.origin.x -= incWid
206 combinedViewController.view.isHidden = false
207 window?.setFrame(winFrame, display: true, animate: true)
210 private func hideCombinedView() {
212 if !isCombinedMode { return }
214 isCombinedMode = false
216 var winFrame = window!.frame
217 let decWid = combinedViewController.view.frame.maxX
218 winFrame.size.width -= decWid
219 winFrame.origin.x += decWid
220 window?.setFrame(winFrame, display: true, animate: true)
221 combinedViewController.view.isHidden = true
226 extension BroserWindowController {
228 private func showView(number: Int) {
230 informations.selectTabViewItem(at: number)
233 @IBAction func reloadContent(_ sender: AnyObject?) {
235 gameViewController.reloadContent(sender)
238 @IBAction func deleteCacheAndReload(_ sender: AnyObject?) {
240 gameViewController.deleteCacheAndReload(sender)
243 @IBAction func clearQuestList(_ sender: AnyObject?) {
245 let store = ServerDataStore.oneTimeEditor()
246 store.quests().forEach { store.delete($0) }
249 @IBAction func selectView(_ sender: AnyObject?) {
251 guard let tag = sender?.tag else { return }
253 showView(number: tag)
256 @IBAction func changeMainTab(_ sender: AnyObject?) {
258 guard let segment = sender?.selectedSegment else { return }
260 showView(number: segment)
263 @IBAction func screenShot(_ sender: AnyObject?) {
265 gameViewController.screenShot(sender)
268 @IBAction func toggleAnchorageSize(_ sender: AnyObject?) {
270 let current = ancherageRepariTimerViewController.controlSize
271 var diff = AncherageRepairTimerViewController.regularHeight - AncherageRepairTimerViewController.smallHeight
272 let newSize: NSControl.ControlSize = {
274 if current == .regular {
283 ancherageRepariTimerViewController.controlSize = newSize
285 var frame = informations.frame
286 frame.size.height -= diff
287 frame.origin.y += diff
288 informations.frame = frame
290 UserDefaults.standard[.screenshotButtonSize] = newSize
293 @IBAction func showHideCombinedView(_ sender: AnyObject?) {
295 isCombinedMode ? hideCombinedView() : showCombinedView()
298 @IBAction func fleetListAbove(_ sender: AnyObject?) {
300 setFleetView(position: .above, animate: true)
303 @IBAction func fleetListBelow(_ sender: AnyObject?) {
305 setFleetView(position: .below, animate: true)
308 @IBAction func fleetListDivide(_ sender: AnyObject?) {
310 setFleetView(position: .divided, animate: true)
313 @IBAction func fleetListSimple(_ sender: AnyObject?) {
315 setFleetView(position: .oldStyle, animate: true)
318 @IBAction func reorderToDoubleLine(_ sender: AnyObject?) {
320 fleetViewController.shipOrder = .doubleLine
321 UserDefaults.standard[.fleetViewShipOrder] = .doubleLine
324 @IBAction func reorderToLeftToRight(_ sender: AnyObject?) {
326 fleetViewController.shipOrder = .leftToRight
327 UserDefaults.standard[.fleetViewShipOrder] = .leftToRight
330 @IBAction func selectNextFleet(_ sender: AnyObject?) {
332 fleetViewController.selectNextFleet(sender)
335 @IBAction func selectPreviousFleet(_ sender: AnyObject?) {
337 fleetViewController.selectPreviousFleet(sender)
340 @IBAction func changeSakutekiCalculator(_ sender: Any?) {
342 fleetViewController.changeSakutekiCalculator(sender)
345 override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
347 guard let action: Selector = menuItem.action else { return false }
350 case Selector.reloadContent, Selector.screenShot, Selector.deleteCacheAndReload:
351 return gameViewController.validateMenuItem(menuItem)
353 case Selector.selectView, Selector.selectNextFleet, Selector.selectPreviousFleet:
356 case Selector.fleetListAbove:
357 menuItem.state = (fleetViewPosition == .above ? .on : .off)
360 case Selector.fleetListBelow:
361 menuItem.state = (fleetViewPosition == .below ? .on : .off)
364 case Selector.fleetListDivide:
365 menuItem.state = (fleetViewPosition == .divided ? .on : .off)
368 case Selector.fleetListSimple:
369 menuItem.state = (fleetViewPosition == .oldStyle ? .on : .off)
372 case Selector.reorderToDoubleLine:
373 menuItem.state = (fleetViewController.shipOrder == .doubleLine ? .on : .off)
376 case Selector.reorderToLeftToRight:
377 menuItem.state = (fleetViewController.shipOrder == .leftToRight ? .on: .off)
380 case Selector.clearQuestList:
383 case Selector.showHideCombinedView:
386 menuItem.title = NSLocalizedString("Hide Combined View", comment: "View menu, hide combined view")
390 menuItem.title = NSLocalizedString("Show Combined View", comment: "View menu, show combined view")
393 if fleetViewPosition == .oldStyle { return false }
397 case Selector.toggleAnchorageSize:
400 case Selector.changeSakutekiCalculator:
401 return fleetViewController.validateMenuItem(menuItem)
409 extension BroserWindowController {
411 private static let margin: CGFloat = 1.0
412 private static let flashTopMargin: CGFloat = 4.0
414 private func changeFleetViewForFleetViewPositionIfNeeded(position newPosition: FleetViewPosition) {
416 if fleetViewPosition == newPosition { return }
417 if fleetViewPosition != .oldStyle && newPosition != .oldStyle { return }
418 if newPosition == .oldStyle && isCombinedMode { hideCombinedView() }
420 let type: FleetViewType = (newPosition == .oldStyle) ? .minimumViewType : .detailViewType
422 guard let newController = FleetViewController(viewType: type)
425 newController.enableAnimation = true
426 newController.shipOrder = fleetViewController.shipOrder
427 replace(fleetViewController.view, with: newController)
428 fleetViewController = newController
431 private func windowHeightForFleetViewPosition(position newPosition: FleetViewPosition) -> CGFloat {
433 guard var contentHeight = window!.contentView?.frame.size.height
436 if fleetViewPosition == newPosition { return contentHeight }
437 if fleetViewPosition == .oldStyle {
439 contentHeight += FleetViewController.heightDifference
441 if newPosition == .oldStyle {
443 contentHeight -= FleetViewController.heightDifference
449 private func windowFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
451 var contentRect = window!.frame
453 if fleetViewPosition == newPosition { return contentRect }
454 if fleetViewPosition == .oldStyle {
456 contentRect.size.height += FleetViewController.heightDifference
457 contentRect.origin.y -= FleetViewController.heightDifference
459 if newPosition == .oldStyle {
461 contentRect.size.height -= FleetViewController.heightDifference
462 contentRect.origin.y += FleetViewController.heightDifference
468 private func flashFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
470 let contentHeight = windowHeightForFleetViewPosition(position: newPosition)
472 var flashRect = gameViewController.view.frame
476 flashY = contentHeight - flashRect.height - fleetViewController.normalHeight
479 flashY = contentHeight - flashRect.height
482 flashY = contentHeight - flashRect.height - fleetViewController.upsideHeight - BroserWindowController.margin
485 flashY = contentHeight - flashRect.height - BroserWindowController.flashTopMargin
488 flashRect.origin.y = flashY
493 private func fleetViewFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
495 let contentHeight = windowHeightForFleetViewPosition(position: newPosition)
496 let flashRect = gameViewController.view.frame
497 var fleetListRect = fleetViewController.view.frame
499 var fleetViewHeight: CGFloat
500 var fleetViewY: CGFloat
504 fleetViewHeight = fleetViewController.normalHeight
505 fleetViewY = contentHeight - fleetViewHeight
508 fleetViewHeight = fleetViewController.normalHeight
509 fleetViewY = contentHeight - fleetViewHeight - flashRect.height - BroserWindowController.margin
512 fleetViewHeight = fleetViewController.normalHeight + flashRect.height + BroserWindowController.margin + BroserWindowController.margin
513 fleetViewY = contentHeight - fleetViewHeight
516 fleetViewHeight = FleetViewController.oldStyleFleetViewHeight
517 fleetViewY = contentHeight - fleetViewHeight - flashRect.height - BroserWindowController.margin - BroserWindowController.flashTopMargin
520 fleetListRect.size.height = fleetViewHeight
521 fleetListRect.origin.y = fleetViewY
526 private func setFleetView(position newPosition: FleetViewPosition, animate: Bool) {
528 guard let window = window else { return }
530 changeFleetViewForFleetViewPositionIfNeeded(position: newPosition)
531 let winFrame = windowFrameForFleetViewPosition(position: newPosition)
532 let flashRect = flashFrameForFleetViewPosition(position: newPosition)
533 let fleetListRect = fleetViewFrameForFleetViewPosition(position: newPosition)
535 fleetViewPosition = newPosition
536 UserDefaults.standard[.fleetViewPosition] = newPosition
540 let winAnime: [NSViewAnimation.Key: Any] = [.target: window,
541 .endFrame: NSValue(rect: winFrame) ]
542 let flashAnime: [NSViewAnimation.Key: Any] = [.target: gameViewController.view,
543 .endFrame: NSValue(rect: flashRect) ]
544 let fleetAnime: [NSViewAnimation.Key: Any] = [.target: fleetViewController.view,
545 .endFrame: NSValue(rect: fleetListRect) ]
547 let anime = NSViewAnimation(viewAnimations: [winAnime, flashAnime, fleetAnime])
553 window.setFrame(winFrame, display: false)
554 gameViewController.view.frame = flashRect
555 fleetViewController.view.frame = fleetListRect
561 @available(OSX 10.12.2, *)
562 private var mainTouchBars: [Int: NSTouchBar] = [:]
563 @available(OSX 10.12.2, *)
564 private var shipTypeButtons: [Int: NSPopoverTouchBarItem] = [:]
565 @available(OSX 10.12.2, *)
566 private var shipTypeSegments: [Int: NSSegmentedControl] = [:]
568 @available(OSX 10.12.2, *)
569 extension BroserWindowController {
571 @IBOutlet var mainTouchBar: NSTouchBar! {
573 get { return mainTouchBars[hashValue] }
574 set { mainTouchBars[hashValue] = newValue }
577 @IBOutlet var shipTypeButton: NSPopoverTouchBarItem! {
579 get { return shipTypeButtons[hashValue] }
580 set { shipTypeButtons[hashValue] = newValue }
583 @IBOutlet var shipTypeSegment: NSSegmentedControl! {
585 get { return shipTypeSegments[hashValue] }
586 set { shipTypeSegments[hashValue] = newValue }
589 override func makeTouchBar() -> NSTouchBar? {
591 if let mainTouchBar = mainTouchBar { return mainTouchBar }
594 Bundle.main.loadNibNamed(NSNib.Name("BroswerTouchBar"), owner: self, topLevelObjects: &array)
596 shipTypeSegment.bind(.selectedIndex,
597 to: tabViewItemViewControllers[0],
598 withKeyPath: #keyPath(MainTabVIewItemViewController.selectedShipType),
600 let o = selectedMainTabIndex
601 selectedMainTabIndex = o
603 changeMainTabHandler = { [weak self] in
605 guard let `self` = self else { return }
607 self.shipTypeButton.dismissPopover(nil)
608 self.shipTypeSegment.unbind(.selectedIndex)
610 guard let button = self.shipTypeButton.view as? NSButton
613 let vc = self.tabViewItemViewControllers[$0]
614 button.isHidden = !vc.hasShipTypeSelector
615 self.shipTypeSegment.bind(.selectedIndex,
617 withKeyPath: #keyPath(MainTabVIewItemViewController.selectedShipType),