2 // BroserWindowController.swift
5 // Created by Hori,Masaki on 2016/12/31.
6 // Copyright © 2016年 Hori,Masaki. All rights reserved.
11 final class BroserWindowController: NSWindowController {
13 enum FleetViewPosition: Int {
18 case oldStyle = 0xffffffff
21 @objc override class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String> {
25 case #keyPath(flagShipName): return [#keyPath(flagShipID)]
31 @objc let managedObjectContext = ServerDataStore.default.context
35 NotificationCenter.default.removeObserver(self)
38 @IBOutlet weak var placeholder: NSView!
39 @IBOutlet weak var combinedViewPlaceholder: NSView!
40 @IBOutlet weak var deckPlaceholder: NSView!
41 @IBOutlet weak var stokerContainer: NSView!
42 @IBOutlet weak var resourcePlaceholder: NSView!
43 @IBOutlet weak var ancherageRepariTimerPlaceholder: NSView!
44 @IBOutlet weak var informations: NSTabView!
45 @IBOutlet var deckContoller: NSArrayController!
47 override var windowNibName: NSNib.Name {
49 return .nibName(instanceOf: self)
52 @objc var flagShipID: Int = 0
53 @objc var flagShipName: String? {
54 return ServerDataStore.default.ship(by: flagShipID)?.name
56 var changeMainTabHandler: ((Int) -> Void)?
57 @objc dynamic var selectedMainTabIndex: Int = 0 {
60 changeMainTabHandler?(selectedMainTabIndex)
64 private var gameViewController: GameViewController!
65 private var fleetViewController: FleetViewController!
66 private var tabViewItemViewControllers: [MainTabVIewItemViewController] = []
67 private var ancherageRepariTimerViewController: AncherageRepairTimerViewController!
68 private var resourceViewController: ResourceViewController!
69 private var docksViewController: DocksViewController!
70 private var shipViewController: ShipViewController!
71 private var powerUpViewController: PowerUpSupportViewController!
72 private var strengthedListViewController: StrengthenListViewController!
73 private var repairListViewController: RepairListViewController!
74 private var combinedViewController: CombileViewController!
76 private var fleetViewPosition: FleetViewPosition = .above
77 private var isCombinedMode = false
78 private var isExtShpMode = false
81 override func windowDidLoad() {
85 gameViewController = GameViewController()
86 replace(placeholder, with: gameViewController)
88 resourceViewController = ResourceViewController()
89 replace(resourcePlaceholder, with: resourceViewController)
91 ancherageRepariTimerViewController = AncherageRepairTimerViewController()
92 replace(ancherageRepariTimerPlaceholder, with: ancherageRepariTimerViewController)
93 if UserDefaults.standard[.screenshotButtonSize] == .small { toggleAnchorageSize(nil) }
95 tabViewItemViewControllers = [
96 DocksViewController(),
98 PowerUpSupportViewController(),
99 StrengthenListViewController(),
100 RepairListViewController()
102 tabViewItemViewControllers.enumerated().forEach {
105 let item = informations.tabViewItem(at: $0.offset)
106 item.viewController = $0.element
109 fleetViewController = FleetViewController(viewType: .detailViewType)
110 replace(deckPlaceholder, with: fleetViewController)
111 setFleetView(position: UserDefaults.standard[.fleetViewPosition], animate: false)
112 fleetViewController.enableAnimation = false
113 fleetViewController.shipOrder = UserDefaults.standard[.fleetViewShipOrder]
114 fleetViewController.enableAnimation = true
115 fleetViewController.delegate = self
117 bind(NSBindingName(rawValue: #keyPath(flagShipID)), to: deckContoller, withKeyPath: "selection.ship_0", options: nil)
119 NotificationCenter.default
120 .addObserver(forName: .CombinedDidCange, object: nil, queue: nil) {
122 guard UserDefaults.standard[.autoCombinedView] else { return }
123 guard let type = $0.userInfo?[CombinedCommand.userInfoKey] as? CombineType else { return }
125 if !Thread.isMainThread { Thread.sleep(forTimeInterval: 0.1) }
127 DispatchQueue.main.async {
131 self.hideCombinedView()
133 case .maneuver, .water, .transportation:
134 self.showCombinedView()
139 if UserDefaults.standard[.lastHasCombinedView] { showCombinedView() }
142 override func swipe(with event: NSEvent) {
144 guard UserDefaults.standard[.useSwipeChangeCombinedView] else { return }
146 if event.deltaX > 0 {
151 if event.deltaX < 0 {
157 @objc func windowWillClose(_ notification: Notification) {
159 UserDefaults.standard[.lastHasCombinedView] = isCombinedMode
162 private func replace(_ view: NSView, with viewController: NSViewController) {
164 viewController.view.frame = view.frame
165 viewController.view.autoresizingMask = view.autoresizingMask
166 view.superview?.replaceSubview(view, with: viewController.view)
169 private func showCombinedView() {
171 if isCombinedMode { return }
173 if fleetViewPosition == .oldStyle { return }
175 isCombinedMode = true
177 if combinedViewController == nil {
179 combinedViewController = CombileViewController()
180 combinedViewController.view.isHidden = true
181 replace(combinedViewPlaceholder, with: combinedViewController)
184 var winFrame = window!.frame
185 let incWid = combinedViewController.view.frame.maxX
186 winFrame.size.width += incWid
187 winFrame.origin.x -= incWid
188 combinedViewController.view.isHidden = false
189 window?.setFrame(winFrame, display: true, animate: true)
192 private func hideCombinedView() {
194 if !isCombinedMode { return }
196 isCombinedMode = false
198 var winFrame = window!.frame
199 let decWid = combinedViewController.view.frame.maxX
200 winFrame.size.width -= decWid
201 winFrame.origin.x += decWid
202 window?.setFrame(winFrame, display: true, animate: true)
203 combinedViewController.view.isHidden = true
208 extension BroserWindowController {
210 private func showView(number: Int) {
212 informations.selectTabViewItem(at: number)
215 @IBAction func reloadContent(_ sender: AnyObject?) {
217 gameViewController.reloadContent(sender)
220 @IBAction func deleteCacheAndReload(_ sender: AnyObject?) {
222 gameViewController.deleteCacheAndReload(sender)
225 @IBAction func clearQuestList(_ sender: AnyObject?) {
227 let store = ServerDataStore.oneTimeEditor()
228 store.quests().forEach(store.delete)
231 @IBAction func selectView(_ sender: AnyObject?) {
233 guard let tag = sender?.tag else { return }
235 showView(number: tag)
238 @IBAction func changeMainTab(_ sender: AnyObject?) {
240 guard let segment = sender?.selectedSegment else { return }
242 showView(number: segment)
245 @IBAction func screenShot(_ sender: AnyObject?) {
247 gameViewController.screenShot(sender)
250 @IBAction func toggleAnchorageSize(_ sender: AnyObject?) {
252 let current = ancherageRepariTimerViewController.controlSize
253 var diff = AncherageRepairTimerViewController.regularHeight - AncherageRepairTimerViewController.smallHeight
254 let newSize: NSControl.ControlSize = {
256 if current == .regular {
265 ancherageRepariTimerViewController.controlSize = newSize
267 var frame = informations.frame
268 frame.size.height -= diff
269 frame.origin.y += diff
270 informations.frame = frame
272 UserDefaults.standard[.screenshotButtonSize] = newSize
275 @IBAction func showHideCombinedView(_ sender: AnyObject?) {
277 isCombinedMode ? hideCombinedView() : showCombinedView()
280 @IBAction func fleetListAbove(_ sender: AnyObject?) {
282 setFleetView(position: .above, animate: true)
285 @IBAction func fleetListBelow(_ sender: AnyObject?) {
287 setFleetView(position: .below, animate: true)
290 @IBAction func fleetListDivide(_ sender: AnyObject?) {
292 setFleetView(position: .divided, animate: true)
295 @IBAction func fleetListSimple(_ sender: AnyObject?) {
297 setFleetView(position: .oldStyle, animate: true)
300 @IBAction func reorderToDoubleLine(_ sender: AnyObject?) {
302 fleetViewController.shipOrder = .doubleLine
303 UserDefaults.standard[.fleetViewShipOrder] = .doubleLine
306 @IBAction func reorderToLeftToRight(_ sender: AnyObject?) {
308 fleetViewController.shipOrder = .leftToRight
309 UserDefaults.standard[.fleetViewShipOrder] = .leftToRight
312 @IBAction func selectNextFleet(_ sender: AnyObject?) {
314 fleetViewController.selectNextFleet(sender)
317 @IBAction func selectPreviousFleet(_ sender: AnyObject?) {
319 fleetViewController.selectPreviousFleet(sender)
322 @IBAction func changeSakutekiCalculator(_ sender: Any?) {
324 fleetViewController.changeSakutekiCalculator(sender)
327 override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
329 guard let action: Selector = menuItem.action else { return false }
333 case #selector(BroserWindowController.reloadContent(_:)),
334 #selector(BroserWindowController.screenShot(_:)),
335 #selector(BroserWindowController.deleteCacheAndReload(_:)):
337 return gameViewController.validateMenuItem(menuItem)
339 case #selector(BroserWindowController.selectView(_:)),
340 #selector(BroserWindowController.selectNextFleet(_:)),
341 #selector(BroserWindowController.selectPreviousFleet(_:)):
345 case #selector(BroserWindowController.fleetListAbove(_:)):
346 menuItem.state = (fleetViewPosition == .above ? .on : .off)
349 case #selector(BroserWindowController.fleetListBelow(_:)):
350 menuItem.state = (fleetViewPosition == .below ? .on : .off)
353 case #selector(BroserWindowController.fleetListDivide(_:)):
354 menuItem.state = (fleetViewPosition == .divided ? .on : .off)
357 case #selector(BroserWindowController.fleetListSimple(_:)):
358 menuItem.state = (fleetViewPosition == .oldStyle ? .on : .off)
361 case #selector(BroserWindowController.reorderToDoubleLine(_:)):
362 menuItem.state = (fleetViewController.shipOrder == .doubleLine ? .on : .off)
365 case #selector(BroserWindowController.reorderToLeftToRight(_:)):
366 menuItem.state = (fleetViewController.shipOrder == .leftToRight ? .on: .off)
369 case #selector(BroserWindowController.clearQuestList(_:)):
372 case #selector(BroserWindowController.showHideCombinedView(_:)):
375 menuItem.title = LocalizedStrings.hideCombinedView.string
379 menuItem.title = LocalizedStrings.showCombinedView.string
382 if fleetViewPosition == .oldStyle { return false }
386 case #selector(BroserWindowController.toggleAnchorageSize(_:)):
389 case #selector(BroserWindowController.changeSakutekiCalculator(_:)):
390 return fleetViewController.validateMenuItem(menuItem)
398 extension BroserWindowController {
400 private static let margin: CGFloat = 1.0
401 private static let flashTopMargin: CGFloat = 4.0
403 private func changeFleetViewForFleetViewPositionIfNeeded(position newPosition: FleetViewPosition) {
405 if fleetViewPosition == newPosition { return }
406 if fleetViewPosition != .oldStyle && newPosition != .oldStyle { return }
407 if newPosition == .oldStyle && isCombinedMode { hideCombinedView() }
409 let type: FleetViewType = (newPosition == .oldStyle) ? .minimumViewType : .detailViewType
411 guard let newController = FleetViewController(viewType: type) else { return }
413 newController.enableAnimation = true
414 newController.shipOrder = fleetViewController.shipOrder
415 replace(fleetViewController.view, with: newController)
416 fleetViewController = newController
417 fleetViewController.delegate = self
420 private func windowHeightForFleetViewPosition(position newPosition: FleetViewPosition) -> CGFloat {
422 guard var contentHeight = window!.contentView?.frame.size.height else { return 0.0 }
424 if fleetViewPosition == newPosition { return contentHeight }
425 if fleetViewPosition == .oldStyle {
427 contentHeight += FleetViewController.heightDifference
429 if newPosition == .oldStyle {
431 contentHeight -= FleetViewController.heightDifference
437 private func windowFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
439 var contentRect = window!.frame
441 if fleetViewPosition == newPosition { return contentRect }
442 if fleetViewPosition == .oldStyle {
444 contentRect.size.height += FleetViewController.heightDifference
445 contentRect.origin.y -= FleetViewController.heightDifference
447 if newPosition == .oldStyle {
449 contentRect.size.height -= FleetViewController.heightDifference
450 contentRect.origin.y += FleetViewController.heightDifference
456 private func flashFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
458 let contentHeight = windowHeightForFleetViewPosition(position: newPosition)
460 var flashRect = gameViewController.view.frame
464 flashY = contentHeight - flashRect.height - fleetViewController.normalHeight
467 flashY = contentHeight - flashRect.height
470 flashY = contentHeight - flashRect.height - fleetViewController.upsideHeight - BroserWindowController.margin
473 flashY = contentHeight - flashRect.height - BroserWindowController.flashTopMargin
476 flashRect.origin.y = flashY
481 private func fleetViewFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
483 let contentHeight = windowHeightForFleetViewPosition(position: newPosition)
484 let flashRect = gameViewController.view.frame
485 var fleetListRect = fleetViewController.view.frame
487 var fleetViewHeight: CGFloat
488 var fleetViewY: CGFloat
492 fleetViewHeight = fleetViewController.normalHeight
493 fleetViewY = contentHeight - fleetViewHeight
496 fleetViewHeight = fleetViewController.normalHeight
497 fleetViewY = contentHeight - fleetViewHeight - flashRect.height - BroserWindowController.margin
500 fleetViewHeight = fleetViewController.normalHeight + flashRect.height + BroserWindowController.margin + BroserWindowController.margin
501 fleetViewY = contentHeight - fleetViewHeight
504 fleetViewHeight = FleetViewController.oldStyleFleetViewHeight
505 fleetViewY = contentHeight - fleetViewHeight - flashRect.height - BroserWindowController.margin - BroserWindowController.flashTopMargin
508 fleetListRect.size.height = fleetViewHeight
509 fleetListRect.origin.y = fleetViewY
514 private func setFleetView(position newPosition: FleetViewPosition, animate: Bool) {
516 guard let window = window else { return }
518 changeFleetViewForFleetViewPositionIfNeeded(position: newPosition)
519 let winFrame = windowFrameForFleetViewPosition(position: newPosition)
520 let flashRect = flashFrameForFleetViewPosition(position: newPosition)
521 let fleetListRect = fleetViewFrameForFleetViewPosition(position: newPosition)
523 fleetViewPosition = newPosition
524 UserDefaults.standard[.fleetViewPosition] = newPosition
528 let winAnime: [NSViewAnimation.Key: Any] = [.target: window,
529 .endFrame: NSValue(rect: winFrame) ]
530 let flashAnime: [NSViewAnimation.Key: Any] = [.target: gameViewController.view,
531 .endFrame: NSValue(rect: flashRect) ]
532 let fleetAnime: [NSViewAnimation.Key: Any] = [.target: fleetViewController.view,
533 .endFrame: NSValue(rect: fleetListRect) ]
535 let anime = NSViewAnimation(viewAnimations: [winAnime, flashAnime, fleetAnime])
541 window.setFrame(winFrame, display: false)
542 gameViewController.view.frame = flashRect
543 fleetViewController.view.frame = fleetListRect
549 extension BroserWindowController: FleetViewControllerDelegate {
551 func changeShowsExtShip(_ fleetViewController: FleetViewController, showsExtShip: Bool) {
553 guard self.fleetViewController == fleetViewController else { return }
555 if isExtShpMode && !showsExtShip {
558 let diffHeight = fleetViewController.shipViewSize.height
560 var iFrame = informations.frame
561 iFrame.origin.y -= diffHeight
562 iFrame.size.height += diffHeight
563 informations.animator().frame = iFrame
565 var sFrame = stokerContainer.frame
566 sFrame.origin.y -= diffHeight
567 stokerContainer.animator().frame = sFrame
571 } else if !isExtShpMode && showsExtShip {
574 let diffHeight = fleetViewController.shipViewSize.height
576 var iFrame = informations.frame
577 iFrame.origin.y += diffHeight
578 iFrame.size.height -= diffHeight
579 informations.animator().frame = iFrame
581 var sFrame = stokerContainer.frame
582 sFrame.origin.y += diffHeight
583 stokerContainer.animator().frame = sFrame
590 @available(OSX 10.12.2, *)
591 private var mainTouchBars: [Int: NSTouchBar] = [:]
592 @available(OSX 10.12.2, *)
593 private var shipTypeButtons: [Int: NSPopoverTouchBarItem] = [:]
594 @available(OSX 10.12.2, *)
595 private var shipTypeSegments: [Int: NSSegmentedControl] = [:]
597 @available(OSX 10.12.2, *)
598 extension BroserWindowController {
600 @IBOutlet var mainTouchBar: NSTouchBar! {
602 get { return mainTouchBars[hashValue] }
603 set { mainTouchBars[hashValue] = newValue }
606 @IBOutlet var shipTypeButton: NSPopoverTouchBarItem! {
608 get { return shipTypeButtons[hashValue] }
609 set { shipTypeButtons[hashValue] = newValue }
612 @IBOutlet var shipTypeSegment: NSSegmentedControl! {
614 get { return shipTypeSegments[hashValue] }
615 set { shipTypeSegments[hashValue] = newValue }
618 override func makeTouchBar() -> NSTouchBar? {
620 if let mainTouchBar = mainTouchBar { return mainTouchBar }
622 Bundle.main.loadNibNamed(NSNib.Name("BroswerTouchBar"), owner: self, topLevelObjects: nil)
624 shipTypeSegment.bind(.selectedIndex,
625 to: tabViewItemViewControllers[0],
626 withKeyPath: #keyPath(MainTabVIewItemViewController.selectedShipType),
628 let o = selectedMainTabIndex
629 selectedMainTabIndex = o
631 changeMainTabHandler = { [weak self] in
633 guard let `self` = self else { return }
635 self.shipTypeButton.dismissPopover(nil)
636 self.shipTypeSegment.unbind(.selectedIndex)
638 guard let button = self.shipTypeButton.view as? NSButton else { return }
640 let vc = self.tabViewItemViewControllers[$0]
641 button.isHidden = !vc.hasShipTypeSelector
642 self.shipTypeSegment.bind(.selectedIndex,
644 withKeyPath: #keyPath(MainTabVIewItemViewController.selectedShipType),