|
// |
|
// PreferencesTabViewController.swift |
|
// Light Table |
|
// |
|
// Copyright 2022 Florian Pircher |
|
// |
|
// Licensed under the Apache License, Version 2.0 (the "License"); |
|
// you may not use this file except in compliance with the License. |
|
// You may obtain a copy of the License at |
|
// |
|
// http://www.apache.org/licenses/LICENSE-2.0 |
|
// |
|
// Unless required by applicable law or agreed to in writing, software |
|
// distributed under the License is distributed on an "AS IS" BASIS, |
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
// See the License for the specific language governing permissions and |
|
// limitations under the License. |
|
// |
|
|
|
import Cocoa |
|
|
|
final class PreferencesTabViewController: NSTabViewController { |
|
private struct ToolbarItemSpecs { |
|
let identifier: NSToolbarItem.Identifier |
|
let index: Int |
|
let label: String |
|
let title: String |
|
let image: NSImage? |
|
} |
|
|
|
private var toolbarItemsSpecs: [ToolbarItemSpecs] = [] |
|
private var bottomConstraint: NSLayoutConstraint! |
|
|
|
override func awakeFromNib() { |
|
super.awakeFromNib() |
|
|
|
guard let window = tabView.window else { |
|
return |
|
} |
|
window.toolbarStyle = .preference |
|
|
|
toolbarItemsSpecs = tabView.tabViewItems.enumerated().map { index, tabItem in |
|
ToolbarItemSpecs( |
|
identifier: NSToolbarItem.Identifier("preference:\(index)"), |
|
index: index, |
|
label: tabItem.label, |
|
title: tabItem.viewController?.title ?? tabItem.label, |
|
image: tabItem.image) |
|
} |
|
|
|
let toolbar = NSToolbar() |
|
toolbar.delegate = self |
|
window.toolbar = toolbar |
|
|
|
if let firstSpec = toolbarItemsSpecs.first { |
|
tabView.selectTabViewItem(at: firstSpec.index) |
|
window.title = firstSpec.title |
|
toolbar.selectedItemIdentifier = firstSpec.identifier |
|
} |
|
|
|
if let superview = tabView.superview { |
|
tabView.removeFromSuperview() |
|
superview.addSubview(tabView) |
|
NSLayoutConstraint.activate([ |
|
superview.topAnchor.constraint(equalTo: tabView.topAnchor), |
|
superview.leadingAnchor.constraint(equalTo: tabView.leadingAnchor), |
|
superview.trailingAnchor.constraint(equalTo: tabView.trailingAnchor), |
|
]) |
|
bottomConstraint = superview.bottomAnchor.constraint(equalTo: tabView.bottomAnchor) |
|
bottomConstraint.isActive = true |
|
} |
|
} |
|
|
|
override func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { |
|
toolbarItemsSpecs.map { $0.identifier } |
|
} |
|
|
|
override func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { |
|
toolbarAllowedItemIdentifiers(toolbar) |
|
} |
|
|
|
override func toolbarSelectableItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { |
|
toolbarAllowedItemIdentifiers(toolbar) |
|
} |
|
|
|
override func toolbarImmovableItemIdentifiers(_ toolbar: NSToolbar) -> Set<NSToolbarItem.Identifier> { |
|
Set(toolbarAllowedItemIdentifiers(toolbar)) |
|
} |
|
|
|
override func toolbar(_ toolbar: NSToolbar, itemIdentifier: NSToolbarItem.Identifier, canBeInsertedAt index: Int) -> Bool { |
|
true |
|
} |
|
|
|
override func toolbarWillAddItem(_ notification: Notification) {} |
|
|
|
override func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? { |
|
guard let spec = toolbarItemsSpecs.first(where: { $0.identifier == itemIdentifier }) else { |
|
return nil |
|
} |
|
let item = NSToolbarItem(itemIdentifier: spec.identifier) |
|
item.label = spec.label |
|
item.image = spec.image |
|
item.target = self |
|
item.action = #selector(Self.selectToolbarItem(_:)) |
|
return item |
|
} |
|
|
|
@objc func selectToolbarItem(_ sender: NSToolbarItem) { |
|
guard let spec = toolbarItemsSpecs.first(where: { $0.identifier == sender.itemIdentifier }), |
|
let window = tabView.window, |
|
tabView.tabViewItems.indices.contains(spec.index) |
|
else { |
|
return |
|
} |
|
let newTabViewItem = tabView.tabViewItems[spec.index] |
|
guard let newContentView = newTabViewItem.view else { |
|
return |
|
} |
|
|
|
let contentFrame = tabView.frame |
|
let newContentSize = newContentView.fittingSize |
|
let windowFrame = window.frame |
|
let windowHeightOffset = windowFrame.height - contentFrame.height |
|
let newWindowSize = CGSize( |
|
width: max(windowFrame.width, newContentSize.width), |
|
height: newContentSize.height + windowHeightOffset) |
|
let windowOrigin = windowFrame.origin |
|
let newWindowOrigin = CGPoint( |
|
x: windowOrigin.x + ((newWindowSize.width - windowFrame.width) / -2), |
|
y: windowOrigin.y + (windowFrame.height - newWindowSize.height)) |
|
let newWindowFrame = CGRect(origin: newWindowOrigin, size: newWindowSize) |
|
|
|
tabView.isHidden = true |
|
bottomConstraint.isActive = false |
|
|
|
window.setFrame(newWindowFrame, display: true, animate: true) |
|
tabView.selectTabViewItem(at: spec.index) |
|
|
|
bottomConstraint.isActive = true |
|
tabView.isHidden = false |
|
|
|
window.title = spec.title |
|
} |
|
} |