2017-02-26 14:00:19 +03:00
|
|
|
/**
|
|
|
|
* Tae Won Ha - http://taewon.de - @hataewon
|
|
|
|
* See LICENSE
|
|
|
|
*/
|
|
|
|
|
|
|
|
import Cocoa
|
|
|
|
import RxSwift
|
|
|
|
import PureLayout
|
2017-06-25 22:29:30 +03:00
|
|
|
import SwiftNeoVim
|
2017-02-26 14:00:19 +03:00
|
|
|
|
|
|
|
class OpenedFileList: NSView,
|
|
|
|
UiComponent,
|
|
|
|
NSTableViewDataSource,
|
2017-06-25 22:29:30 +03:00
|
|
|
NSTableViewDelegate,
|
|
|
|
ThemedView {
|
2017-02-26 14:00:19 +03:00
|
|
|
|
|
|
|
typealias StateType = MainWindow.State
|
|
|
|
|
|
|
|
enum Action {
|
|
|
|
|
|
|
|
case open(NeoVimBuffer)
|
|
|
|
}
|
|
|
|
|
2017-06-27 07:41:16 +03:00
|
|
|
fileprivate(set) var theme = Theme.default
|
2017-06-25 22:29:30 +03:00
|
|
|
|
2017-02-26 14:00:19 +03:00
|
|
|
required init(source: Observable<StateType>, emitter: ActionEmitter, state: StateType) {
|
2017-04-26 20:40:42 +03:00
|
|
|
self.emit = emitter.typedEmit()
|
2017-02-26 14:00:19 +03:00
|
|
|
self.uuid = state.uuid
|
|
|
|
|
|
|
|
self.genericIcon = FileUtils.icon(forType: "public.data")
|
|
|
|
|
2017-06-27 07:41:16 +03:00
|
|
|
self.usesTheme = state.appearance.usesTheme
|
2017-06-30 00:43:02 +03:00
|
|
|
self.showsFileIcon = state.appearance.showsFileIcon
|
2017-06-27 07:41:16 +03:00
|
|
|
|
2017-02-26 14:00:19 +03:00
|
|
|
super.init(frame: .zero)
|
|
|
|
|
|
|
|
self.bufferList.dataSource = self
|
2017-06-26 00:17:00 +03:00
|
|
|
self.bufferList.allowsEmptySelection = true
|
2017-02-26 14:00:19 +03:00
|
|
|
self.bufferList.delegate = self
|
|
|
|
self.bufferList.target = self
|
|
|
|
self.bufferList.doubleAction = #selector(OpenedFileList.doubleClickAction)
|
|
|
|
|
|
|
|
self.addViews()
|
|
|
|
|
|
|
|
source
|
|
|
|
.observeOn(MainScheduler.instance)
|
|
|
|
.subscribe(onNext: { state in
|
2017-06-27 07:41:16 +03:00
|
|
|
let themeChanged = changeTheme(
|
|
|
|
themePrefChanged: state.appearance.usesTheme != self.usesTheme,
|
|
|
|
themeChanged: state.appearance.theme.mark != self.lastThemeMark,
|
|
|
|
usesTheme: state.appearance.usesTheme,
|
2017-06-28 20:55:43 +03:00
|
|
|
forTheme: { self.updateTheme(state.appearance.theme) },
|
|
|
|
forDefaultTheme: { self.updateTheme(Marked(Theme.default)) })
|
2017-06-27 07:41:16 +03:00
|
|
|
|
|
|
|
self.usesTheme = state.appearance.usesTheme
|
|
|
|
|
2017-02-26 14:00:19 +03:00
|
|
|
let buffers = state.buffers.removingDuplicatesPreservingFromBeginning()
|
2017-06-30 00:43:02 +03:00
|
|
|
if self.buffers == buffers && !themeChanged && self.showsFileIcon == state.appearance.showsFileIcon {
|
2017-02-26 14:00:19 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-06-30 00:43:02 +03:00
|
|
|
self.showsFileIcon = state.appearance.showsFileIcon
|
2017-02-26 14:00:19 +03:00
|
|
|
self.buffers = buffers
|
|
|
|
self.bufferList.reloadData()
|
|
|
|
self.adjustFileViewWidth()
|
|
|
|
})
|
2017-04-22 17:30:40 +03:00
|
|
|
.disposed(by: self.disposeBag)
|
2017-02-26 14:00:19 +03:00
|
|
|
}
|
2017-06-25 22:29:30 +03:00
|
|
|
|
2017-04-22 16:56:13 +03:00
|
|
|
fileprivate let emit: (UuidAction<Action>) -> Void
|
2017-02-26 14:00:19 +03:00
|
|
|
fileprivate let disposeBag = DisposeBag()
|
|
|
|
|
|
|
|
fileprivate let uuid: String
|
2017-06-27 07:41:16 +03:00
|
|
|
fileprivate var usesTheme: Bool
|
2017-06-25 22:29:30 +03:00
|
|
|
fileprivate var lastThemeMark = Token()
|
2017-06-30 00:43:02 +03:00
|
|
|
fileprivate var showsFileIcon: Bool
|
2017-02-26 14:00:19 +03:00
|
|
|
|
|
|
|
fileprivate let bufferList = NSTableView.standardTableView()
|
|
|
|
fileprivate let genericIcon: NSImage
|
|
|
|
|
|
|
|
fileprivate var buffers = [NeoVimBuffer]()
|
|
|
|
|
|
|
|
required init?(coder: NSCoder) {
|
|
|
|
fatalError("init(coder:) has not been implemented")
|
|
|
|
}
|
|
|
|
|
2017-06-28 20:55:43 +03:00
|
|
|
fileprivate func updateTheme(_ theme: Marked<Theme>) {
|
|
|
|
self.theme = theme.payload
|
|
|
|
self.bufferList.enclosingScrollView?.backgroundColor = self.theme.background
|
|
|
|
self.bufferList.backgroundColor = self.theme.background
|
|
|
|
self.lastThemeMark = theme.mark
|
|
|
|
}
|
|
|
|
|
2017-02-26 14:00:19 +03:00
|
|
|
fileprivate func addViews() {
|
|
|
|
let scrollView = NSScrollView.standardScrollView()
|
|
|
|
scrollView.borderType = .noBorder
|
|
|
|
scrollView.documentView = self.bufferList
|
|
|
|
|
|
|
|
self.addSubview(scrollView)
|
|
|
|
scrollView.autoPinEdgesToSuperviewEdges()
|
|
|
|
}
|
2017-06-25 22:29:30 +03:00
|
|
|
|
2017-02-26 14:00:19 +03:00
|
|
|
fileprivate func adjustFileViewWidth() {
|
|
|
|
let maxWidth = self.buffers.reduce(CGFloat(0)) { (curMaxWidth, buffer) in
|
|
|
|
return max(self.text(for: buffer).size().width, curMaxWidth)
|
|
|
|
}
|
|
|
|
|
|
|
|
let column = self.bufferList.tableColumns[0]
|
2017-06-25 22:29:30 +03:00
|
|
|
column.minWidth = maxWidth + ThemedTableCell.widthWithoutText
|
2017-02-26 14:00:19 +03:00
|
|
|
column.maxWidth = column.minWidth
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: - Actions
|
|
|
|
extension OpenedFileList {
|
|
|
|
|
2017-10-22 15:33:18 +03:00
|
|
|
@objc func doubleClickAction(_ sender: Any?) {
|
2017-02-26 14:00:19 +03:00
|
|
|
let clickedRow = self.bufferList.clickedRow
|
|
|
|
guard clickedRow >= 0 && clickedRow < self.buffers.count else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-04-22 16:56:13 +03:00
|
|
|
self.emit(UuidAction(uuid: self.uuid, action: .open(self.buffers[clickedRow])))
|
2017-02-26 14:00:19 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: - NSTableViewDataSource
|
|
|
|
extension OpenedFileList {
|
|
|
|
|
|
|
|
@objc(numberOfRowsInTableView:)
|
|
|
|
func numberOfRows(in tableView: NSTableView) -> Int {
|
|
|
|
return self.buffers.count
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: - NSTableViewDelegate
|
|
|
|
extension OpenedFileList {
|
|
|
|
|
2017-06-25 22:29:30 +03:00
|
|
|
public func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? {
|
2017-10-22 15:33:18 +03:00
|
|
|
return tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier("buffer-row-view"), owner: self) as? ThemedTableRow
|
2017-06-25 22:29:30 +03:00
|
|
|
?? ThemedTableRow(withIdentifier: "buffer-row-view", themedView: self)
|
|
|
|
}
|
|
|
|
|
2017-02-26 14:00:19 +03:00
|
|
|
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
|
2017-10-22 15:33:18 +03:00
|
|
|
let cachedCell = (tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier("buffer-cell-view"), owner: self) as? ThemedTableCell)?.reset()
|
2017-06-25 22:29:30 +03:00
|
|
|
let cell = cachedCell ?? ThemedTableCell(withIdentifier: "buffer-cell-view")
|
2017-02-26 14:00:19 +03:00
|
|
|
|
|
|
|
let buffer = self.buffers[row]
|
|
|
|
cell.attributedText = self.text(for: buffer)
|
2017-06-30 00:43:02 +03:00
|
|
|
|
|
|
|
guard self.showsFileIcon else {
|
|
|
|
return cell
|
|
|
|
}
|
|
|
|
|
2017-02-26 14:00:19 +03:00
|
|
|
cell.image = self.icon(for: buffer)
|
|
|
|
|
|
|
|
return cell
|
|
|
|
}
|
|
|
|
|
|
|
|
fileprivate func text(for buffer: NeoVimBuffer) -> NSAttributedString {
|
|
|
|
guard let name = buffer.name else {
|
|
|
|
return NSAttributedString(string: "No Name")
|
|
|
|
}
|
|
|
|
|
|
|
|
guard let url = buffer.url else {
|
|
|
|
return NSAttributedString(string: name)
|
|
|
|
}
|
|
|
|
|
|
|
|
let pathInfo = url.pathComponents.dropFirst().dropLast().reversed().joined(separator: " / ") + " /"
|
|
|
|
let rowText = NSMutableAttributedString(string: "\(name) — \(pathInfo)")
|
2017-06-29 18:15:14 +03:00
|
|
|
|
2017-10-22 15:33:18 +03:00
|
|
|
rowText.addAttribute(NSAttributedStringKey.foregroundColor,
|
2017-06-29 18:15:14 +03:00
|
|
|
value: self.theme.foreground,
|
2017-10-22 22:43:03 +03:00
|
|
|
range: NSRange(location: 0, length: name.count))
|
2017-06-29 18:15:14 +03:00
|
|
|
|
2017-10-22 15:33:18 +03:00
|
|
|
rowText.addAttribute(NSAttributedStringKey.foregroundColor,
|
2017-06-29 18:15:14 +03:00
|
|
|
value: self.theme.foreground.brightening(by: 1.15),
|
2017-11-10 14:17:47 +03:00
|
|
|
range: NSRange(location: name.count, length: pathInfo.count + 3))
|
2017-02-26 14:00:19 +03:00
|
|
|
|
|
|
|
return rowText
|
|
|
|
}
|
|
|
|
|
|
|
|
fileprivate func icon(for buffer: NeoVimBuffer) -> NSImage? {
|
|
|
|
if let url = buffer.url {
|
|
|
|
return FileUtils.icon(forUrl: url)
|
|
|
|
}
|
|
|
|
|
|
|
|
return self.genericIcon
|
|
|
|
}
|
|
|
|
}
|