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 // call from menu item
201 @IBAction func selectView(_ sender: AnyObject?) {
203 guard let tag = sender?.tag else { return }
205 showView(number: tag)
208 // call from touch bar
209 @IBAction func changeMainTab(_ sender: AnyObject?) {
211 guard let segment = sender?.selectedSegment else { return }
213 showView(number: segment)
216 @IBAction func screenShot(_ sender: AnyObject?) {
218 gameViewController.screenShot(sender)
221 @IBAction func toggleAnchorageSize(_ sender: AnyObject?) {
223 let current = ancherageRepariTimerViewController.controlSize
224 var diff = AncherageRepairTimerViewController.regularHeight - AncherageRepairTimerViewController.smallHeight
225 let newSize: NSControl.ControlSize = {
227 if current == .regular {
236 ancherageRepariTimerViewController.controlSize = newSize
238 var frame = informantionViewController.view.frame
239 frame.size.height -= diff
240 frame.origin.y += diff
241 informantionViewController.view.frame = frame
243 UserDefaults.standard[.screenshotButtonSize] = newSize
246 @IBAction func showHideCombinedView(_ sender: AnyObject?) {
248 isCombinedMode ? hideCombinedView() : showCombinedView()
251 @IBAction func fleetListAbove(_ sender: AnyObject?) {
253 setFleetView(position: .above, animate: true)
256 @IBAction func fleetListBelow(_ sender: AnyObject?) {
258 setFleetView(position: .below, animate: true)
261 @IBAction func fleetListDivide(_ sender: AnyObject?) {
263 setFleetView(position: .divided, animate: true)
266 @IBAction func fleetListSimple(_ sender: AnyObject?) {
268 setFleetView(position: .oldStyle, animate: true)
271 @IBAction func reorderToDoubleLine(_ sender: AnyObject?) {
273 fleetViewController.shipOrder = .doubleLine
274 UserDefaults.standard[.fleetViewShipOrder] = .doubleLine
277 @IBAction func reorderToLeftToRight(_ sender: AnyObject?) {
279 fleetViewController.shipOrder = .leftToRight
280 UserDefaults.standard[.fleetViewShipOrder] = .leftToRight
283 @IBAction func selectNextFleet(_ sender: AnyObject?) {
285 fleetViewController.selectNextFleet(sender)
288 @IBAction func selectPreviousFleet(_ sender: AnyObject?) {
290 fleetViewController.selectPreviousFleet(sender)
293 @IBAction func changeSakutekiCalculator(_ sender: Any?) {
295 fleetViewController.changeSakutekiCalculator(sender)
298 override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
300 guard let action: Selector = menuItem.action else { return false }
304 case #selector(BroserWindowController.reloadContent(_:)),
305 #selector(BroserWindowController.screenShot(_:)),
306 #selector(BroserWindowController.deleteCacheAndReload(_:)):
308 return gameViewController.validateMenuItem(menuItem)
310 case #selector(BroserWindowController.selectView(_:)),
311 #selector(BroserWindowController.selectNextFleet(_:)),
312 #selector(BroserWindowController.selectPreviousFleet(_:)):
316 case #selector(BroserWindowController.fleetListAbove(_:)):
317 menuItem.state = (fleetViewPosition == .above ? .on : .off)
320 case #selector(BroserWindowController.fleetListBelow(_:)):
321 menuItem.state = (fleetViewPosition == .below ? .on : .off)
324 case #selector(BroserWindowController.fleetListDivide(_:)):
325 menuItem.state = (fleetViewPosition == .divided ? .on : .off)
328 case #selector(BroserWindowController.fleetListSimple(_:)):
329 menuItem.state = (fleetViewPosition == .oldStyle ? .on : .off)
332 case #selector(BroserWindowController.reorderToDoubleLine(_:)):
333 menuItem.state = (fleetViewController.shipOrder == .doubleLine ? .on : .off)
336 case #selector(BroserWindowController.reorderToLeftToRight(_:)):
337 menuItem.state = (fleetViewController.shipOrder == .leftToRight ? .on: .off)
340 case #selector(BroserWindowController.clearQuestList(_:)):
343 case #selector(BroserWindowController.showHideCombinedView(_:)):
346 menuItem.title = LocalizedStrings.hideCombinedView.string
350 menuItem.title = LocalizedStrings.showCombinedView.string
353 if fleetViewPosition == .oldStyle { return false }
357 case #selector(BroserWindowController.toggleAnchorageSize(_:)):
360 case #selector(BroserWindowController.changeSakutekiCalculator(_:)):
361 return fleetViewController.validateMenuItem(menuItem)
369 extension BroserWindowController {
371 private static let margin: CGFloat = 1.0
372 private static let flashTopMargin: CGFloat = 4.0
374 private func changeFleetViewForFleetViewPositionIfNeeded(position newPosition: FleetViewPosition) {
376 if fleetViewPosition == newPosition { return }
377 if fleetViewPosition != .oldStyle && newPosition != .oldStyle { return }
378 if newPosition == .oldStyle && isCombinedMode { hideCombinedView() }
380 let type: FleetViewType = (newPosition == .oldStyle) ? .minimumViewType : .detailViewType
382 guard let newController = FleetViewController(viewType: type) else { return }
384 newController.enableAnimation = true
385 newController.shipOrder = fleetViewController.shipOrder
386 replace(view: fleetViewController.view, with: newController)
387 fleetViewController = newController
388 fleetViewController.delegate = self
391 private func windowHeightForFleetViewPosition(position newPosition: FleetViewPosition) -> CGFloat {
393 guard var contentHeight = window!.contentView?.frame.size.height else { return 0.0 }
395 if fleetViewPosition == newPosition { return contentHeight }
396 if fleetViewPosition == .oldStyle {
398 contentHeight += FleetViewController.heightDifference
400 if newPosition == .oldStyle {
402 contentHeight -= FleetViewController.heightDifference
408 private func windowFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
410 var contentRect = window!.frame
412 if fleetViewPosition == newPosition { return contentRect }
413 if fleetViewPosition == .oldStyle {
415 contentRect.size.height += FleetViewController.heightDifference
416 contentRect.origin.y -= FleetViewController.heightDifference
418 if newPosition == .oldStyle {
420 contentRect.size.height -= FleetViewController.heightDifference
421 contentRect.origin.y += FleetViewController.heightDifference
427 private func flashFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
429 let contentHeight = windowHeightForFleetViewPosition(position: newPosition)
431 var flashRect = gameViewController.view.frame
435 flashY = contentHeight - flashRect.height - fleetViewController.normalHeight
438 flashY = contentHeight - flashRect.height
441 flashY = contentHeight - flashRect.height - fleetViewController.upsideHeight - BroserWindowController.margin
444 flashY = contentHeight - flashRect.height - BroserWindowController.flashTopMargin
447 flashRect.origin.y = flashY
452 private func fleetViewFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
454 let contentHeight = windowHeightForFleetViewPosition(position: newPosition)
455 let flashRect = gameViewController.view.frame
456 var fleetListRect = fleetViewController.view.frame
458 var fleetViewHeight: CGFloat
459 var fleetViewY: CGFloat
463 fleetViewHeight = fleetViewController.normalHeight
464 fleetViewY = contentHeight - fleetViewHeight
467 fleetViewHeight = fleetViewController.normalHeight
468 fleetViewY = contentHeight - fleetViewHeight - flashRect.height - BroserWindowController.margin
471 fleetViewHeight = fleetViewController.normalHeight + flashRect.height + BroserWindowController.margin + BroserWindowController.margin
472 fleetViewY = contentHeight - fleetViewHeight
475 fleetViewHeight = FleetViewController.oldStyleFleetViewHeight
476 fleetViewY = contentHeight - fleetViewHeight - flashRect.height - BroserWindowController.margin - BroserWindowController.flashTopMargin
479 fleetListRect.size.height = fleetViewHeight
480 fleetListRect.origin.y = fleetViewY
485 private func setFleetView(position newPosition: FleetViewPosition, animate: Bool) {
487 guard let window = window else { return }
489 changeFleetViewForFleetViewPositionIfNeeded(position: newPosition)
490 let winFrame = windowFrameForFleetViewPosition(position: newPosition)
491 let flashRect = flashFrameForFleetViewPosition(position: newPosition)
492 let fleetListRect = fleetViewFrameForFleetViewPosition(position: newPosition)
494 fleetViewPosition = newPosition
495 UserDefaults.standard[.fleetViewPosition] = newPosition
499 let winAnime = ViewAnimationAttributes(target: window, endFrame: winFrame)
500 let flashAnime = ViewAnimationAttributes(target: gameViewController.view, endFrame: flashRect)
501 let fleetAnime = ViewAnimationAttributes(target: fleetViewController.view, endFrame: fleetListRect)
503 let anime = ViewAnimation(viewAnimations: [winAnime, flashAnime, fleetAnime])
509 window.setFrame(winFrame, display: false)
510 gameViewController.view.frame = flashRect
511 fleetViewController.view.frame = fleetListRect
517 extension BroserWindowController: FleetViewControllerDelegate {
519 func changeShowsExtShip(_ fleetViewController: FleetViewController, showsExtShip: Bool) {
521 guard self.fleetViewController == fleetViewController else { return }
523 if isExtShpMode && !showsExtShip {
526 let diffHeight = fleetViewController.shipViewSize.height
528 var iFrame = informantionViewController.view.frame
529 iFrame.origin.y -= diffHeight
530 iFrame.size.height += diffHeight
531 informantionViewController.view.animator().frame = iFrame
533 var sFrame = stokerContainer.frame
534 sFrame.origin.y -= diffHeight
535 stokerContainer.animator().frame = sFrame
539 } else if !isExtShpMode && showsExtShip {
542 let diffHeight = fleetViewController.shipViewSize.height
544 var iFrame = informantionViewController.view.frame
545 iFrame.origin.y += diffHeight
546 iFrame.size.height -= diffHeight
547 informantionViewController.view.animator().frame = iFrame
549 var sFrame = stokerContainer.frame
550 sFrame.origin.y += diffHeight
551 stokerContainer.animator().frame = sFrame
558 @available(OSX 10.12.2, *)
559 private var mainTouchBars: [Int: NSTouchBar] = [:]
560 @available(OSX 10.12.2, *)
561 private var shipTypeButtons: [Int: NSPopoverTouchBarItem] = [:]
562 @available(OSX 10.12.2, *)
563 private var shipTypeSegments: [Int: NSSegmentedControl] = [:]
565 @available(OSX 10.12.2, *)
566 extension BroserWindowController {
568 @IBOutlet private var mainTouchBar: NSTouchBar! {
570 get { return mainTouchBars[hashValue] }
571 set { mainTouchBars[hashValue] = newValue }
574 @IBOutlet private var shipTypeButton: NSPopoverTouchBarItem! {
576 get { return shipTypeButtons[hashValue] }
577 set { shipTypeButtons[hashValue] = newValue }
580 @IBOutlet private var shipTypeSegment: NSSegmentedControl! {
582 get { return shipTypeSegments[hashValue] }
583 set { shipTypeSegments[hashValue] = newValue }
586 override func makeTouchBar() -> NSTouchBar? {
588 if let mainTouchBar = mainTouchBar { return mainTouchBar }
590 Bundle.main.loadNibNamed(NSNib.Name("BroswerTouchBar"), owner: self, topLevelObjects: nil)
592 shipTypeSegment.bind(.selectedIndex,
593 to: informantionViewController,
594 withKeyPath: #keyPath(InformationTabViewController.selectedShipType),
597 informantionViewController.selectionDidChangeHandler = { [weak self] in
599 guard let `self` = self else { return }
601 self.shipTypeButton.dismissPopover(nil)
603 guard let button = self.shipTypeButton.view as? NSButton else { return }
604 button.isHidden = !self.informantionViewController.hasShipTypeSelector
606 self.shipTypeSegment.bind(.selectedIndex,
607 to: self.informantionViewController,
608 withKeyPath: #keyPath(InformationTabViewController.selectedShipType),