Skip to content
This repository was archived by the owner on Mar 5, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 27 additions & 7 deletions Demo/Demo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
293D26F1270870A000333635 /* GroupOutlineViewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 293D26F0270870A000333635 /* GroupOutlineViewItem.swift */; };
DD61CF5624717B1D00CA0BE9 /* DiffableDataSourceSnapshot+Additions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD61CF5524717B1D00CA0BE9 /* DiffableDataSourceSnapshot+Additions.swift */; };
DD61CF582471882A00CA0BE9 /* MasterOutlineViewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD61CF572471882A00CA0BE9 /* MasterOutlineViewItem.swift */; };
DDCAB953246475AB00E5AA41 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCAB952246475AB00E5AA41 /* MainViewController.swift */; };
Expand All @@ -22,6 +23,7 @@
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
293D26F0270870A000333635 /* GroupOutlineViewItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GroupOutlineViewItem.swift; sourceTree = "<group>"; };
DD61CF5524717B1D00CA0BE9 /* DiffableDataSourceSnapshot+Additions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DiffableDataSourceSnapshot+Additions.swift"; sourceTree = "<group>"; };
DD61CF572471882A00CA0BE9 /* MasterOutlineViewItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MasterOutlineViewItem.swift; sourceTree = "<group>"; };
DDCAB952246475AB00E5AA41 /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -50,6 +52,27 @@
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
293D26EE2706F38B00333635 /* Left Panel */ = {
isa = PBXGroup;
children = (
DDCAB9542464771600E5AA41 /* MasterViewController.swift */,
DD61CF572471882A00CA0BE9 /* MasterOutlineViewItem.swift */,
293D26F0270870A000333635 /* GroupOutlineViewItem.swift */,
);
name = "Left Panel";
sourceTree = "<group>";
};
293D26EF2706F39A00333635 /* Right Panel */ = {
isa = PBXGroup;
children = (
DDCAB9562464785700E5AA41 /* DetailViewController.swift */,
DDCAB9582464796400E5AA41 /* EmptyViewController.swift */,
DDCAB95A24647B7200E5AA41 /* SingleViewController.swift */,
DDCAB95C24647BBF00E5AA41 /* MultiViewController.swift */,
);
name = "Right Panel";
sourceTree = "<group>";
};
DDCB690F245DDAFD00489647 = {
isa = PBXGroup;
children = (
Expand All @@ -73,16 +96,12 @@
DDCB691B245DDAFD00489647 /* AppDelegate.swift */,
DDCB691F245DDB0000489647 /* Assets.xcassets */,
DDCB6925245DDB0000489647 /* Demo.entitlements */,
DDCAB9562464785700E5AA41 /* DetailViewController.swift */,
DDCAB952246475AB00E5AA41 /* MainViewController.swift */,
293D26EE2706F38B00333635 /* Left Panel */,
293D26EF2706F39A00333635 /* Right Panel */,
DD61CF5524717B1D00CA0BE9 /* DiffableDataSourceSnapshot+Additions.swift */,
DDCAB9582464796400E5AA41 /* EmptyViewController.swift */,
DDCB6924245DDB0000489647 /* Info.plist */,
DDCB6921245DDB0000489647 /* Main.storyboard */,
DDCAB952246475AB00E5AA41 /* MainViewController.swift */,
DD61CF572471882A00CA0BE9 /* MasterOutlineViewItem.swift */,
DDCAB9542464771600E5AA41 /* MasterViewController.swift */,
DDCAB95C24647BBF00E5AA41 /* MultiViewController.swift */,
DDCAB95A24647B7200E5AA41 /* SingleViewController.swift */,
);
path = Demo;
sourceTree = "<group>";
Expand Down Expand Up @@ -175,6 +194,7 @@
DDCAB95B24647B7200E5AA41 /* SingleViewController.swift in Sources */,
DD61CF5624717B1D00CA0BE9 /* DiffableDataSourceSnapshot+Additions.swift in Sources */,
DDCAB95D24647BBF00E5AA41 /* MultiViewController.swift in Sources */,
293D26F1270870A000333635 /* GroupOutlineViewItem.swift in Sources */,
DDCB691C245DDAFD00489647 /* AppDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
1 change: 0 additions & 1 deletion Demo/Demo/DetailViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ final class DetailViewController: NSViewController {
let viewController = NSTabViewController()
viewController.tabStyle = .unspecified
viewController.transitionOptions = []
viewController.view.wantsLayer = false
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need this change?

viewController.tabViewItems = [emptyViewController, singleViewController, multiViewController]
.map(NSTabViewItem.init(viewController:))
return viewController
Expand Down
81 changes: 48 additions & 33 deletions Demo/Demo/DiffableDataSourceSnapshot+Additions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,62 @@ import Foundation
import OutlineViewDiffableDataSource

extension DiffableDataSourceSnapshot {

/// Appends items from the text view.
mutating func fillItem(_ selectedItem: Item?, with string: String) {
let lines = string.components(separatedBy: .newlines)

for line in lines {
// Each line should have a "parent (/ child)?". Since this is a free-form text field, we need to find
// existing items in the side bar
let titles = line.components(separatedBy: "/").map { $0.trimmingCharacters(in: .whitespaces) }.filter { $0.isEmpty == false }

guard !titles.isEmpty else { continue }

// Use title as the unique ID in this case
let rootItem = getItemForID(titles[0]) ?? MasterGroupOutlineViewItem(id: titles[0], title: titles[0])
let sideBarItem = getItemForID(titles[0]) ?? MasterOutlineViewItem(id: titles[0], title: titles[0])

let foundRootItem = containsItem(rootItem)
let foundSidebarItem = containsItem(sideBarItem)

switch titles.count {
case 1:
let groupItem = GroupOutlineViewItem(id: titles[0], title: titles[0])
let masterItem = MasterOutlineViewItem(title: titles[0])
if containsItem(groupItem) == false, containsItem(masterItem) == false {
if selectedItem == nil {
appendItems([groupItem])
} else {
appendItems([masterItem], into: selectedItem)
case 1:
// No child specified
if foundRootItem == false, foundSidebarItem == false {
if let selectedItem = selectedItem {
appendItems([sideBarItem], into: selectedItem)
}
else {
appendItems([rootItem], into: nil)
}
}
}
case 2:
let parentGroupItem = GroupOutlineViewItem(id: titles[0], title: titles[0])
let parentMasterItem = MasterOutlineViewItem(title: titles[0])
var parentItem: NSObject?
if containsItem(parentGroupItem) == false, containsItem(parentMasterItem) == false {
if selectedItem == nil {
appendItems([parentGroupItem])
parentItem = parentGroupItem

} else {
appendItems([parentMasterItem], into: selectedItem)
parentItem = parentMasterItem
case 2:
let childItemToAdd = MasterOutlineViewItem(id: titles[1], title: titles[1])

if containsItem(childItemToAdd) == false {
// Parent / Child specified. Find
var parentItemToUse: OutlineViewItem?

if foundRootItem == false, foundSidebarItem == false {
if selectedItem == nil {
appendItems([rootItem])
parentItemToUse = rootItem

} else {
appendItems([sideBarItem], into: selectedItem)
parentItemToUse = sideBarItem
}
} else if foundRootItem {
parentItemToUse = rootItem
} else if foundSidebarItem {
parentItemToUse = sideBarItem
}

appendItems([childItemToAdd], into: parentItemToUse)
}
} else if containsItem(parentGroupItem) {
parentItem = parentGroupItem
} else if containsItem(parentMasterItem) {
parentItem = parentMasterItem
}
let childItem = MasterOutlineViewItem(title: titles[1])
if containsItem(childItem) == false {
appendItems([childItem], into: parentItem)
}
default:
continue
default:
continue
}
}
}
Expand Down
37 changes: 23 additions & 14 deletions Demo/Demo/EmptyViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import OutlineViewDiffableDataSource

/// The number of controls for an empty outline view selection.
final class EmptyViewController: NSViewController {

/// Multiline text editor for the outline contents.
private lazy var scrollableEditor: NSScrollView = {
let scrollView = NSTextView.scrollablePlainDocumentContentTextView()
Expand Down Expand Up @@ -56,17 +55,25 @@ extension EmptyViewController {

guard let textView = scrollableEditor.documentView as? NSTextView, textView.string.isEmpty else { return }
textView.string = """
Parent 1 / Child 11
Parent 1 / Child 12
Parent 1 / Child 13
Parent 2
Parent 2 / Child 21
Child 21 / Leaf 211
Child 21 / Leaf 212
Parent 3 / Child 31
Parent 3 / Child 32
Parent 3 / Child 33
Cars / Toyota
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice one 👍

Cars / Honda
Cars / Tesla
Phones
Phones / Samsung
Samsung / Samsung Note
Samsung / Samsung Nexus
Samsung Nexus / Nexus 5
OS / macOS
OS / Windows
OS / Linux
"""

// Parent 3
// Parent 3 / Child 31
// Child 31 / Child 33
// Child 33 / Child 32

fillSidebar(nil)
}
}

Expand All @@ -77,9 +84,11 @@ private extension EmptyViewController {
/// Replaces the whole tree with the given contents.
@IBAction func fillSidebar(_ sender: Any?) {
guard let textView = scrollableEditor.documentView as? NSTextView else { return }

// Create a new snapshot from entered Parent / Child items.
var snapshot: DiffableDataSourceSnapshot = .init()
snapshot.fillItem(nil, with: textView.string)
snapshotBinding.wrappedValue = snapshot
snapshotBinding.wrappedValue = snapshot
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check the whitespace leftover.

}

/// Replaces text with sidebar contents.
Expand All @@ -89,8 +98,8 @@ private extension EmptyViewController {
var items: [String] = []
snapshot.enumerateItems { item, parentItem in
items.append([
(parentItem as? GroupOutlineViewItem)?.title ?? (parentItem as? MasterOutlineViewItem)?.title,
(item as? GroupOutlineViewItem)?.title ?? (item as? MasterOutlineViewItem)?.title,
(parentItem as? MasterGroupOutlineViewItem)?.title ?? (parentItem as? MasterOutlineViewItem)?.title,
(item as? MasterGroupOutlineViewItem)?.title ?? (item as? MasterOutlineViewItem)?.title,
].compactMap { $0 }.joined(separator: " / "))
}
textView.string = items.joined(separator: "\n")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,37 +1,22 @@
import AppKit
import OutlineViewDiffableDataSource

/// Default root item with buttons ‘Show’ and ‘Hide’, not intended for subclassing.
public final class GroupOutlineViewItem: NSObject, OutlineViewItem {

/// Unique identifier for diffing.
public let id: String

public final class MasterGroupOutlineViewItem: GroupOutlineViewItem {
/// Display string.
public let title: String

/// Show as Group.
public let isGroup: Bool = true

/// Deny selection.
public let isSelectable: Bool = false

/// Creates a “standard” root item for the sidebar.
public init(id: String, title: String) {
self.id = id
self.title = title
super.init(id: id)
}

/// Returns an appropriate cell view type.
public func cellViewType(for tableColumn: NSTableColumn?) -> NSTableCellView.Type { GroupTableCellView.self }
public override func cellViewType(for tableColumn: NSTableColumn?) -> NSTableCellView.Type { GroupTableCellView.self }

/// Necessary for sets.
public override var hash: Int { title.hash }

/// Necessary for outline view reloading.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please explain why this isn’t necessary anymore?

public override func isEqual(_ object: Any?) -> Bool {
guard let groupItem = object as? GroupOutlineViewItem else { return false }
return groupItem.id == id
}
}

// MARK: - Private API
Expand Down Expand Up @@ -86,7 +71,7 @@ private final class GroupTableCellView: NSTableCellView {
/// Retrieves new title from the associated group item.
override var objectValue: Any? {
didSet {
if let label = textField, let groupItem = objectValue as? GroupOutlineViewItem {
if let label = textField, let groupItem = objectValue as? MasterGroupOutlineViewItem {
label.stringValue = groupItem.title
}
}
Expand Down
20 changes: 8 additions & 12 deletions Demo/Demo/MasterOutlineViewItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,21 @@ import AppKit
import OutlineViewDiffableDataSource

/// Sidebar iitems.
class MasterOutlineViewItem: NSObject, OutlineViewItem {

class MasterOutlineViewItem: OutlineViewItem {
/// Visible string.
let title: String

/// Creates a new item ready for insertion into the sidebar.
init(title: String) { self.title = title }

init(id: String, title: String) {
self.title = title
super.init(id: id)
}

/// Returns a private cell view type.
func cellViewType(for tableColumn: NSTableColumn?) -> NSTableCellView.Type { MasterCellView.self }
override func cellViewType(for tableColumn: NSTableColumn?) -> NSTableCellView.Type { MasterCellView.self }

/// Necessary for sets.
/// Necessary for supporting drag-n-drop and expand-collapse.
override var hash: Int { title.hash }

/// Necessary for outline view reloading.
override func isEqual(_ object: Any?) -> Bool {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

guard let masterItem = object as? MasterOutlineViewItem else { return false }
return masterItem.title == title
}
}

// MARK: - Private API
Expand Down
Loading