2 // BroserWindowController.swift
5 // Created by Hori,Masaki on 2016/12/31.
6 // Copyright © 2016年 Hori,Masaki. All rights reserved.
12 fileprivate extension Selector {
13 static let reloadContent = #selector(BroserWindowController.reloadContent(_:))
14 static let deleteCacheAndReload = #selector(BroserWindowController.deleteCacheAndReload(_:))
15 static let clearQuestList = #selector(BroserWindowController.clearQuestList(_:))
16 static let selectView = #selector(BroserWindowController.selectView(_:))
17 static let changeMainTab = #selector(BroserWindowController.changeMainTab(_:))
18 static let screenShot = #selector(BroserWindowController.screenShot(_:))
19 static let toggleAnchorageSize = #selector(BroserWindowController.toggleAnchorageSize(_:))
20 static let showHideCombinedView = #selector(BroserWindowController.showHideCombinedView(_:))
21 static let fleetListAbove = #selector(BroserWindowController.fleetListAbove(_:))
22 static let fleetListBelow = #selector(BroserWindowController.fleetListBelow(_:))
23 static let fleetListDivide = #selector(BroserWindowController.fleetListDivide(_:))
24 static let fleetListSimple = #selector(BroserWindowController.fleetListSimple(_:))
25 static let reorderToDoubleLine = #selector(BroserWindowController.reorderToDoubleLine(_:))
26 static let reorderToLeftToRight = #selector(BroserWindowController.reorderToLeftToRight(_:))
27 static let selectNextFleet = #selector(BroserWindowController.selectNextFleet(_:))
28 static let selectPreviousFleet = #selector(BroserWindowController.selectPreviousFleet(_:))
29 static let changeSakutekiCalculator = #selector(BroserWindowController.changeSakutekiCalculator(_:))
32 class BroserWindowController: NSWindowController {
33 enum FleetViewPosition: Int {
37 case oldStyle = 0xffffffff
40 class func keyPathsForValuesAffectingFlagShipName() -> Set<String> {
41 return [#keyPath(flagShipID)]
44 let managedObjectContext = ServerDataStore.default.context
47 NotificationCenter.default.removeObserver(self)
50 @IBOutlet weak var placeholder: NSView!
51 @IBOutlet weak var combinedViewPlaceholder: NSView!
52 @IBOutlet weak var deckPlaceholder: NSView!
53 @IBOutlet weak var resourcePlaceholder: NSView!
54 @IBOutlet weak var ancherageRepariTimerPlaceholder: NSView!
55 @IBOutlet weak var informations: NSTabView!
56 @IBOutlet var deckContoller: NSArrayController!
58 override var windowNibName: String! {
59 return "BroserWindowController"
61 var flagShipID: Int = 0
62 var flagShipName: String? {
63 return ServerDataStore.default.ship(by: flagShipID)?.name
65 var changeMainTabHandler: ((Int) -> Void)?
66 dynamic var selectedMainTabIndex: Int = 0 {
68 changeMainTabHandler?(selectedMainTabIndex)
72 fileprivate var gameViewController: GameViewController!
73 fileprivate var fleetViewController: FleetViewController!
74 fileprivate var tabViewItemViewControllers: [MainTabVIewItemViewController] = []
75 fileprivate var ancherageRepariTimerViewController: AncherageRepairTimerViewController!
76 private var resourceViewController: ResourceViewController!
77 private var docksViewController: DocksViewController!
78 private var shipViewController: ShipViewController!
79 private var powerUpViewController: PowerUpSupportViewController!
80 private var strengthedListViewController: StrengthenListViewController!
81 private var repairListViewController: RepairListViewController!
82 private var combinedViewController: CombileViewController!
84 fileprivate var fleetViewPosition: FleetViewPosition = .above
85 fileprivate var isCombinedMode = false
88 override func windowDidLoad() {
91 gameViewController = GameViewController()
92 replace(placeholder, with: gameViewController)
94 resourceViewController = ResourceViewController()
95 replace(resourcePlaceholder, with: resourceViewController)
97 ancherageRepariTimerViewController = AncherageRepairTimerViewController()
98 replace(ancherageRepariTimerPlaceholder, with: ancherageRepariTimerViewController)
99 if UserDefaults.standard.screenshotButtonSize == .small { toggleAnchorageSize(nil) }
101 tabViewItemViewControllers = [
102 DocksViewController(),
103 ShipViewController(),
104 PowerUpSupportViewController(),
105 StrengthenListViewController(),
106 RepairListViewController()
108 tabViewItemViewControllers.enumerated().forEach {
109 let _ = $0.element.view
110 let item = informations.tabViewItem(at: $0.offset)
111 item.viewController = $0.element
114 fleetViewController = FleetViewController(viewType: .detailViewType)
115 replace(deckPlaceholder, with: fleetViewController)
116 setFleetView(position: UserDefaults.standard.fleetViewPosition, animate: false)
117 fleetViewController.enableAnimation = false
118 fleetViewController.shipOrder = UserDefaults.standard.fleetViewShipOrder
119 fleetViewController.enableAnimation = true
121 bind(#keyPath(flagShipID), to: deckContoller, withKeyPath: "selection.ship_0", options: nil)
123 NotificationCenter.default
124 .addObserver(forName: .CombinedDidCange, object: nil, queue: nil) {
125 guard UserDefaults.standard.autoCombinedView,
126 let type = $0.userInfo?[CombinedCommand.userInfoKey] as? CombineType
128 if !Thread.isMainThread { Thread.sleep(forTimeInterval: 0.1) }
129 DispatchQueue.main.async {
132 self.hideCombinedView()
133 case .maneuver, .water, .transportation:
134 self.showCombinedView()
139 if UserDefaults.standard.lastHasCombinedView { showCombinedView() }
141 override func swipe(with event: NSEvent) {
142 guard UserDefaults.standard.useSwipeChangeCombinedView else { return }
143 if event.deltaX > 0 {
146 if event.deltaX < 0 {
151 func windowWillClose(_ notification: Notification) {
152 UserDefaults.standard.lastHasCombinedView = isCombinedMode
155 fileprivate func replace(_ view: NSView, with viewController: NSViewController) {
156 viewController.view.frame = view.frame
157 viewController.view.autoresizingMask = view.autoresizingMask
158 self.window?.contentView?.replaceSubview(view, with: viewController.view)
160 fileprivate func showCombinedView() {
161 if isCombinedMode { return }
162 if fleetViewPosition == .oldStyle { return }
163 isCombinedMode = true
164 if combinedViewController == nil {
165 combinedViewController = CombileViewController()
166 combinedViewController.view.isHidden = true
167 replace(combinedViewPlaceholder, with: combinedViewController)
170 var winFrame = window!.frame
171 let incWid = combinedViewController.view.frame.maxX
172 winFrame.size.width += incWid
173 winFrame.origin.x -= incWid
174 combinedViewController.view.isHidden = false
175 window?.setFrame(winFrame, display: true, animate: true)
177 fileprivate func hideCombinedView() {
178 if !isCombinedMode { return }
179 isCombinedMode = false
180 var winFrame = window!.frame
181 let decWid = combinedViewController.view.frame.maxX
182 winFrame.size.width -= decWid
183 winFrame.origin.x += decWid
184 window?.setFrame(winFrame, display: true, animate: true)
185 combinedViewController.view.isHidden = true
189 extension BroserWindowController {
190 private func showView(number: Int) {
191 informations.selectTabViewItem(at: number)
193 @IBAction func reloadContent(_ sender: AnyObject?) {
194 gameViewController.reloadContent(sender)
196 @IBAction func deleteCacheAndReload(_ sender: AnyObject?) {
197 gameViewController.deleteCacheAndReload(sender)
199 @IBAction func clearQuestList(_ sender: AnyObject?) {
200 let store = ServerDataStore.oneTimeEditor()
201 store.quests().forEach { store.delete($0) }
203 @IBAction func selectView(_ sender: AnyObject?) {
204 guard let tag = sender?.tag else { return }
205 showView(number: tag)
207 @IBAction func changeMainTab(_ sender: AnyObject?) {
208 guard let segment = sender?.selectedSegment else { return }
209 showView(number: segment)
211 @IBAction func screenShot(_ sender: AnyObject?) {
212 gameViewController.screenShot(sender)
214 @IBAction func toggleAnchorageSize(_ sender: AnyObject?) {
215 let current = ancherageRepariTimerViewController.controlSize
216 var diff = AncherageRepairTimerViewController.regularHeight - AncherageRepairTimerViewController.smallHeight
217 let newSize: NSControlSize = {
218 if current == .regular {
224 ancherageRepariTimerViewController.controlSize = newSize
226 var frame = informations.frame
227 frame.size.height -= diff
228 frame.origin.y += diff
229 informations.frame = frame
231 UserDefaults.standard.screenshotButtonSize = newSize
233 @IBAction func showHideCombinedView(_ sender: AnyObject?) {
234 isCombinedMode ? hideCombinedView() : showCombinedView()
237 @IBAction func fleetListAbove(_ sender: AnyObject?) {
238 setFleetView(position: .above, animate: true)
240 @IBAction func fleetListBelow(_ sender: AnyObject?) {
241 setFleetView(position: .below, animate: true)
243 @IBAction func fleetListDivide(_ sender: AnyObject?) {
244 setFleetView(position: .divided, animate: true)
246 @IBAction func fleetListSimple(_ sender: AnyObject?) {
247 setFleetView(position: .oldStyle, animate: true)
249 @IBAction func reorderToDoubleLine(_ sender: AnyObject?) {
250 fleetViewController.shipOrder = .doubleLine
251 UserDefaults.standard.fleetViewShipOrder = .doubleLine
253 @IBAction func reorderToLeftToRight(_ sender: AnyObject?) {
254 fleetViewController.shipOrder = .leftToRight
255 UserDefaults.standard.fleetViewShipOrder = .leftToRight
257 @IBAction func selectNextFleet(_ sender: AnyObject?) {
258 fleetViewController.selectNextFleet(sender)
260 @IBAction func selectPreviousFleet(_ sender: AnyObject?) {
261 fleetViewController.selectPreviousFleet(sender)
263 @IBAction func changeSakutekiCalculator(_ sender: Any?) {
264 fleetViewController.changeSakutekiCalculator(sender)
267 override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
268 guard let action: Selector = menuItem.action else { return false }
270 case Selector.reloadContent, Selector.screenShot, Selector.deleteCacheAndReload:
271 return gameViewController.validateMenuItem(menuItem)
272 case Selector.selectView, Selector.selectNextFleet, Selector.selectPreviousFleet:
274 case Selector.fleetListAbove:
275 menuItem.state = fleetViewPosition == .above ? NSOnState : NSOffState
277 case Selector.fleetListBelow:
278 menuItem.state = fleetViewPosition == .below ? NSOnState : NSOffState
280 case Selector.fleetListDivide:
281 menuItem.state = fleetViewPosition == .divided ? NSOnState : NSOffState
283 case Selector.fleetListSimple:
284 menuItem.state = fleetViewPosition == .oldStyle ? NSOnState : NSOffState
286 case Selector.reorderToDoubleLine:
287 menuItem.state = fleetViewController.shipOrder == .doubleLine ? NSOnState : NSOffState
289 case Selector.reorderToLeftToRight:
290 menuItem.state = fleetViewController.shipOrder == .leftToRight ? NSOnState: NSOffState
292 case Selector.clearQuestList:
294 case Selector.showHideCombinedView:
296 menuItem.title = NSLocalizedString("Hide Combined View", comment: "View menu, hide combined view")
298 menuItem.title = NSLocalizedString("Show Combined View", comment: "View menu, show combined view")
300 if fleetViewPosition == .oldStyle { return false }
302 case Selector.toggleAnchorageSize:
304 case Selector.changeSakutekiCalculator:
305 return fleetViewController.validateMenuItem(menuItem)
312 extension BroserWindowController {
313 private static let margin: CGFloat = 1.0
314 private static let flashTopMargin: CGFloat = 4.0
316 private func changeFleetViewForFleetViewPositionIfNeeded(position newPosition: FleetViewPosition) {
317 if fleetViewPosition == newPosition { return }
318 if fleetViewPosition != .oldStyle && newPosition != .oldStyle { return }
319 if newPosition == .oldStyle && isCombinedMode { hideCombinedView() }
320 let type: FleetViewType = (newPosition == .oldStyle) ? .minimumViewType : .detailViewType
321 guard let newController = FleetViewController(viewType: type) else { return }
322 newController.enableAnimation = true
323 newController.shipOrder = fleetViewController.shipOrder
324 replace(fleetViewController.view, with: newController)
325 fleetViewController = newController
327 private func windowHeightForFleetViewPosition(position newPosition: FleetViewPosition) -> CGFloat {
328 guard var contentHeight = window!.contentView?.frame.size.height else { return 0.0 }
329 if fleetViewPosition == newPosition { return contentHeight }
330 if fleetViewPosition == .oldStyle {
331 contentHeight += FleetViewController.heightDifference
333 if newPosition == .oldStyle {
334 contentHeight -= FleetViewController.heightDifference
338 private func windowFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
339 var contentRect = window!.frame
340 if fleetViewPosition == newPosition { return contentRect }
341 if fleetViewPosition == .oldStyle {
342 contentRect.size.height += FleetViewController.heightDifference
343 contentRect.origin.y -= FleetViewController.heightDifference
345 if newPosition == .oldStyle {
346 contentRect.size.height -= FleetViewController.heightDifference
347 contentRect.origin.y += FleetViewController.heightDifference
351 private func flashFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
352 let contentHeight = windowHeightForFleetViewPosition(position: newPosition)
353 var flashRect = gameViewController.view.frame
357 flashY = contentHeight - flashRect.height - fleetViewController.normalHeight
359 flashY = contentHeight - flashRect.height
361 flashY = contentHeight
363 - fleetViewController.upsideHeight
364 - BroserWindowController.margin
366 flashY = contentHeight - flashRect.height - BroserWindowController.flashTopMargin
368 flashRect.origin.y = flashY
371 private func fleetViewFrameForFleetViewPosition(position newPosition: FleetViewPosition) -> NSRect {
372 let contentHeight = windowHeightForFleetViewPosition(position: newPosition)
373 let flashRect = gameViewController.view.frame
374 var fleetListRect = fleetViewController.view.frame
376 var fleetViewHeight: CGFloat
377 var fleetViewY: CGFloat
380 fleetViewHeight = fleetViewController.normalHeight
381 fleetViewY = contentHeight - fleetViewHeight
383 fleetViewHeight = fleetViewController.normalHeight
384 fleetViewY = contentHeight
387 - BroserWindowController.margin
389 fleetViewHeight = fleetViewController.normalHeight
391 + BroserWindowController.margin
392 + BroserWindowController.margin
393 fleetViewY = contentHeight - fleetViewHeight
395 fleetViewHeight = FleetViewController.oldStyleFleetViewHeight
396 fleetViewY = contentHeight
399 - BroserWindowController.margin
400 - BroserWindowController.flashTopMargin
402 fleetListRect.size.height = fleetViewHeight
403 fleetListRect.origin.y = fleetViewY
406 fileprivate func setFleetView(position newPosition: FleetViewPosition, animate: Bool) {
407 guard let window = window else { return }
408 changeFleetViewForFleetViewPositionIfNeeded(position: newPosition)
409 let winFrame = windowFrameForFleetViewPosition(position: newPosition)
410 let flashRect = flashFrameForFleetViewPosition(position: newPosition)
411 let fleetListRect = fleetViewFrameForFleetViewPosition(position: newPosition)
413 fleetViewPosition = newPosition
414 UserDefaults.standard.fleetViewPosition = newPosition
417 let winAnime: [String: Any] = [ NSViewAnimationTargetKey: window,
418 NSViewAnimationEndFrameKey: NSValue(rect: winFrame) ]
419 let flashAnime: [String: Any] = [ NSViewAnimationTargetKey: gameViewController.view,
420 NSViewAnimationEndFrameKey: NSValue(rect: flashRect) ]
421 let fleetAnime: [String: Any] = [ NSViewAnimationTargetKey: fleetViewController.view,
422 NSViewAnimationEndFrameKey: NSValue(rect: fleetListRect) ]
423 let anime = NSViewAnimation(viewAnimations: [winAnime, flashAnime, fleetAnime])
426 window.setFrame(winFrame, display: false)
427 gameViewController.view.frame = flashRect
428 fleetViewController.view.frame = fleetListRect
434 @available(OSX 10.12.2, *)
435 fileprivate var mainTouchBars: [Int: NSTouchBar] = [:]
436 @available(OSX 10.12.2, *)
437 fileprivate var shipTypeButtons: [Int: NSPopoverTouchBarItem] = [:]
438 @available(OSX 10.12.2, *)
439 fileprivate var shipTypeSegments: [Int: NSSegmentedControl] = [:]
441 @available(OSX 10.12.2, *)
442 extension BroserWindowController {
443 @IBOutlet var mainTouchBar: NSTouchBar! {
444 get { return mainTouchBars[hashValue] }
445 set { mainTouchBars[hashValue] = newValue }
447 @IBOutlet var shipTypeButton: NSPopoverTouchBarItem! {
448 get { return shipTypeButtons[hashValue] }
449 set { shipTypeButtons[hashValue] = newValue }
451 @IBOutlet var shipTypeSegment: NSSegmentedControl! {
452 get { return shipTypeSegments[hashValue] }
453 set { shipTypeSegments[hashValue] = newValue }
456 override func makeTouchBar() -> NSTouchBar? {
457 if let mainTouchBar = mainTouchBar { return mainTouchBar }
458 var array: NSArray = []
459 Bundle.main.loadNibNamed("BroswerTouchBar", owner: self, topLevelObjects: &array)
461 shipTypeSegment.bind(NSSelectedIndexBinding,
462 to: tabViewItemViewControllers[0],
463 withKeyPath: #keyPath(MainTabVIewItemViewController.selectedShipType),
465 let o = selectedMainTabIndex
466 selectedMainTabIndex = o
468 changeMainTabHandler = { [weak self] in
469 guard let `self` = self else { return }
470 self.shipTypeButton.dismissPopover(nil)
471 self.shipTypeSegment.unbind(NSSelectedIndexBinding)
472 guard let button = self.shipTypeButton.view as? NSButton else { return }
473 let vc = self.tabViewItemViewControllers[$0]
474 button.isHidden = !vc.hasShipTypeSelector
475 self.shipTypeSegment.bind(NSSelectedIndexBinding,
477 withKeyPath: #keyPath(MainTabVIewItemViewController.selectedShipType),