NetNewsWire/Tree/Sources/Tree/TreeController.swift

135 lines
3.0 KiB
Swift

//
// TreeController.swift
// NetNewsWire
//
// Created by Brent Simmons on 5/29/16.
// Copyright © 2016 Ranchero Software, LLC. All rights reserved.
//
import Foundation
public protocol TreeControllerDelegate: AnyObject {
@MainActor func treeController(treeController: TreeController, childNodesFor: Node) -> [Node]?
}
public typealias NodeVisitBlock = (_ : Node) -> Void
@MainActor public final class TreeController {
private weak var delegate: TreeControllerDelegate?
public let rootNode: Node
public init(delegate: TreeControllerDelegate, rootNode: Node) {
self.delegate = delegate
self.rootNode = rootNode
rebuild()
}
public convenience init(delegate: TreeControllerDelegate) {
self.init(delegate: delegate, rootNode: Node.genericRootNode())
}
@discardableResult
public func rebuild() -> Bool {
// Rebuild and re-sort. Return true if any changes in the entire tree.
return rebuildChildNodes(node: rootNode)
}
public func visitNodes(_ visitBlock: NodeVisitBlock) {
visitNode(rootNode, visitBlock)
}
public func nodeInArrayRepresentingObject(nodes: [Node], representedObject: AnyObject, recurse: Bool = false) -> Node? {
for oneNode in nodes {
if oneNode.representedObject === representedObject {
return oneNode
}
if recurse, oneNode.canHaveChildNodes {
if let foundNode = nodeInArrayRepresentingObject(nodes: oneNode.childNodes, representedObject: representedObject, recurse: recurse) {
return foundNode
}
}
}
return nil
}
public func nodeInTreeRepresentingObject(_ representedObject: AnyObject) -> Node? {
return nodeInArrayRepresentingObject(nodes: [rootNode], representedObject: representedObject, recurse: true)
}
public func normalizedSelectedNodes(_ nodes: [Node]) -> [Node] {
// An array of nodes might include a leaf node and its parent. Remove the leaf node.
var normalizedNodes = [Node]()
for node in nodes {
if !node.hasAncestor(in: nodes) {
normalizedNodes += [node]
}
}
return normalizedNodes
}
}
private extension TreeController {
func visitNode(_ node: Node, _ visitBlock: NodeVisitBlock) {
visitBlock(node)
for oneChildNode in node.childNodes {
visitNode(oneChildNode, visitBlock)
}
}
func nodeArraysAreEqual(_ nodeArray1: [Node]?, _ nodeArray2: [Node]?) -> Bool {
if nodeArray1 == nil && nodeArray2 == nil {
return true
}
if nodeArray1 != nil && nodeArray2 == nil {
return false
}
if nodeArray1 == nil && nodeArray2 != nil {
return false
}
return nodeArray1! == nodeArray2!
}
func rebuildChildNodes(node: Node) -> Bool {
if !node.canHaveChildNodes {
return false
}
let childNodes = delegate?.treeController(treeController: self, childNodesFor: node) ?? [Node]()
var childNodesDidChange = !nodeArraysAreEqual(childNodes, node.childNodes)
if childNodesDidChange {
node.childNodes = childNodes
}
for oneChildNode in childNodes {
if rebuildChildNodes(node: oneChildNode) {
childNodesDidChange = true
}
}
return childNodesDidChange
}
}