diff --git a/iOS/Master/Cell/MasterTableViewCell.swift b/iOS/Master/Cell/MasterTableViewCell.swift index b08760865..d973faf1f 100644 --- a/iOS/Master/Cell/MasterTableViewCell.swift +++ b/iOS/Master/Cell/MasterTableViewCell.swift @@ -19,7 +19,7 @@ class MasterTableViewCell : UITableViewCell { weak var delegate: MasterTableViewCellDelegate? var allowDisclosureSelection = false - + override var accessibilityLabel: String? { set {} get { @@ -32,6 +32,24 @@ class MasterTableViewCell : UITableViewCell { } } + var indent = false { + didSet { + if indent != oldValue { + setNeedsLayout() + } + } + } + + var disclosureExpanded = false { + didSet { + if disclosureExpanded { + accessoryButton?.setImage(AppAssets.chevronDownImage, for: .normal) + } else { + accessoryButton?.setImage(AppAssets.chevronRightImage, for: .normal) + } + } + } + var faviconImage: UIImage? { didSet { if let image = faviconImage { @@ -106,7 +124,7 @@ class MasterTableViewCell : UITableViewCell { override func layoutSubviews() { super.layoutSubviews() - let layout = MasterTableViewCellLayout(cellSize: bounds.size, shouldShowImage: shouldShowImage, label: titleView, unreadCountView: unreadCountView, showingEditingControl: showingEditControl) + let layout = MasterTableViewCellLayout(cellSize: bounds.size, shouldShowImage: shouldShowImage, label: titleView, unreadCountView: unreadCountView, showingEditingControl: showingEditControl, indent: indent) layoutWith(layout) } @@ -116,7 +134,7 @@ class MasterTableViewCell : UITableViewCell { return } - if sender.imageView?.image == AppAssets.chevronRightImage { + if !disclosureExpanded { sender.setImage(AppAssets.chevronDownImage, for: .normal) delegate?.disclosureSelected(self, expanding: true) } else { @@ -124,6 +142,8 @@ class MasterTableViewCell : UITableViewCell { delegate?.disclosureSelected(self, expanding: false) } + disclosureExpanded = !disclosureExpanded + } } @@ -138,13 +158,21 @@ private extension MasterTableViewCell { } func addAccessoryView() { + let button = UIButton(type: .roundedRect) button.frame = CGRect(x: 0, y: 0, width: 25.0, height: 25.0) - button.setImage(AppAssets.chevronRightImage, for: .normal) + + if disclosureExpanded { + button.setImage(AppAssets.chevronDownImage, for: .normal) + } else { + button.setImage(AppAssets.chevronRightImage, for: .normal) + } + button.tintColor = AppAssets.chevronDisclosureColor button.addTarget(self, action: #selector(buttonPressed(_:)), for: UIControl.Event.touchUpInside) accessoryButton = button accessoryView = button + } func addSubviewAtInit(_ view: UIView) { diff --git a/iOS/Master/Cell/MasterTableViewCellLayout.swift b/iOS/Master/Cell/MasterTableViewCellLayout.swift index b1e15aed8..3f06f02ee 100644 --- a/iOS/Master/Cell/MasterTableViewCellLayout.swift +++ b/iOS/Master/Cell/MasterTableViewCellLayout.swift @@ -23,7 +23,7 @@ struct MasterTableViewCellLayout { let titleRect: CGRect let unreadCountRect: CGRect - init(cellSize: CGSize, shouldShowImage: Bool, label: UILabel, unreadCountView: MasterUnreadCountView, showingEditingControl: Bool) { + init(cellSize: CGSize, shouldShowImage: Bool, label: UILabel, unreadCountView: MasterUnreadCountView, showingEditingControl: Bool, indent: Bool) { let adjustedWidth: CGFloat = { if showingEditingControl { @@ -37,8 +37,9 @@ struct MasterTableViewCellLayout { var rFavicon = CGRect.zero if shouldShowImage { - let indent = showingEditingControl ? MasterTableViewCellLayout.imageMarginLeft + 40 : MasterTableViewCellLayout.imageMarginLeft - rFavicon = CGRect(x: indent, y: 0.0, width: MasterTableViewCellLayout.imageSize.width, height: MasterTableViewCellLayout.imageSize.height) + var indentX = showingEditingControl ? MasterTableViewCellLayout.imageMarginLeft + 40 : MasterTableViewCellLayout.imageMarginLeft + indentX = indent ? indentX + 20 : indentX + rFavicon = CGRect(x: indentX, y: 0.0, width: MasterTableViewCellLayout.imageSize.width, height: MasterTableViewCellLayout.imageSize.height) rFavicon = MasterTableViewCellLayout.centerVertically(rFavicon, bounds) } self.faviconRect = rFavicon diff --git a/iOS/Master/MasterViewController.swift b/iOS/Master/MasterViewController.swift index eb1e90901..962cd03be 100644 --- a/iOS/Master/MasterViewController.swift +++ b/iOS/Master/MasterViewController.swift @@ -17,6 +17,9 @@ class MasterViewController: UITableViewController, UndoableCommandRunner { var undoableCommands = [UndoableCommand]() var animatingChanges = false + var expandedNodes = [Node]() + var shadowTable = [[Node]]() + let treeControllerDelegate = MasterTreeControllerDelegate() lazy var treeController: TreeController = { return TreeController(delegate: treeControllerDelegate) @@ -44,6 +47,13 @@ class MasterViewController: UITableViewController, UndoableCommandRunner { refreshControl = UIRefreshControl() refreshControl!.addTarget(self, action: #selector(refreshAccounts(_:)), for: .valueChanged) + // Set up the backing structures + for _ in treeController.rootNode.childNodes { + shadowTable.append([Node]()) + } + + rebuildShadow() + } override func viewWillAppear(_ animated: Bool) { @@ -124,7 +134,7 @@ class MasterViewController: UITableViewController, UndoableCommandRunner { } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return treeController.rootNode.childAtIndex(section)?.numberOfChildNodes ?? 0 + return shadowTable[section].count } override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { @@ -302,12 +312,17 @@ class MasterViewController: UITableViewController, UndoableCommandRunner { // MARK: API func configure(_ cell: MasterTableViewCell, _ node: Node) { + cell.delegate = self - cell.allowDisclosureSelection = node.canHaveChildNodes + cell.indent = node.parent?.representedObject is Folder + cell.disclosureExpanded = expandedNodes.contains(node) + cell.allowDisclosureSelection = node.canHaveChildNodes + cell.name = nameFor(node) configureUnreadCount(cell, node) configureFavicon(cell, node) cell.shouldShowImage = node.representedObject is SmallIconProvider + } func configureUnreadCount(_ cell: MasterTableViewCell, _ node: Node) { @@ -341,8 +356,8 @@ class MasterViewController: UITableViewController, UndoableCommandRunner { func delete(indexPath: IndexPath) { - guard let containerNode = treeController.rootNode.childAtIndex(indexPath.section), - let deleteNode = containerNode.childAtIndex(indexPath.row), + guard let deleteNode = nodeFor(indexPath: indexPath), + let containerNode = deleteNode.parent, let container = containerNode.representedObject as? Container else { return } @@ -357,7 +372,7 @@ class MasterViewController: UITableViewController, UndoableCommandRunner { container.deleteFolder(folder) } - treeController.rebuild() + rebuildBackingStructures() tableView.deleteRows(at: [indexPath], with: .automatic) animatingChanges = false @@ -405,7 +420,7 @@ class MasterViewController: UITableViewController, UndoableCommandRunner { } func nodeFor(indexPath: IndexPath) -> Node? { - return treeController.rootNode.childAtIndex(indexPath.section)?.childAtIndex(indexPath.row) + return shadowTable[indexPath.section][indexPath.row] } } @@ -433,7 +448,11 @@ extension MasterViewController: UIDocumentPickerDelegate { extension MasterViewController: MasterTableViewCellDelegate { func disclosureSelected(_ sender: MasterTableViewCell, expanding: Bool) { -// let indexSet = tableView.indexPath(for: sender) + if expanding { + expandCell(sender) + } else { + collapseCell(sender) + } } } @@ -444,7 +463,7 @@ private extension MasterViewController { func rebuildTreeAndReloadDataIfNeeded() { if !animatingChanges && !BatchUpdate.shared.isPerforming { - treeController.rebuild() + rebuildBackingStructures() tableView.reloadData() } } @@ -475,4 +494,96 @@ private extension MasterViewController { } } + func rebuildBackingStructures() { + treeController.rebuild() + rebuildShadow() + } + + func rebuildShadow() { + + for i in 0..