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] else { return }
137 guard let type = $0.userInfo?[CombinedCommand.userInfoKey] as? CombineType else { return }
139 if !Thread.isMainThread { Thread.sleep(forTimeInterval: 0.1) }
141 DispatchQueue.main.async {
145 self.hideCombinedView()
147 case .maneuver, .water, .transportation:
148 self.showCombinedView()
153 if UserDefaults.standard[.lastHasCombinedView] { showCombinedView() }
156 override func swipe(with event: NSEvent) {
158 guard UserDefaults.standard[.useSwipeChangeCombinedView] else { return }
160 if event.deltaX > 0 {
166 if event.deltaX < 0 {
173 @objc func windowWillClose(_ notification: Notification) {
175 UserDefaults.standard[.lastHasCombinedView] = isCombinedMode
178 private func replace(_ view: NSView, with viewController: NSViewController) {
180 viewController.view.frame = view.frame
181 viewController.view.autoresizingMask = view.autoresizingMask
182 self.window?.contentView?.replaceSubview(view, with: viewController.view)
185 private func showCombinedView() {
187 if isCombinedMode { return }
189 if fleetViewPosition == .oldStyle { return }
191 isCombinedMode = true
193 if combinedViewController == nil {
195 combinedViewController = CombileViewController()
196 combinedViewController.view.isHidden = true
197 replace(combinedViewPlaceholder, with: combinedViewController)
200 var winFrame = window!.frame
201 let incWid = combinedViewController.view.frame.maxX
202 winFrame.size.width += incWid
203 winFrame.origin.x -= incWid
204 combinedViewController.view.isHidden = false
205 window?.setFrame(winFrame, display: true, animate: true)
208 private func hideCombinedView() {
210 if !isCombinedMode { return }
212 isCombinedMode = false
214 var winFrame = window!.frame
215 let decWid = combinedViewController.view.frame.maxX
216 winFrame.size.width -= decWid
217 winFrame.origin.x += decWid
218 window?.setFrame(winFrame, display: true, animate: true)
219 combinedViewController.view.isHidden = true
224 extension BroserWindowController {
226 private func showView(number: Int) {
228 informations.selectTabViewItem(at: number)
231 @IBAction func reloadContent(_ sender: AnyObject?) {
233 gameViewController.reloadContent(sender)
236 @IBAction func deleteCacheAndReload(_ sender: AnyObject?) {
238 gameViewController.deleteCacheAndReload(sender)
241 @IBAction func clearQuestList(_ sender: AnyObject?) {
243 let store = ServerDataStore.oneTimeEditor()
244 store.quests().forEach { store.delete($0) }
247 @IBAction func selectView(_ sender: AnyObject?) {
249 guard let tag = sender?.tag else { return }
251 showView(number: tag)
254 @IBAction func changeMainTab(_ sender: AnyObject?) {
256 guard let segment = sender?.selectedSegment else { return }
258 showView(number: segment)
261 @IBAction func screenShot(_ sender: AnyObject?) {
263 gameViewController.screenShot(sender)
266 @IBAction func toggleAnchorageSize(_ sender: AnyObject?) {
268 let current = ancherageRepariTimerViewController.controlSize
269 var diff = AncherageRepairTimerViewController.regularHeight - AncherageRepairTimerViewController.smallHeight
270 let newSize: NSControl.ControlSize = {
272 if current == .regular {
281 ancherageRepariTimerViewController.controlSize = newSize
283 var frame = informations.frame
284 frame.size.height -= diff
285 frame.origin.y += diff
286 informations.frame = frame
288 UserDefaults.standard[.screenshotButtonSize] = newSize
291 @IBAction func showHideCombinedView(_ sender: AnyObject?) {
293 isCombinedMode ? hideCombinedView() : showCombinedView()
296 @IBAction func fleetListAbove(_ sender: AnyObject?) {
298 setFleetView(position: .above, animate: true)
301 @IBAction func fleetListBelow(_ sender: AnyObject?) {
303 setFleetView(position: .below, animate: true)
306 @IBAction func fleetListDivide(_ sender: AnyObject?) {
308 setFleetView(position: .divided, animate: true)
311 @IBAction func fleetListSimple(_ sender: AnyObject?) {
313 setFleetView(position: .oldStyle, animate: true)
316 @IBAction func reorderToDoubleLine(_ sender: AnyObject?) {
318 fleetViewController.shipOrder = .doubleLine
319 UserDefaults.standard[.fleetViewShipOrder] = .doubleLine
322 @IBAction func reorderToLeftToRight(_ sender: AnyObject?) {
324 fleetViewController.shipOrder = .leftToRight
325 UserDefaults.standard[.fleetViewShipOrder] = .leftToRight
328 @IBAction func selectNextFleet(_ sender: AnyObject?) {
330 fleetViewController.selectNextFleet(sender)
333 @IBAction func selectPreviousFleet(_ sender: AnyObject?) {
335 fleetViewController.selectPreviousFleet(sender)
338 @IBAction func changeSakutekiCalculator(_ sender: Any?) {
340 fleetViewController.changeSakutekiCalculator(sender)
343 override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
345 guard let action: Selector = menuItem.action else { return false }
348 case Selector.reloadContent, Selector.screenShot, Selector.deleteCacheAndReload:
349 return gameViewController.validateMenuItem(menuItem)
351 case Selector.selectView, Selector.selectNextFleet, Selector.selectPreviousFleet:
354 case Selector.fleetListAbove:
355 menuItem.state = (fleetViewPosition == .above ? .on : .off)
358 case Selector.fleetListBelow:
359 menuItem.state = (fleetViewPosition == .below ? .on : .off)
362 case Selector.fleetListDivide:
363 menuItem.state = (fleetViewPosition == .divided ? .on : .off)
366 case Selector.fleetListSimple:
367 menuItem.state = (fleetViewPosition == .oldStyle ? .on : .off)
370 case Selector.reorderToDoubleLine:
371 menuItem.state = (fleetViewController.shipOrder == .doubleLine ? .on : .off)
374 case Selector.reorderToLeftToRight:
375 menuItem.state = (fleetViewController.shipOrder == .leftToRight ? .on: .off)
378 case Selector.clearQuestList:
381 case Selector.showHideCombinedView:
384 menuItem.title = NSLocalizedString("Hide Combined View", comment: "View menu, hide combined view")
388 menuItem.title = NSLocalizedString("Show Combined View", comment: "View menu, show combined view")
391 if fleetViewPosition == .oldStyle { return false }
395 case Selector.toggleAnchorageSize:
398 case Selector.changeSakutekiCalculator:
399 return fleetViewController.validateMenuItem(menuItem)
407 extension BroserWindowController {
409 private static let margin: CGFloat = 1.0
410 private static let flashTopMargin: CGFloat = 4.0
412 private func changeFleetViewForFleetViewPositionIfNeeded(position newPosition: FleetViewPosition) {
414 if fleetViewPosition == newPosition { return }
415 if fleetViewPosition != .oldStyle && newPosition != .oldStyle { return }
416 if newPosition == .oldStyle && isCombinedMode { hideCombinedView() }
418 let type: FleetViewType = (newPosition == .oldStyle) ? .minimumViewType : .detailViewType
420 guard let newController = FleetViewController(viewType: type) else { return }
422 newController.enableAnimation = true
423 newController.shipOrder = fleetViewController.shipOrder
424 replace(fleetViewController.view, with: newController)
425 fleetViewController = newController
428 private func windowHeightForFleetViewPosition(position newPosition: FleetViewPosition) -> CGFloat {
430 guard var contentHeight = window!.contentView?.frame.size.height else { return 0.0 }
432 if fleetViewPosition == newPosition { return contentHeight }
433 if fleetViewPosition == .oldStyle {
435 contentHeight += FleetViewController.heightDifference
437 if newPosition == .oldStyle {
439 contentHeight -= FleetViewController.heightDifference
445 private func windowFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
447 var contentRect = window!.frame
449 if fleetViewPosition == newPosition { return contentRect }
450 if fleetViewPosition == .oldStyle {
452 contentRect.size.height += FleetViewController.heightDifference
453 contentRect.origin.y -= FleetViewController.heightDifference
455 if newPosition == .oldStyle {
457 contentRect.size.height -= FleetViewController.heightDifference
458 contentRect.origin.y += FleetViewController.heightDifference
464 private func flashFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
466 let contentHeight = windowHeightForFleetViewPosition(position: newPosition)
468 var flashRect = gameViewController.view.frame
472 flashY = contentHeight - flashRect.height - fleetViewController.normalHeight
475 flashY = contentHeight - flashRect.height
478 flashY = contentHeight - flashRect.height - fleetViewController.upsideHeight - BroserWindowController.margin
481 flashY = contentHeight - flashRect.height - BroserWindowController.flashTopMargin
484 flashRect.origin.y = flashY
489 private func fleetViewFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
491 let contentHeight = windowHeightForFleetViewPosition(position: newPosition)
492 let flashRect = gameViewController.view.frame
493 var fleetListRect = fleetViewController.view.frame
495 var fleetViewHeight: CGFloat
496 var fleetViewY: CGFloat
500 fleetViewHeight = fleetViewController.normalHeight
501 fleetViewY = contentHeight - fleetViewHeight
504 fleetViewHeight = fleetViewController.normalHeight
505 fleetViewY = contentHeight - fleetViewHeight - flashRect.height - BroserWindowController.margin
508 fleetViewHeight = fleetViewController.normalHeight + flashRect.height + BroserWindowController.margin + BroserWindowController.margin
509 fleetViewY = contentHeight - fleetViewHeight
512 fleetViewHeight = FleetViewController.oldStyleFleetViewHeight
513 fleetViewY = contentHeight - fleetViewHeight - flashRect.height - BroserWindowController.margin - BroserWindowController.flashTopMargin
516 fleetListRect.size.height = fleetViewHeight
517 fleetListRect.origin.y = fleetViewY
522 private func setFleetView(position newPosition: FleetViewPosition, animate: Bool) {
524 guard let window = window else { return }
526 changeFleetViewForFleetViewPositionIfNeeded(position: newPosition)
527 let winFrame = windowFrameForFleetViewPosition(position: newPosition)
528 let flashRect = flashFrameForFleetViewPosition(position: newPosition)
529 let fleetListRect = fleetViewFrameForFleetViewPosition(position: newPosition)
531 fleetViewPosition = newPosition
532 UserDefaults.standard[.fleetViewPosition] = newPosition
536 let winAnime: [NSViewAnimation.Key: Any] = [.target: window,
537 .endFrame: NSValue(rect: winFrame) ]
538 let flashAnime: [NSViewAnimation.Key: Any] = [.target: gameViewController.view,
539 .endFrame: NSValue(rect: flashRect) ]
540 let fleetAnime: [NSViewAnimation.Key: Any] = [.target: fleetViewController.view,
541 .endFrame: NSValue(rect: fleetListRect) ]
543 let anime = NSViewAnimation(viewAnimations: [winAnime, flashAnime, fleetAnime])
549 window.setFrame(winFrame, display: false)
550 gameViewController.view.frame = flashRect
551 fleetViewController.view.frame = fleetListRect
557 @available(OSX 10.12.2, *)
558 private var mainTouchBars: [Int: NSTouchBar] = [:]
559 @available(OSX 10.12.2, *)
560 private var shipTypeButtons: [Int: NSPopoverTouchBarItem] = [:]
561 @available(OSX 10.12.2, *)
562 private var shipTypeSegments: [Int: NSSegmentedControl] = [:]
564 @available(OSX 10.12.2, *)
565 extension BroserWindowController {
567 @IBOutlet var mainTouchBar: NSTouchBar! {
569 get { return mainTouchBars[hashValue] }
570 set { mainTouchBars[hashValue] = newValue }
573 @IBOutlet var shipTypeButton: NSPopoverTouchBarItem! {
575 get { return shipTypeButtons[hashValue] }
576 set { shipTypeButtons[hashValue] = newValue }
579 @IBOutlet var shipTypeSegment: NSSegmentedControl! {
581 get { return shipTypeSegments[hashValue] }
582 set { shipTypeSegments[hashValue] = newValue }
585 override func makeTouchBar() -> NSTouchBar? {
587 if let mainTouchBar = mainTouchBar { return mainTouchBar }
590 Bundle.main.loadNibNamed(NSNib.Name("BroswerTouchBar"), owner: self, topLevelObjects: &array)
592 shipTypeSegment.bind(.selectedIndex,
593 to: tabViewItemViewControllers[0],
594 withKeyPath: #keyPath(MainTabVIewItemViewController.selectedShipType),
596 let o = selectedMainTabIndex
597 selectedMainTabIndex = o
599 changeMainTabHandler = { [weak self] in
601 guard let `self` = self else { return }
603 self.shipTypeButton.dismissPopover(nil)
604 self.shipTypeSegment.unbind(.selectedIndex)
606 guard let button = self.shipTypeButton.view as? NSButton else { return }
608 let vc = self.tabViewItemViewControllers[$0]
609 button.isHidden = !vc.hasShipTypeSelector
610 self.shipTypeSegment.bind(.selectedIndex,
612 withKeyPath: #keyPath(MainTabVIewItemViewController.selectedShipType),