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 private weak var placeholder: NSView!
39 @IBOutlet private weak var combinedViewPlaceholder: NSView!
40 @IBOutlet private weak var deckPlaceholder: NSView!
41 @IBOutlet private weak var stokerContainer: NSView!
42 @IBOutlet private weak var resourcePlaceholder: NSView!
43 @IBOutlet private weak var ancherageRepariTimerPlaceholder: NSView!
44 @IBOutlet private weak var informationsPlaceholder: NSView!
45 @IBOutlet private 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
57 private var gameViewController: GameViewController!
58 private var fleetViewController: FleetViewController!
59 @objc private var informantionViewController = InformationTabViewController()
60 private var ancherageRepariTimerViewController: AncherageRepairTimerViewController!
61 private var resourceViewController: ResourceViewController!
62 private var combinedViewController: CombileViewController!
64 private var fleetViewPosition: FleetViewPosition = .above
65 private var isCombinedMode = false
66 private var isExtShpMode = false
69 override func windowDidLoad() {
73 gameViewController = GameViewController()
74 replace(view: placeholder, with: gameViewController)
76 replace(view: informationsPlaceholder, with: informantionViewController)
78 resourceViewController = ResourceViewController()
79 replace(view: resourcePlaceholder, with: resourceViewController)
81 ancherageRepariTimerViewController = AncherageRepairTimerViewController()
82 replace(view: ancherageRepariTimerPlaceholder, with: ancherageRepariTimerViewController)
83 if UserDefaults.standard[.screenshotButtonSize] == .small { toggleAnchorageSize(nil) }
85 fleetViewController = FleetViewController(viewType: .detailViewType)
86 replace(view: deckPlaceholder, with: fleetViewController)
87 setFleetView(position: UserDefaults.standard[.fleetViewPosition], animate: false)
88 fleetViewController.enableAnimation = false
89 fleetViewController.shipOrder = UserDefaults.standard[.fleetViewShipOrder]
90 fleetViewController.enableAnimation = true
91 fleetViewController.delegate = self
93 bind(NSBindingName(rawValue: #keyPath(flagShipID)), to: deckContoller, withKeyPath: "selection.ship_0", options: nil)
95 NotificationCenter.default
96 .addObserver(forName: .CombinedDidCange, object: nil, queue: nil) {
98 guard UserDefaults.standard[.autoCombinedView] else { return }
99 guard let type = $0.userInfo?[CombinedCommand.userInfoKey] as? CombineType else { return }
101 if !Thread.isMainThread { Thread.sleep(forTimeInterval: 0.1) }
103 DispatchQueue.main.async {
107 self.hideCombinedView()
109 case .maneuver, .water, .transportation:
110 self.showCombinedView()
115 if UserDefaults.standard[.lastHasCombinedView] { showCombinedView() }
118 override func swipe(with event: NSEvent) {
120 guard UserDefaults.standard[.useSwipeChangeCombinedView] else { return }
122 if event.deltaX > 0 {
127 if event.deltaX < 0 {
133 @objc func windowWillClose(_ notification: Notification) {
135 UserDefaults.standard[.lastHasCombinedView] = isCombinedMode
138 private func showCombinedView() {
140 if isCombinedMode { return }
142 if fleetViewPosition == .oldStyle { return }
144 isCombinedMode = true
146 if combinedViewController == nil {
148 combinedViewController = CombileViewController()
149 combinedViewController.view.isHidden = true
150 replace(view: combinedViewPlaceholder, with: combinedViewController)
153 var winFrame = window!.frame
154 let incWid = combinedViewController.view.frame.maxX
155 winFrame.size.width += incWid
156 winFrame.origin.x -= incWid
157 combinedViewController.view.isHidden = false
158 window?.setFrame(winFrame, display: true, animate: true)
161 private func hideCombinedView() {
163 if !isCombinedMode { return }
165 isCombinedMode = false
167 var winFrame = window!.frame
168 let decWid = combinedViewController.view.frame.maxX
169 winFrame.size.width -= decWid
170 winFrame.origin.x += decWid
171 window?.setFrame(winFrame, display: true, animate: true)
172 combinedViewController.view.isHidden = true
177 extension BroserWindowController {
179 private func showView(number: Int) {
181 informantionViewController.selectionIndex = number
184 @IBAction func reloadContent(_ sender: AnyObject?) {
186 gameViewController.reloadContent(sender)
189 @IBAction func deleteCacheAndReload(_ sender: AnyObject?) {
191 gameViewController.deleteCacheAndReload(sender)
194 @IBAction func clearQuestList(_ sender: AnyObject?) {
196 let store = ServerDataStore.oneTimeEditor()
197 store.quests().forEach(store.delete)
200 @IBAction func selectView(_ sender: AnyObject?) {
202 guard let tag = sender?.tag else { return }
204 showView(number: tag)
207 @IBAction func changeMainTab(_ sender: AnyObject?) {
209 guard let segment = sender?.selectedSegment else { return }
211 showView(number: segment)
214 @IBAction func screenShot(_ sender: AnyObject?) {
216 gameViewController.screenShot(sender)
219 @IBAction func toggleAnchorageSize(_ sender: AnyObject?) {
221 let current = ancherageRepariTimerViewController.controlSize
222 var diff = AncherageRepairTimerViewController.regularHeight - AncherageRepairTimerViewController.smallHeight
223 let newSize: NSControl.ControlSize = {
225 if current == .regular {
234 ancherageRepariTimerViewController.controlSize = newSize
236 var frame = informantionViewController.view.frame
237 frame.size.height -= diff
238 frame.origin.y += diff
239 informantionViewController.view.frame = frame
241 UserDefaults.standard[.screenshotButtonSize] = newSize
244 @IBAction func showHideCombinedView(_ sender: AnyObject?) {
246 isCombinedMode ? hideCombinedView() : showCombinedView()
249 @IBAction func fleetListAbove(_ sender: AnyObject?) {
251 setFleetView(position: .above, animate: true)
254 @IBAction func fleetListBelow(_ sender: AnyObject?) {
256 setFleetView(position: .below, animate: true)
259 @IBAction func fleetListDivide(_ sender: AnyObject?) {
261 setFleetView(position: .divided, animate: true)
264 @IBAction func fleetListSimple(_ sender: AnyObject?) {
266 setFleetView(position: .oldStyle, animate: true)
269 @IBAction func reorderToDoubleLine(_ sender: AnyObject?) {
271 fleetViewController.shipOrder = .doubleLine
272 UserDefaults.standard[.fleetViewShipOrder] = .doubleLine
275 @IBAction func reorderToLeftToRight(_ sender: AnyObject?) {
277 fleetViewController.shipOrder = .leftToRight
278 UserDefaults.standard[.fleetViewShipOrder] = .leftToRight
281 @IBAction func selectNextFleet(_ sender: AnyObject?) {
283 fleetViewController.selectNextFleet(sender)
286 @IBAction func selectPreviousFleet(_ sender: AnyObject?) {
288 fleetViewController.selectPreviousFleet(sender)
291 @IBAction func changeSakutekiCalculator(_ sender: Any?) {
293 fleetViewController.changeSakutekiCalculator(sender)
296 override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
298 guard let action: Selector = menuItem.action else { return false }
302 case #selector(BroserWindowController.reloadContent(_:)),
303 #selector(BroserWindowController.screenShot(_:)),
304 #selector(BroserWindowController.deleteCacheAndReload(_:)):
306 return gameViewController.validateMenuItem(menuItem)
308 case #selector(BroserWindowController.selectView(_:)),
309 #selector(BroserWindowController.selectNextFleet(_:)),
310 #selector(BroserWindowController.selectPreviousFleet(_:)):
314 case #selector(BroserWindowController.fleetListAbove(_:)):
315 menuItem.state = (fleetViewPosition == .above ? .on : .off)
318 case #selector(BroserWindowController.fleetListBelow(_:)):
319 menuItem.state = (fleetViewPosition == .below ? .on : .off)
322 case #selector(BroserWindowController.fleetListDivide(_:)):
323 menuItem.state = (fleetViewPosition == .divided ? .on : .off)
326 case #selector(BroserWindowController.fleetListSimple(_:)):
327 menuItem.state = (fleetViewPosition == .oldStyle ? .on : .off)
330 case #selector(BroserWindowController.reorderToDoubleLine(_:)):
331 menuItem.state = (fleetViewController.shipOrder == .doubleLine ? .on : .off)
334 case #selector(BroserWindowController.reorderToLeftToRight(_:)):
335 menuItem.state = (fleetViewController.shipOrder == .leftToRight ? .on: .off)
338 case #selector(BroserWindowController.clearQuestList(_:)):
341 case #selector(BroserWindowController.showHideCombinedView(_:)):
344 menuItem.title = LocalizedStrings.hideCombinedView.string
348 menuItem.title = LocalizedStrings.showCombinedView.string
351 if fleetViewPosition == .oldStyle { return false }
355 case #selector(BroserWindowController.toggleAnchorageSize(_:)):
358 case #selector(BroserWindowController.changeSakutekiCalculator(_:)):
359 return fleetViewController.validateMenuItem(menuItem)
367 extension BroserWindowController {
369 private static let margin: CGFloat = 1.0
370 private static let flashTopMargin: CGFloat = 4.0
372 private func changeFleetViewForFleetViewPositionIfNeeded(position newPosition: FleetViewPosition) {
374 if fleetViewPosition == newPosition { return }
375 if fleetViewPosition != .oldStyle && newPosition != .oldStyle { return }
376 if newPosition == .oldStyle && isCombinedMode { hideCombinedView() }
378 let type: FleetViewType = (newPosition == .oldStyle) ? .minimumViewType : .detailViewType
380 guard let newController = FleetViewController(viewType: type) else { return }
382 newController.enableAnimation = true
383 newController.shipOrder = fleetViewController.shipOrder
384 replace(view: fleetViewController.view, with: newController)
385 fleetViewController = newController
386 fleetViewController.delegate = self
389 private func windowHeightForFleetViewPosition(position newPosition: FleetViewPosition) -> CGFloat {
391 guard var contentHeight = window!.contentView?.frame.size.height else { return 0.0 }
393 if fleetViewPosition == newPosition { return contentHeight }
394 if fleetViewPosition == .oldStyle {
396 contentHeight += FleetViewController.heightDifference
398 if newPosition == .oldStyle {
400 contentHeight -= FleetViewController.heightDifference
406 private func windowFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
408 var contentRect = window!.frame
410 if fleetViewPosition == newPosition { return contentRect }
411 if fleetViewPosition == .oldStyle {
413 contentRect.size.height += FleetViewController.heightDifference
414 contentRect.origin.y -= FleetViewController.heightDifference
416 if newPosition == .oldStyle {
418 contentRect.size.height -= FleetViewController.heightDifference
419 contentRect.origin.y += FleetViewController.heightDifference
425 private func flashFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
427 let contentHeight = windowHeightForFleetViewPosition(position: newPosition)
429 var flashRect = gameViewController.view.frame
433 flashY = contentHeight - flashRect.height - fleetViewController.normalHeight
436 flashY = contentHeight - flashRect.height
439 flashY = contentHeight - flashRect.height - fleetViewController.upsideHeight - BroserWindowController.margin
442 flashY = contentHeight - flashRect.height - BroserWindowController.flashTopMargin
445 flashRect.origin.y = flashY
450 private func fleetViewFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
452 let contentHeight = windowHeightForFleetViewPosition(position: newPosition)
453 let flashRect = gameViewController.view.frame
454 var fleetListRect = fleetViewController.view.frame
456 var fleetViewHeight: CGFloat
457 var fleetViewY: CGFloat
461 fleetViewHeight = fleetViewController.normalHeight
462 fleetViewY = contentHeight - fleetViewHeight
465 fleetViewHeight = fleetViewController.normalHeight
466 fleetViewY = contentHeight - fleetViewHeight - flashRect.height - BroserWindowController.margin
469 fleetViewHeight = fleetViewController.normalHeight + flashRect.height + BroserWindowController.margin + BroserWindowController.margin
470 fleetViewY = contentHeight - fleetViewHeight
473 fleetViewHeight = FleetViewController.oldStyleFleetViewHeight
474 fleetViewY = contentHeight - fleetViewHeight - flashRect.height - BroserWindowController.margin - BroserWindowController.flashTopMargin
477 fleetListRect.size.height = fleetViewHeight
478 fleetListRect.origin.y = fleetViewY
483 private func setFleetView(position newPosition: FleetViewPosition, animate: Bool) {
485 guard let window = window else { return }
487 changeFleetViewForFleetViewPositionIfNeeded(position: newPosition)
488 let winFrame = windowFrameForFleetViewPosition(position: newPosition)
489 let flashRect = flashFrameForFleetViewPosition(position: newPosition)
490 let fleetListRect = fleetViewFrameForFleetViewPosition(position: newPosition)
492 fleetViewPosition = newPosition
493 UserDefaults.standard[.fleetViewPosition] = newPosition
497 let winAnime = ViewAnimationAttributes(target: window, endFrame: winFrame)
498 let flashAnime = ViewAnimationAttributes(target: gameViewController.view, endFrame: flashRect)
499 let fleetAnime = ViewAnimationAttributes(target: fleetViewController.view, endFrame: fleetListRect)
501 let anime = ViewAnimation(viewAnimations: [winAnime, flashAnime, fleetAnime])
507 window.setFrame(winFrame, display: false)
508 gameViewController.view.frame = flashRect
509 fleetViewController.view.frame = fleetListRect
515 extension BroserWindowController: FleetViewControllerDelegate {
517 func changeShowsExtShip(_ fleetViewController: FleetViewController, showsExtShip: Bool) {
519 guard self.fleetViewController == fleetViewController else { return }
521 if isExtShpMode && !showsExtShip {
524 let diffHeight = fleetViewController.shipViewSize.height
526 var iFrame = informantionViewController.view.frame
527 iFrame.origin.y -= diffHeight
528 iFrame.size.height += diffHeight
529 informantionViewController.view.animator().frame = iFrame
531 var sFrame = stokerContainer.frame
532 sFrame.origin.y -= diffHeight
533 stokerContainer.animator().frame = sFrame
537 } else if !isExtShpMode && showsExtShip {
540 let diffHeight = fleetViewController.shipViewSize.height
542 var iFrame = informantionViewController.view.frame
543 iFrame.origin.y += diffHeight
544 iFrame.size.height -= diffHeight
545 informantionViewController.view.animator().frame = iFrame
547 var sFrame = stokerContainer.frame
548 sFrame.origin.y += diffHeight
549 stokerContainer.animator().frame = sFrame
556 @available(OSX 10.12.2, *)
557 private var mainTouchBars: [Int: NSTouchBar] = [:]
558 @available(OSX 10.12.2, *)
559 private var shipTypeButtons: [Int: NSPopoverTouchBarItem] = [:]
560 @available(OSX 10.12.2, *)
561 private var shipTypeSegments: [Int: NSSegmentedControl] = [:]
563 @available(OSX 10.12.2, *)
564 extension BroserWindowController {
566 @IBOutlet private var mainTouchBar: NSTouchBar! {
568 get { return mainTouchBars[hashValue] }
569 set { mainTouchBars[hashValue] = newValue }
572 @IBOutlet private var shipTypeButton: NSPopoverTouchBarItem! {
574 get { return shipTypeButtons[hashValue] }
575 set { shipTypeButtons[hashValue] = newValue }
578 @IBOutlet private var shipTypeSegment: NSSegmentedControl! {
580 get { return shipTypeSegments[hashValue] }
581 set { shipTypeSegments[hashValue] = newValue }
584 override func makeTouchBar() -> NSTouchBar? {
586 if let mainTouchBar = mainTouchBar { return mainTouchBar }
588 Bundle.main.loadNibNamed(NSNib.Name("BroswerTouchBar"), owner: self, topLevelObjects: nil)
590 shipTypeSegment.bind(.selectedIndex,
591 to: informantionViewController,
592 withKeyPath: #keyPath(InformationTabViewController.selectedShipType),
595 informantionViewController.selectionDidChangeHandler = { [weak self] in
597 guard let `self` = self else { return }
599 self.shipTypeButton.dismissPopover(nil)
601 guard let button = self.shipTypeButton.view as? NSButton else { return }
602 button.isHidden = !self.informantionViewController.hasShipTypeSelector
604 self.shipTypeSegment.bind(.selectedIndex,
605 to: self.informantionViewController,
606 withKeyPath: #keyPath(InformationTabViewController.selectedShipType),