Acord/src/TitleBarView.swift

165 lines
5.3 KiB
Swift

import Cocoa
class TitleBarAccessoryController: NSTitlebarAccessoryViewController {
let titleView = TitleBarView()
override func loadView() {
let container = NSView(frame: NSRect(x: 0, y: 0, width: 400, height: 28))
titleView.translatesAutoresizingMaskIntoConstraints = false
container.addSubview(titleView)
NSLayoutConstraint.activate([
titleView.leadingAnchor.constraint(equalTo: container.leadingAnchor),
titleView.trailingAnchor.constraint(equalTo: container.trailingAnchor),
titleView.topAnchor.constraint(equalTo: container.topAnchor),
titleView.bottomAnchor.constraint(equalTo: container.bottomAnchor),
])
self.view = container
}
override func viewDidLoad() {
super.viewDidLoad()
layoutAttribute = .bottom
fullScreenMinHeight = 28
}
}
class TitleBarView: NSView {
private let label = NSTextField(labelWithString: "")
private let editor = NSTextField()
private(set) var isEditing = false
var title: String = "" {
didSet {
if !isEditing {
let dt = displayTitle
label.stringValue = dt.isEmpty ? "Untitled" : dt
label.textColor = dt.isEmpty ? Theme.current.overlay0 : Theme.current.text
}
}
}
var onCommit: ((String) -> Void)?
private var displayTitle: String {
let trimmed = title.trimmingCharacters(in: .whitespaces)
if trimmed.hasPrefix("# ") {
return String(trimmed.dropFirst(2))
}
return trimmed
}
override init(frame: NSRect) {
super.init(frame: frame)
setup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setup()
}
private func setup() {
label.font = .systemFont(ofSize: 13, weight: .semibold)
label.textColor = Theme.current.overlay0
label.backgroundColor = .clear
label.isBezeled = false
label.isEditable = false
label.isSelectable = false
label.alignment = .center
label.lineBreakMode = .byTruncatingTail
label.cell?.truncatesLastVisibleLine = true
label.translatesAutoresizingMaskIntoConstraints = false
label.stringValue = "Untitled"
editor.font = .systemFont(ofSize: 13, weight: .semibold)
editor.textColor = Theme.current.text
editor.backgroundColor = Theme.current.surface0
editor.isBezeled = false
editor.isEditable = true
editor.isSelectable = true
editor.alignment = .center
editor.focusRingType = .none
editor.cell?.lineBreakMode = .byTruncatingTail
editor.translatesAutoresizingMaskIntoConstraints = false
editor.isHidden = true
editor.delegate = self
addSubview(label)
addSubview(editor)
NSLayoutConstraint.activate([
label.centerXAnchor.constraint(equalTo: centerXAnchor),
label.centerYAnchor.constraint(equalTo: centerYAnchor),
label.widthAnchor.constraint(lessThanOrEqualTo: widthAnchor, multiplier: 0.5),
editor.centerXAnchor.constraint(equalTo: centerXAnchor),
editor.centerYAnchor.constraint(equalTo: centerYAnchor),
editor.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.5),
])
let dblClick = NSClickGestureRecognizer(target: self, action: #selector(handleDoubleClick(_:)))
dblClick.numberOfClicksRequired = 2
dblClick.delaysPrimaryMouseButtonEvents = false
addGestureRecognizer(dblClick)
}
@objc private func handleDoubleClick(_ sender: NSClickGestureRecognizer) {
if !isEditing { beginEditing() }
}
override func mouseDown(with event: NSEvent) {
if event.clickCount == 2 && !isEditing {
beginEditing()
return
}
super.mouseDown(with: event)
}
func beginEditing() {
isEditing = true
editor.stringValue = title
label.isHidden = true
editor.isHidden = false
window?.makeFirstResponder(editor)
editor.currentEditor()?.selectAll(nil)
}
func endEditing() {
guard isEditing else { return }
isEditing = false
let raw = editor.stringValue
onCommit?(raw)
let dt = displayTitle
label.stringValue = dt.isEmpty ? "Untitled" : dt
label.textColor = dt.isEmpty ? Theme.current.overlay0 : Theme.current.text
editor.isHidden = true
label.isHidden = false
}
func updateColors() {
let dt = displayTitle
label.textColor = dt.isEmpty ? Theme.current.overlay0 : Theme.current.text
editor.textColor = Theme.current.text
editor.backgroundColor = Theme.current.surface0
}
}
extension TitleBarView: NSTextFieldDelegate {
func controlTextDidEndEditing(_ obj: Notification) {
endEditing()
}
func control(_ control: NSControl, textView: NSTextView, doCommandBy sel: Selector) -> Bool {
if sel == #selector(NSResponder.insertNewline(_:)) {
endEditing()
NotificationCenter.default.post(name: .focusEditor, object: nil)
return true
}
if sel == #selector(NSResponder.cancelOperation(_:)) {
endEditing()
return true
}
return false
}
}