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 override class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String> {
47 case #keyPath(flagShipName): return [#keyPath(flagShipID)]
53 @objc let managedObjectContext = ServerDataStore.default.context
57 NotificationCenter.default.removeObserver(self)
60 @IBOutlet weak var placeholder: NSView!
61 @IBOutlet weak var combinedViewPlaceholder: NSView!
62 @IBOutlet weak var deckPlaceholder: NSView!
63 @IBOutlet weak var resourcePlaceholder: NSView!
64 @IBOutlet weak var ancherageRepariTimerPlaceholder: NSView!
65 @IBOutlet weak var informations: NSTabView!
66 @IBOutlet var deckContoller: NSArrayController!
68 override var windowNibName: NSNib.Name {
70 return .nibName(instanceOf: self)
73 @objc var flagShipID: Int = 0
74 @objc var flagShipName: String? {
75 return ServerDataStore.default.ship(by: flagShipID)?.name
77 var changeMainTabHandler: ((Int) -> Void)?
78 @objc dynamic var selectedMainTabIndex: Int = 0 {
81 changeMainTabHandler?(selectedMainTabIndex)
85 private var gameViewController: GameViewController!
86 private var fleetViewController: FleetViewController!
87 private var tabViewItemViewControllers: [MainTabVIewItemViewController] = []
88 private var ancherageRepariTimerViewController: AncherageRepairTimerViewController!
89 private var resourceViewController: ResourceViewController!
90 private var docksViewController: DocksViewController!
91 private var shipViewController: ShipViewController!
92 private var powerUpViewController: PowerUpSupportViewController!
93 private var strengthedListViewController: StrengthenListViewController!
94 private var repairListViewController: RepairListViewController!
95 private var combinedViewController: CombileViewController!
97 private var fleetViewPosition: FleetViewPosition = .above
98 private var isCombinedMode = false
101 override func windowDidLoad() {
103 super.windowDidLoad()
105 gameViewController = GameViewController()
106 replace(placeholder, with: gameViewController)
108 resourceViewController = ResourceViewController()
109 replace(resourcePlaceholder, with: resourceViewController)
111 ancherageRepariTimerViewController = AncherageRepairTimerViewController()
112 replace(ancherageRepariTimerPlaceholder, with: ancherageRepariTimerViewController)
113 if UserDefaults.standard[.screenshotButtonSize] == .small { toggleAnchorageSize(nil) }
115 tabViewItemViewControllers = [
116 DocksViewController(),
117 ShipViewController(),
118 PowerUpSupportViewController(),
119 StrengthenListViewController(),
120 RepairListViewController()
122 tabViewItemViewControllers.enumerated().forEach {
125 let item = informations.tabViewItem(at: $0.offset)
126 item.viewController = $0.element
129 fleetViewController = FleetViewController(viewType: .detailViewType)
130 replace(deckPlaceholder, with: fleetViewController)
131 setFleetView(position: UserDefaults.standard[.fleetViewPosition], animate: false)
132 fleetViewController.enableAnimation = false
133 fleetViewController.shipOrder = UserDefaults.standard[.fleetViewShipOrder]
134 fleetViewController.enableAnimation = true
136 bind(NSBindingName(rawValue: #keyPath(flagShipID)), to: deckContoller, withKeyPath: "selection.ship_0", options: nil)
138 NotificationCenter.default
139 .addObserver(forName: .CombinedDidCange, object: nil, queue: nil) {
141 guard UserDefaults.standard[.autoCombinedView] else { return }
142 guard let type = $0.userInfo?[CombinedCommand.userInfoKey] as? CombineType else { return }
144 if !Thread.isMainThread { Thread.sleep(forTimeInterval: 0.1) }
146 DispatchQueue.main.async {
150 self.hideCombinedView()
152 case .maneuver, .water, .transportation:
153 self.showCombinedView()
158 if UserDefaults.standard[.lastHasCombinedView] { showCombinedView() }
161 override func swipe(with event: NSEvent) {
163 guard UserDefaults.standard[.useSwipeChangeCombinedView] else { return }
165 if event.deltaX > 0 {
171 if event.deltaX < 0 {
178 @objc func windowWillClose(_ notification: Notification) {
180 UserDefaults.standard[.lastHasCombinedView] = isCombinedMode
183 private func replace(_ view: NSView, with viewController: NSViewController) {
185 viewController.view.frame = view.frame
186 viewController.view.autoresizingMask = view.autoresizingMask
187 self.window?.contentView?.replaceSubview(view, with: viewController.view)
190 private func showCombinedView() {
192 if isCombinedMode { return }
194 if fleetViewPosition == .oldStyle { return }
196 isCombinedMode = true
198 if combinedViewController == nil {
200 combinedViewController = CombileViewController()
201 combinedViewController.view.isHidden = true
202 replace(combinedViewPlaceholder, with: combinedViewController)
205 var winFrame = window!.frame
206 let incWid = combinedViewController.view.frame.maxX
207 winFrame.size.width += incWid
208 winFrame.origin.x -= incWid
209 combinedViewController.view.isHidden = false
210 window?.setFrame(winFrame, display: true, animate: true)
213 private func hideCombinedView() {
215 if !isCombinedMode { return }
217 isCombinedMode = false
219 var winFrame = window!.frame
220 let decWid = combinedViewController.view.frame.maxX
221 winFrame.size.width -= decWid
222 winFrame.origin.x += decWid
223 window?.setFrame(winFrame, display: true, animate: true)
224 combinedViewController.view.isHidden = true
229 extension BroserWindowController {
231 private func showView(number: Int) {
233 informations.selectTabViewItem(at: number)
236 @IBAction func reloadContent(_ sender: AnyObject?) {
238 gameViewController.reloadContent(sender)
241 @IBAction func deleteCacheAndReload(_ sender: AnyObject?) {
243 gameViewController.deleteCacheAndReload(sender)
246 @IBAction func clearQuestList(_ sender: AnyObject?) {
248 let store = ServerDataStore.oneTimeEditor()
249 store.quests().forEach { store.delete($0) }
252 @IBAction func selectView(_ sender: AnyObject?) {
254 guard let tag = sender?.tag else { return }
256 showView(number: tag)
259 @IBAction func changeMainTab(_ sender: AnyObject?) {
261 guard let segment = sender?.selectedSegment else { return }
263 showView(number: segment)
266 @IBAction func screenShot(_ sender: AnyObject?) {
268 gameViewController.screenShot(sender)
271 @IBAction func toggleAnchorageSize(_ sender: AnyObject?) {
273 let current = ancherageRepariTimerViewController.controlSize
274 var diff = AncherageRepairTimerViewController.regularHeight - AncherageRepairTimerViewController.smallHeight
275 let newSize: NSControl.ControlSize = {
277 if current == .regular {
286 ancherageRepariTimerViewController.controlSize = newSize
288 var frame = informations.frame
289 frame.size.height -= diff
290 frame.origin.y += diff
291 informations.frame = frame
293 UserDefaults.standard[.screenshotButtonSize] = newSize
296 @IBAction func showHideCombinedView(_ sender: AnyObject?) {
298 isCombinedMode ? hideCombinedView() : showCombinedView()
301 @IBAction func fleetListAbove(_ sender: AnyObject?) {
303 setFleetView(position: .above, animate: true)
306 @IBAction func fleetListBelow(_ sender: AnyObject?) {
308 setFleetView(position: .below, animate: true)
311 @IBAction func fleetListDivide(_ sender: AnyObject?) {
313 setFleetView(position: .divided, animate: true)
316 @IBAction func fleetListSimple(_ sender: AnyObject?) {
318 setFleetView(position: .oldStyle, animate: true)
321 @IBAction func reorderToDoubleLine(_ sender: AnyObject?) {
323 fleetViewController.shipOrder = .doubleLine
324 UserDefaults.standard[.fleetViewShipOrder] = .doubleLine
327 @IBAction func reorderToLeftToRight(_ sender: AnyObject?) {
329 fleetViewController.shipOrder = .leftToRight
330 UserDefaults.standard[.fleetViewShipOrder] = .leftToRight
333 @IBAction func selectNextFleet(_ sender: AnyObject?) {
335 fleetViewController.selectNextFleet(sender)
338 @IBAction func selectPreviousFleet(_ sender: AnyObject?) {
340 fleetViewController.selectPreviousFleet(sender)
343 @IBAction func changeSakutekiCalculator(_ sender: Any?) {
345 fleetViewController.changeSakutekiCalculator(sender)
348 override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
350 guard let action: Selector = menuItem.action else { return false }
353 case Selector.reloadContent, Selector.screenShot, Selector.deleteCacheAndReload:
354 return gameViewController.validateMenuItem(menuItem)
356 case Selector.selectView, Selector.selectNextFleet, Selector.selectPreviousFleet:
359 case Selector.fleetListAbove:
360 menuItem.state = (fleetViewPosition == .above ? .on : .off)
363 case Selector.fleetListBelow:
364 menuItem.state = (fleetViewPosition == .below ? .on : .off)
367 case Selector.fleetListDivide:
368 menuItem.state = (fleetViewPosition == .divided ? .on : .off)
371 case Selector.fleetListSimple:
372 menuItem.state = (fleetViewPosition == .oldStyle ? .on : .off)
375 case Selector.reorderToDoubleLine:
376 menuItem.state = (fleetViewController.shipOrder == .doubleLine ? .on : .off)
379 case Selector.reorderToLeftToRight:
380 menuItem.state = (fleetViewController.shipOrder == .leftToRight ? .on: .off)
383 case Selector.clearQuestList:
386 case Selector.showHideCombinedView:
389 menuItem.title = LocalizedStrings.hideCombinedView.string
393 menuItem.title = LocalizedStrings.showCombinedView.string
396 if fleetViewPosition == .oldStyle { return false }
400 case Selector.toggleAnchorageSize:
403 case Selector.changeSakutekiCalculator:
404 return fleetViewController.validateMenuItem(menuItem)
412 extension BroserWindowController {
414 private static let margin: CGFloat = 1.0
415 private static let flashTopMargin: CGFloat = 4.0
417 private func changeFleetViewForFleetViewPositionIfNeeded(position newPosition: FleetViewPosition) {
419 if fleetViewPosition == newPosition { return }
420 if fleetViewPosition != .oldStyle && newPosition != .oldStyle { return }
421 if newPosition == .oldStyle && isCombinedMode { hideCombinedView() }
423 let type: FleetViewType = (newPosition == .oldStyle) ? .minimumViewType : .detailViewType
425 guard let newController = FleetViewController(viewType: type) else { return }
427 newController.enableAnimation = true
428 newController.shipOrder = fleetViewController.shipOrder
429 replace(fleetViewController.view, with: newController)
430 fleetViewController = newController
433 private func windowHeightForFleetViewPosition(position newPosition: FleetViewPosition) -> CGFloat {
435 guard var contentHeight = window!.contentView?.frame.size.height else { return 0.0 }
437 if fleetViewPosition == newPosition { return contentHeight }
438 if fleetViewPosition == .oldStyle {
440 contentHeight += FleetViewController.heightDifference
442 if newPosition == .oldStyle {
444 contentHeight -= FleetViewController.heightDifference
450 private func windowFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
452 var contentRect = window!.frame
454 if fleetViewPosition == newPosition { return contentRect }
455 if fleetViewPosition == .oldStyle {
457 contentRect.size.height += FleetViewController.heightDifference
458 contentRect.origin.y -= FleetViewController.heightDifference
460 if newPosition == .oldStyle {
462 contentRect.size.height -= FleetViewController.heightDifference
463 contentRect.origin.y += FleetViewController.heightDifference
469 private func flashFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
471 let contentHeight = windowHeightForFleetViewPosition(position: newPosition)
473 var flashRect = gameViewController.view.frame
477 flashY = contentHeight - flashRect.height - fleetViewController.normalHeight
480 flashY = contentHeight - flashRect.height
483 flashY = contentHeight - flashRect.height - fleetViewController.upsideHeight - BroserWindowController.margin
486 flashY = contentHeight - flashRect.height - BroserWindowController.flashTopMargin
489 flashRect.origin.y = flashY
494 private func fleetViewFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
496 let contentHeight = windowHeightForFleetViewPosition(position: newPosition)
497 let flashRect = gameViewController.view.frame
498 var fleetListRect = fleetViewController.view.frame
500 var fleetViewHeight: CGFloat
501 var fleetViewY: CGFloat
505 fleetViewHeight = fleetViewController.normalHeight
506 fleetViewY = contentHeight - fleetViewHeight
509 fleetViewHeight = fleetViewController.normalHeight
510 fleetViewY = contentHeight - fleetViewHeight - flashRect.height - BroserWindowController.margin
513 fleetViewHeight = fleetViewController.normalHeight + flashRect.height + BroserWindowController.margin + BroserWindowController.margin
514 fleetViewY = contentHeight - fleetViewHeight
517 fleetViewHeight = FleetViewController.oldStyleFleetViewHeight
518 fleetViewY = contentHeight - fleetViewHeight - flashRect.height - BroserWindowController.margin - BroserWindowController.flashTopMargin
521 fleetListRect.size.height = fleetViewHeight
522 fleetListRect.origin.y = fleetViewY
527 private func setFleetView(position newPosition: FleetViewPosition, animate: Bool) {
529 guard let window = window else { return }
531 changeFleetViewForFleetViewPositionIfNeeded(position: newPosition)
532 let winFrame = windowFrameForFleetViewPosition(position: newPosition)
533 let flashRect = flashFrameForFleetViewPosition(position: newPosition)
534 let fleetListRect = fleetViewFrameForFleetViewPosition(position: newPosition)
536 fleetViewPosition = newPosition
537 UserDefaults.standard[.fleetViewPosition] = newPosition
541 let winAnime: [NSViewAnimation.Key: Any] = [.target: window,
542 .endFrame: NSValue(rect: winFrame) ]
543 let flashAnime: [NSViewAnimation.Key: Any] = [.target: gameViewController.view,
544 .endFrame: NSValue(rect: flashRect) ]
545 let fleetAnime: [NSViewAnimation.Key: Any] = [.target: fleetViewController.view,
546 .endFrame: NSValue(rect: fleetListRect) ]
548 let anime = NSViewAnimation(viewAnimations: [winAnime, flashAnime, fleetAnime])
554 window.setFrame(winFrame, display: false)
555 gameViewController.view.frame = flashRect
556 fleetViewController.view.frame = fleetListRect
562 @available(OSX 10.12.2, *)
563 private var mainTouchBars: [Int: NSTouchBar] = [:]
564 @available(OSX 10.12.2, *)
565 private var shipTypeButtons: [Int: NSPopoverTouchBarItem] = [:]
566 @available(OSX 10.12.2, *)
567 private var shipTypeSegments: [Int: NSSegmentedControl] = [:]
569 @available(OSX 10.12.2, *)
570 extension BroserWindowController {
572 @IBOutlet var mainTouchBar: NSTouchBar! {
574 get { return mainTouchBars[hashValue] }
575 set { mainTouchBars[hashValue] = newValue }
578 @IBOutlet var shipTypeButton: NSPopoverTouchBarItem! {
580 get { return shipTypeButtons[hashValue] }
581 set { shipTypeButtons[hashValue] = newValue }
584 @IBOutlet var shipTypeSegment: NSSegmentedControl! {
586 get { return shipTypeSegments[hashValue] }
587 set { shipTypeSegments[hashValue] = newValue }
590 override func makeTouchBar() -> NSTouchBar? {
592 if let mainTouchBar = mainTouchBar { return mainTouchBar }
595 Bundle.main.loadNibNamed(NSNib.Name("BroswerTouchBar"), owner: self, topLevelObjects: &array)
597 shipTypeSegment.bind(.selectedIndex,
598 to: tabViewItemViewControllers[0],
599 withKeyPath: #keyPath(MainTabVIewItemViewController.selectedShipType),
601 let o = selectedMainTabIndex
602 selectedMainTabIndex = o
604 changeMainTabHandler = { [weak self] in
606 guard let `self` = self else { return }
608 self.shipTypeButton.dismissPopover(nil)
609 self.shipTypeSegment.unbind(.selectedIndex)
611 guard let button = self.shipTypeButton.view as? NSButton else { return }
613 let vc = self.tabViewItemViewControllers[$0]
614 button.isHidden = !vc.hasShipTypeSelector
615 self.shipTypeSegment.bind(.selectedIndex,
617 withKeyPath: #keyPath(MainTabVIewItemViewController.selectedShipType),