2016-08-14 22:46:03 +03:00
|
|
|
/**
|
|
|
|
* Tae Won Ha - http://taewon.de - @hataewon
|
|
|
|
* See LICENSE
|
|
|
|
*/
|
|
|
|
|
|
|
|
import Cocoa
|
2016-12-18 19:34:08 +03:00
|
|
|
import CocoaMarkdown
|
2016-08-14 22:46:03 +03:00
|
|
|
|
2016-12-04 23:33:45 +03:00
|
|
|
extension NSImage {
|
|
|
|
|
|
|
|
func tinting(with color: NSColor) -> NSImage {
|
|
|
|
|
|
|
|
let result: NSImage = self.copy() as! NSImage
|
|
|
|
|
|
|
|
result.lockFocus()
|
|
|
|
color.set()
|
|
|
|
NSRectFillUsingOperation(CGRect(origin: .zero, size: self.size), .sourceAtop)
|
|
|
|
result.unlockFocus()
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-14 22:46:03 +03:00
|
|
|
extension NSButton {
|
|
|
|
|
|
|
|
var boolState: Bool {
|
|
|
|
get {
|
|
|
|
return self.state == NSOnState ? true : false
|
|
|
|
}
|
|
|
|
|
|
|
|
set {
|
|
|
|
self.state = newValue ? NSOnState : NSOffState
|
2017-01-03 19:44:25 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
extension NSMenuItem {
|
|
|
|
|
|
|
|
var boolState: Bool {
|
|
|
|
get {
|
|
|
|
return self.state == NSOnState ? true : false
|
|
|
|
}
|
|
|
|
|
|
|
|
set {
|
|
|
|
self.state = newValue ? NSOnState : NSOffState
|
2016-08-14 22:46:03 +03:00
|
|
|
}
|
|
|
|
}
|
2016-09-03 00:55:19 +03:00
|
|
|
}
|
|
|
|
|
2016-09-10 22:46:43 +03:00
|
|
|
extension NSAttributedString {
|
|
|
|
|
2016-09-18 21:49:42 +03:00
|
|
|
func draw(at point: CGPoint, angle: CGFloat) {
|
2016-09-25 18:50:33 +03:00
|
|
|
var translation = AffineTransform.identity
|
|
|
|
var rotation = AffineTransform.identity
|
2016-09-18 21:49:42 +03:00
|
|
|
|
2016-09-25 18:50:33 +03:00
|
|
|
translation.translate(x: point.x, y: point.y)
|
|
|
|
rotation.rotate(byRadians: angle)
|
2016-09-18 21:49:42 +03:00
|
|
|
|
2016-09-25 18:50:33 +03:00
|
|
|
(translation as NSAffineTransform).concat()
|
|
|
|
(rotation as NSAffineTransform).concat()
|
2016-09-18 21:49:42 +03:00
|
|
|
|
2016-09-25 18:50:33 +03:00
|
|
|
self.draw(at: CGPoint.zero)
|
2016-09-18 21:49:42 +03:00
|
|
|
|
|
|
|
rotation.invert()
|
|
|
|
translation.invert()
|
|
|
|
|
2016-09-25 18:50:33 +03:00
|
|
|
(rotation as NSAffineTransform).concat()
|
|
|
|
(translation as NSAffineTransform).concat()
|
2016-09-18 21:49:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// From https://developer.apple.com/library/mac/qa/qa1487/_index.html
|
2016-09-25 18:50:33 +03:00
|
|
|
static func link(withUrl url: URL, text: String, font: NSFont? = nil) -> NSAttributedString {
|
2016-09-10 22:46:43 +03:00
|
|
|
let attrString = NSMutableAttributedString(string: text)
|
|
|
|
let range = NSRange(location: 0, length: attrString.length)
|
|
|
|
|
|
|
|
attrString.beginEditing()
|
|
|
|
if font != nil {
|
|
|
|
attrString.addAttribute(NSFontAttributeName, value: font!, range: range)
|
|
|
|
}
|
|
|
|
attrString.addAttribute(NSLinkAttributeName, value: url.absoluteString, range: range)
|
2016-09-25 18:50:33 +03:00
|
|
|
attrString.addAttribute(NSForegroundColorAttributeName, value: NSColor.blue, range: range)
|
2016-09-10 22:46:43 +03:00
|
|
|
attrString.addAttribute(NSUnderlineStyleAttributeName,
|
2016-09-25 18:50:33 +03:00
|
|
|
value: NSNumber(value: NSUnderlineStyle.styleSingle.rawValue as Int), range: range)
|
2016-09-10 22:46:43 +03:00
|
|
|
attrString.endEditing()
|
|
|
|
|
|
|
|
return attrString
|
|
|
|
}
|
|
|
|
|
|
|
|
var wholeRange: NSRange {
|
|
|
|
return NSRange(location: 0, length: self.length)
|
|
|
|
}
|
2016-12-18 19:34:08 +03:00
|
|
|
|
|
|
|
static func infoLabel(markdown: String) -> NSAttributedString {
|
|
|
|
let size = NSFont.smallSystemFontSize()
|
|
|
|
let document = CMDocument(data: markdown.data(using: .utf8), options: .normalize)
|
|
|
|
|
|
|
|
let attrs = CMTextAttributes()
|
|
|
|
attrs?.textAttributes = [
|
|
|
|
NSFontAttributeName: NSFont.systemFont(ofSize: size),
|
|
|
|
NSForegroundColorAttributeName: NSColor.gray,
|
|
|
|
]
|
|
|
|
attrs?.inlineCodeAttributes = [
|
|
|
|
NSFontAttributeName: NSFont.userFixedPitchFont(ofSize: size)!,
|
|
|
|
NSForegroundColorAttributeName: NSColor.gray,
|
|
|
|
]
|
|
|
|
|
|
|
|
let renderer = CMAttributedStringRenderer(document: document, attributes: attrs)
|
|
|
|
renderer?.register(CMHTMLStrikethroughTransformer())
|
|
|
|
renderer?.register(CMHTMLSuperscriptTransformer())
|
|
|
|
renderer?.register(CMHTMLUnderlineTransformer())
|
|
|
|
|
|
|
|
guard let result = renderer?.render() else {
|
|
|
|
preconditionFailure("Wrong markdown: \(markdown)")
|
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
2016-09-10 22:46:43 +03:00
|
|
|
}
|
|
|
|
|
2016-09-18 21:49:42 +03:00
|
|
|
extension NSView {
|
|
|
|
|
|
|
|
func removeAllSubviews() {
|
|
|
|
self.subviews.forEach { $0.removeFromSuperview() }
|
|
|
|
}
|
|
|
|
|
|
|
|
func removeAllConstraints() {
|
|
|
|
self.removeConstraints(self.constraints)
|
|
|
|
}
|
2016-10-03 16:03:18 +03:00
|
|
|
|
|
|
|
var isFirstResponder: Bool {
|
|
|
|
return self.window?.firstResponder == self
|
|
|
|
}
|
|
|
|
|
|
|
|
func beFirstResponder() {
|
|
|
|
self.window?.makeFirstResponder(self)
|
|
|
|
}
|
2016-09-18 21:49:42 +03:00
|
|
|
}
|
|
|
|
|
2016-09-03 00:55:19 +03:00
|
|
|
extension NSTableView {
|
|
|
|
|
2016-09-07 23:06:17 +03:00
|
|
|
static func standardTableView() -> NSTableView {
|
2016-09-03 00:55:19 +03:00
|
|
|
let tableView = NSTableView(frame: CGRect.zero)
|
|
|
|
|
2016-09-13 09:32:58 +03:00
|
|
|
let column = NSTableColumn(identifier: "name")
|
2016-09-25 18:50:33 +03:00
|
|
|
column.isEditable = false
|
2016-09-13 09:32:58 +03:00
|
|
|
|
|
|
|
tableView.addTableColumn(column)
|
2016-09-25 18:50:33 +03:00
|
|
|
tableView.rowSizeStyle = .default
|
2016-09-03 00:55:19 +03:00
|
|
|
tableView.sizeLastColumnToFit()
|
|
|
|
tableView.allowsEmptySelection = false
|
|
|
|
tableView.allowsMultipleSelection = false
|
|
|
|
tableView.headerView = nil
|
2016-09-25 18:50:33 +03:00
|
|
|
tableView.focusRingType = .none
|
2016-09-07 23:06:17 +03:00
|
|
|
|
|
|
|
return tableView
|
|
|
|
}
|
|
|
|
|
|
|
|
static func standardSourceListTableView() -> NSTableView {
|
|
|
|
let tableView = self.standardTableView()
|
2016-09-25 18:50:33 +03:00
|
|
|
tableView.selectionHighlightStyle = .sourceList
|
2016-09-03 00:55:19 +03:00
|
|
|
|
|
|
|
return tableView
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-27 19:02:05 +03:00
|
|
|
extension NSOutlineView {
|
|
|
|
|
|
|
|
static func standardOutlineView() -> NSOutlineView {
|
|
|
|
let outlineView = NSOutlineView(frame: CGRect.zero)
|
2016-10-03 16:03:18 +03:00
|
|
|
NSOutlineView.configure(toStandard: outlineView)
|
|
|
|
return outlineView
|
|
|
|
}
|
2016-09-27 19:02:05 +03:00
|
|
|
|
2016-10-03 16:03:18 +03:00
|
|
|
static func configure(toStandard outlineView: NSOutlineView) {
|
2016-09-27 19:02:05 +03:00
|
|
|
let column = NSTableColumn(identifier: "name")
|
2016-11-25 17:25:55 +03:00
|
|
|
column.resizingMask = .autoresizingMask
|
2016-09-27 19:02:05 +03:00
|
|
|
column.isEditable = false
|
|
|
|
|
|
|
|
outlineView.addTableColumn(column)
|
|
|
|
outlineView.outlineTableColumn = column
|
|
|
|
outlineView.allowsEmptySelection = false
|
|
|
|
outlineView.allowsMultipleSelection = false
|
|
|
|
outlineView.headerView = nil
|
|
|
|
outlineView.focusRingType = .none
|
|
|
|
}
|
2016-10-02 13:25:57 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
The selected item. When the selection is empty, then returns `nil`. When multiple items are selected, then returns
|
|
|
|
the last selected item.
|
|
|
|
*/
|
|
|
|
var selectedItem: Any? {
|
|
|
|
if self.selectedRow < 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return self.item(atRow: self.selectedRow)
|
|
|
|
}
|
2016-10-03 16:03:18 +03:00
|
|
|
|
2016-11-12 18:42:10 +03:00
|
|
|
var clickedItem: Any? {
|
|
|
|
if self.clickedRow < 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return self.item(atRow: self.clickedRow)
|
|
|
|
}
|
|
|
|
|
2016-10-03 16:03:18 +03:00
|
|
|
func toggle(item: Any) {
|
|
|
|
if self.isItemExpanded(item) {
|
|
|
|
self.collapseItem(item)
|
|
|
|
} else {
|
|
|
|
self.expandItem(item)
|
|
|
|
}
|
|
|
|
}
|
2016-09-27 19:02:05 +03:00
|
|
|
}
|
|
|
|
|
2016-09-03 00:55:19 +03:00
|
|
|
extension NSScrollView {
|
|
|
|
|
|
|
|
static func standardScrollView() -> NSScrollView {
|
|
|
|
let scrollView = NSScrollView(frame: CGRect.zero)
|
|
|
|
|
|
|
|
scrollView.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
scrollView.hasVerticalScroller = true
|
|
|
|
scrollView.hasHorizontalScroller = true
|
|
|
|
scrollView.autohidesScrollers = true
|
2016-09-25 18:50:33 +03:00
|
|
|
scrollView.borderType = .bezelBorder
|
2016-09-03 00:55:19 +03:00
|
|
|
|
|
|
|
return scrollView
|
|
|
|
}
|
|
|
|
}
|