2016-11-27 16:50:08 +03:00
|
|
|
/**
|
|
|
|
* Tae Won Ha - http://taewon.de - @hataewon
|
|
|
|
* See LICENSE
|
|
|
|
*/
|
|
|
|
|
|
|
|
import Cocoa
|
|
|
|
import PureLayout
|
2016-11-27 23:37:30 +03:00
|
|
|
import CocoaFontAwesome
|
2016-11-27 16:50:08 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
This class is the base class for inner toolbars for workspace tools. It's got two default buttons:
|
|
|
|
- Close button
|
|
|
|
- Cog button: not shown when there's no menu
|
|
|
|
*/
|
2016-12-03 01:42:02 +03:00
|
|
|
class InnerToolBar: NSView, NSUserInterfaceValidations {
|
2016-11-27 16:50:08 +03:00
|
|
|
|
2016-12-04 23:19:20 +03:00
|
|
|
fileprivate static let separatorColor = NSColor.controlShadowColor
|
|
|
|
fileprivate static let separatorThickness = CGFloat(1)
|
|
|
|
fileprivate static let height = InnerToolBar.iconDimension + 2 + 2 + InnerToolBar.separatorThickness
|
2016-11-27 23:50:19 +03:00
|
|
|
|
|
|
|
static fileprivate let backgroundColor = NSColor(red: 0.899, green: 0.934, blue: 0.997, alpha: 1)
|
2016-11-27 18:41:52 +03:00
|
|
|
|
2016-11-27 16:50:08 +03:00
|
|
|
required init?(coder: NSCoder) {
|
|
|
|
fatalError("init(coder:) has not been implemented")
|
|
|
|
}
|
|
|
|
|
2016-12-30 06:32:08 +03:00
|
|
|
fileprivate let titleField = NSTextField(forAutoLayout: ())
|
|
|
|
fileprivate let closeButton = NSButton(forAutoLayout: ())
|
|
|
|
fileprivate let cogButton = NSPopUpButton(forAutoLayout: ())
|
2016-11-27 23:50:19 +03:00
|
|
|
|
2016-12-03 01:42:02 +03:00
|
|
|
fileprivate let locToSelector: [WorkspaceBarLocation: Selector] = [
|
|
|
|
.top: #selector(InnerToolBar.moveToTopAction(_:)),
|
|
|
|
.right: #selector(InnerToolBar.moveToRightAction(_:)),
|
|
|
|
.bottom: #selector(InnerToolBar.moveToBottomAction(_:)),
|
|
|
|
.left: #selector(InnerToolBar.moveToLeftAction(_:)),
|
|
|
|
]
|
|
|
|
|
2016-11-27 23:50:19 +03:00
|
|
|
// MARK: - API
|
2016-12-04 23:19:20 +03:00
|
|
|
static let toolbarHeight = InnerToolBar.iconDimension
|
|
|
|
static let iconDimension = CGFloat(19)
|
|
|
|
static let iconColor = NSColor.darkGray
|
|
|
|
|
2016-12-30 06:32:08 +03:00
|
|
|
static func configureToStandardIconButton(button: NSButton, iconName: CocoaFontAwesome.FontAwesome) {
|
|
|
|
let icon = NSImage.fontAwesomeIcon(name: iconName,
|
|
|
|
textColor: InnerToolBar.iconColor,
|
|
|
|
dimension: InnerToolBar.iconDimension)
|
|
|
|
|
2016-12-04 23:19:20 +03:00
|
|
|
button.imagePosition = .imageOnly
|
2016-12-30 06:32:08 +03:00
|
|
|
button.image = icon
|
2016-12-04 23:19:20 +03:00
|
|
|
button.isBordered = false
|
|
|
|
|
|
|
|
// The following disables the square appearing when pushed.
|
|
|
|
let cell = button.cell as? NSButtonCell
|
|
|
|
cell?.highlightsBy = .contentsCellMask
|
|
|
|
}
|
2016-11-27 23:50:19 +03:00
|
|
|
|
2016-12-30 06:32:08 +03:00
|
|
|
var customMenuItems: [NSMenuItem]? {
|
|
|
|
didSet {
|
|
|
|
self.removeCustomUiElements()
|
|
|
|
self.addViews()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var customToolbar: NSView? {
|
|
|
|
didSet {
|
|
|
|
self.removeCustomUiElements()
|
|
|
|
self.addViews()
|
|
|
|
}
|
|
|
|
}
|
2016-12-04 19:04:55 +03:00
|
|
|
|
2016-12-23 18:12:22 +03:00
|
|
|
weak var tool: WorkspaceTool? {
|
2016-12-04 22:36:04 +03:00
|
|
|
didSet {
|
|
|
|
self.titleField.stringValue = self.tool?.title ?? ""
|
2016-12-07 23:04:47 +03:00
|
|
|
|
|
|
|
let toolTitle = self.tool?.title ?? "Tool"
|
|
|
|
self.closeButton.toolTip = "Close \(toolTitle)"
|
|
|
|
self.cogButton.toolTip = "\(toolTitle) Settings"
|
2016-12-04 22:36:04 +03:00
|
|
|
}
|
|
|
|
}
|
2016-11-27 18:41:52 +03:00
|
|
|
|
2016-11-27 16:50:08 +03:00
|
|
|
override var intrinsicContentSize: CGSize {
|
2016-11-27 18:41:52 +03:00
|
|
|
if #available(macOS 10.11, *) {
|
2016-11-27 23:50:19 +03:00
|
|
|
return CGSize(width: NSViewNoIntrinsicMetric, height: InnerToolBar.height)
|
2016-11-27 18:41:52 +03:00
|
|
|
} else {
|
2016-11-27 23:50:19 +03:00
|
|
|
return CGSize(width: -1, height: InnerToolBar.height)
|
2016-11-27 18:41:52 +03:00
|
|
|
}
|
2016-11-27 16:50:08 +03:00
|
|
|
}
|
|
|
|
|
2016-12-04 19:04:55 +03:00
|
|
|
init(customToolbar: NSView? = nil, customMenuItems: [NSMenuItem] = []) {
|
|
|
|
self.customMenuItems = customMenuItems
|
|
|
|
self.customToolbar = customToolbar
|
2016-11-27 16:50:08 +03:00
|
|
|
|
2016-12-04 19:04:55 +03:00
|
|
|
super.init(frame: .zero)
|
|
|
|
self.configureForAutoLayout()
|
2016-11-27 23:50:19 +03:00
|
|
|
|
2016-11-27 16:50:08 +03:00
|
|
|
// Because other views also want layer, this view also must want layer. Otherwise the z-index ordering is not set
|
|
|
|
// correctly: views w/ wantsLayer = false are behind views w/ wantsLayer = true even when added later.
|
|
|
|
self.wantsLayer = true
|
2016-11-27 23:50:19 +03:00
|
|
|
self.layer?.backgroundColor = InnerToolBar.backgroundColor.cgColor
|
2016-11-27 18:41:52 +03:00
|
|
|
|
|
|
|
self.addViews()
|
|
|
|
}
|
|
|
|
|
|
|
|
override func draw(_ dirtyRect: NSRect) {
|
|
|
|
InnerToolBar.separatorColor.set()
|
2016-11-27 23:50:19 +03:00
|
|
|
let bottomSeparatorRect = self.bottomSeparatorRect()
|
|
|
|
if dirtyRect.intersects(bottomSeparatorRect) {
|
|
|
|
NSRectFill(bottomSeparatorRect)
|
|
|
|
}
|
2016-11-27 18:41:52 +03:00
|
|
|
|
2016-11-27 23:50:19 +03:00
|
|
|
let innerSeparatorRect = self.innerSeparatorRect()
|
|
|
|
if dirtyRect.intersects(innerSeparatorRect) {
|
|
|
|
NSRectFill(innerSeparatorRect)
|
2016-11-27 18:41:52 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-30 06:32:08 +03:00
|
|
|
fileprivate func removeCustomUiElements() {
|
|
|
|
[self.titleField, self.customToolbar, self.closeButton, self.cogButton].forEach { $0?.removeFromSuperview() }
|
|
|
|
self.cogButton.menu = nil
|
|
|
|
}
|
|
|
|
|
2016-11-27 18:41:52 +03:00
|
|
|
fileprivate func addViews() {
|
2016-12-04 22:36:04 +03:00
|
|
|
let title = self.titleField
|
2016-11-27 18:41:52 +03:00
|
|
|
let close = self.closeButton
|
|
|
|
let cog = self.cogButton
|
|
|
|
|
2016-12-04 22:36:04 +03:00
|
|
|
title.isBezeled = false
|
|
|
|
title.drawsBackground = false
|
|
|
|
title.isEditable = false
|
|
|
|
title.isSelectable = false
|
|
|
|
title.controlSize = .small
|
|
|
|
|
2016-12-30 06:32:08 +03:00
|
|
|
InnerToolBar.configureToStandardIconButton(button: close, iconName: .timesCircle)
|
2016-12-04 18:36:57 +03:00
|
|
|
close.target = self
|
|
|
|
close.action = #selector(InnerToolBar.closeAction)
|
2016-11-27 23:50:19 +03:00
|
|
|
|
2016-12-04 19:04:55 +03:00
|
|
|
let cogIcon = NSImage.fontAwesomeIcon(name: .cog,
|
2016-12-04 23:19:20 +03:00
|
|
|
textColor: InnerToolBar.iconColor,
|
2016-12-04 19:04:55 +03:00
|
|
|
dimension: InnerToolBar.iconDimension)
|
|
|
|
cog.configureForAutoLayout()
|
2016-11-27 23:50:19 +03:00
|
|
|
cog.imagePosition = .imageOnly
|
|
|
|
cog.pullsDown = true
|
|
|
|
cog.isBordered = false
|
2016-11-27 18:41:52 +03:00
|
|
|
|
2016-11-27 23:50:19 +03:00
|
|
|
let cogCell = cog.cell as? NSPopUpButtonCell
|
|
|
|
cogCell?.arrowPosition = .noArrow
|
|
|
|
|
|
|
|
let cogMenu = NSMenu()
|
|
|
|
|
|
|
|
let cogMenuItem = NSMenuItem(title: "Cog", action: nil, keyEquivalent: "")
|
|
|
|
cogMenuItem.image = cogIcon
|
|
|
|
|
|
|
|
let moveToMenu = NSMenu()
|
2016-12-03 01:42:02 +03:00
|
|
|
let topMenuItem = NSMenuItem(title: "Top",
|
2016-12-04 18:36:57 +03:00
|
|
|
action: #selector(InnerToolBar.moveToTopAction),
|
2016-12-03 01:42:02 +03:00
|
|
|
keyEquivalent: "")
|
|
|
|
topMenuItem.target = self
|
|
|
|
let rightMenuItem = NSMenuItem(title: "Right",
|
2016-12-04 18:36:57 +03:00
|
|
|
action: #selector(InnerToolBar.moveToRightAction),
|
2016-12-03 01:42:02 +03:00
|
|
|
keyEquivalent: "")
|
|
|
|
rightMenuItem.target = self
|
|
|
|
let bottomMenuItem = NSMenuItem(title: "Bottom",
|
2016-12-04 18:36:57 +03:00
|
|
|
action: #selector(InnerToolBar.moveToBottomAction),
|
2016-12-03 01:42:02 +03:00
|
|
|
keyEquivalent: "")
|
|
|
|
bottomMenuItem.target = self
|
|
|
|
let leftMenuItem = NSMenuItem(title: "Left",
|
2016-12-04 18:36:57 +03:00
|
|
|
action: #selector(InnerToolBar.moveToLeftAction),
|
2016-12-03 01:42:02 +03:00
|
|
|
keyEquivalent: "")
|
|
|
|
leftMenuItem.target = self
|
2016-12-04 19:04:55 +03:00
|
|
|
|
2016-11-27 23:50:19 +03:00
|
|
|
moveToMenu.addItem(leftMenuItem)
|
|
|
|
moveToMenu.addItem(rightMenuItem)
|
|
|
|
moveToMenu.addItem(bottomMenuItem)
|
|
|
|
moveToMenu.addItem(topMenuItem)
|
|
|
|
|
|
|
|
let moveToMenuItem = NSMenuItem(
|
|
|
|
title: "Move To",
|
|
|
|
action: nil,
|
|
|
|
keyEquivalent: ""
|
|
|
|
)
|
|
|
|
moveToMenuItem.submenu = moveToMenu
|
|
|
|
|
|
|
|
cogMenu.addItem(cogMenuItem)
|
2016-12-04 19:04:55 +03:00
|
|
|
|
2016-12-30 06:32:08 +03:00
|
|
|
if self.customMenuItems?.isEmpty == false {
|
|
|
|
self.customMenuItems?.forEach(cogMenu.addItem)
|
2016-12-04 19:04:55 +03:00
|
|
|
cogMenu.addItem(NSMenuItem.separator())
|
|
|
|
}
|
|
|
|
|
2016-11-27 23:50:19 +03:00
|
|
|
cogMenu.addItem(moveToMenuItem)
|
|
|
|
|
|
|
|
cog.menu = cogMenu
|
2016-11-27 18:41:52 +03:00
|
|
|
|
2016-12-04 19:04:55 +03:00
|
|
|
if let customToolbar = self.customToolbar {
|
2016-12-04 23:19:20 +03:00
|
|
|
customToolbar.configureForAutoLayout()
|
2016-12-04 19:04:55 +03:00
|
|
|
self.addSubview(customToolbar)
|
|
|
|
}
|
2016-12-04 22:36:04 +03:00
|
|
|
self.addSubview(title)
|
2016-11-27 18:41:52 +03:00
|
|
|
self.addSubview(close)
|
|
|
|
self.addSubview(cog)
|
|
|
|
|
2016-12-04 22:36:04 +03:00
|
|
|
title.autoAlignAxis(toSuperviewAxis: .horizontal)
|
|
|
|
title.autoPinEdge(toSuperviewEdge: .left, withInset: 4)
|
|
|
|
|
2016-11-27 18:41:52 +03:00
|
|
|
close.autoPinEdge(toSuperviewEdge: .top, withInset: 2)
|
|
|
|
close.autoPinEdge(toSuperviewEdge: .right, withInset: 2)
|
|
|
|
|
2016-11-27 23:50:19 +03:00
|
|
|
cog.autoPinEdge(.right, to: .left, of: close, withOffset: 5)
|
|
|
|
cog.autoPinEdge(toSuperviewEdge: .top, withInset: -1)
|
2016-12-04 19:04:55 +03:00
|
|
|
|
|
|
|
if let customToolbar = self.customToolbar {
|
|
|
|
customToolbar.autoPinEdge(toSuperviewEdge: .top, withInset: 2)
|
|
|
|
customToolbar.autoPinEdge(.right, to: .left, of: cog, withOffset: 5 - InnerToolBar.separatorThickness)
|
|
|
|
customToolbar.autoPinEdge(toSuperviewEdge: .bottom, withInset: 2 + InnerToolBar.separatorThickness)
|
2016-12-04 22:36:04 +03:00
|
|
|
customToolbar.autoPinEdge(.left, to: .right, of: title, withOffset: 2)
|
2016-12-04 19:04:55 +03:00
|
|
|
}
|
2016-11-27 18:41:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
fileprivate func bottomSeparatorRect() -> CGRect {
|
|
|
|
let bounds = self.bounds
|
|
|
|
return CGRect(x: bounds.minX, y: bounds.minY, width: bounds.width, height: InnerToolBar.separatorThickness)
|
2016-11-27 16:50:08 +03:00
|
|
|
}
|
2016-11-27 23:50:19 +03:00
|
|
|
|
|
|
|
fileprivate func innerSeparatorRect() -> CGRect {
|
|
|
|
let cogBounds = self.cogButton.frame
|
|
|
|
let bounds = self.bounds
|
|
|
|
return CGRect(x: cogBounds.minX + 6, y: bounds.minY + 4, width: 1, height: bounds.height - 4 - 4)
|
|
|
|
}
|
2016-11-27 16:50:08 +03:00
|
|
|
}
|
|
|
|
|
2016-12-03 01:42:02 +03:00
|
|
|
// MARK: - Actions
|
|
|
|
extension InnerToolBar {
|
|
|
|
|
2016-12-04 18:36:57 +03:00
|
|
|
func closeAction(_ sender: Any?) {
|
|
|
|
self.tool?.toggle()
|
|
|
|
}
|
2016-12-03 01:42:02 +03:00
|
|
|
|
2016-12-04 18:36:57 +03:00
|
|
|
func moveToTopAction(_ sender: Any?) {
|
|
|
|
self.move(to: .top)
|
2016-12-03 01:42:02 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func moveToRightAction(_ sender: Any?) {
|
2016-12-04 18:36:57 +03:00
|
|
|
self.move(to: .right)
|
2016-12-03 01:42:02 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func moveToBottomAction(_ sender: Any?) {
|
2016-12-04 18:36:57 +03:00
|
|
|
self.move(to: .bottom)
|
2016-12-03 01:42:02 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func moveToLeftAction(_ sender: Any?) {
|
2016-12-04 18:36:57 +03:00
|
|
|
self.move(to: .left)
|
|
|
|
}
|
|
|
|
|
|
|
|
fileprivate func move(to location: WorkspaceBarLocation) {
|
|
|
|
guard let tool = self.tool else {
|
|
|
|
return
|
|
|
|
}
|
2016-12-03 01:42:02 +03:00
|
|
|
|
2016-12-04 18:36:57 +03:00
|
|
|
tool.workspace?.move(tool: tool, to: location)
|
2016-12-03 01:42:02 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: - NSUserInterfaceValidations
|
|
|
|
extension InnerToolBar {
|
|
|
|
|
|
|
|
func validateUserInterfaceItem(_ item: NSValidatedUserInterfaceItem) -> Bool {
|
|
|
|
guard let loc = self.tool?.location else {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
if item.action == self.locToSelector[loc] {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|