2016-09-18 21:49:42 +03:00
|
|
|
/**
|
|
|
|
* Tae Won Ha - http://taewon.de - @hataewon
|
|
|
|
* See LICENSE
|
|
|
|
*/
|
|
|
|
|
|
|
|
import Cocoa
|
|
|
|
|
2016-11-17 02:16:03 +03:00
|
|
|
class WorkspaceToolButton: NSView, NSDraggingSource {
|
2016-11-14 00:16:56 +03:00
|
|
|
|
2016-09-25 18:50:33 +03:00
|
|
|
static fileprivate let titlePadding = CGSize(width: 8, height: 2)
|
2016-11-15 00:28:28 +03:00
|
|
|
static fileprivate let dummyButton = WorkspaceToolButton(title: "Dummy")
|
2016-09-18 21:49:42 +03:00
|
|
|
|
2017-06-24 23:26:41 +03:00
|
|
|
fileprivate var isHighlighted = false
|
|
|
|
|
|
|
|
fileprivate let title: NSMutableAttributedString
|
2016-09-25 18:50:33 +03:00
|
|
|
fileprivate var trackingArea = NSTrackingArea()
|
2017-06-24 23:26:41 +03:00
|
|
|
|
2016-09-25 01:44:10 +03:00
|
|
|
required init?(coder: NSCoder) {
|
|
|
|
fatalError("init(coder:) has not been implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: - API
|
2016-11-15 00:28:28 +03:00
|
|
|
static let toolUti = "com.qvacua.vimr.tool"
|
|
|
|
|
2016-11-16 23:06:23 +03:00
|
|
|
static func ==(left: WorkspaceToolButton, right: WorkspaceToolButton) -> Bool {
|
|
|
|
guard let leftTool = left.tool, let rightTool = right.tool else {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return leftTool == rightTool
|
|
|
|
}
|
|
|
|
|
2016-11-15 00:28:28 +03:00
|
|
|
var location = WorkspaceBarLocation.top
|
2016-09-18 21:49:42 +03:00
|
|
|
var isSelected: Bool {
|
|
|
|
return self.tool?.isSelected ?? false
|
|
|
|
}
|
|
|
|
|
2017-06-25 01:11:12 +03:00
|
|
|
var theme: Workspace.Theme {
|
|
|
|
return self.tool?.theme ?? Workspace.Theme.default
|
2017-06-24 18:58:21 +03:00
|
|
|
}
|
|
|
|
|
2016-09-18 21:49:42 +03:00
|
|
|
weak var tool: WorkspaceTool?
|
|
|
|
|
2016-11-15 00:28:28 +03:00
|
|
|
static func dimension() -> CGFloat {
|
|
|
|
return self.dummyButton.intrinsicContentSize.height
|
|
|
|
}
|
|
|
|
|
|
|
|
static func size(forLocation loc: WorkspaceBarLocation) -> CGSize {
|
|
|
|
switch loc {
|
|
|
|
case .top, .bottom:
|
|
|
|
return self.dummyButton.intrinsicContentSize
|
|
|
|
case .right, .left:
|
|
|
|
return CGSize(width: self.dummyButton.intrinsicContentSize.height,
|
|
|
|
height: self.dummyButton.intrinsicContentSize.width)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-25 01:44:10 +03:00
|
|
|
init(title: String) {
|
2017-06-24 23:26:41 +03:00
|
|
|
self.title = NSMutableAttributedString(string: title, attributes: [
|
|
|
|
NSFontAttributeName: NSFont.systemFont(ofSize: 11),
|
2016-09-25 01:44:10 +03:00
|
|
|
])
|
|
|
|
|
2017-06-24 23:26:41 +03:00
|
|
|
super.init(frame: .zero)
|
2016-11-15 00:28:28 +03:00
|
|
|
self.configureForAutoLayout()
|
2016-09-25 01:44:10 +03:00
|
|
|
|
2017-06-24 23:26:41 +03:00
|
|
|
self.title.addAttribute(NSForegroundColorAttributeName,
|
|
|
|
value: self.theme.foreground,
|
|
|
|
range: NSRange(location: 0, length: self.title.length))
|
|
|
|
|
2016-09-25 01:44:10 +03:00
|
|
|
self.wantsLayer = true
|
|
|
|
}
|
|
|
|
|
2017-06-24 23:26:41 +03:00
|
|
|
func repaint() {
|
|
|
|
if self.isHighlighted {
|
|
|
|
self.highlight()
|
|
|
|
} else {
|
|
|
|
self.dehighlight()
|
|
|
|
}
|
|
|
|
|
2017-06-29 09:12:34 +03:00
|
|
|
self.title.addAttribute(NSForegroundColorAttributeName,
|
|
|
|
value: self.theme.foreground,
|
|
|
|
range: NSRange(location: 0, length: self.title.length))
|
2017-06-24 23:26:41 +03:00
|
|
|
|
|
|
|
self.needsDisplay = true
|
|
|
|
}
|
|
|
|
|
2016-09-25 01:44:10 +03:00
|
|
|
func highlight() {
|
2017-06-24 23:26:41 +03:00
|
|
|
self.isHighlighted = true
|
2017-06-24 18:58:21 +03:00
|
|
|
self.layer?.backgroundColor = self.theme.barButtonHighlight.cgColor
|
2016-09-18 21:49:42 +03:00
|
|
|
}
|
|
|
|
|
2016-09-25 01:44:10 +03:00
|
|
|
func dehighlight() {
|
2017-06-24 23:26:41 +03:00
|
|
|
self.isHighlighted = false
|
2017-06-24 18:58:21 +03:00
|
|
|
self.layer?.backgroundColor = self.theme.barButtonBackground.cgColor
|
2016-09-25 01:44:10 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: - NSView
|
|
|
|
extension WorkspaceToolButton {
|
|
|
|
|
2016-09-18 21:49:42 +03:00
|
|
|
override var intrinsicContentSize: NSSize {
|
|
|
|
let titleSize = self.title.size()
|
2017-06-24 23:26:41 +03:00
|
|
|
|
2016-09-18 21:49:42 +03:00
|
|
|
let padding = WorkspaceToolButton.titlePadding
|
|
|
|
switch self.location {
|
|
|
|
case .top, .bottom:
|
|
|
|
return CGSize(width: titleSize.width + 2 * padding.width, height: titleSize.height + 2 * padding.height)
|
|
|
|
case .right, .left:
|
|
|
|
return CGSize(width: titleSize.height + 2 * padding.height, height: titleSize.width + 2 * padding.width)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-25 18:50:33 +03:00
|
|
|
override func draw(_ dirtyRect: NSRect) {
|
|
|
|
super.draw(dirtyRect)
|
2017-06-24 23:26:41 +03:00
|
|
|
|
2016-09-18 21:49:42 +03:00
|
|
|
let padding = WorkspaceToolButton.titlePadding
|
|
|
|
switch self.location {
|
|
|
|
case .top, .bottom:
|
2016-09-25 18:50:33 +03:00
|
|
|
self.title.draw(at: CGPoint(x: padding.width, y: padding.height))
|
2016-09-18 21:49:42 +03:00
|
|
|
case .right:
|
2017-04-17 12:04:50 +03:00
|
|
|
self.title.draw(at: CGPoint(x: padding.height, y: self.bounds.height - padding.width), angle: -(.pi / 2))
|
2016-09-18 21:49:42 +03:00
|
|
|
case .left:
|
2017-04-17 12:04:50 +03:00
|
|
|
self.title.draw(at: CGPoint(x: self.bounds.width - padding.height, y: padding.width), angle: .pi / 2)
|
2016-09-18 21:49:42 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override func updateTrackingAreas() {
|
|
|
|
self.removeTrackingArea(self.trackingArea)
|
|
|
|
|
|
|
|
self.trackingArea = NSTrackingArea(rect: self.bounds,
|
2016-09-25 18:50:33 +03:00
|
|
|
options: [.mouseEnteredAndExited, .activeInActiveApp],
|
2016-09-18 21:49:42 +03:00
|
|
|
owner: self,
|
|
|
|
userInfo: nil)
|
|
|
|
self.addTrackingArea(self.trackingArea)
|
|
|
|
|
|
|
|
super.updateTrackingAreas()
|
|
|
|
}
|
|
|
|
|
2016-09-25 18:50:33 +03:00
|
|
|
override func mouseDown(with event: NSEvent) {
|
2016-11-14 00:16:56 +03:00
|
|
|
guard let nextEvent = self.window!.nextEvent(matching: [NSLeftMouseUpMask, NSLeftMouseDraggedMask]) else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
switch nextEvent.type {
|
|
|
|
|
|
|
|
case NSLeftMouseUp:
|
|
|
|
self.tool?.toggle()
|
|
|
|
return
|
|
|
|
|
|
|
|
case NSLeftMouseDragged:
|
|
|
|
let pasteboardItem = NSPasteboardItem()
|
2016-11-17 02:16:03 +03:00
|
|
|
pasteboardItem.setString(self.tool!.uuid, forType: WorkspaceToolButton.toolUti)
|
2016-11-14 00:16:56 +03:00
|
|
|
|
|
|
|
let draggingItem = NSDraggingItem(pasteboardWriter: pasteboardItem)
|
2017-06-24 23:26:41 +03:00
|
|
|
draggingItem.setDraggingFrame(self.bounds, contents: self.snapshot())
|
2016-11-14 00:16:56 +03:00
|
|
|
|
2016-11-17 02:16:03 +03:00
|
|
|
self.beginDraggingSession(with: [draggingItem], event: event, source: self)
|
2016-11-14 00:16:56 +03:00
|
|
|
return
|
|
|
|
|
|
|
|
default:
|
|
|
|
return
|
2017-04-17 12:04:50 +03:00
|
|
|
|
2016-11-14 00:16:56 +03:00
|
|
|
}
|
2016-09-18 21:49:42 +03:00
|
|
|
}
|
|
|
|
|
2016-09-25 18:50:33 +03:00
|
|
|
override func mouseEntered(with _: NSEvent) {
|
2016-09-18 21:49:42 +03:00
|
|
|
if self.isSelected {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
self.highlight()
|
|
|
|
}
|
|
|
|
|
2016-09-25 18:50:33 +03:00
|
|
|
override func mouseExited(with _: NSEvent) {
|
2016-09-18 21:49:42 +03:00
|
|
|
if self.isSelected {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
self.dehighlight()
|
|
|
|
}
|
2016-11-14 00:16:56 +03:00
|
|
|
|
2016-11-18 00:51:47 +03:00
|
|
|
// Modified version of snapshot() from https://www.raywenderlich.com/136272/drag-and-drop-tutorial-for-macos
|
2016-11-14 00:16:56 +03:00
|
|
|
fileprivate func snapshot() -> NSImage {
|
|
|
|
let pdfData = self.dataWithPDF(inside: self.bounds)
|
2016-11-18 00:51:47 +03:00
|
|
|
guard let image = NSImage(data: pdfData) else {
|
|
|
|
return NSImage()
|
|
|
|
}
|
|
|
|
|
|
|
|
let result = NSImage()
|
|
|
|
let rect = CGRect(origin: .zero, size: image.size)
|
|
|
|
result.size = rect.size
|
|
|
|
|
|
|
|
result.lockFocus()
|
2017-06-24 18:58:21 +03:00
|
|
|
self.theme.barButtonHighlight.set()
|
2016-11-18 00:51:47 +03:00
|
|
|
NSRectFill(rect)
|
|
|
|
image.draw(in: rect)
|
|
|
|
result.unlockFocus()
|
|
|
|
|
|
|
|
return result
|
2016-11-14 00:16:56 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-17 02:16:03 +03:00
|
|
|
// MARK: - NSDraggingSource
|
2016-11-14 00:16:56 +03:00
|
|
|
extension WorkspaceToolButton {
|
2017-04-17 12:04:50 +03:00
|
|
|
|
2017-06-24 23:26:41 +03:00
|
|
|
@objc(draggingSession: sourceOperationMaskForDraggingContext:)
|
2016-11-17 02:16:03 +03:00
|
|
|
func draggingSession(_ session: NSDraggingSession, sourceOperationMaskFor ctc: NSDraggingContext) -> NSDragOperation {
|
|
|
|
return .move
|
|
|
|
}
|
2016-11-14 00:16:56 +03:00
|
|
|
|
2017-06-24 23:26:41 +03:00
|
|
|
@objc(draggingSession: endedAtPoint:operation:)
|
2016-11-17 02:16:03 +03:00
|
|
|
func draggingSession(_ session: NSDraggingSession, endedAt screenPoint: NSPoint, operation: NSDragOperation) {
|
|
|
|
guard let pointInWindow = self.window?.convertFromScreen(CGRect(origin: screenPoint, size: .zero)) else {
|
2016-11-14 00:35:05 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-11-17 02:16:03 +03:00
|
|
|
let pointInView = self.convert(pointInWindow, from: nil)
|
|
|
|
// Sometimes if the drag ends, the button does not get dehighlighted.
|
2016-11-18 00:43:32 +03:00
|
|
|
if !self.frame.contains(pointInView) && !(self.tool?.isSelected ?? false) {
|
2016-11-17 02:16:03 +03:00
|
|
|
self.dehighlight()
|
2016-11-14 00:35:05 +03:00
|
|
|
}
|
2016-11-14 00:16:56 +03:00
|
|
|
}
|
2016-09-18 21:49:42 +03:00
|
|
|
}
|