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 resourcePlaceholder: NSView!
42 @IBOutlet weak var ancherageRepariTimerPlaceholder: NSView!
43 @IBOutlet weak var informations: NSTabView!
44 @IBOutlet var deckContoller: NSArrayController!
46 override var windowNibName: NSNib.Name {
48 return .nibName(instanceOf: self)
51 @objc var flagShipID: Int = 0
52 @objc var flagShipName: String? {
53 return ServerDataStore.default.ship(by: flagShipID)?.name
55 var changeMainTabHandler: ((Int) -> Void)?
56 @objc dynamic var selectedMainTabIndex: Int = 0 {
59 changeMainTabHandler?(selectedMainTabIndex)
63 private var gameViewController: GameViewController!
64 private var fleetViewController: FleetViewController!
65 private var tabViewItemViewControllers: [MainTabVIewItemViewController] = []
66 private var ancherageRepariTimerViewController: AncherageRepairTimerViewController!
67 private var resourceViewController: ResourceViewController!
68 private var docksViewController: DocksViewController!
69 private var shipViewController: ShipViewController!
70 private var powerUpViewController: PowerUpSupportViewController!
71 private var strengthedListViewController: StrengthenListViewController!
72 private var repairListViewController: RepairListViewController!
73 private var combinedViewController: CombileViewController!
75 private var fleetViewPosition: FleetViewPosition = .above
76 private var isCombinedMode = false
79 override func windowDidLoad() {
83 gameViewController = GameViewController()
84 replace(placeholder, with: gameViewController)
86 resourceViewController = ResourceViewController()
87 replace(resourcePlaceholder, with: resourceViewController)
89 ancherageRepariTimerViewController = AncherageRepairTimerViewController()
90 replace(ancherageRepariTimerPlaceholder, with: ancherageRepariTimerViewController)
91 if UserDefaults.standard[.screenshotButtonSize] == .small { toggleAnchorageSize(nil) }
93 tabViewItemViewControllers = [
94 DocksViewController(),
96 PowerUpSupportViewController(),
97 StrengthenListViewController(),
98 RepairListViewController()
100 tabViewItemViewControllers.enumerated().forEach {
103 let item = informations.tabViewItem(at: $0.offset)
104 item.viewController = $0.element
107 fleetViewController = FleetViewController(viewType: .detailViewType)
108 replace(deckPlaceholder, with: fleetViewController)
109 setFleetView(position: UserDefaults.standard[.fleetViewPosition], animate: false)
110 fleetViewController.enableAnimation = false
111 fleetViewController.shipOrder = UserDefaults.standard[.fleetViewShipOrder]
112 fleetViewController.enableAnimation = true
114 bind(NSBindingName(rawValue: #keyPath(flagShipID)), to: deckContoller, withKeyPath: "selection.ship_0", options: nil)
116 NotificationCenter.default
117 .addObserver(forName: .CombinedDidCange, object: nil, queue: nil) {
119 guard UserDefaults.standard[.autoCombinedView] else { return }
120 guard let type = $0.userInfo?[CombinedCommand.userInfoKey] as? CombineType else { return }
122 if !Thread.isMainThread { Thread.sleep(forTimeInterval: 0.1) }
124 DispatchQueue.main.async {
128 self.hideCombinedView()
130 case .maneuver, .water, .transportation:
131 self.showCombinedView()
136 if UserDefaults.standard[.lastHasCombinedView] { showCombinedView() }
139 override func swipe(with event: NSEvent) {
141 guard UserDefaults.standard[.useSwipeChangeCombinedView] else { return }
143 if event.deltaX > 0 {
148 if event.deltaX < 0 {
154 @objc func windowWillClose(_ notification: Notification) {
156 UserDefaults.standard[.lastHasCombinedView] = isCombinedMode
159 private func replace(_ view: NSView, with viewController: NSViewController) {
161 viewController.view.frame = view.frame
162 viewController.view.autoresizingMask = view.autoresizingMask
163 self.window?.contentView?.replaceSubview(view, with: viewController.view)
166 private func showCombinedView() {
168 if isCombinedMode { return }
170 if fleetViewPosition == .oldStyle { return }
172 isCombinedMode = true
174 if combinedViewController == nil {
176 combinedViewController = CombileViewController()
177 combinedViewController.view.isHidden = true
178 replace(combinedViewPlaceholder, with: combinedViewController)
181 var winFrame = window!.frame
182 let incWid = combinedViewController.view.frame.maxX
183 winFrame.size.width += incWid
184 winFrame.origin.x -= incWid
185 combinedViewController.view.isHidden = false
186 window?.setFrame(winFrame, display: true, animate: true)
189 private func hideCombinedView() {
191 if !isCombinedMode { return }
193 isCombinedMode = false
195 var winFrame = window!.frame
196 let decWid = combinedViewController.view.frame.maxX
197 winFrame.size.width -= decWid
198 winFrame.origin.x += decWid
199 window?.setFrame(winFrame, display: true, animate: true)
200 combinedViewController.view.isHidden = true
205 extension BroserWindowController {
207 private func showView(number: Int) {
209 informations.selectTabViewItem(at: number)
212 @IBAction func reloadContent(_ sender: AnyObject?) {
214 gameViewController.reloadContent(sender)
217 @IBAction func deleteCacheAndReload(_ sender: AnyObject?) {
219 gameViewController.deleteCacheAndReload(sender)
222 @IBAction func clearQuestList(_ sender: AnyObject?) {
224 let store = ServerDataStore.oneTimeEditor()
225 store.quests().forEach(store.delete)
228 @IBAction func selectView(_ sender: AnyObject?) {
230 guard let tag = sender?.tag else { return }
232 showView(number: tag)
235 @IBAction func changeMainTab(_ sender: AnyObject?) {
237 guard let segment = sender?.selectedSegment else { return }
239 showView(number: segment)
242 @IBAction func screenShot(_ sender: AnyObject?) {
244 gameViewController.screenShot(sender)
247 @IBAction func toggleAnchorageSize(_ sender: AnyObject?) {
249 let current = ancherageRepariTimerViewController.controlSize
250 var diff = AncherageRepairTimerViewController.regularHeight - AncherageRepairTimerViewController.smallHeight
251 let newSize: NSControl.ControlSize = {
253 if current == .regular {
262 ancherageRepariTimerViewController.controlSize = newSize
264 var frame = informations.frame
265 frame.size.height -= diff
266 frame.origin.y += diff
267 informations.frame = frame
269 UserDefaults.standard[.screenshotButtonSize] = newSize
272 @IBAction func showHideCombinedView(_ sender: AnyObject?) {
274 isCombinedMode ? hideCombinedView() : showCombinedView()
277 @IBAction func fleetListAbove(_ sender: AnyObject?) {
279 setFleetView(position: .above, animate: true)
282 @IBAction func fleetListBelow(_ sender: AnyObject?) {
284 setFleetView(position: .below, animate: true)
287 @IBAction func fleetListDivide(_ sender: AnyObject?) {
289 setFleetView(position: .divided, animate: true)
292 @IBAction func fleetListSimple(_ sender: AnyObject?) {
294 setFleetView(position: .oldStyle, animate: true)
297 @IBAction func reorderToDoubleLine(_ sender: AnyObject?) {
299 fleetViewController.shipOrder = .doubleLine
300 UserDefaults.standard[.fleetViewShipOrder] = .doubleLine
303 @IBAction func reorderToLeftToRight(_ sender: AnyObject?) {
305 fleetViewController.shipOrder = .leftToRight
306 UserDefaults.standard[.fleetViewShipOrder] = .leftToRight
309 @IBAction func selectNextFleet(_ sender: AnyObject?) {
311 fleetViewController.selectNextFleet(sender)
314 @IBAction func selectPreviousFleet(_ sender: AnyObject?) {
316 fleetViewController.selectPreviousFleet(sender)
319 @IBAction func changeSakutekiCalculator(_ sender: Any?) {
321 fleetViewController.changeSakutekiCalculator(sender)
324 override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
326 guard let action: Selector = menuItem.action else { return false }
330 case #selector(BroserWindowController.reloadContent(_:)),
331 #selector(BroserWindowController.screenShot(_:)),
332 #selector(BroserWindowController.deleteCacheAndReload(_:)):
334 return gameViewController.validateMenuItem(menuItem)
336 case #selector(BroserWindowController.selectView(_:)),
337 #selector(BroserWindowController.selectNextFleet(_:)),
338 #selector(BroserWindowController.selectPreviousFleet(_:)):
342 case #selector(BroserWindowController.fleetListAbove(_:)):
343 menuItem.state = (fleetViewPosition == .above ? .on : .off)
346 case #selector(BroserWindowController.fleetListBelow(_:)):
347 menuItem.state = (fleetViewPosition == .below ? .on : .off)
350 case #selector(BroserWindowController.fleetListDivide(_:)):
351 menuItem.state = (fleetViewPosition == .divided ? .on : .off)
354 case #selector(BroserWindowController.fleetListSimple(_:)):
355 menuItem.state = (fleetViewPosition == .oldStyle ? .on : .off)
358 case #selector(BroserWindowController.reorderToDoubleLine(_:)):
359 menuItem.state = (fleetViewController.shipOrder == .doubleLine ? .on : .off)
362 case #selector(BroserWindowController.reorderToLeftToRight(_:)):
363 menuItem.state = (fleetViewController.shipOrder == .leftToRight ? .on: .off)
366 case #selector(BroserWindowController.clearQuestList(_:)):
369 case #selector(BroserWindowController.showHideCombinedView(_:)):
372 menuItem.title = LocalizedStrings.hideCombinedView.string
376 menuItem.title = LocalizedStrings.showCombinedView.string
379 if fleetViewPosition == .oldStyle { return false }
383 case #selector(BroserWindowController.toggleAnchorageSize(_:)):
386 case #selector(BroserWindowController.changeSakutekiCalculator(_:)):
387 return fleetViewController.validateMenuItem(menuItem)
395 extension BroserWindowController {
397 private static let margin: CGFloat = 1.0
398 private static let flashTopMargin: CGFloat = 4.0
400 private func changeFleetViewForFleetViewPositionIfNeeded(position newPosition: FleetViewPosition) {
402 if fleetViewPosition == newPosition { return }
403 if fleetViewPosition != .oldStyle && newPosition != .oldStyle { return }
404 if newPosition == .oldStyle && isCombinedMode { hideCombinedView() }
406 let type: FleetViewType = (newPosition == .oldStyle) ? .minimumViewType : .detailViewType
408 guard let newController = FleetViewController(viewType: type) else { return }
410 newController.enableAnimation = true
411 newController.shipOrder = fleetViewController.shipOrder
412 replace(fleetViewController.view, with: newController)
413 fleetViewController = newController
416 private func windowHeightForFleetViewPosition(position newPosition: FleetViewPosition) -> CGFloat {
418 guard var contentHeight = window!.contentView?.frame.size.height else { return 0.0 }
420 if fleetViewPosition == newPosition { return contentHeight }
421 if fleetViewPosition == .oldStyle {
423 contentHeight += FleetViewController.heightDifference
425 if newPosition == .oldStyle {
427 contentHeight -= FleetViewController.heightDifference
433 private func windowFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
435 var contentRect = window!.frame
437 if fleetViewPosition == newPosition { return contentRect }
438 if fleetViewPosition == .oldStyle {
440 contentRect.size.height += FleetViewController.heightDifference
441 contentRect.origin.y -= FleetViewController.heightDifference
443 if newPosition == .oldStyle {
445 contentRect.size.height -= FleetViewController.heightDifference
446 contentRect.origin.y += FleetViewController.heightDifference
452 private func flashFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
454 let contentHeight = windowHeightForFleetViewPosition(position: newPosition)
456 var flashRect = gameViewController.view.frame
460 flashY = contentHeight - flashRect.height - fleetViewController.normalHeight
463 flashY = contentHeight - flashRect.height
466 flashY = contentHeight - flashRect.height - fleetViewController.upsideHeight - BroserWindowController.margin
469 flashY = contentHeight - flashRect.height - BroserWindowController.flashTopMargin
472 flashRect.origin.y = flashY
477 private func fleetViewFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
479 let contentHeight = windowHeightForFleetViewPosition(position: newPosition)
480 let flashRect = gameViewController.view.frame
481 var fleetListRect = fleetViewController.view.frame
483 var fleetViewHeight: CGFloat
484 var fleetViewY: CGFloat
488 fleetViewHeight = fleetViewController.normalHeight
489 fleetViewY = contentHeight - fleetViewHeight
492 fleetViewHeight = fleetViewController.normalHeight
493 fleetViewY = contentHeight - fleetViewHeight - flashRect.height - BroserWindowController.margin
496 fleetViewHeight = fleetViewController.normalHeight + flashRect.height + BroserWindowController.margin + BroserWindowController.margin
497 fleetViewY = contentHeight - fleetViewHeight
500 fleetViewHeight = FleetViewController.oldStyleFleetViewHeight
501 fleetViewY = contentHeight - fleetViewHeight - flashRect.height - BroserWindowController.margin - BroserWindowController.flashTopMargin
504 fleetListRect.size.height = fleetViewHeight
505 fleetListRect.origin.y = fleetViewY
510 private func setFleetView(position newPosition: FleetViewPosition, animate: Bool) {
512 guard let window = window else { return }
514 changeFleetViewForFleetViewPositionIfNeeded(position: newPosition)
515 let winFrame = windowFrameForFleetViewPosition(position: newPosition)
516 let flashRect = flashFrameForFleetViewPosition(position: newPosition)
517 let fleetListRect = fleetViewFrameForFleetViewPosition(position: newPosition)
519 fleetViewPosition = newPosition
520 UserDefaults.standard[.fleetViewPosition] = newPosition
524 let winAnime: [NSViewAnimation.Key: Any] = [.target: window,
525 .endFrame: NSValue(rect: winFrame) ]
526 let flashAnime: [NSViewAnimation.Key: Any] = [.target: gameViewController.view,
527 .endFrame: NSValue(rect: flashRect) ]
528 let fleetAnime: [NSViewAnimation.Key: Any] = [.target: fleetViewController.view,
529 .endFrame: NSValue(rect: fleetListRect) ]
531 let anime = NSViewAnimation(viewAnimations: [winAnime, flashAnime, fleetAnime])
537 window.setFrame(winFrame, display: false)
538 gameViewController.view.frame = flashRect
539 fleetViewController.view.frame = fleetListRect
545 @available(OSX 10.12.2, *)
546 private var mainTouchBars: [Int: NSTouchBar] = [:]
547 @available(OSX 10.12.2, *)
548 private var shipTypeButtons: [Int: NSPopoverTouchBarItem] = [:]
549 @available(OSX 10.12.2, *)
550 private var shipTypeSegments: [Int: NSSegmentedControl] = [:]
552 @available(OSX 10.12.2, *)
553 extension BroserWindowController {
555 @IBOutlet var mainTouchBar: NSTouchBar! {
557 get { return mainTouchBars[hashValue] }
558 set { mainTouchBars[hashValue] = newValue }
561 @IBOutlet var shipTypeButton: NSPopoverTouchBarItem! {
563 get { return shipTypeButtons[hashValue] }
564 set { shipTypeButtons[hashValue] = newValue }
567 @IBOutlet var shipTypeSegment: NSSegmentedControl! {
569 get { return shipTypeSegments[hashValue] }
570 set { shipTypeSegments[hashValue] = newValue }
573 override func makeTouchBar() -> NSTouchBar? {
575 if let mainTouchBar = mainTouchBar { return mainTouchBar }
578 Bundle.main.loadNibNamed(NSNib.Name("BroswerTouchBar"), owner: self, topLevelObjects: &array)
580 shipTypeSegment.bind(.selectedIndex,
581 to: tabViewItemViewControllers[0],
582 withKeyPath: #keyPath(MainTabVIewItemViewController.selectedShipType),
584 let o = selectedMainTabIndex
585 selectedMainTabIndex = o
587 changeMainTabHandler = { [weak self] in
589 guard let `self` = self else { return }
591 self.shipTypeButton.dismissPopover(nil)
592 self.shipTypeSegment.unbind(.selectedIndex)
594 guard let button = self.shipTypeButton.view as? NSButton else { return }
596 let vc = self.tabViewItemViewControllers[$0]
597 button.isHidden = !vc.hasShipTypeSelector
598 self.shipTypeSegment.bind(.selectedIndex,
600 withKeyPath: #keyPath(MainTabVIewItemViewController.selectedShipType),