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(view: placeholder, with: gameViewController)
88 resourceViewController = ResourceViewController()
89 replace(view: resourcePlaceholder, with: resourceViewController)
91 ancherageRepariTimerViewController = AncherageRepairTimerViewController()
92 replace(view: 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(view: 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 showCombinedView() {
164 if isCombinedMode { return }
166 if fleetViewPosition == .oldStyle { return }
168 isCombinedMode = true
170 if combinedViewController == nil {
172 combinedViewController = CombileViewController()
173 combinedViewController.view.isHidden = true
174 replace(view: combinedViewPlaceholder, with: combinedViewController)
177 var winFrame = window!.frame
178 let incWid = combinedViewController.view.frame.maxX
179 winFrame.size.width += incWid
180 winFrame.origin.x -= incWid
181 combinedViewController.view.isHidden = false
182 window?.setFrame(winFrame, display: true, animate: true)
185 private func hideCombinedView() {
187 if !isCombinedMode { return }
189 isCombinedMode = false
191 var winFrame = window!.frame
192 let decWid = combinedViewController.view.frame.maxX
193 winFrame.size.width -= decWid
194 winFrame.origin.x += decWid
195 window?.setFrame(winFrame, display: true, animate: true)
196 combinedViewController.view.isHidden = true
201 extension BroserWindowController {
203 private func showView(number: Int) {
205 informations.selectTabViewItem(at: number)
208 @IBAction func reloadContent(_ sender: AnyObject?) {
210 gameViewController.reloadContent(sender)
213 @IBAction func deleteCacheAndReload(_ sender: AnyObject?) {
215 gameViewController.deleteCacheAndReload(sender)
218 @IBAction func clearQuestList(_ sender: AnyObject?) {
220 let store = ServerDataStore.oneTimeEditor()
221 store.quests().forEach(store.delete)
224 @IBAction func selectView(_ sender: AnyObject?) {
226 guard let tag = sender?.tag else { return }
228 showView(number: tag)
231 @IBAction func changeMainTab(_ sender: AnyObject?) {
233 guard let segment = sender?.selectedSegment else { return }
235 showView(number: segment)
238 @IBAction func screenShot(_ sender: AnyObject?) {
240 gameViewController.screenShot(sender)
243 @IBAction func toggleAnchorageSize(_ sender: AnyObject?) {
245 let current = ancherageRepariTimerViewController.controlSize
246 var diff = AncherageRepairTimerViewController.regularHeight - AncherageRepairTimerViewController.smallHeight
247 let newSize: NSControl.ControlSize = {
249 if current == .regular {
258 ancherageRepariTimerViewController.controlSize = newSize
260 var frame = informations.frame
261 frame.size.height -= diff
262 frame.origin.y += diff
263 informations.frame = frame
265 UserDefaults.standard[.screenshotButtonSize] = newSize
268 @IBAction func showHideCombinedView(_ sender: AnyObject?) {
270 isCombinedMode ? hideCombinedView() : showCombinedView()
273 @IBAction func fleetListAbove(_ sender: AnyObject?) {
275 setFleetView(position: .above, animate: true)
278 @IBAction func fleetListBelow(_ sender: AnyObject?) {
280 setFleetView(position: .below, animate: true)
283 @IBAction func fleetListDivide(_ sender: AnyObject?) {
285 setFleetView(position: .divided, animate: true)
288 @IBAction func fleetListSimple(_ sender: AnyObject?) {
290 setFleetView(position: .oldStyle, animate: true)
293 @IBAction func reorderToDoubleLine(_ sender: AnyObject?) {
295 fleetViewController.shipOrder = .doubleLine
296 UserDefaults.standard[.fleetViewShipOrder] = .doubleLine
299 @IBAction func reorderToLeftToRight(_ sender: AnyObject?) {
301 fleetViewController.shipOrder = .leftToRight
302 UserDefaults.standard[.fleetViewShipOrder] = .leftToRight
305 @IBAction func selectNextFleet(_ sender: AnyObject?) {
307 fleetViewController.selectNextFleet(sender)
310 @IBAction func selectPreviousFleet(_ sender: AnyObject?) {
312 fleetViewController.selectPreviousFleet(sender)
315 @IBAction func changeSakutekiCalculator(_ sender: Any?) {
317 fleetViewController.changeSakutekiCalculator(sender)
320 override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
322 guard let action: Selector = menuItem.action else { return false }
326 case #selector(BroserWindowController.reloadContent(_:)),
327 #selector(BroserWindowController.screenShot(_:)),
328 #selector(BroserWindowController.deleteCacheAndReload(_:)):
330 return gameViewController.validateMenuItem(menuItem)
332 case #selector(BroserWindowController.selectView(_:)),
333 #selector(BroserWindowController.selectNextFleet(_:)),
334 #selector(BroserWindowController.selectPreviousFleet(_:)):
338 case #selector(BroserWindowController.fleetListAbove(_:)):
339 menuItem.state = (fleetViewPosition == .above ? .on : .off)
342 case #selector(BroserWindowController.fleetListBelow(_:)):
343 menuItem.state = (fleetViewPosition == .below ? .on : .off)
346 case #selector(BroserWindowController.fleetListDivide(_:)):
347 menuItem.state = (fleetViewPosition == .divided ? .on : .off)
350 case #selector(BroserWindowController.fleetListSimple(_:)):
351 menuItem.state = (fleetViewPosition == .oldStyle ? .on : .off)
354 case #selector(BroserWindowController.reorderToDoubleLine(_:)):
355 menuItem.state = (fleetViewController.shipOrder == .doubleLine ? .on : .off)
358 case #selector(BroserWindowController.reorderToLeftToRight(_:)):
359 menuItem.state = (fleetViewController.shipOrder == .leftToRight ? .on: .off)
362 case #selector(BroserWindowController.clearQuestList(_:)):
365 case #selector(BroserWindowController.showHideCombinedView(_:)):
368 menuItem.title = LocalizedStrings.hideCombinedView.string
372 menuItem.title = LocalizedStrings.showCombinedView.string
375 if fleetViewPosition == .oldStyle { return false }
379 case #selector(BroserWindowController.toggleAnchorageSize(_:)):
382 case #selector(BroserWindowController.changeSakutekiCalculator(_:)):
383 return fleetViewController.validateMenuItem(menuItem)
391 extension BroserWindowController {
393 private static let margin: CGFloat = 1.0
394 private static let flashTopMargin: CGFloat = 4.0
396 private func changeFleetViewForFleetViewPositionIfNeeded(position newPosition: FleetViewPosition) {
398 if fleetViewPosition == newPosition { return }
399 if fleetViewPosition != .oldStyle && newPosition != .oldStyle { return }
400 if newPosition == .oldStyle && isCombinedMode { hideCombinedView() }
402 let type: FleetViewType = (newPosition == .oldStyle) ? .minimumViewType : .detailViewType
404 guard let newController = FleetViewController(viewType: type) else { return }
406 newController.enableAnimation = true
407 newController.shipOrder = fleetViewController.shipOrder
408 replace(view: fleetViewController.view, with: newController)
409 fleetViewController = newController
410 fleetViewController.delegate = self
413 private func windowHeightForFleetViewPosition(position newPosition: FleetViewPosition) -> CGFloat {
415 guard var contentHeight = window!.contentView?.frame.size.height else { return 0.0 }
417 if fleetViewPosition == newPosition { return contentHeight }
418 if fleetViewPosition == .oldStyle {
420 contentHeight += FleetViewController.heightDifference
422 if newPosition == .oldStyle {
424 contentHeight -= FleetViewController.heightDifference
430 private func windowFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
432 var contentRect = window!.frame
434 if fleetViewPosition == newPosition { return contentRect }
435 if fleetViewPosition == .oldStyle {
437 contentRect.size.height += FleetViewController.heightDifference
438 contentRect.origin.y -= FleetViewController.heightDifference
440 if newPosition == .oldStyle {
442 contentRect.size.height -= FleetViewController.heightDifference
443 contentRect.origin.y += FleetViewController.heightDifference
449 private func flashFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
451 let contentHeight = windowHeightForFleetViewPosition(position: newPosition)
453 var flashRect = gameViewController.view.frame
457 flashY = contentHeight - flashRect.height - fleetViewController.normalHeight
460 flashY = contentHeight - flashRect.height
463 flashY = contentHeight - flashRect.height - fleetViewController.upsideHeight - BroserWindowController.margin
466 flashY = contentHeight - flashRect.height - BroserWindowController.flashTopMargin
469 flashRect.origin.y = flashY
474 private func fleetViewFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
476 let contentHeight = windowHeightForFleetViewPosition(position: newPosition)
477 let flashRect = gameViewController.view.frame
478 var fleetListRect = fleetViewController.view.frame
480 var fleetViewHeight: CGFloat
481 var fleetViewY: CGFloat
485 fleetViewHeight = fleetViewController.normalHeight
486 fleetViewY = contentHeight - fleetViewHeight
489 fleetViewHeight = fleetViewController.normalHeight
490 fleetViewY = contentHeight - fleetViewHeight - flashRect.height - BroserWindowController.margin
493 fleetViewHeight = fleetViewController.normalHeight + flashRect.height + BroserWindowController.margin + BroserWindowController.margin
494 fleetViewY = contentHeight - fleetViewHeight
497 fleetViewHeight = FleetViewController.oldStyleFleetViewHeight
498 fleetViewY = contentHeight - fleetViewHeight - flashRect.height - BroserWindowController.margin - BroserWindowController.flashTopMargin
501 fleetListRect.size.height = fleetViewHeight
502 fleetListRect.origin.y = fleetViewY
507 private func setFleetView(position newPosition: FleetViewPosition, animate: Bool) {
509 guard let window = window else { return }
511 changeFleetViewForFleetViewPositionIfNeeded(position: newPosition)
512 let winFrame = windowFrameForFleetViewPosition(position: newPosition)
513 let flashRect = flashFrameForFleetViewPosition(position: newPosition)
514 let fleetListRect = fleetViewFrameForFleetViewPosition(position: newPosition)
516 fleetViewPosition = newPosition
517 UserDefaults.standard[.fleetViewPosition] = newPosition
521 let winAnime = ViewAnimationAttributes(target: window, endFrame: winFrame)
522 let flashAnime = ViewAnimationAttributes(target: gameViewController.view, endFrame: flashRect)
523 let fleetAnime = ViewAnimationAttributes(target: fleetViewController.view, endFrame: fleetListRect)
525 let anime = ViewAnimation(viewAnimations: [winAnime, flashAnime, fleetAnime])
531 window.setFrame(winFrame, display: false)
532 gameViewController.view.frame = flashRect
533 fleetViewController.view.frame = fleetListRect
539 extension BroserWindowController: FleetViewControllerDelegate {
541 func changeShowsExtShip(_ fleetViewController: FleetViewController, showsExtShip: Bool) {
543 guard self.fleetViewController == fleetViewController else { return }
545 if isExtShpMode && !showsExtShip {
548 let diffHeight = fleetViewController.shipViewSize.height
550 var iFrame = informations.frame
551 iFrame.origin.y -= diffHeight
552 iFrame.size.height += diffHeight
553 informations.animator().frame = iFrame
555 var sFrame = stokerContainer.frame
556 sFrame.origin.y -= diffHeight
557 stokerContainer.animator().frame = sFrame
561 } else if !isExtShpMode && showsExtShip {
564 let diffHeight = fleetViewController.shipViewSize.height
566 var iFrame = informations.frame
567 iFrame.origin.y += diffHeight
568 iFrame.size.height -= diffHeight
569 informations.animator().frame = iFrame
571 var sFrame = stokerContainer.frame
572 sFrame.origin.y += diffHeight
573 stokerContainer.animator().frame = sFrame
580 @available(OSX 10.12.2, *)
581 private var mainTouchBars: [Int: NSTouchBar] = [:]
582 @available(OSX 10.12.2, *)
583 private var shipTypeButtons: [Int: NSPopoverTouchBarItem] = [:]
584 @available(OSX 10.12.2, *)
585 private var shipTypeSegments: [Int: NSSegmentedControl] = [:]
587 @available(OSX 10.12.2, *)
588 extension BroserWindowController {
590 @IBOutlet private var mainTouchBar: NSTouchBar! {
592 get { return mainTouchBars[hashValue] }
593 set { mainTouchBars[hashValue] = newValue }
596 @IBOutlet private var shipTypeButton: NSPopoverTouchBarItem! {
598 get { return shipTypeButtons[hashValue] }
599 set { shipTypeButtons[hashValue] = newValue }
602 @IBOutlet private var shipTypeSegment: NSSegmentedControl! {
604 get { return shipTypeSegments[hashValue] }
605 set { shipTypeSegments[hashValue] = newValue }
608 override func makeTouchBar() -> NSTouchBar? {
610 if let mainTouchBar = mainTouchBar { return mainTouchBar }
612 Bundle.main.loadNibNamed(NSNib.Name("BroswerTouchBar"), owner: self, topLevelObjects: nil)
614 shipTypeSegment.bind(.selectedIndex,
615 to: tabViewItemViewControllers[0],
616 withKeyPath: #keyPath(MainTabVIewItemViewController.selectedShipType),
618 let o = selectedMainTabIndex
619 selectedMainTabIndex = o
621 changeMainTabHandler = { [weak self] in
623 guard let `self` = self else { return }
625 self.shipTypeButton.dismissPopover(nil)
626 self.shipTypeSegment.unbind(.selectedIndex)
628 guard let button = self.shipTypeButton.view as? NSButton else { return }
630 let vc = self.tabViewItemViewControllers[$0]
631 button.isHidden = !vc.hasShipTypeSelector
632 self.shipTypeSegment.bind(.selectedIndex,
634 withKeyPath: #keyPath(MainTabVIewItemViewController.selectedShipType),