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 {
21 case oldStyle = 0xffffffff
24 @objc override class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String> {
28 case #keyPath(flagShipName): return [#keyPath(flagShipID)]
35 @objc let managedObjectContext = ServerDataStore.default.context
39 NotificationCenter.default.removeObserver(self)
42 @IBOutlet private weak var placeholder: NSView!
43 @IBOutlet private weak var combinedViewPlaceholder: NSView!
44 @IBOutlet private weak var deckPlaceholder: NSView!
45 @IBOutlet private weak var stokerContainer: NSView!
46 @IBOutlet private weak var resourcePlaceholder: NSView!
47 @IBOutlet private weak var ancherageRepariTimerPlaceholder: NSView!
48 @IBOutlet private weak var informationsPlaceholder: NSView!
49 @IBOutlet private var deckContoller: NSArrayController!
51 override var windowNibName: NSNib.Name {
53 return .nibName(instanceOf: self)
56 @objc var flagShipID: Int = 0
57 @objc var flagShipName: String? {
59 return ServerDataStore.default.ship(by: flagShipID)?.name
62 private var gameViewController: GameViewController!
63 private var fleetViewController: FleetViewController!
64 @objc private var informantionViewController = InformationTabViewController()
65 private var ancherageRepariTimerViewController: AncherageRepairTimerViewController!
66 private var resourceViewController: ResourceViewController!
67 private var combinedViewController: CombileViewController!
69 private var fleetViewPosition: FleetViewPosition = .above
70 private var isCombinedMode = false
71 private var isExtShpMode = false
74 override func windowDidLoad() {
78 gameViewController = GameViewController()
79 replace(view: placeholder, with: gameViewController)
81 replace(view: informationsPlaceholder, with: informantionViewController)
83 resourceViewController = ResourceViewController()
84 replace(view: resourcePlaceholder, with: resourceViewController)
86 ancherageRepariTimerViewController = AncherageRepairTimerViewController()
87 replace(view: ancherageRepariTimerPlaceholder, with: ancherageRepariTimerViewController)
88 if UserDefaults.standard[.screenshotButtonSize] == .small { toggleAnchorageSize(nil) }
90 fleetViewController = FleetViewController(viewType: .detailViewType)
91 replace(view: deckPlaceholder, with: fleetViewController)
92 setFleetView(position: UserDefaults.standard[.fleetViewPosition], animate: false)
93 fleetViewController.enableAnimation = false
94 fleetViewController.shipOrder = UserDefaults.standard[.fleetViewShipOrder]
95 fleetViewController.enableAnimation = true
96 fleetViewController.delegate = self
98 bind(NSBindingName(rawValue: #keyPath(flagShipID)), to: deckContoller, withKeyPath: "selection.ship_0", options: nil)
100 NotificationCenter.default
101 .addObserver(forName: .CombinedDidCange, object: nil, queue: nil) {
103 guard UserDefaults.standard[.autoCombinedView] else {
107 guard let type = $0.userInfo?[CombinedCommand.userInfoKey] as? CombineType else {
112 if !Thread.isMainThread { Thread.sleep(forTimeInterval: 0.1) }
114 DispatchQueue.main.async {
119 self.hideCombinedView()
121 case .maneuver, .water, .transportation:
122 self.showCombinedView()
128 if UserDefaults.standard[.lastHasCombinedView] {
134 override func swipe(with event: NSEvent) {
136 guard UserDefaults.standard[.useSwipeChangeCombinedView] else {
141 if event.deltaX > 0 {
146 if event.deltaX < 0 {
152 @objc func windowWillClose(_ notification: Notification) {
154 UserDefaults.standard[.lastHasCombinedView] = isCombinedMode
157 private func showCombinedView() {
164 if fleetViewPosition == .oldStyle {
169 isCombinedMode = true
171 if combinedViewController == nil {
173 combinedViewController = CombileViewController()
174 combinedViewController.view.isHidden = true
175 replace(view: combinedViewPlaceholder, with: combinedViewController)
178 var winFrame = window!.frame
179 let incWid = combinedViewController.view.frame.maxX
180 winFrame.size.width += incWid
181 winFrame.origin.x -= incWid
182 combinedViewController.view.isHidden = false
183 window?.setFrame(winFrame, display: true, animate: true)
186 private func hideCombinedView() {
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 informantionViewController.selectionIndex = 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.sync { store.quests().forEach(store.delete) }
228 // call from menu item
229 @IBAction func selectView(_ sender: AnyObject?) {
231 guard let tag = sender?.tag else {
236 showView(number: tag)
239 // call from touch bar
240 @IBAction func changeMainTab(_ sender: AnyObject?) {
242 guard let segment = sender?.selectedSegment else {
247 showView(number: segment)
250 @IBAction func screenShot(_ sender: AnyObject?) {
252 gameViewController.screenShot(sender)
255 @IBAction func toggleAnchorageSize(_ sender: AnyObject?) {
257 let current = ancherageRepariTimerViewController.controlSize
258 var diff = AncherageRepairTimerViewController.regularHeight - AncherageRepairTimerViewController.smallHeight
259 let newSize: NSControl.ControlSize = {
261 if current == .regular {
270 ancherageRepariTimerViewController.controlSize = newSize
272 var frame = informantionViewController.view.frame
273 frame.size.height -= diff
274 frame.origin.y += diff
275 informantionViewController.view.frame = frame
277 UserDefaults.standard[.screenshotButtonSize] = newSize
280 @IBAction func showHideCombinedView(_ sender: AnyObject?) {
282 isCombinedMode ? hideCombinedView() : showCombinedView()
285 @IBAction func fleetListAbove(_ sender: AnyObject?) {
287 setFleetView(position: .above, animate: true)
290 @IBAction func fleetListBelow(_ sender: AnyObject?) {
292 setFleetView(position: .below, animate: true)
295 @IBAction func fleetListDivide(_ sender: AnyObject?) {
297 setFleetView(position: .divided, animate: true)
300 @IBAction func fleetListSimple(_ sender: AnyObject?) {
302 setFleetView(position: .oldStyle, animate: true)
305 @IBAction func reorderToDoubleLine(_ sender: AnyObject?) {
307 fleetViewController.shipOrder = .doubleLine
308 UserDefaults.standard[.fleetViewShipOrder] = .doubleLine
311 @IBAction func reorderToLeftToRight(_ sender: AnyObject?) {
313 fleetViewController.shipOrder = .leftToRight
314 UserDefaults.standard[.fleetViewShipOrder] = .leftToRight
317 @IBAction func selectNextFleet(_ sender: AnyObject?) {
319 fleetViewController.selectNextFleet(sender)
322 @IBAction func selectPreviousFleet(_ sender: AnyObject?) {
324 fleetViewController.selectPreviousFleet(sender)
327 @IBAction func changeSakutekiCalculator(_ sender: Any?) {
329 fleetViewController.changeSakutekiCalculator(sender)
332 override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
334 guard let action: Selector = menuItem.action else {
341 case #selector(BroserWindowController.reloadContent(_:)),
342 #selector(BroserWindowController.screenShot(_:)),
343 #selector(BroserWindowController.deleteCacheAndReload(_:)):
345 return gameViewController.validateMenuItem(menuItem)
347 case #selector(BroserWindowController.selectView(_:)),
348 #selector(BroserWindowController.selectNextFleet(_:)),
349 #selector(BroserWindowController.selectPreviousFleet(_:)):
353 case #selector(BroserWindowController.fleetListAbove(_:)):
355 menuItem.state = (fleetViewPosition == .above ? .on : .off)
359 case #selector(BroserWindowController.fleetListBelow(_:)):
361 menuItem.state = (fleetViewPosition == .below ? .on : .off)
365 case #selector(BroserWindowController.fleetListDivide(_:)):
367 menuItem.state = (fleetViewPosition == .divided ? .on : .off)
371 case #selector(BroserWindowController.fleetListSimple(_:)):
373 menuItem.state = (fleetViewPosition == .oldStyle ? .on : .off)
377 case #selector(BroserWindowController.reorderToDoubleLine(_:)):
379 menuItem.state = (fleetViewController.shipOrder == .doubleLine ? .on : .off)
383 case #selector(BroserWindowController.reorderToLeftToRight(_:)):
385 menuItem.state = (fleetViewController.shipOrder == .leftToRight ? .on: .off)
389 case #selector(BroserWindowController.clearQuestList(_:)):
393 case #selector(BroserWindowController.showHideCombinedView(_:)):
397 menuItem.title = LocalizedStrings.hideCombinedView.string
401 menuItem.title = LocalizedStrings.showCombinedView.string
404 if fleetViewPosition == .oldStyle { return false }
408 case #selector(BroserWindowController.toggleAnchorageSize(_:)):
412 case #selector(BroserWindowController.changeSakutekiCalculator(_:)):
414 return fleetViewController.validateMenuItem(menuItem)
424 extension BroserWindowController {
426 private static let margin: CGFloat = 1.0
427 private static let flashTopMargin: CGFloat = 4.0
429 private func changeFleetViewForFleetViewPositionIfNeeded(position newPosition: FleetViewPosition) {
431 if fleetViewPosition == newPosition {
435 if fleetViewPosition != .oldStyle && newPosition != .oldStyle {
439 if newPosition == .oldStyle && isCombinedMode {
444 let type: FleetViewType = (newPosition == .oldStyle) ? .minimumViewType : .detailViewType
446 guard let newController = FleetViewController(viewType: type) else {
451 newController.enableAnimation = true
452 newController.shipOrder = fleetViewController.shipOrder
453 replace(view: fleetViewController.view, with: newController)
454 fleetViewController = newController
455 fleetViewController.delegate = self
458 private func windowHeightForFleetViewPosition(position newPosition: FleetViewPosition) -> CGFloat {
460 guard var contentHeight = window!.contentView?.frame.size.height else {
465 if fleetViewPosition == newPosition {
469 if fleetViewPosition == .oldStyle {
471 contentHeight += FleetViewController.heightDifference
473 if newPosition == .oldStyle {
475 contentHeight -= FleetViewController.heightDifference
481 private func windowFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
483 var contentRect = window!.frame
485 if fleetViewPosition == newPosition {
489 if fleetViewPosition == .oldStyle {
491 contentRect.size.height += FleetViewController.heightDifference
492 contentRect.origin.y -= FleetViewController.heightDifference
494 if newPosition == .oldStyle {
496 contentRect.size.height -= FleetViewController.heightDifference
497 contentRect.origin.y += FleetViewController.heightDifference
503 private func flashFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
505 let contentHeight = windowHeightForFleetViewPosition(position: newPosition)
507 var flashRect = gameViewController.view.frame
512 flashY = contentHeight - flashRect.height - fleetViewController.normalHeight
515 flashY = contentHeight - flashRect.height
518 flashY = contentHeight - flashRect.height - fleetViewController.upsideHeight - BroserWindowController.margin
521 flashY = contentHeight - flashRect.height - BroserWindowController.flashTopMargin
525 flashRect.origin.y = flashY
530 private func fleetViewFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
532 let contentHeight = windowHeightForFleetViewPosition(position: newPosition)
533 let flashRect = gameViewController.view.frame
534 var fleetListRect = fleetViewController.view.frame
536 var fleetViewHeight: CGFloat
537 var fleetViewY: CGFloat
542 fleetViewHeight = fleetViewController.normalHeight
543 fleetViewY = contentHeight - fleetViewHeight
546 fleetViewHeight = fleetViewController.normalHeight
547 fleetViewY = contentHeight - fleetViewHeight - flashRect.height - BroserWindowController.margin
550 fleetViewHeight = fleetViewController.normalHeight + flashRect.height + BroserWindowController.margin + BroserWindowController.margin
551 fleetViewY = contentHeight - fleetViewHeight
554 fleetViewHeight = FleetViewController.oldStyleFleetViewHeight
555 fleetViewY = contentHeight - fleetViewHeight - flashRect.height - BroserWindowController.margin - BroserWindowController.flashTopMargin
559 fleetListRect.size.height = fleetViewHeight
560 fleetListRect.origin.y = fleetViewY
565 private func setFleetView(position newPosition: FleetViewPosition, animate: Bool) {
567 guard let window = window else {
572 changeFleetViewForFleetViewPositionIfNeeded(position: newPosition)
573 let winFrame = windowFrameForFleetViewPosition(position: newPosition)
574 let flashRect = flashFrameForFleetViewPosition(position: newPosition)
575 let fleetListRect = fleetViewFrameForFleetViewPosition(position: newPosition)
577 fleetViewPosition = newPosition
578 UserDefaults.standard[.fleetViewPosition] = newPosition
582 let winAnime = ViewAnimationAttributes(target: window, endFrame: winFrame)
583 let flashAnime = ViewAnimationAttributes(target: gameViewController.view, endFrame: flashRect)
584 let fleetAnime = ViewAnimationAttributes(target: fleetViewController.view, endFrame: fleetListRect)
586 let anime = ViewAnimation(viewAnimations: [winAnime, flashAnime, fleetAnime])
592 window.setFrame(winFrame, display: false)
593 gameViewController.view.frame = flashRect
594 fleetViewController.view.frame = fleetListRect
600 extension BroserWindowController: FleetViewControllerDelegate {
602 func changeShowsExtShip(_ fleetViewController: FleetViewController, showsExtShip: Bool) {
604 guard self.fleetViewController == fleetViewController else {
609 if isExtShpMode && !showsExtShip {
612 let diffHeight = fleetViewController.shipViewSize.height
614 var iFrame = informantionViewController.view.frame
615 iFrame.origin.y -= diffHeight
616 iFrame.size.height += diffHeight
617 informantionViewController.view.animator().frame = iFrame
619 var sFrame = stokerContainer.frame
620 sFrame.origin.y -= diffHeight
621 stokerContainer.animator().frame = sFrame
625 } else if !isExtShpMode && showsExtShip {
628 let diffHeight = fleetViewController.shipViewSize.height
630 var iFrame = informantionViewController.view.frame
631 iFrame.origin.y += diffHeight
632 iFrame.size.height -= diffHeight
633 informantionViewController.view.animator().frame = iFrame
635 var sFrame = stokerContainer.frame
636 sFrame.origin.y += diffHeight
637 stokerContainer.animator().frame = sFrame
644 @available(OSX 10.12.2, *)
645 private var mainTouchBars: [Int: NSTouchBar] = [:]
646 @available(OSX 10.12.2, *)
647 private var shipTypeButtons: [Int: NSPopoverTouchBarItem] = [:]
648 @available(OSX 10.12.2, *)
649 private var shipTypeSegments: [Int: NSSegmentedControl] = [:]
651 @available(OSX 10.12.2, *)
652 extension BroserWindowController {
654 @IBOutlet private var mainTouchBar: NSTouchBar! {
656 get { return mainTouchBars[hashValue] }
657 set { mainTouchBars[hashValue] = newValue }
660 @IBOutlet private var shipTypeButton: NSPopoverTouchBarItem! {
662 get { return shipTypeButtons[hashValue] }
663 set { shipTypeButtons[hashValue] = newValue }
666 @IBOutlet private var shipTypeSegment: NSSegmentedControl! {
668 get { return shipTypeSegments[hashValue] }
669 set { shipTypeSegments[hashValue] = newValue }
672 override func makeTouchBar() -> NSTouchBar? {
674 if let mainTouchBar = mainTouchBar { return mainTouchBar }
676 Bundle.main.loadNibNamed(NSNib.Name("BroswerTouchBar"), owner: self, topLevelObjects: nil)
678 shipTypeSegment.bind(.selectedIndex,
679 to: informantionViewController,
680 withKeyPath: #keyPath(InformationTabViewController.selectedShipType),
683 informantionViewController.selectionDidChangeHandler = { [weak self] in
685 guard let `self` = self else {
690 self.shipTypeButton.dismissPopover(nil)
692 guard let button = self.shipTypeButton.view as? NSButton else {
696 button.isHidden = !self.informantionViewController.hasShipTypeSelector
698 self.shipTypeSegment.bind(.selectedIndex,
699 to: self.informantionViewController,
700 withKeyPath: #keyPath(InformationTabViewController.selectedShipType),