mirror of
https://github.com/qvacua/vimr.git
synced 2024-12-01 10:02:36 +03:00
Render items using bindings
This commit is contained in:
parent
bbf83c7856
commit
16c6eb8c86
@ -181,7 +181,9 @@ extension FileBrowser {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if NOPE
|
||||||
self.fileView.select(url)
|
self.fileView.select(url)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func refreshAction(_ sender: Any?) {
|
@objc func refreshAction(_ sender: Any?) {
|
||||||
|
@ -10,12 +10,186 @@ import RxSwift
|
|||||||
|
|
||||||
class FileOutlineView: NSOutlineView,
|
class FileOutlineView: NSOutlineView,
|
||||||
UiComponent,
|
UiComponent,
|
||||||
NSOutlineViewDataSource,
|
|
||||||
NSOutlineViewDelegate,
|
NSOutlineViewDelegate,
|
||||||
ThemedView {
|
ThemedView {
|
||||||
|
|
||||||
typealias StateType = MainWindow.State
|
typealias StateType = MainWindow.State
|
||||||
|
|
||||||
|
@objc dynamic var content = [Node]()
|
||||||
|
private(set) var theme = Theme.default
|
||||||
|
|
||||||
|
required init(
|
||||||
|
source: Observable<StateType>,
|
||||||
|
emitter: ActionEmitter,
|
||||||
|
state: StateType
|
||||||
|
) {
|
||||||
|
self.emit = emitter.typedEmit()
|
||||||
|
self.uuid = state.uuid
|
||||||
|
self.root = Node(url: state.cwd)
|
||||||
|
|
||||||
|
super.init(frame: .zero)
|
||||||
|
|
||||||
|
NSOutlineView.configure(toStandard: self)
|
||||||
|
self.delegate = self
|
||||||
|
|
||||||
|
guard Bundle.main.loadNibNamed(
|
||||||
|
NSNib.Name("FileBrowserMenu"),
|
||||||
|
owner: self,
|
||||||
|
topLevelObjects: nil
|
||||||
|
) else {
|
||||||
|
NSLog("WARN: FileBrowserMenu.xib could not be loaded")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.treeController.childrenKeyPath = "children"
|
||||||
|
self.treeController.leafKeyPath = "isLeaf"
|
||||||
|
self.treeController.countKeyPath = "childrenCount"
|
||||||
|
self.treeController.objectClass = Node.self
|
||||||
|
self.treeController.avoidsEmptySelection = false
|
||||||
|
self.treeController.sortDescriptors = [
|
||||||
|
NSSortDescriptor(key: "isLeaf", ascending: true), // Folders first,
|
||||||
|
NSSortDescriptor(key: "displayName", ascending: true) // then, name
|
||||||
|
]
|
||||||
|
self.treeController.bind(.contentArray, to: self, withKeyPath: "content")
|
||||||
|
self.bind(.content, to: self.treeController, withKeyPath: "arrangedObjects")
|
||||||
|
self.bind(.selectionIndexPaths,
|
||||||
|
to: self.treeController,
|
||||||
|
withKeyPath: "selectionIndexPaths")
|
||||||
|
|
||||||
|
self.reloadRoot()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
private let emit: (UuidAction<FileBrowser.Action>) -> Void
|
||||||
|
private let disposeBag = DisposeBag()
|
||||||
|
|
||||||
|
private let uuid: String
|
||||||
|
|
||||||
|
private var root: Node
|
||||||
|
private let treeController = NSTreeController()
|
||||||
|
|
||||||
|
private func childNodes(for node: Node) -> [Node] {
|
||||||
|
if node.isChildrenScanned {
|
||||||
|
return node.children ?? []
|
||||||
|
}
|
||||||
|
|
||||||
|
return FileUtils.directDescendants(of: node.url).map { Node(url: $0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private func reloadRoot() {
|
||||||
|
let children = self.childNodes(for: self.root)
|
||||||
|
|
||||||
|
self.root.children = children
|
||||||
|
self.content.append(contentsOf: children)
|
||||||
|
|
||||||
|
self.adjustColumnWidth()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func adjustColumnWidth() {
|
||||||
|
let indent = self.indentationPerLevel
|
||||||
|
var maxRow = (0, CGFloat(20.0))
|
||||||
|
for i in (0..<self.numberOfRows) {
|
||||||
|
guard let width = (self.view(
|
||||||
|
atColumn: 0, row: i, makeIfNecessary: true
|
||||||
|
) as? ThemedTableCell)?.fittingSize.width else {
|
||||||
|
stdoutLog.error("Could not convert to ThemedTableCell!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let level = CGFloat(self.level(forRow: i))
|
||||||
|
maxRow = (i, max(maxRow.1, level * indent + width))
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tableColumns[0].width = maxRow.1 + CGFloat(30)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - NSOutlineViewDelegate
|
||||||
|
extension FileOutlineView {
|
||||||
|
|
||||||
|
func outlineView(
|
||||||
|
_ outlineView: NSOutlineView,
|
||||||
|
rowViewForItem item: Any
|
||||||
|
) -> NSTableRowView? {
|
||||||
|
return self.makeView(
|
||||||
|
withIdentifier: NSUserInterfaceItemIdentifier("file-row-view"),
|
||||||
|
owner: self
|
||||||
|
) as? ThemedTableRow ?? ThemedTableRow(withIdentifier: "file-row-view",
|
||||||
|
themedView: self)
|
||||||
|
}
|
||||||
|
|
||||||
|
func outlineView(
|
||||||
|
_: NSOutlineView,
|
||||||
|
viewFor tableColumn: NSTableColumn?,
|
||||||
|
item: Any
|
||||||
|
) -> NSView? {
|
||||||
|
guard let treeNode = item as? NSTreeNode,
|
||||||
|
let node = treeNode.representedObject as? Node else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let cellView = self.makeView(
|
||||||
|
withIdentifier: NSUserInterfaceItemIdentifier("file-cell-view"),
|
||||||
|
owner: self
|
||||||
|
) as? ThemedTableCell ?? ThemedTableCell(withIdentifier: "file-cell-view")
|
||||||
|
|
||||||
|
cellView.isDir = node.isDir
|
||||||
|
cellView.text = node.displayName
|
||||||
|
|
||||||
|
let icon = FileUtils.icon(forUrl: node.url)
|
||||||
|
cellView.image = node.isHidden
|
||||||
|
? icon?.tinting(with: NSColor.white.withAlphaComponent(0.4))
|
||||||
|
: icon
|
||||||
|
|
||||||
|
return cellView
|
||||||
|
}
|
||||||
|
|
||||||
|
func outlineView(_: NSOutlineView, heightOfRowByItem item: Any) -> CGFloat {
|
||||||
|
return 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Node: NSObject {
|
||||||
|
|
||||||
|
@objc dynamic var url: URL
|
||||||
|
@objc dynamic var isLeaf: Bool
|
||||||
|
@objc dynamic var isHidden: Bool
|
||||||
|
@objc dynamic var children: [Node]?
|
||||||
|
|
||||||
|
@objc dynamic var childrenCount: Int {
|
||||||
|
return self.children?.count ?? -1
|
||||||
|
}
|
||||||
|
@objc dynamic var displayName: String {
|
||||||
|
return self.url.lastPathComponent
|
||||||
|
}
|
||||||
|
|
||||||
|
var isDir: Bool {
|
||||||
|
return !self.isLeaf
|
||||||
|
}
|
||||||
|
var isChildrenScanned = false
|
||||||
|
|
||||||
|
override var hash: Int {
|
||||||
|
return self.url.hashValue
|
||||||
|
}
|
||||||
|
|
||||||
|
init(url: URL) {
|
||||||
|
self.url = url
|
||||||
|
self.isLeaf = !url.isDir
|
||||||
|
self.isHidden = url.isHidden
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FileOutlineViewOld: NSOutlineView,
|
||||||
|
UiComponent,
|
||||||
|
NSOutlineViewDataSource,
|
||||||
|
NSOutlineViewDelegate,
|
||||||
|
ThemedView {
|
||||||
|
|
||||||
|
typealias StateType = MainWindow.State
|
||||||
|
|
||||||
private(set) var theme = Theme.default
|
private(set) var theme = Theme.default
|
||||||
|
|
||||||
required init(
|
required init(
|
||||||
@ -53,7 +227,7 @@ class FileOutlineView: NSOutlineView,
|
|||||||
// in the background... Dunno why it worked before the redesign... -_-
|
// in the background... Dunno why it worked before the redesign... -_-
|
||||||
self.menu?.items.forEach { $0.target = self }
|
self.menu?.items.forEach { $0.target = self }
|
||||||
|
|
||||||
self.doubleAction = #selector(FileOutlineView.doubleClickAction)
|
self.doubleAction = #selector(FileOutlineViewOld.doubleClickAction)
|
||||||
|
|
||||||
source
|
source
|
||||||
.filter { !self.shouldReloadData(for: $0) }
|
.filter { !self.shouldReloadData(for: $0) }
|
||||||
@ -294,7 +468,7 @@ class FileOutlineView: NSOutlineView,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - NSOutlineViewDataSource
|
// MARK: - NSOutlineViewDataSource
|
||||||
extension FileOutlineView {
|
extension FileOutlineViewOld {
|
||||||
|
|
||||||
private func scanChildrenIfNecessary(_ fileBrowserItem: FileBrowserItem) {
|
private func scanChildrenIfNecessary(_ fileBrowserItem: FileBrowserItem) {
|
||||||
guard fileBrowserItem.isChildrenScanned == false else {
|
guard fileBrowserItem.isChildrenScanned == false else {
|
||||||
@ -413,7 +587,7 @@ extension FileOutlineView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - NSOutlineViewDelegate
|
// MARK: - NSOutlineViewDelegate
|
||||||
extension FileOutlineView {
|
extension FileOutlineViewOld {
|
||||||
|
|
||||||
func outlineView(
|
func outlineView(
|
||||||
_ outlineView: NSOutlineView, rowViewForItem item: Any
|
_ outlineView: NSOutlineView, rowViewForItem item: Any
|
||||||
@ -441,7 +615,7 @@ extension FileOutlineView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Actions
|
// MARK: - Actions
|
||||||
extension FileOutlineView {
|
extension FileOutlineViewOld {
|
||||||
|
|
||||||
@IBAction func doubleClickAction(_: Any?) {
|
@IBAction func doubleClickAction(_: Any?) {
|
||||||
guard let item = self.clickedItem as? FileBrowserItem else {
|
guard let item = self.clickedItem as? FileBrowserItem else {
|
||||||
@ -517,7 +691,7 @@ extension FileOutlineView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - NSUserInterfaceValidations
|
// MARK: - NSUserInterfaceValidations
|
||||||
extension FileOutlineView {
|
extension FileOutlineViewOld {
|
||||||
|
|
||||||
override func validateUserInterfaceItem(
|
override func validateUserInterfaceItem(
|
||||||
_ item: NSValidatedUserInterfaceItem
|
_ item: NSValidatedUserInterfaceItem
|
||||||
@ -535,7 +709,7 @@ extension FileOutlineView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - NSView
|
// MARK: - NSView
|
||||||
extension FileOutlineView {
|
extension FileOutlineViewOld {
|
||||||
|
|
||||||
override func keyDown(with event: NSEvent) {
|
override func keyDown(with event: NSEvent) {
|
||||||
guard let char = event.charactersIgnoringModifiers?.first else {
|
guard let char = event.charactersIgnoringModifiers?.first else {
|
||||||
|
@ -57,16 +57,16 @@ class ThemedTableCell: NSTableCellView {
|
|||||||
static let font = NSFont.systemFont(ofSize: 12)
|
static let font = NSFont.systemFont(ofSize: 12)
|
||||||
static let widthWithoutText = CGFloat(2 + 16 + 4 + 2)
|
static let widthWithoutText = CGFloat(2 + 16 + 4 + 2)
|
||||||
|
|
||||||
static func width(with text: String) -> CGFloat {
|
// static func width(with text: String) -> CGFloat {
|
||||||
let attrStr = NSAttributedString(string: text, attributes: [NSAttributedString.Key.font: ThemedTableCell.font])
|
// let attrStr = NSAttributedString(string: text, attributes: [NSAttributedString.Key.font: ThemedTableCell.font])
|
||||||
|
//
|
||||||
return self.widthWithoutText + attrStr.size().width
|
// return self.widthWithoutText + attrStr.size().width
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
override var intrinsicContentSize: CGSize {
|
// override var intrinsicContentSize: CGSize {
|
||||||
return CGSize(width: ThemedTableCell.widthWithoutText + self._textField.intrinsicContentSize.width,
|
// return CGSize(width: ThemedTableCell.widthWithoutText + self._textField.intrinsicContentSize.width,
|
||||||
height: max(self._textField.intrinsicContentSize.height, 16))
|
// height: max(self._textField.intrinsicContentSize.height, 16))
|
||||||
}
|
// }
|
||||||
|
|
||||||
var isDir = false
|
var isDir = false
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user