├── .gitignore
├── .spi.yml
├── .swiftpm
└── xcode
│ ├── package.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ └── florianzand.xcuserdatad
│ │ └── IDEFindNavigatorScopes.plist
│ └── xcuserdata
│ └── florianzand.xcuserdatad
│ ├── xcdebugger
│ └── Breakpoints_v2.xcbkptlist
│ └── xcschemes
│ └── xcschememanagement.plist
├── Example
├── Example.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── swiftpm
│ │ │ └── Package.resolved
│ └── xcuserdata
│ │ └── florianzand.xcuserdatad
│ │ ├── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
└── Example
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AccentColor.colorset
│ │ └── Contents.json
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
│ ├── Base.lproj
│ └── Main.storyboard
│ ├── Example.entitlements
│ ├── MainViewController.swift
│ ├── Model
│ ├── CollectionItem.swift
│ ├── OutlineItem.swift
│ ├── Section.swift
│ └── SidebarItem.swift
│ ├── OutlineSidebarViewController.swift
│ ├── Sample Images
│ ├── About Blank.png
│ ├── Berghain.png
│ ├── Cosmojelly.png
│ ├── Dystopian City.png
│ ├── Fireworker Monkey.png
│ ├── Majestic Mouser.png
│ ├── Neil Catstrong.png
│ ├── Oxi.png
│ ├── Plasmawhale.png
│ ├── Tresor.png
│ ├── Underground.png
│ └── Vapor Cat.png
│ ├── SplitViewController.swift
│ └── TableSidebarViewController.swift
├── LICENSE
├── Package.swift
├── README.md
└── Sources
└── AdvancedCollectionTableView
├── Configuration
├── Configurations
│ ├── NSItemContentConfiguration
│ │ ├── NSItemContentConfiguration+Badge.swift
│ │ ├── NSItemContentConfiguration+Content.swift
│ │ ├── NSItemContentConfiguration+Image.swift
│ │ ├── NSItemContentConfiguration.swift
│ │ ├── NSItemContentView+Badge.swift
│ │ ├── NSItemContentView+Content.swift
│ │ ├── NSItemContentView.swift
│ │ └── unused
│ │ │ ├── NSItemContentConfiguration+Transition.swift
│ │ │ ├── NSItemContentView+Badge_old.swift
│ │ │ └── NSItemContentViewSwiftUI.swift
│ ├── NSListContentConfiguration
│ │ ├── Accessory
│ │ │ ├── Accessories
│ │ │ │ ├── TextAccessory.swift
│ │ │ │ └── TextStackAccessory.swift
│ │ │ ├── NSListContentView+Accessory+Image.swift
│ │ │ ├── NSListContentView+Accessory.swift
│ │ │ └── NSListContentView+AccessoryView.swift
│ │ ├── NSListContentConfiguration+Badge.swift
│ │ ├── NSListContentConfiguration+Image.swift
│ │ ├── NSListContentConfiguration.swift
│ │ ├── NSListContentView+Badge.swift
│ │ ├── NSListContentView+ImageView.swift
│ │ └── NSListContentView.swift
│ └── Shared
│ │ ├── ListItemTextField.swift
│ │ ├── Protocols.swift
│ │ ├── TextProperties.swift
│ │ └── TextStackView.swift
├── Extensions
│ ├── NSCollectionView+
│ │ ├── NSCollectionView+.swift
│ │ └── NSCollectionViewItem+.swift
│ ├── NSTableView+
│ │ ├── NSTableCellVew+.swift
│ │ ├── NSTableRowView+.swift
│ │ └── NSTableView+.swift
│ └── Shared
│ │ └── TableCollectionObserverView.swift
└── States
│ ├── ConfigurationState.swift
│ ├── NSItemConfigurationState.swift
│ └── NSListConfigurationState.swift
├── DiffableDataSource
├── NSCollectionView
│ ├── CollectionViewDiffableDataSource+Delegate.swift
│ └── CollectionViewDiffableDataSource.swift
├── NSOutlineView
│ ├── unused
│ │ ├── CellOutlineVItem.swift
│ │ └── OutlineViewDiffableDataSourceSnapshot+OutlineItem.swift
│ ├── OutlineViewDiffableDataSource+Delegate.swift
│ ├── OutlineViewDiffableDataSource.swift
│ └── Snapshot
│ │ ├── OutlineChangeInstruction.swift
│ │ ├── OutlineViewDiffableDataSourceSnapshot+Node.swift
│ │ ├── OutlineViewDiffableDataSourceSnapshot.swift
│ │ └── OutlineViewDiffableDataSourceTransaction.swift
├── NSTableView
│ ├── TableViewDiffableDataSource+Delegate.swift
│ └── TableViewDiffableDataSource.swift
└── Shared
│ ├── DiffableDataSourceTransaction.swift
│ ├── EmptyView.swift
│ └── QuicklookPreviewItem.swift
├── Documentation
└── AdvancedCollectionTableView.docc
│ ├── AdvancedCollectionTableView.md
│ ├── Docs
│ ├── Configurating Collection Items.md
│ └── Configurating Table Cells.md
│ ├── Extensions
│ ├── AppKit Extensions
│ │ ├── NSCollecionView
│ │ │ ├── NSCollectionView+.md
│ │ │ ├── NSCollectionViewDiffableDataSource+.md
│ │ │ ├── NSCollectionViewDiffableDataSource+DeletingHandlers.md
│ │ │ ├── NSCollectionViewItem+.md
│ │ │ └── NSCollectionViewSupplementaryRegistration.md
│ │ └── NSTableView
│ │ │ ├── NSTableCellView+.md
│ │ │ ├── NSTableRowView+.md
│ │ │ ├── NSTableView+.md
│ │ │ ├── NSTableViewCellRegistration.md
│ │ │ ├── NSTableViewDiffableDataSource+.md
│ │ │ └── NSTableViewDiffableDataSource+DeletingHandlers.md
│ ├── Configuration
│ │ ├── Configurations
│ │ │ ├── ItemContentConfiguration
│ │ │ │ ├── NSItemContentConfiguration+Badge.md
│ │ │ │ ├── NSItemContentConfiguration+Content.md
│ │ │ │ ├── NSItemContentConfiguration+ImageProperties.md
│ │ │ │ ├── NSItemContentConfiguration.md
│ │ │ │ └── NSItemContentView.md
│ │ │ ├── ListContentConfiguration
│ │ │ │ ├── NSListContentConfiguration+Badge.md
│ │ │ │ ├── NSListContentConfiguration+Image.md
│ │ │ │ ├── NSListContentConfiguration.md
│ │ │ │ └── NSListContentView.md
│ │ │ └── Shared
│ │ │ │ └── TextProperties.md
│ │ └── States
│ │ │ ├── NSItemConfigurationState.md
│ │ │ └── NSListConfigurationState.md
│ ├── DiffableDataSource
│ │ ├── CollectionViewDiffableData
│ │ │ ├── CollectionViewDiffableDataSource+DeletingHandlers.md
│ │ │ ├── CollectionViewDiffableDataSource+DisplayHandlers.md
│ │ │ ├── CollectionViewDiffableDataSource+DragDropHandlers.md
│ │ │ ├── CollectionViewDiffableDataSource+HighlightHandlers.md
│ │ │ ├── CollectionViewDiffableDataSource+HoverHandlers.md
│ │ │ ├── CollectionViewDiffableDataSource+PrefetchHandlers.md
│ │ │ ├── CollectionViewDiffableDataSource+ReorderingHandlers.md
│ │ │ ├── CollectionViewDiffableDataSource+SelectionHandlers.md
│ │ │ ├── CollectionViewDiffableDataSource-Protocol-Implementations.md
│ │ │ └── CollectionViewDiffableDataSource.md
│ │ ├── OutlineViewDiffableDataSource
│ │ │ ├── OutlineNode.md
│ │ │ ├── OutlineViewDiffableDataSource+ColumnHandlers.md
│ │ │ ├── OutlineViewDiffableDataSource+DeletingHandlers.md
│ │ │ ├── OutlineViewDiffableDataSource+ExpansionHandlers.md
│ │ │ ├── OutlineViewDiffableDataSource+HoverHandlers.md
│ │ │ ├── OutlineViewDiffableDataSource+ReorderingHandlers.md
│ │ │ ├── OutlineViewDiffableDataSource+SelectionHandlers.md
│ │ │ ├── OutlineViewDiffableDataSource-Protocol-Implementations.md
│ │ │ ├── OutlineViewDiffableDataSource.md
│ │ │ ├── OutlineViewDiffableDataSourceSnapshot.md
│ │ │ └── OutlineViewDiffableDataSourceTransaction.md
│ │ ├── Shared
│ │ │ ├── NSDiffableDataSourceSnapshotApplyOption.md
│ │ │ └── NSDiffableDataSourceTransaction.md
│ │ └── TableViewDiffableDataSource
│ │ │ ├── TableViewDiffableDataSource+ColumnHandlers.md
│ │ │ ├── TableViewDiffableDataSource+DeletingHandlers.md
│ │ │ ├── TableViewDiffableDataSource+DragDropHandlers.md
│ │ │ ├── TableViewDiffableDataSource+HoverHandlers.md
│ │ │ ├── TableViewDiffableDataSource+ReorderingHandlers.md
│ │ │ ├── TableViewDiffableDataSource+SelectionHandlers.md
│ │ │ ├── TableViewDiffableDataSource-Protocol Implementations.md
│ │ │ └── TableViewDiffableDataSource.md
│ └── Registration
│ │ ├── NSCollectionView
│ │ ├── ItemRegistration.md
│ │ └── SupplementaryRegistration.md
│ │ └── NSTableView
│ │ ├── CellRegistration.md
│ │ ├── NSTableSectionHeaderView.md
│ │ └── RowViewRegistration.md
│ └── Resources
│ ├── Extension.svg
│ ├── NSItemContentConfiguration.png
│ └── NSListContentConfiguration.png
├── Extensions
├── NSCollectionView
│ ├── NSCollectionView+DragSessionMove.swift
│ ├── NSCollectionView+ReconfigureItem.swift
│ ├── NSCollectionView+Register.swift
│ ├── NSCollectionViewDiffableDataSource+.swift
│ ├── NSCollectionViewDiffableDataSource+Apply.swift
│ ├── NSCollectionViewDiffableDataSource+Delete.swift
│ ├── NSCollectionViewDiffableDataSource+Registration.swift
│ └── unused
│ │ └── NSCollectionView+SelfSizing.swift
├── NSTableView
│ ├── NSTableView+DragSessionMove.swift
│ ├── NSTableView+ReconfigureRows.swift
│ ├── NSTableView+Register.swift
│ ├── NSTableViewDiffableDataSource+.swift
│ ├── NSTableViewDiffableDataSource+Apply.swift
│ ├── NSTableViewDiffableDataSource+Delete.swift
│ └── NSTableViewDiffableDataSource+Registration.swift
└── Shared
│ ├── NSDiffableDataSourceSnapshot+.swift
│ ├── NSDiffableDataSourceSnapshot+ApplyOption.swift
│ ├── NSPasteboardItem+.swift
│ └── NSView+Transform.swift
└── Registrations
├── NSCollectionView
├── ItemRegistration.swift
└── SupplementaryRegistration.swift
└── NSTableView
├── CellRegistration.swift
└── RowRegistration.swift
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | *.resolved
3 | **/.DS_Store
4 | .swiftpm/xcode/xcuserdata/florianzand.xcuserdatad/xcschemes/xcschememanagement.plist
5 | *.xcuserstate
6 | *.resolved
7 | *.resolved
8 |
--------------------------------------------------------------------------------
/.spi.yml:
--------------------------------------------------------------------------------
1 | version: 1
2 | builder:
3 | configs:
4 | - documentation_targets: [AdvancedCollectionTableView]
5 | custom_documentation_parameters: [--include-extended-types]
6 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/xcuserdata/florianzand.xcuserdatad/IDEFindNavigatorScopes.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/xcuserdata/florianzand.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/xcuserdata/florianzand.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | AdvancedCollectionTableView-Package.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 1
11 |
12 | AdvancedCollectionTableView.xcscheme_^#shared#^_
13 |
14 | orderHint
15 | 0
16 |
17 | AdvancedCollectionTableViewInterpose-Package.xcscheme_^#shared#^_
18 |
19 | orderHint
20 | 0
21 |
22 | AdvancedCollectionTableViewInterpose.xcscheme_^#shared#^_
23 |
24 | orderHint
25 | 1
26 |
27 | AdvancedCollectionTableViewObjC.xcscheme_^#shared#^_
28 |
29 | orderHint
30 | 2
31 |
32 |
33 | SuppressBuildableAutocreation
34 |
35 | AdvancedCollectionTableView
36 |
37 | primary
38 |
39 |
40 | AdvancedCollectionTableViewObjC
41 |
42 | primary
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "originHash" : "9a3fc9d4e8c660ad4f02d3fe7cb14be2d4a8737c02d84fc43a36ece1c84e8b31",
3 | "pins" : [
4 | {
5 | "identity" : "fzquicklook",
6 | "kind" : "remoteSourceControl",
7 | "location" : "https://github.com/flocked/FZQuicklook.git",
8 | "state" : {
9 | "branch" : "main",
10 | "revision" : "835a344797c7b7bf4dbf98d77dd60a7cb6d4c7cf"
11 | }
12 | },
13 | {
14 | "identity" : "fzswiftutils",
15 | "kind" : "remoteSourceControl",
16 | "location" : "https://github.com/flocked/FZSwiftUtils.git",
17 | "state" : {
18 | "branch" : "main",
19 | "revision" : "5af861b31f0128f4edf2bb4ae5e8868345631f71"
20 | }
21 | },
22 | {
23 | "identity" : "fzuikit",
24 | "kind" : "remoteSourceControl",
25 | "location" : "https://github.com/flocked/FZUIKit.git",
26 | "state" : {
27 | "branch" : "main",
28 | "revision" : "2524cf36f5a2174a3ae51ce413b940a7d040961c"
29 | }
30 | }
31 | ],
32 | "version" : 3
33 | }
34 |
--------------------------------------------------------------------------------
/Example/Example.xcodeproj/xcuserdata/florianzand.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/Example/Example.xcodeproj/xcuserdata/florianzand.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | CollectionViewSidebarTemplate.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 | CollectionViewTemplate.xcscheme_^#shared#^_
13 |
14 | orderHint
15 | 0
16 |
17 | Example.xcscheme_^#shared#^_
18 |
19 | orderHint
20 | 0
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Example/Example/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // CollectionViewTemplate
4 | //
5 | // Created by Florian Zand on 19.01.23.
6 | //
7 |
8 | import Cocoa
9 |
10 | @main
11 | class AppDelegate: NSObject, NSApplicationDelegate {
12 | func applicationDidFinishLaunching(_: Notification) {
13 | // Insert code here to initialize your application
14 | }
15 |
16 | func applicationWillTerminate(_: Notification) {
17 | // Insert code here to tear down your application
18 | }
19 |
20 | func applicationSupportsSecureRestorableState(_: NSApplication) -> Bool {
21 | true
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Example/Example/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Example/Example/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "scale" : "1x",
6 | "size" : "16x16"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "scale" : "2x",
11 | "size" : "16x16"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "scale" : "1x",
16 | "size" : "32x32"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "scale" : "2x",
21 | "size" : "32x32"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "scale" : "1x",
26 | "size" : "128x128"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "scale" : "2x",
31 | "size" : "128x128"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "scale" : "1x",
36 | "size" : "256x256"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "scale" : "2x",
41 | "size" : "256x256"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "scale" : "1x",
46 | "size" : "512x512"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "scale" : "2x",
51 | "size" : "512x512"
52 | }
53 | ],
54 | "info" : {
55 | "author" : "xcode",
56 | "version" : 1
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Example/Example/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/Example/Example.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Example/Example/MainViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MainViewController.swift
3 | //
4 | //
5 | // Created by Florian Zand on 19.01.23.
6 | //
7 |
8 | import AppKit
9 | import AdvancedCollectionTableView
10 |
11 | class MainViewController: NSViewController {
12 | typealias DataSource = CollectionViewDiffableDataSource
13 | typealias ItemRegistration = NSCollectionView.ItemRegistration
14 |
15 | @IBOutlet var collectionView: NSCollectionView!
16 |
17 | var galleryItems = GalleryItem.sampleItems
18 |
19 | lazy var dataSource = DataSource(collectionView: collectionView, itemRegistration: itemRegistration)
20 |
21 | let itemRegistration = ItemRegistration() { collectionViewItem, _, galleryItem in
22 | /// Configurate the item
23 | var configuration = NSItemContentConfiguration()
24 | configuration.text = galleryItem.title
25 | configuration.secondaryText = galleryItem.detail
26 | configuration.image = NSImage(named: galleryItem.title)
27 | configuration.contentProperties.shadow = .black(opacity: 0.5, radius: 5.0)
28 | if let badgeText = galleryItem.badgeText {
29 | configuration.badges = [.text(badgeText, color: galleryItem.badgeColor, type: .attachment)]
30 | }
31 |
32 | if galleryItem.isFavorite {
33 | configuration.badges = [.text("", color: .systemRed, shape: .circle, type: .attachment)]
34 | }
35 |
36 | /// Apply the configuration
37 | collectionViewItem.contentConfiguration = configuration
38 |
39 | /// Gets called when the item state changes (on selection, mouse hover, etc.)
40 | collectionViewItem.configurationUpdateHandler = { item, state in
41 | /// Updates the configuration based on whether the mouse is hovering the item
42 | configuration.contentProperties.scaleTransform = state.isHovered ? 1.03 : 1.0
43 | configuration.overlayView = state.isHovered ? NSView(color: .white, opacity: 0.25) : nil
44 |
45 | /// Apply the updated configuration animated.
46 | item.contentConfiguration = configuration
47 | }
48 | }
49 |
50 | override func viewDidLoad() {
51 | super.viewDidLoad()
52 |
53 | collectionView.collectionViewLayout = .grid(columns: 3)
54 | collectionView.dataSource = dataSource
55 |
56 | /// Enables deleting selected items via backspace key.
57 | dataSource.deletingHandlers.canDelete = { selectedItems in return selectedItems }
58 | dataSource.deletingHandlers.didDelete = { deletedItems, _ in
59 | self.galleryItems.remove(deletedItems)
60 | }
61 |
62 | /// Enables reordering selected items by dragging them.
63 | dataSource.reorderingHandlers.canReorder = { selectedItems in return true }
64 | dataSource.reorderingHandlers.didReorder = { transaction in
65 | self.galleryItems = self.galleryItems.applying(transaction.difference)!
66 | }
67 |
68 | dataSource.rightClickHandler = { selectedItems in
69 | selectedItems.forEach({ item in
70 | item.isFavorite = !item.isFavorite
71 | })
72 | /// Reconfigurates items without reloading them by calling the item registration handler.
73 | self.dataSource.reconfigureElements(selectedItems)
74 | }
75 |
76 | applySnapshot(using: galleryItems)
77 |
78 | dataSource.selectElements([galleryItems.first!], scrollPosition: .top)
79 | collectionView.makeFirstResponder()
80 | }
81 |
82 | func applySnapshot(using items: [GalleryItem]) {
83 | var snapshot = dataSource.emptySnapshot()
84 | snapshot.appendSections([.main])
85 | snapshot.appendItems(items, toSection: .main)
86 | dataSource.apply(snapshot, .withoutAnimation)
87 | }
88 | }
89 |
90 | private extension NSView {
91 | /// Creates a colored view.
92 | convenience init(color: NSColor, opacity: CGFloat) {
93 | self.init(frame: .zero)
94 | backgroundColor = color
95 | alphaValue = opacity
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/Example/Example/Model/CollectionItem.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CollectionItem.swift
3 | //
4 | //
5 | // Created by Florian Zand on 24.01.23.
6 | //
7 |
8 | import AppKit
9 | import FZQuicklook
10 |
11 | public class GalleryItem: NSObject, Identifiable {
12 |
13 | public let title: String
14 | public let detail: String
15 | public let badgeText: String?
16 | public let badgeColor: NSColor
17 | public var isFavorite: Bool
18 |
19 | public init(_ title: String, detail: String, badgeText: String? = nil, badgeColor: NSColor = .controlAccentColor, isFavorite: Bool = false) {
20 | self.title = title
21 | self.detail = detail
22 | self.badgeText = badgeText
23 | self.badgeColor = badgeColor
24 | self.isFavorite = isFavorite
25 | }
26 |
27 | static let sampleItems = [GalleryItem("Neil Catstrong", detail: "Liquid Ink"),
28 | GalleryItem("Majestic Mouser", detail: "Painted by Vermeer"),
29 | GalleryItem("Vapor Cat", detail: "Vaporwave", badgeText: "new"),
30 | GalleryItem("Cosmojelly", detail: "Oil on Canvas"),
31 | GalleryItem("Plasmawhale", detail: "Science Fiction", badgeText: "favorite", badgeColor: .systemPurple),
32 | GalleryItem("Berghain", detail: "Surrealist Painting"),
33 | GalleryItem("About Blank", detail: "Oil Painting"),
34 | GalleryItem("Fireworker Monkey", detail: "Japanese Manga"),
35 | GalleryItem("Dystopian City", detail: "Oil Painting", isFavorite: true),
36 | GalleryItem("Underground", detail: "Oil on Canvas"),
37 | GalleryItem("Tresor", detail: "Painting", isFavorite: true),
38 | GalleryItem("Oxi", detail: "Oil on Canvas")]
39 | }
40 |
41 | /// By conforming to `QuicklookPreviewable` and providing `previewItemURL` the items can be previewed by pressing spacebar which opens a Quicklook panel (simliar to Finder).
42 | extension GalleryItem: QuicklookPreviewable {
43 | public var previewItemURL: URL? {
44 | Bundle.main.urlForImageResource(title)
45 | }
46 |
47 | public var previewItemTitle: String? {
48 | title
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Example/Example/Model/OutlineItem.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OutlineItem.swift
3 | // OutlineTest
4 | //
5 | // Created by Florian Zand on 19.01.25.
6 | //
7 |
8 | import Foundation
9 |
10 | public struct OutlineItem: Hashable, Identifiable, ExpressibleByStringLiteral, CustomStringConvertible {
11 |
12 | public let title: String
13 |
14 | public init(_ title: String) {
15 | self.title = title
16 | }
17 |
18 | public init(stringLiteral value: String) {
19 | self.title = value
20 | }
21 |
22 | public var description: String {
23 | title
24 | }
25 |
26 | public var id: String {
27 | title
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Example/Example/Model/Section.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Section.swift
3 | //
4 | //
5 | // Created by Florian Zand on 22.06.23.
6 | //
7 |
8 | import Foundation
9 |
10 | public enum Section: String, Hashable, Identifiable {
11 | case main = "Main"
12 | case section2 = "Section 2"
13 | case section3 = "Section 3"
14 |
15 | public var title: String {
16 | rawValue
17 | }
18 |
19 | public var id: String {
20 | rawValue
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Example/Example/Model/SidebarItem.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SidebarItem.swift
3 | //
4 | //
5 | // Created by Florian Zand on 22.06.23.
6 | //
7 |
8 | import Foundation
9 |
10 | public class SidebarItem: NSObject, Identifiable {
11 |
12 | public let title: String
13 | public let symbolName: String
14 | public var isFavorite: Bool = false
15 |
16 | public init(_ title: String, symbolName: String) {
17 | self.title = title
18 | self.symbolName = symbolName
19 | }
20 |
21 | static let sampleItems1 = [SidebarItem("Messages", symbolName: "message.fill"),
22 | SidebarItem("Photos", symbolName: "photo"),
23 | SidebarItem("Videos", symbolName: "film")]
24 | static let sampleItems2 = [SidebarItem("Archive", symbolName: "tray.full")]
25 | static let sampleItems3 = [SidebarItem("News", symbolName: "newspaper")]
26 | }
27 |
--------------------------------------------------------------------------------
/Example/Example/OutlineSidebarViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OutlineSidebarViewController.swift
3 | //
4 | //
5 | // Created by Florian Zand on 19.01.23.
6 | //
7 |
8 | import AppKit
9 | import AdvancedCollectionTableView
10 |
11 | class OutlineSidebarViewController: NSViewController {
12 | typealias DataSource = OutlineViewDiffableDataSource
13 | typealias CellRegistration = NSTableView.CellRegistration
14 |
15 | @IBOutlet var outlineView: NSOutlineView!
16 |
17 | lazy var dataSource = DataSource(outlineView: outlineView, cellRegistration: cellRegistration)
18 |
19 | let cellRegistration = CellRegistration { tableCell, _, _, outlineItem in
20 | var configuration = tableCell.defaultContentConfiguration()
21 | configuration.text = outlineItem.title
22 | tableCell.contentConfiguration = configuration
23 | }
24 |
25 | override func viewDidLoad() {
26 | super.viewDidLoad()
27 |
28 | outlineView.dataSource = dataSource
29 | dataSource.applyGroupItemCellRegistration(cellRegistration)
30 |
31 | /// Enables reordering selected rows by dragging them.
32 | dataSource.reorderingHandlers.canReorder = { _,_ in return true }
33 |
34 | /// Enables deleting selected items via backspace key.
35 | dataSource.deletingHandlers.canDelete = { items in return items }
36 |
37 | applySnapshot()
38 | }
39 |
40 | func applySnapshot() {
41 | var snapshot = dataSource.emptySnapshot()
42 | let rootItems: [OutlineItem] = ["Root 1", "Root 2", "Root 3", "Root 4", "Root 5"]
43 | snapshot.append(rootItems)
44 | rootItems.forEach { rootItem in
45 | let childItems = (1...5).map { OutlineItem("\(rootItem.title).\($0)") }
46 | snapshot.append(childItems, to: rootItem)
47 | childItems.forEach { childItem in
48 | let grandchildItems = (1...5).map { OutlineItem("\(childItem.title).\($0)") }
49 | snapshot.append(grandchildItems, to: childItem)
50 | }
51 | }
52 | dataSource.apply(snapshot, .withoutAnimation)
53 | dataSource.expand(rootItems)
54 | }
55 |
56 | @IBAction func segmentedPressed(_ segmentedControl: NSSegmentedControl) {
57 | (view.window?.contentViewController as? SplitViewController)?.swapSidebar()
58 | segmentedControl.selectedSegment = 1
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Example/Example/Sample Images/About Blank.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flocked/AdvancedCollectionTableView/094735ce186ccf19d26eb50cd0eb0d72fa44bcce/Example/Example/Sample Images/About Blank.png
--------------------------------------------------------------------------------
/Example/Example/Sample Images/Berghain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flocked/AdvancedCollectionTableView/094735ce186ccf19d26eb50cd0eb0d72fa44bcce/Example/Example/Sample Images/Berghain.png
--------------------------------------------------------------------------------
/Example/Example/Sample Images/Cosmojelly.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flocked/AdvancedCollectionTableView/094735ce186ccf19d26eb50cd0eb0d72fa44bcce/Example/Example/Sample Images/Cosmojelly.png
--------------------------------------------------------------------------------
/Example/Example/Sample Images/Dystopian City.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flocked/AdvancedCollectionTableView/094735ce186ccf19d26eb50cd0eb0d72fa44bcce/Example/Example/Sample Images/Dystopian City.png
--------------------------------------------------------------------------------
/Example/Example/Sample Images/Fireworker Monkey.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flocked/AdvancedCollectionTableView/094735ce186ccf19d26eb50cd0eb0d72fa44bcce/Example/Example/Sample Images/Fireworker Monkey.png
--------------------------------------------------------------------------------
/Example/Example/Sample Images/Majestic Mouser.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flocked/AdvancedCollectionTableView/094735ce186ccf19d26eb50cd0eb0d72fa44bcce/Example/Example/Sample Images/Majestic Mouser.png
--------------------------------------------------------------------------------
/Example/Example/Sample Images/Neil Catstrong.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flocked/AdvancedCollectionTableView/094735ce186ccf19d26eb50cd0eb0d72fa44bcce/Example/Example/Sample Images/Neil Catstrong.png
--------------------------------------------------------------------------------
/Example/Example/Sample Images/Oxi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flocked/AdvancedCollectionTableView/094735ce186ccf19d26eb50cd0eb0d72fa44bcce/Example/Example/Sample Images/Oxi.png
--------------------------------------------------------------------------------
/Example/Example/Sample Images/Plasmawhale.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flocked/AdvancedCollectionTableView/094735ce186ccf19d26eb50cd0eb0d72fa44bcce/Example/Example/Sample Images/Plasmawhale.png
--------------------------------------------------------------------------------
/Example/Example/Sample Images/Tresor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flocked/AdvancedCollectionTableView/094735ce186ccf19d26eb50cd0eb0d72fa44bcce/Example/Example/Sample Images/Tresor.png
--------------------------------------------------------------------------------
/Example/Example/Sample Images/Underground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flocked/AdvancedCollectionTableView/094735ce186ccf19d26eb50cd0eb0d72fa44bcce/Example/Example/Sample Images/Underground.png
--------------------------------------------------------------------------------
/Example/Example/Sample Images/Vapor Cat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flocked/AdvancedCollectionTableView/094735ce186ccf19d26eb50cd0eb0d72fa44bcce/Example/Example/Sample Images/Vapor Cat.png
--------------------------------------------------------------------------------
/Example/Example/SplitViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SplitViewController.swift
3 | //
4 | //
5 | // Created by Florian Zand on 20.01.25.
6 | //
7 |
8 | import Cocoa
9 |
10 | class SplitViewController: NSSplitViewController {
11 | func swapSidebar() {
12 | splitViewItems[0].isCollapsed = true
13 | splitViewItems.swapAt(0, 2)
14 | splitViewItems[0].isCollapsed = false
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Example/Example/TableSidebarViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TableSidebarViewController.swift
3 | //
4 | //
5 | // Created by Florian Zand on 19.01.23.
6 | //
7 |
8 | import AppKit
9 | import AdvancedCollectionTableView
10 |
11 | class TableSidebarViewController: NSViewController {
12 | typealias DataSource = TableViewDiffableDataSource
13 | typealias CellRegistration = NSTableView.CellRegistration
14 | typealias SectionHeaderRegistration = NSTableView.CellRegistration
15 |
16 | @IBOutlet var tableView: NSTableView!
17 |
18 | lazy var dataSource = DataSource(tableView: tableView, cellRegistration: cellRegistration)
19 |
20 | let cellRegistration = CellRegistration { tableCell, _, _, sidebarItem in
21 | /// `defaultContentConfiguration` returns a table cell content configuration with default styling based on the table view it's displayed at (in this case a sidebar table).
22 | var configuration = tableCell.defaultContentConfiguration()
23 | configuration.text = sidebarItem.title
24 | configuration.image = NSImage(systemSymbolName: sidebarItem.symbolName)
25 | if sidebarItem.isFavorite {
26 | configuration.badge = .symbolImage("star.fill", color: .systemYellow, backgroundColor: nil)
27 | }
28 | tableCell.contentConfiguration = configuration
29 | }
30 |
31 | let sectionHeaderRegistration = SectionHeaderRegistration { sectionHeaderCell, _, _, section in
32 | var configuration = sectionHeaderCell.defaultContentConfiguration()
33 | configuration.text = section.title
34 | sectionHeaderCell.contentConfiguration = configuration
35 | }
36 |
37 | override func viewDidLoad() {
38 | super.viewDidLoad()
39 |
40 | tableView.dataSource = dataSource
41 | dataSource.applySectionHeaderRegistration(sectionHeaderRegistration)
42 |
43 | /// Enables reordering selected rows by dragging them.
44 | dataSource.reorderingHandlers.canReorder = { selectedItems in return selectedItems }
45 |
46 | /// Enables deleting selected rows via backspace key.
47 | dataSource.deletingHandlers.canDelete = { selectedItems in return selectedItems }
48 |
49 | /// Enable dropping strings to the table view by checking if the drop contains strings.
50 | dataSource.droppingHandlers.canDrop = { drop in
51 | return !drop.content.strings.isEmpty
52 | }
53 |
54 | /// Provides sidebar items for the dropped strings.
55 | dataSource.droppingHandlers.items = { drop in
56 | return drop.content.strings.compactMap({ SidebarItem($0, symbolName: "photo") })
57 | }
58 |
59 | /// Swipe row actions for deleting and favoriting an item.
60 | dataSource.rowActionProvider = { swippedItem, edge in
61 | if edge == .leading {
62 | /// Left swipe
63 | return [NSTableViewRowAction.regular(symbolName: swippedItem.isFavorite ? "star" : "star.fill", color: .systemYellow) { _,_ in
64 | swippedItem.isFavorite = !swippedItem.isFavorite
65 | self.dataSource.reconfigureItems([swippedItem])
66 | self.tableView.rowActionsVisible = false
67 | }]
68 | } else {
69 | /// Right swipe
70 | return [NSTableViewRowAction.destructive(symbolName: "trash.fill") { _,_ in
71 | var snapshot = self.dataSource.snapshot()
72 | snapshot.deleteItems([swippedItem])
73 | self.dataSource.apply(snapshot)
74 | }]
75 | }
76 | }
77 |
78 | applySnapshot()
79 | }
80 |
81 | func applySnapshot() {
82 | var snapshot = dataSource.emptySnapshot()
83 | snapshot.appendSections([.main, .section2, .section3])
84 | snapshot.appendItems(SidebarItem.sampleItems1, toSection: .main)
85 | snapshot.appendItems(SidebarItem.sampleItems2, toSection: .section2)
86 | snapshot.appendItems(SidebarItem.sampleItems3, toSection: .section3)
87 | dataSource.apply(snapshot, .usingReloadData)
88 | }
89 |
90 | @IBAction func segmentedPressed(_ segmentedControl: NSSegmentedControl) {
91 | (view.window?.contentViewController as? SplitViewController)?.swapSidebar()
92 | segmentedControl.selectedSegment = 0
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Florian Zand
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version: 5.5
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "AdvancedCollectionTableView",
8 | platforms: [
9 | .macOS("12.0"),
10 | ],
11 | products: [
12 | .library(
13 | name: "AdvancedCollectionTableView",
14 | targets: ["AdvancedCollectionTableView"]
15 | ),
16 | ],
17 | dependencies: [
18 | .package(url: "https://github.com/flocked/FZSwiftUtils.git", branch: "main"),
19 | .package(url: "https://github.com/flocked/FZUIKit.git", branch: "main"),
20 | .package(url: "https://github.com/flocked/FZQuicklook.git", branch: "main"),
21 | ],
22 | targets: [
23 | .target(
24 | name: "AdvancedCollectionTableView",
25 | dependencies: ["FZSwiftUtils", "FZUIKit", "FZQuicklook"], path: "Sources/AdvancedCollectionTableView"
26 | ),
27 | ]
28 | )
29 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Configuration/Configurations/NSItemContentConfiguration/NSItemContentConfiguration+Content.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSItemContentConfiguration+Content.swift
3 | //
4 | //
5 | // Created by Florian Zand on 07.08.23.
6 | //
7 |
8 | import AppKit
9 | import FZSwiftUtils
10 | import FZUIKit
11 | import SwiftUI
12 |
13 | public extension NSItemContentConfiguration {
14 | /// Properties that affect the content that displays the image and view.
15 | struct ContentProperties: Hashable {
16 | /// The corner radius of the content.
17 | public var cornerRadius: CGFloat = 10.0
18 |
19 | /// The maximum size of the content.
20 | public var maximumSize = ProposedSize()
21 |
22 | public struct ProposedSize: Hashable {
23 | /// The proposed width.
24 | public var width: CGFloat?
25 | /// The proposed height.
26 | public var height: CGFloat?
27 | /// The proposed Sizing mode. The default value is `absolute`.
28 | public var mode: Mode = .absolute
29 |
30 | /// Proposed Sizing mode.
31 | public enum Mode: Int, Hashable {
32 | /// The `width` and `height` is absolute.
33 | case absolute
34 | /// The `width` and `height` is relative.
35 | case relative
36 | }
37 | }
38 |
39 | /**
40 | The scaling of the content view.
41 |
42 | The default is `1.0`, which displays the content view at it's original scale. A larger value will display the content view at a larger, a smaller value at a smaller size.
43 | */
44 | public var scaleTransform: Scale = 1.0
45 |
46 | /**
47 | The rotation of the content view, in degrees.
48 |
49 | The default is `zero`, which displays the content view with no rotation.
50 | */
51 | public var rotation: Rotation = .zero
52 |
53 | /// The background color.
54 | public var backgroundColor: NSColor? = .lightGray
55 |
56 | /// The color transformer for resolving the background color.
57 | public var backgroundColorTransformer: ColorTransformer?
58 |
59 | /// Generates the resolved background color for the specified background color, using the background color and color transformer.
60 | public func resolvedBackgroundColor() -> NSColor? {
61 | if let backgroundColor = backgroundColor {
62 | return backgroundColorTransformer?(backgroundColor) ?? backgroundColor
63 | }
64 | return nil
65 | }
66 |
67 | /// The visual effect background of the content.
68 | public var visualEffect: VisualEffectConfiguration?
69 |
70 | /// The border of the content.
71 | public var border: BorderConfiguration = .none()
72 |
73 | /// The border transformer for resolving the border.
74 | public var borderTransformer: BorderTransformer? = nil
75 |
76 | /// Generates the resolved border, using the border and border transformer.
77 | public func resolvedBorder() -> BorderConfiguration {
78 | borderTransformer?(border) ?? border
79 | }
80 |
81 | var borderStateTransformer: BorderTransformer? = nil
82 |
83 | func _resolvedBorder() -> BorderConfiguration {
84 | let border = borderTransformer?(border) ?? border
85 | return borderStateTransformer?(border) ?? border
86 | }
87 |
88 | /// The shadow properties.
89 | public var shadow: ShadowConfiguration = .black()
90 |
91 | /// The shadow transformer for resolving the shadow.
92 | public var shadowTransformer: ShadowTransformer? = nil
93 |
94 | /// Generates the resolved shadow, using the shadow and shadow transformer.
95 | public func resolvedShadow() -> ShadowConfiguration {
96 | shadowTransformer?(shadow) ?? shadow
97 | }
98 |
99 | var shadowStateTransformer: ShadowTransformer? = nil
100 |
101 | func _resolvedShadow() -> ShadowConfiguration {
102 | let shadow = shadowTransformer?(shadow) ?? shadow
103 | return shadowStateTransformer?(shadow) ?? shadow
104 | }
105 |
106 | /// The text for the tooltip of the content.
107 | public var toolTip: String? = nil
108 |
109 | init() {}
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Configuration/Configurations/NSItemContentConfiguration/NSItemContentConfiguration+Image.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSItemContentConfiguration+Image.swift
3 | //
4 | //
5 | // Created by Florian Zand on 08.12.24.
6 | //
7 |
8 | import AppKit
9 | import FZSwiftUtils
10 | import FZUIKit
11 | import SwiftUI
12 |
13 | public extension NSItemContentConfiguration {
14 | /// Properties that affect the image of the content.
15 | struct ImageProperties: Hashable {
16 | /// The scaling of the image.
17 | public enum ImageScaling {
18 | /// The image is resized to fit the bounds size, while still preserving the aspect ratio of the image.
19 | case fit
20 | /// The image is resized to completely fill the bounds rectangle, while still preserving the aspect ratio of the image. The image is centered in the axis it exceeds.
21 | case fill
22 | /// The image is resized to the entire bounds rectangle.
23 | case resize
24 | /// The image isn't resized.
25 | case none
26 |
27 | var gravity: CALayerContentsGravity {
28 | switch self {
29 | case .fit: return .resizeAspect
30 | case .fill: return .resizeAspectFill
31 | case .resize: return .resize
32 | case .none: return .center
33 | }
34 | }
35 |
36 | var scaling: ImageView.ImageScaling {
37 | switch self {
38 | case .fit: return .scaleToFit
39 | case .fill: return .scaleToFill
40 | case .resize: return .resize
41 | case .none: return .none
42 | }
43 | }
44 |
45 | var swiftui: ContentMode {
46 | switch self {
47 | case .none: return .fit
48 | case .fit: return .fit
49 | case .fill: return .fill
50 | case .resize: return .fit
51 | }
52 | }
53 |
54 | var shouldResize: Bool {
55 | self == .fit
56 | }
57 | }
58 |
59 | /// The symbol configuration for the image.
60 | public var symbolConfiguration: ImageSymbolConfiguration?
61 |
62 | /// The image scaling.
63 | public var scaling: ImageScaling = .fit
64 |
65 | /// The tint color for an image that is a template or symbol image.
66 | public var tintColor: NSColor?
67 |
68 | /// The color transformer for resolving the image tint color.
69 | public var tintColorTransformer: ColorTransformer?
70 |
71 | /// Generates the resolved image tint color for the specified tint color, using the tint color and tint color transformer.
72 | public func resolvedTintColor() -> NSColor? {
73 | if let tintColor = tintColor {
74 | return tintColorTransformer?(tintColor) ?? tintColor
75 | }
76 | return nil
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Configuration/Configurations/NSItemContentConfiguration/unused/NSItemContentConfiguration+Transition.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSItemContentConfiguration+Transition.swift
3 | //
4 | //
5 | // Created by Florian Zand on 29.07.23.
6 | //
7 |
8 | import AppKit
9 | import FZUIKit
10 |
11 | /*
12 | // Currently not used
13 | extension NSItemContentConfiguration {
14 | func animate(with configuration: TransitionConfiguration) {
15 | guard let view = (self.collectionViewItem?.contentView as? NSItemContentView), let collectionView = self.collectionViewItem?._collectionView else { return }
16 | var backgroundView: NSView? = nil
17 | if let backgroundColor = configuration.backgroundColor {
18 | if let _backgroundView = collectionView.superview?.subviews(type: TransitionBackgroundView.self).first {
19 | backgroundView = _backgroundView
20 | _backgroundView.frame = collectionView.frame
21 | collectionView.superview?.addSubview(backgroundView!)
22 | } else {
23 | backgroundView = TransitionBackgroundView(frame: collectionView.frame)
24 | backgroundView?.backgroundColor = backgroundColor
25 | backgroundView?.alphaValue = 0.0
26 | collectionView.superview?.addSubview(backgroundView!)
27 | }
28 | }
29 | collectionView.superview?.addSubview(view)
30 | Wave.animate(withSpring: .defaultAnimated, animations: {
31 | if let alpha = configuration.text.alpha {
32 | view.textField.animator.alpha = alpha
33 | }
34 | if let alpha = configuration.secondaryText.alpha {
35 | view.secondaryTextField.animator.alpha = alpha
36 | }
37 | if let alpha = configuration.content.alpha {
38 | view.contentView.animator.alpha = alpha
39 | }
40 | if let frame = configuration.text.frame {
41 | view.textField.animator.frame = frame
42 | }
43 | if let frame = configuration.secondaryText.frame {
44 | view.secondaryTextField.animator.frame = frame
45 | }
46 | if let frame = configuration.content.frame {
47 | view.contentView.animator.frame = frame
48 | }
49 | backgroundView?.animator.alpha = 1.0
50 | collectionView.animator.alpha = 0.0
51 | }, completion: { _,_ in
52 | configuration.completion?()
53 | backgroundView?.removeFromSuperview()
54 | })
55 | }
56 |
57 | struct TransitionConfiguration {
58 | public struct TransitionItem: Hashable {
59 | var alpha: CGFloat? = nil
60 | var frame: CGRect? = nil
61 | }
62 |
63 | public enum AnimationType: Int, Hashable {
64 | case spring
65 | case easeInOut
66 | case easeIn
67 | case easeOut
68 | case linear
69 | }
70 |
71 | var text: TransitionItem = TransitionItem()
72 | var secondaryText: TransitionItem = TransitionItem()
73 | var content: TransitionItem = TransitionItem()
74 |
75 | var backgroundColor: NSColor? = nil
76 | var fading: Bool = true
77 | var animationTime: CGFloat = 0.1
78 | var animationType: AnimationType = .spring
79 | var completion: (()->())? = nil
80 | }
81 |
82 | class TransitionBackgroundView: NSView {
83 | override var tag: Int {
84 | return 34576542
85 | }
86 | }
87 | }
88 |
89 | */
90 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Configuration/Configurations/NSListContentConfiguration/Accessory/Accessories/TextAccessory.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TextAccessory.swift
3 | //
4 | //
5 | // Created by Florian Zand on 16.03.25.
6 | //
7 |
8 | import Foundation
9 |
10 | struct TextAccessory {
11 | /**
12 | The text.
13 |
14 | This value supersedes the ``attributedText`` property.
15 | */
16 | public var text: String? {
17 | didSet {
18 | guard text != nil else { return }
19 | attributedText = nil
20 | }
21 | }
22 |
23 | /**
24 | An attributed variant of the text.
25 |
26 | This value supersedes the ``text`` property.
27 | */
28 | public var attributedText: AttributedString? {
29 | didSet {
30 | guard attributedText != nil else { return }
31 | text = nil
32 | }
33 | }
34 |
35 | /**
36 | The placeholder text.
37 |
38 | This value supersedes the ``attributedPlaceholderText`` property.
39 | */
40 | public var placeholderText: String? {
41 | didSet {
42 | guard placeholderText != nil else { return }
43 | attributedPlaceholderText = nil
44 | }
45 | }
46 |
47 | /**
48 | An attributed variant of the placeholder text.
49 |
50 | This value supersedes the ``placeholderText`` property.
51 | */
52 | public var attributedPlaceholderText: AttributedString? {
53 | didSet {
54 | guard attributedPlaceholderText != nil else { return }
55 | placeholderText = nil
56 | }
57 | }
58 |
59 | /// Properties for configuring the primary text.
60 | public var textProperties: TextProperties = .primary
61 | }
62 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Configuration/Configurations/NSListContentConfiguration/Accessory/Accessories/TextStackAccessory.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TextStackAccessory.swift
3 | //
4 | //
5 | // Created by Florian Zand on 16.03.25.
6 | //
7 |
8 | import Foundation
9 |
10 | struct TextStackAccessory {
11 | /**
12 | The leading text.
13 |
14 | This value supersedes the ``attributedLeadingText`` property.
15 | */
16 | public var leadingText: String? {
17 | didSet {
18 | guard leadingText != nil else { return }
19 | attributedLeadingText = nil
20 | }
21 | }
22 |
23 | /**
24 | An attributed variant of the leading text.
25 |
26 | This value supersedes the ``leadingText`` property.
27 | */
28 | public var attributedLeadingText: AttributedString? {
29 | didSet {
30 | guard attributedLeadingText != nil else { return }
31 | leadingText = nil
32 | }
33 | }
34 |
35 | /**
36 | The leading placeholder text.
37 |
38 | This value supersedes the ``attributedPlaceholderLeadingText`` property.
39 | */
40 | public var placeholderLeadingText: String? {
41 | didSet {
42 | guard placeholderLeadingText != nil else { return }
43 | attributedPlaceholderLeadingText = nil
44 | }
45 | }
46 |
47 | /**
48 | An attributed variant of the leading placeholder text.
49 |
50 | This value supersedes the ``placeholderLeadingText`` property.
51 | */
52 | public var attributedPlaceholderLeadingText: AttributedString? {
53 | didSet {
54 | guard attributedPlaceholderLeadingText != nil else { return }
55 | placeholderLeadingText = nil
56 | }
57 | }
58 |
59 | /**
60 | The trailing text.
61 |
62 | This value supersedes the ``attributedTrailingText`` property.
63 | */
64 | public var trailingText: String? {
65 | didSet {
66 | guard trailingText != nil else { return }
67 | attributedTrailingText = nil
68 | }
69 | }
70 |
71 | /**
72 | An attributed variant of the trailing text.
73 |
74 | This value supersedes the ``trailingText`` property.
75 | */
76 | public var attributedTrailingText: AttributedString? {
77 | didSet {
78 | guard attributedLeadingText != nil else { return }
79 | trailingText = nil
80 | }
81 | }
82 |
83 | /**
84 | The trailing placeholder text.
85 |
86 | This value supersedes the ``attributedPlaceholderTrailingText`` property.
87 | */
88 | public var placeholderTrailingText: String? {
89 | didSet {
90 | guard placeholderLeadingText != nil else { return }
91 | attributedPlaceholderTrailingText = nil
92 | }
93 | }
94 |
95 | /**
96 | An attributed variant of the trailing placeholder text.
97 |
98 | This value supersedes the ``placeholderTrailingText`` property.
99 | */
100 | public var attributedPlaceholderTrailingText: AttributedString? {
101 | didSet {
102 | guard attributedPlaceholderTrailingText != nil else { return }
103 | placeholderTrailingText = nil
104 | }
105 | }
106 |
107 |
108 | /// Properties for configuring the primary text.
109 | public var leadingTextProperties: TextProperties = .primary
110 |
111 | /// Properties for configuring the primary text.
112 | public var trailingTextProperties: TextProperties = .primary
113 |
114 | /// The spacing between the leading and trailing text.
115 | public var leadingToTrailingTextSpacing: CGFloat = 4.0
116 | }
117 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Configuration/Configurations/NSListContentConfiguration/Accessory/NSListContentView+Accessory.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSListContentView+Accessory.swift
3 | //
4 | //
5 | // Created by Florian Zand on 19.06.23.
6 | //
7 |
8 | import AppKit
9 | import FZSwiftUtils
10 | import FZUIKit
11 | import SwiftUI
12 |
13 | // Currently unused. It allows to add additional information to a list configuration like an additional title. See the Mac Mail app and it's list of emails. Each entry displays the title and content of the email + the sender and date on top.
14 | extension NSListContentConfiguration {
15 | /**
16 | Configuration for a list accessory.
17 |
18 | An accessory displays additional content at the bottom or top of a list item.
19 | */
20 | struct Accessory: Hashable {
21 |
22 | /// Position of the accessory.
23 | public enum Position: Int, Hashable {
24 | /// The accessory is positioned at the top of the list item.
25 | case top
26 | /// The accessory is positioned at the bottom of the list item.
27 | case bottom
28 | }
29 |
30 | /// The leading accessory item.
31 | var leading: AccessoryProperties = {
32 | var properties = AccessoryProperties()
33 | properties.textProperties.alignment = .left
34 | properties.secondaryTextProperties.alignment = .left
35 | return properties
36 | }()
37 |
38 | /// The trailing accessory item.
39 | var trailing: AccessoryProperties = {
40 | var properties = AccessoryProperties()
41 | properties.textProperties.alignment = .right
42 | properties.secondaryTextProperties.alignment = .right
43 | return properties
44 | }()
45 |
46 | /// The position of the accessory.
47 | public var position: Position = .top
48 |
49 | /// The padding to the next accessory.
50 | public var padding: CGFloat = 4.0
51 | }
52 | }
53 |
54 | extension NSListContentConfiguration {
55 | /// Properties for a list accessory item.
56 | struct AccessoryProperties: Hashable {
57 | // MARK: Customizing content
58 |
59 | /// The primary text.
60 | public var text: String?
61 |
62 | /// An attributed variant of the primary text.
63 | public var attributedText: AttributedString?
64 |
65 | /// The secondary text.
66 | public var secondaryText: String?
67 |
68 | /// An attributed variant of the secondary text.
69 | public var secondaryAttributedText: AttributedString?
70 |
71 | /// The image.
72 | public var image: NSImage?
73 |
74 | // MARK: Customizing appearance
75 |
76 | /// Properties for configuring the primary text.
77 | public var textProperties: TextProperties = .primary
78 |
79 | /// Properties for configuring the secondary text.
80 | public var secondaryTextProperties: TextProperties = .secondary
81 |
82 | /// Properties for configuring the image.
83 | public var imageProperties = ImageProperties()
84 |
85 | // MARK: Customizing layout
86 |
87 | /// The padding to the next accessory item.
88 | public var padding: CGFloat = 4.0
89 |
90 | /// The padding between the image and text.
91 | public var imageToTextPadding: CGFloat = 8.0
92 |
93 | /// The padding between primary and secndary text.
94 | public var textToSecondaryTextPadding: CGFloat = 2.0
95 |
96 | var hasText: Bool {
97 | text != nil || attributedText != nil
98 | }
99 |
100 | var hasSecondaryText: Bool {
101 | secondaryText != nil || secondaryAttributedText != nil
102 | }
103 |
104 | var hasContent: Bool {
105 | image != nil
106 | }
107 |
108 | var isVisible: Bool {
109 | image != nil || hasText || hasSecondaryText
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Configuration/Configurations/NSListContentConfiguration/NSListContentView+ImageView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSListContentView+ImageView.swift
3 | //
4 | //
5 | // Created by Florian Zand on 28.07.23.
6 | //
7 |
8 | import AppKit
9 | import FZSwiftUtils
10 | import FZUIKit
11 | import SwiftUI
12 |
13 | extension NSListContentView {
14 | class ListImageView: ImageView {
15 | var properties: NSListContentConfiguration.ImageProperties {
16 | didSet {
17 | guard oldValue != properties else { return }
18 | update()
19 | }
20 | }
21 |
22 | override var image: NSImage? {
23 | didSet { isHidden = image == nil }
24 | }
25 |
26 | override var intrinsicContentSize: NSSize {
27 | var intrinsicContentSize = super.intrinsicContentSize
28 |
29 | intrinsicContentSize = intrinsicContentSize.clamped(min: reservedLayoutSize)
30 |
31 | if reservedLayoutSize.width == 0, image?.isSymbolImage == true, properties.position.orientation == .horizontal {
32 | intrinsicContentSize.width = (intrinsicContentSize.height * 2.5).rounded(.towardZero)
33 | return intrinsicContentSize
34 | }
35 |
36 | if reservedLayoutSize.width == NSListContentConfiguration.ImageProperties.standardDimension {
37 | // intrinsicContentSize.width = intrinsicContentSize.width.c
38 | }
39 |
40 | if let calculatedSize = calculatedSize {
41 | return calculatedSize
42 | }
43 |
44 | return intrinsicContentSize
45 | }
46 |
47 | var calculatedSize: CGSize? {
48 | didSet {
49 | invalidateIntrinsicContentSize()
50 | }
51 | }
52 |
53 | var verticalConstraint: NSLayoutConstraint?
54 | var reservedLayoutSize: CGSize = .zero {
55 | didSet {
56 | if reservedLayoutSize.width == NSListContentConfiguration.ImageProperties.standardDimension {
57 | reservedLayoutSize.width = 36.0
58 | }
59 | if reservedLayoutSize.height == NSListContentConfiguration.ImageProperties.standardDimension {
60 | reservedLayoutSize.height = 9.0
61 | }
62 | }
63 | }
64 |
65 | func update() {
66 | imageScaling = image?.isSymbolImage == true ? .none : properties.scaling.imageScaling
67 | symbolConfiguration = properties.symbolConfiguration?.nsSymbolConfiguration()
68 | border = properties.resolvedBorder()
69 | backgroundColor = properties.resolvedBackgroundColor()
70 | tintColor = properties.resolvedTintColor()
71 | cornerRadius = properties.cornerRadius
72 | outerShadow = properties.resolvedShadow()
73 | toolTip = properties.toolTip
74 | reservedLayoutSize = properties.reservedLayoutSize
75 | invalidateIntrinsicContentSize()
76 | }
77 |
78 | init(properties: NSListContentConfiguration.ImageProperties) {
79 | self.properties = properties
80 | super.init(frame: .zero)
81 | wantsLayer = true
82 | imageAlignment = .alignCenter
83 | update()
84 | }
85 |
86 | @available(*, unavailable)
87 | required init?(coder _: NSCoder) {
88 | fatalError("init(coder:) has not been implemented")
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Configuration/Configurations/Shared/Protocols.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AutomaticHeightSizable.swift
3 | //
4 | //
5 | // Created by Florian Zand on 20.07.24.
6 | //
7 |
8 | import AppKit
9 | import FZUIKit
10 |
11 | /// Content configurations with views that can autolayout their height.
12 | protocol AutomaticHeightSizable: NSContentConfiguration { }
13 |
14 | extension NSListContentConfiguration: AutomaticHeightSizable { }
15 | extension NSHostingConfiguration: AutomaticHeightSizable { }
16 |
17 | /// Content configuration views with editable text fields.
18 | protocol EditingContentView: NSView { }
19 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Configuration/Configurations/Shared/TextStackView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TextStackView.swift
3 | //
4 | //
5 | // Created by Florian Zand on 02.03.25.
6 | //
7 |
8 | import AppKit
9 | import FZUIKit
10 |
11 | class TextStackView: NSStackView {
12 | let textField = ListItemTextField.wrapping().truncatesLastVisibleLine(true)
13 | let secondaryTextField = ListItemTextField.wrapping().truncatesLastVisibleLine(true)
14 | private var prefersSideBySideTextAndSecondaryText = false
15 | private var previousBounds: CGRect = .zero
16 | private var verticalSpacing: CGFloat = .zero
17 | private var horizontalSpacing: CGFloat = .zero
18 |
19 | func update(with properties: TextStackProperties, for view: NSView) {
20 | textField.isEnabled = properties.isEnabled
21 | textField.properties = properties.textProperties
22 | textField.updateText(properties.text, properties.attributedText, properties.placeholderText, properties.attributedPlaceholderText)
23 | textField.updateLayoutGuide(for: view)
24 | secondaryTextField.isEnabled = properties.isEnabled
25 | secondaryTextField.properties = properties.secondaryTextProperties
26 | secondaryTextField.updateText(properties.secondaryText, properties.secondaryAttributedText, properties.secondaryPlaceholderText, properties.secondaryAttributedPlaceholderText)
27 | secondaryTextField.updateLayoutGuide(for: view)
28 | horizontalSpacing = properties.textToSecondaryTextHorizontalPadding
29 | verticalSpacing = properties.textToSecondaryTextPadding
30 | spacing = orientation == .vertical ? verticalSpacing : horizontalSpacing
31 | prefersSideBySideTextAndSecondaryText = properties.prefersSideBySideTextAndSecondaryText
32 | updateLayout()
33 | }
34 |
35 | override func layout() {
36 | super.layout()
37 | guard previousBounds.size != bounds.size else { return }
38 | previousBounds = bounds
39 | updateLayout()
40 | }
41 |
42 | func updateLayout() {
43 | if prefersSideBySideTextAndSecondaryText, bounds.width >= textField.intrinsicContentSize.width + secondaryTextField.intrinsicContentSize.width + horizontalSpacing {
44 | orientation = .horizontal
45 | spacing = horizontalSpacing
46 | } else {
47 | orientation = .vertical
48 | spacing = verticalSpacing
49 | }
50 | }
51 |
52 | init() {
53 | super.init(frame: .zero)
54 | addArrangedSubview(textField)
55 | addArrangedSubview(secondaryTextField)
56 | orientation = .vertical
57 | alignment = .leading
58 | }
59 |
60 | required init?(coder: NSCoder) {
61 | fatalError("init(coder:) has not been implemented")
62 | }
63 | }
64 |
65 | extension NSListContentConfiguration: TextStackProperties { }
66 | extension NSItemContentConfiguration: TextStackProperties { }
67 |
68 | protocol TextStackProperties {
69 | var text: String? { get }
70 | var attributedText: AttributedString? { get }
71 | var placeholderText: String? { get }
72 | var attributedPlaceholderText: AttributedString? { get }
73 | var secondaryText: String? { get }
74 | var secondaryAttributedText: AttributedString? { get }
75 | var secondaryPlaceholderText: String? { get }
76 | var secondaryAttributedPlaceholderText: AttributedString? { get }
77 | var textToSecondaryTextPadding: CGFloat { get }
78 | var textProperties: TextProperties { get }
79 | var secondaryTextProperties: TextProperties { get }
80 | var isEnabled: Bool { get }
81 | var prefersSideBySideTextAndSecondaryText: Bool { get }
82 | var textToSecondaryTextHorizontalPadding: CGFloat { get }
83 | }
84 |
85 | extension TextStackProperties {
86 | var isEnabled: Bool { true }
87 | var prefersSideBySideTextAndSecondaryText: Bool { false }
88 | var textToSecondaryTextHorizontalPadding: CGFloat { 0.0 }
89 | }
90 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Configuration/Extensions/NSCollectionView+/NSCollectionView+.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSCollectionView+.swift
3 | //
4 | //
5 | // Created by Florian Zand on 01.11.22.
6 | //
7 |
8 | import AppKit
9 | import FZSwiftUtils
10 | import FZUIKit
11 |
12 | extension NSCollectionView {
13 | /// The index path of the item that is hovered by the mouse.
14 | @objc dynamic var hoveredIndexPath: IndexPath? {
15 | get { getAssociatedValue("hoveredIndexPath") }
16 | set {
17 | guard newValue != hoveredIndexPath else { return }
18 | hoveredItem?.isHovered = false
19 | setAssociatedValue(newValue, key: "hoveredIndexPath")
20 | }
21 | }
22 |
23 | /// The item that is hovered by the mouse.
24 | var hoveredItem: NSCollectionViewItem? {
25 | guard let indexPath = hoveredIndexPath else { return nil }
26 | return item(at: indexPath)
27 | }
28 |
29 | func setupObservation(shouldObserve: Bool = true) {
30 | if !shouldObserve {
31 | observerView?._removeFromSuperview()
32 | observerView = nil
33 | } else if observerView == nil {
34 | observerView = .init(for: self)
35 | }
36 | }
37 |
38 | var observerView: TableCollectionObserverView? {
39 | get { getAssociatedValue("collectionViewObserverView") }
40 | set { setAssociatedValue(newValue, key: "collectionViewObserverView") }
41 | }
42 |
43 | var editingView: NSView? {
44 | observerView?.editingView
45 | }
46 |
47 | var activeState: NSItemConfigurationState.ActiveState {
48 | isActive ? isFocused ? .focused : .active : .inactive
49 | }
50 |
51 | var isFocused: Bool {
52 | observerView?.isFocused == true
53 | }
54 |
55 | var isActive: Bool {
56 | window?.isKeyWindow == true
57 | }
58 | }
59 |
60 |
61 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Configuration/Extensions/NSTableView+/NSTableView+.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSTableView+.swift
3 | //
4 | //
5 | // Created by Florian Zand on 10.12.22.
6 | //
7 |
8 | import AppKit
9 | import FZSwiftUtils
10 | import FZUIKit
11 |
12 | extension NSTableView {
13 | func setupObservation(shouldObserve: Bool = true) {
14 | if !shouldObserve {
15 | observerView?._removeFromSuperview()
16 | observerView = nil
17 | } else if observerView == nil {
18 | observerView = .init(for: self)
19 | }
20 | }
21 |
22 | @objc dynamic var hoveredRow: Int {
23 | get { getAssociatedValue("hoveredRow", initialValue: -1) }
24 | set {
25 | guard newValue != hoveredRow else { return }
26 | let previousRow = hoveredRowView
27 | setAssociatedValue(newValue, key: "hoveredRow")
28 | previousRow?.setNeedsAutomaticUpdateConfiguration()
29 | hoveredRowView?.setNeedsAutomaticUpdateConfiguration()
30 | }
31 | }
32 |
33 | var hoveredRowView: NSTableRowView? {
34 | if hoveredRow != -1, hoveredRow < numberOfRows {
35 | return rowView(atRow: hoveredRow, makeIfNecessary: false)
36 | }
37 | return nil
38 | }
39 |
40 | var observerView: TableCollectionObserverView? {
41 | get { getAssociatedValue("tableViewObserverView") }
42 | set { setAssociatedValue(newValue, key: "tableViewObserverView") }
43 | }
44 |
45 | var editingView: NSView? {
46 | observerView?.editingView
47 | }
48 |
49 | var activeState: NSListConfigurationState.ActiveState {
50 | isActive ? isFocused ? .focused : .active : .inactive
51 | }
52 |
53 | var isFocused: Bool {
54 | observerView?.isFocused == true
55 | }
56 |
57 | var isActive: Bool {
58 | window?.isKeyWindow == true
59 | }
60 |
61 | func enableAutomaticRowHeights() {
62 | guard !usesAutomaticRowHeights else { return }
63 | Self.swizzleViewRegistration()
64 | isEnablingAutomaticRowHeights = true
65 | usesAutomaticRowHeights = true
66 | reloadData()
67 | }
68 |
69 | var isEnablingAutomaticRowHeights: Bool {
70 | get { getAssociatedValue("isEnablingAutomaticRowHeights") ?? false }
71 | set { setAssociatedValue(newValue, key: "isEnablingAutomaticRowHeights") }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Configuration/Extensions/Shared/TableCollectionObserverView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TableCollectionObserverView.swift
3 | //
4 | //
5 | // Created by Florian Zand on 25.01.25.
6 | //
7 |
8 | import AppKit
9 | import FZSwiftUtils
10 | import FZUIKit
11 |
12 | class TableCollectionObserverView: NSView {
13 | var tokens: [NotificationToken] = []
14 | lazy var trackingArea = TrackingArea(for: self, options: [.mouseMoved, .mouseEnteredAndExited, .activeInKeyWindow])
15 | weak var collectionView: NSCollectionView?
16 | weak var tableView: NSTableView?
17 | var focusObservation: KeyValueObservation?
18 | var isEnabledObservation: KeyValueObservation?
19 |
20 | var isFocused = false {
21 | didSet {
22 | guard oldValue != isFocused else { return }
23 | collectionView?.visibleItems().forEach { $0.setNeedsAutomaticUpdateConfiguration() }
24 | tableView?.visibleRows().forEach { $0.setNeedsAutomaticUpdateConfiguration() }
25 | }
26 | }
27 |
28 | weak var editingView: NSView? {
29 | didSet {
30 | guard oldValue != editingView else { return }
31 | if collectionView != nil {
32 | (oldValue?.firstSuperview(where: { $0.parentController is NSCollectionViewItem })?.parentController as? NSCollectionViewItem)?.setNeedsAutomaticUpdateConfiguration()
33 | (editingView?.firstSuperview(where: { $0.parentController is NSCollectionViewItem })?.parentController as? NSCollectionViewItem)?.setNeedsAutomaticUpdateConfiguration()
34 | } else {
35 | oldValue?.firstSuperview(for: NSTableRowView.self)?.setNeedsAutomaticUpdateConfiguration()
36 | editingView?.firstSuperview(for: NSTableRowView.self)?.setNeedsAutomaticUpdateConfiguration()
37 | }
38 | }
39 | }
40 |
41 | init(for collectionView: NSCollectionView) {
42 | super.init(frame: .zero)
43 | self.collectionView = collectionView
44 | initialSetup(for: collectionView)
45 | }
46 |
47 | init(for tableView: NSTableView) {
48 | super.init(frame: .zero)
49 | self.tableView = tableView
50 | initialSetup(for: tableView)
51 | isEnabledObservation = tableView.observeChanges(for: \.isEnabled) { [weak self] old, new in
52 | guard let self = self, old != new else { return }
53 | self.tableView?.visibleRows().forEach { $0.setNeedsAutomaticUpdateConfiguration() }
54 | }
55 | }
56 |
57 | func initialSetup(for view: NSView) {
58 | view.addSubview(withConstraint: self)
59 | zPosition = -CGFloat.greatestFiniteMagnitude
60 | isFocused = view.isDescendantFirstResponder
61 | sendToBack()
62 | updateTrackingAreas()
63 | focusObservation = observeChanges(for: \.window?.firstResponder) { [weak self] oldValue, newValue in
64 | guard let self = self, let _view = self.collectionView ?? self.tableView else { return }
65 | if let view = (newValue as? NSView ?? (newValue as? NSText)?.delegate as? NSView), view.isDescendant(of: _view) {
66 | self.isFocused = true
67 | self.editingView = (view as? EditiableView)?.isEditable == true ? view : nil
68 | } else {
69 | self.isFocused = false
70 | }
71 | }
72 | }
73 |
74 | required init?(coder: NSCoder) {
75 | fatalError("init(coder:) has not been implemented")
76 | }
77 |
78 | override func updateTrackingAreas() {
79 | super.updateTrackingAreas()
80 | trackingArea.update()
81 | }
82 |
83 | override func mouseEntered(with event: NSEvent) {
84 | updateHovered(for: event)
85 | }
86 |
87 | override func mouseMoved(with event: NSEvent) {
88 | updateHovered(for: event)
89 | }
90 |
91 | override func mouseExited(with event: NSEvent) {
92 | collectionView?.hoveredIndexPath = nil
93 | tableView?.hoveredRow = -1
94 | }
95 |
96 | func updateHovered(for event: NSEvent) {
97 | if let collectionView = collectionView {
98 | let location = event.location(in: collectionView)
99 | collectionView.hoveredIndexPath = collectionView.indexPathForItem(at: location)
100 | if let item = collectionView.hoveredItem {
101 | if let view = item.view as? NSItemContentView {
102 | item.isHovered = view.isHovering(at: collectionView.convert(location, to: view))
103 | } else {
104 | item.isHovered = true
105 | }
106 | }
107 | } else if let tableView = tableView {
108 | let location = event.location(in: tableView)
109 | tableView.hoveredRow = tableView.row(at: event.location(in: tableView))
110 | }
111 | }
112 |
113 | override func hitTest(_ point: NSPoint) -> NSView? {
114 | return nil
115 | }
116 |
117 | func _removeFromSuperview() {
118 | super.removeFromSuperview()
119 | }
120 |
121 | override func removeFromSuperview() { }
122 |
123 | override func viewWillMove(toWindow newWindow: NSWindow?) {
124 | tokens = []
125 | guard let newWindow = newWindow else { return }
126 | tokens = [NotificationCenter.default.observe(NSWindow.didBecomeKeyNotification, object: newWindow) { [weak self] _ in
127 | guard let self = self else { return }
128 | self.collectionView?.visibleItems().forEach { $0.setNeedsAutomaticUpdateConfiguration() }
129 | self.tableView?.visibleRows().forEach { $0.setNeedsAutomaticUpdateConfiguration() }
130 | }, NotificationCenter.default.observe(NSWindow.didResignKeyNotification, object: newWindow) { [weak self] _ in
131 | guard let self = self else { return }
132 | self.collectionView?.hoveredIndexPath = nil
133 | self.collectionView?.visibleItems().forEach { $0.setNeedsAutomaticUpdateConfiguration() }
134 | self.tableView?.hoveredRow = -1
135 | self.tableView?.visibleRows().forEach { $0.setNeedsAutomaticUpdateConfiguration() }
136 | }]
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Configuration/States/ConfigurationState.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ConfigurationState.swift
3 | //
4 | //
5 | // Created by Florian Zand on 12.10.23.
6 | //
7 |
8 | import Foundation
9 | import FZUIKit
10 |
11 | protocol ConfigurationState: NSConfigurationState {
12 | var isSelected: Bool { get }
13 | var isActive: Bool { get }
14 | var isHovered: Bool { get }
15 | }
16 |
17 | extension NSItemConfigurationState: ConfigurationState { }
18 | extension NSListConfigurationState: ConfigurationState { }
19 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/DiffableDataSource/NSOutlineView/ unused/CellOutlineVItem.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CellOutlineVItem.swift
3 | //
4 | //
5 | // Created by Florian Zand on 20.01.25.
6 | //
7 |
8 | import AppKit
9 |
10 | /*
11 | public protocol OutlineItemObject: AnyObject, Hashable {
12 | /// The parent of the item.
13 | var parent: Self? { get set }
14 |
15 | /// The children of the item.
16 | var children: [Self] { get set }
17 |
18 | /**
19 | A Boolean value indicating whether the item is expanded.
20 |
21 | The default value is `true`.
22 | */
23 | var isExpanded: Bool { get set }
24 |
25 | /**
26 | A Boolean value indicating whether the item can be expanded / collapsed by the user.
27 |
28 | The default value is `true`.
29 | */
30 | var isExpandable: Bool { get }
31 |
32 | /**
33 | A Boolean value indicating whether the item can be selected.
34 |
35 | The default value is `true`.
36 | */
37 | var isSelectable: Bool { get }
38 |
39 | /**
40 | A Boolean value indicating whether the item can be deleted by the user pressing `Backspace`.
41 |
42 | The default value is `false`.
43 | */
44 | var isDeletable: Bool { get }
45 |
46 | /**
47 | A Boolean value indicating whether the item can be reorded.
48 |
49 | The default value is `false`.
50 | */
51 | var isReordable: Bool { get }
52 | }
53 |
54 |
55 | extension OutlineItemObject {
56 | public var isExpandable: Bool { !children.isEmpty }
57 | public var isSelectable: Bool { true }
58 | public var isDeletable: Bool { false }
59 | public var isReordable: Bool { false }
60 |
61 | /// Checks if the specified item is a children of the items or the items children.
62 | public func contains(_ item: Self) -> Bool {
63 | children.contains(item) || children.contains(where: { $0.contains($0) })
64 | }
65 | }
66 | */
67 |
68 | /*
69 | import AppKit
70 |
71 | /// A outline view item that creates it's cell view.
72 | public protocol CellOutlineItem: Hashable {
73 | /// Returns the cell view for the item.
74 | func cellView(_ outlineView: NSOutlineView, column: NSTableColumn, row: Int) -> NSView
75 |
76 | /**
77 | A Boolean value indicating whether the item can be expanded / collapsed by the user.
78 |
79 | The default value is `true`.
80 | */
81 | var isExpandable: Bool { get }
82 |
83 | /**
84 | A Boolean value indicating whether the item can be selected.
85 |
86 | The default value is `true`.
87 | */
88 | var isSelectable: Bool { get }
89 |
90 | /**
91 | A Boolean value indicating whether the item can be deleted by the user pressing `Backspace`.
92 |
93 | The default value is `false`.
94 | */
95 | var isDeletable: Bool { get }
96 |
97 | /**
98 | A Boolean value indicating whether the item can be reorded.
99 |
100 | The default value is `false`.
101 | */
102 | var isReordable: Bool { get }
103 |
104 | /**
105 | A Boolean value indicating whether the user can insert items as children.
106 |
107 | The default value is `true`.
108 | */
109 | var canInsertChildren: Bool { get }
110 |
111 | /**
112 | A Boolean value indicating whether the item is a group item.
113 |
114 | The default value is `false`.
115 | */
116 | var isGroupItem: Bool { get }
117 | }
118 |
119 | extension CellOutlineItem {
120 | public var isExpandable: Bool { true }
121 | public var isSelectable: Bool { true }
122 | public var isDeletable: Bool { false }
123 | public var isReordable: Bool { false }
124 | public var canInsertChildren: Bool { true }
125 | public var isGroupItem: Bool { false }
126 | }
127 |
128 | /// A outline view item that creates it's cell view.
129 | public protocol RegisteredCellOutlineItem: CellOutlineItem {
130 | associatedtype Cell: NSTableCellView
131 | /// The cell registration that creates the cell view for the item.
132 | static var cellRegistration: NSTableView.CellRegistration { get }
133 | }
134 |
135 | extension RegisteredCellOutlineItem {
136 | public func cellView(_ outlineView: NSOutlineView, column: NSTableColumn, row: Int) -> NSView {
137 | outlineView.makeCellView(using: Self.cellRegistration, forColumn: column, row: row, item: self) ?? NSTableCellView()
138 | }
139 | }
140 | */
141 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/DiffableDataSource/NSOutlineView/ unused/OutlineViewDiffableDataSourceSnapshot+OutlineItem.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OutlineViewDiffableDataSourceSnapshot+OutlineItem.swift
3 | //
4 | //
5 | // Created by Florian Zand on 09.01.25.
6 | //
7 |
8 | /*
9 | import AppKit
10 | import FZSwiftUtils
11 |
12 |
13 | extension OutlineViewDiffableDataSourceSnapshot where ItemIdentifierType: ExpandingOutlineItem {
14 | public mutating func append(_ items: [ItemIdentifierType], to parent: ItemIdentifierType? = nil) {
15 | validateItems(items + items.flatMap({ $0.descendants() }))
16 | if let parent = parent {
17 | validateItem(parent, "Parent item does not exist in section snapshot: ")
18 | nodes[parent]?.children.append(contentsOf: items)
19 | } else {
20 | rootItems.append(contentsOf: items)
21 | }
22 | items.forEach({ nodes[$0] = Node(parent: parent) })
23 | items.forEach({ nodes.merge($0.nodes()) })
24 | updateOrderedItems()
25 | }
26 |
27 | private mutating func insert(_ items: [ItemIdentifierType], to item: ItemIdentifierType, before: Bool) {
28 | validateItem(item, "ItemIdentifierType to insert \(before ? "before" : "after") does not exist in section snapshot: ")
29 | validateItems(items + items.flatMap({ $0.descendants() }))
30 | if let rootIndex = rootItems.firstIndex(of: item) {
31 | rootItems.insert(contentsOf: items, at: before ? rootIndex : rootIndex + 1)
32 | items.forEach { nodes[$0] = .init() }
33 | } else if let parent = parent(of: item), let childIndex = nodes[parent]?.children.firstIndex(of: item) {
34 | nodes[parent]?.children.insert(contentsOf: items, at: before ? childIndex : childIndex + 1)
35 | items.forEach { nodes[$0] = .init(parent: parent) }
36 | }
37 | items.forEach({ nodes.merge($0.nodes()) })
38 | updateOrderedItems()
39 | }
40 |
41 | private mutating func insert(_ snapshot: OutlineViewDiffableDataSourceSnapshot, to item: ItemIdentifierType, before: Bool) {
42 | validateItem(item, "ItemIdentifierType to insert \(before ? "before" : "after") does not exist in section snapshot: ")
43 | validateItems(snapshot.items + snapshot.items.flatMap({ $0.descendants() }))
44 | if let rootIndex = rootItems.firstIndex(of: item) {
45 | rootItems.insert(contentsOf: snapshot.rootItems, at: before ? rootIndex : rootIndex + 1)
46 | nodes.merge(snapshot.nodes)
47 | } else if let parentItem = parent(of: item),
48 | let childIndex = nodes[parentItem]?.children.firstIndex(of: item) {
49 | nodes[parentItem]?.children.insert(contentsOf: snapshot.rootItems, at: before ? childIndex : childIndex + 1)
50 | nodes.merge(snapshot.nodes)
51 | snapshot.rootItems.forEach({ nodes[$0]?.parent = parentItem })
52 | }
53 | items.forEach({ nodes.merge($0.nodes()) })
54 | updateOrderedItems()
55 | }
56 | }
57 |
58 | extension ExpandingOutlineItem {
59 | func descendants() -> [Self] {
60 | var items: [Self] = []
61 | for child in children {
62 | items.append(child)
63 | items.append(contentsOf: child.descendants())
64 | }
65 | return items
66 | }
67 |
68 |
69 | func nodes() -> [Self: OutlineViewDiffableDataSourceSnapshot.Node] {
70 | var nodes: [Self: OutlineViewDiffableDataSourceSnapshot.Node] = [:]
71 | func setup(for child: Self, parent: Self?) {
72 | nodes[child] = OutlineViewDiffableDataSourceSnapshot.Node(parent: parent, children: child.children, isExpanded: child.isExpanded)
73 | for _child in child.children {
74 | setup(for: _child, parent: child)
75 | }
76 | }
77 | setup(for: self, parent: nil)
78 | return nodes
79 | }
80 | }
81 | */
82 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/DiffableDataSource/NSOutlineView/Snapshot/OutlineChangeInstruction.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OutlineChangeInstruction.swift
3 | //
4 | //
5 | // Created by Florian Zand on 09.01.25.
6 | //
7 |
8 | import AppKit
9 | import FZSwiftUtils
10 |
11 | enum OutlineChangeInstruction: CustomStringConvertible, Hashable, Equatable {
12 | case insert(_ item: AnyHashable, at: Int, parent: AnyHashable?)
13 | case remove(_ item: AnyHashable, at: Int, parent: AnyHashable?)
14 | case move(_ item: AnyHashable, from: Int, _ fromParent: AnyHashable?, to: Int, _ toParent: AnyHashable?)
15 |
16 | var description: String {
17 | switch self {
18 | case .insert(let item, let index, let parent):
19 | let parent = "\(parent != nil ? "\(parent!)" : "Root")"
20 | return "insert \"\(item)\" in \"\(parent)\" at \(index)"
21 | case .remove(let item, let index, let parent):
22 | let parent = "\(parent != nil ? "\(parent!)" : "Root")"
23 | return "Remove \"\(item)\"from \"\(parent)\" at \(index)"
24 | case .move(let item, let from, let fromParent, let to, let toParent):
25 | let fromParent = "\(fromParent != nil ? "\(fromParent!)" : "Root")"
26 | let toParent = "\(toParent != nil ? "\(toParent!)" : "Root")"
27 | return "Move \"\(item)\" from \"\(fromParent)\" at \(from) to \"\(toParent)\" at \(to)"
28 | }
29 | }
30 | }
31 |
32 | extension OutlineViewDiffableDataSourceSnapshot {
33 | func instructions(forMorphingTo newSnapshot: OutlineViewDiffableDataSourceSnapshot) -> [OutlineChangeInstruction] {
34 | var movedItems: Set- = []
35 | var work = self
36 | work.isCalculatingDiff = true
37 | func calculateSteps(from source: [Item], to destination: [Item], parent: Item? = nil) -> [OutlineChangeInstruction] {
38 | var instructions: [OutlineChangeInstruction] = []
39 | for step in destination.difference(from: source).steps {
40 | switch step {
41 | case .insert(let item, let index):
42 | if let fromIndex = work.childIndex(of: item) {
43 | guard !movedItems.contains(item) else { continue }
44 | movedItems.insert(item)
45 | instructions.append(.move(item, from: fromIndex, work.parent(of: item), to: index, parent))
46 | work.move([item], toIndex: index, of: parent)
47 | } else {
48 | instructions.append(.insert(item, at: index, parent: parent))
49 | work.insert(item, at: index, of: parent)
50 | }
51 | case .remove(let item, let index):
52 | if let toIndex = newSnapshot.childIndex(of: item) {
53 | guard !movedItems.contains(item) else { continue }
54 | movedItems.insert(item)
55 | let newParent = newSnapshot.parent(of: item)
56 | instructions.append(.move(item, from: index, work.parent(of: item), to: toIndex, newParent))
57 | work.move([item], toIndex: toIndex, of: newParent)
58 | } else {
59 | instructions.append(.remove(item, at: index, parent: parent))
60 | work.delete([item])
61 | }
62 | case .move(let item, let from, let to):
63 | instructions.append(.move(item, from: from, parent, to: to, parent))
64 | work.move([item], toIndex: to, of: parent)
65 | }
66 | }
67 | for item in destination {
68 | instructions += calculateSteps(from: work.children(of: item), to: newSnapshot.children(of: item), parent: item)
69 | }
70 | return instructions
71 | }
72 | let instructions = calculateSteps(from: rootItems, to: newSnapshot.rootItems)
73 | return instructions
74 | }
75 | }
76 |
77 | extension NSOutlineView {
78 | func apply(_ snapshot: OutlineViewDiffableDataSourceSnapshot
- , currentSnapshot: OutlineViewDiffableDataSourceSnapshot
- , option: NSDiffableDataSourceSnapshotApplyOption, animation: AnimationOptions, completion: (() -> Void)?) {
79 | func applySnapshot() {
80 | beginUpdates()
81 | for instruction in currentSnapshot.instructions(forMorphingTo: snapshot) {
82 | switch instruction {
83 | case .insert(_, let index, let parent):
84 | insertItems(at: IndexSet(integer: index), inParent: parent, withAnimation: animation)
85 | case .remove(_, let index, let parent):
86 | removeItems(at: IndexSet(integer: index), inParent: parent, withAnimation: animation)
87 | case .move(_, let from, let fromParent, let to, let toParent):
88 | moveItem(at: from, inParent: fromParent, to: to, inParent: toParent)
89 | }
90 | }
91 | expandCollapseItems()
92 | endUpdates()
93 | }
94 |
95 | func expandCollapseItems() {
96 | let oldExpanded = currentSnapshot.expandedItems
97 | let newExpanded = snapshot.expandedItems
98 | oldExpanded.subtracting(newExpanded).forEach({ animator().collapseItem($0) })
99 | newExpanded.subtracting(oldExpanded).forEach({ animator().expandItem($0) })
100 | }
101 |
102 | if option.isReloadData {
103 | reloadData()
104 | expandCollapseItems()
105 | completion?()
106 | } else if let duration = option.animationDuration, duration > 0.0 {
107 | NSView.animate(withDuration: duration, {
108 | applySnapshot()
109 | }, completion: completion)
110 | } else {
111 | NSView.performWithoutAnimation {
112 | applySnapshot()
113 | completion?()
114 | }
115 | }
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/DiffableDataSource/NSOutlineView/Snapshot/OutlineViewDiffableDataSourceSnapshot+Node.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OutlineViewDiffableDataSourceSnapshot+Node.swift
3 | //
4 | //
5 | // Created by Florian Zand on 20.01.25.
6 | //
7 |
8 | import Foundation
9 |
10 | /**
11 | A outline node that can be used to construct the items of a `OutlineViewDiffableDataSourceSnapshot`.
12 |
13 | Example usage:
14 | ```swift
15 | var snapshot = OutlineViewDiffableDataSourceSnapshot {
16 | OutlineNode("Food") {
17 | OutlineNode("Root 1 Child 1")
18 | OutlineNode("Root 1 Child 2")
19 | OutlineNode("Root 1 Child 3")
20 | }.isExpanded(true)
21 | OutlineNode("Root 2")
22 | OutlineNode("Root 3") {
23 | OutlineNode("Root 3 Child 1")
24 | OutlineNode("Root 3 Child 2")
25 | }
26 | }
27 | ```
28 | */
29 | public struct OutlineNode {
30 | /// The item of the node.
31 | public let item: Item
32 |
33 | /// The children of the node.
34 | public let children: [OutlineNode]
35 |
36 | /// A Boolean value indicating whether the item of the node is expanded.
37 | public let isExpanded: Bool
38 |
39 | /// Sets the Boolean value indicating whether the item of the node is expanded.
40 | public func isExpanded(_ isExpanded: Bool) -> Self {
41 | .init(item, children: children, isExpanded: isExpanded)
42 | }
43 |
44 | /*
45 | /// A Boolean value indicating whether the node contains the item.
46 | public func contains(_ item: Item) -> Bool {
47 | children.contains(where: { $0.item == item || $0.contains(item) })
48 | }
49 | */
50 |
51 | /// Creates a node with the specified item.
52 | public init(_ item: Item) {
53 | self.item = item
54 | self.children = []
55 | self.isExpanded = false
56 | }
57 |
58 | /// Creates a node with the specified item and children.
59 | public init(_ item: Item, @Builder _ children: () -> [OutlineNode]) {
60 | self.item = item
61 | self.children = children()
62 | self.isExpanded = false
63 | }
64 |
65 | init(_ item: Item, children: [OutlineNode], isExpanded: Bool) {
66 | self.item = item
67 | self.children = children
68 | self.isExpanded = isExpanded
69 | }
70 | }
71 |
72 | extension OutlineViewDiffableDataSourceSnapshot {
73 | /**
74 | Creates a snapshot from the specified nodes.
75 |
76 | Example usage:
77 | ```swift
78 | var snapshot = OutlineViewDiffableDataSourceSnapshot {
79 | OutlineNode("Food") {
80 | OutlineNode("Root 1 Child 1")
81 | OutlineNode("Root 1 Child 2")
82 | OutlineNode("Root 1 Child 3")
83 | }.isExpanded(true)
84 | OutlineNode("Root 2")
85 | OutlineNode("Root 3") {
86 | OutlineNode("Root 3 Child 1")
87 | OutlineNode("Root 3 Child 2")
88 | }
89 | }
90 | ```
91 | */
92 | public init(@OutlineNode
- .Builder nodes: () -> [OutlineNode
- ]) {
93 | apply(nodes())
94 | }
95 |
96 | /// Adds the items of the specified nodes as child items of the specified parent item in the snapshot.
97 | public mutating func append(@OutlineNode
- .Builder _ nodes: () -> [OutlineNode
- ], to parent: Item? = nil) {
98 | apply(nodes(), to: parent)
99 | }
100 |
101 | /// Inserts the items of the provided nodes immediately after the item with the specified identifier in the snapshot.
102 | public mutating func insert(@OutlineNode
- .Builder _ nodes: () -> [OutlineNode
- ], after item: Item) {
103 | let nodes = nodes()
104 | insert(nodes.compactMap({$0.item}), after: item)
105 | nodes.forEach({ apply($0.children, to: $0.item) })
106 | }
107 |
108 | /// Inserts the items of the provided nodes immediately before the item with the specified identifier in the snapshot.
109 | public mutating func insert(@OutlineNode
- .Builder _ nodes: () -> [OutlineNode
- ], before item: Item) {
110 | let nodes = nodes()
111 | insert(nodes.compactMap({$0.item}), before: item)
112 | nodes.forEach({ apply($0.children, to: $0.item) })
113 | }
114 |
115 | mutating func apply(_ nodes: [OutlineNode
- ], to item: Item? = nil) {
116 | append(nodes.compactMap({$0.item}), to: item)
117 | nodes.forEach({ self.nodes[$0.item]?.isExpanded = $0.isExpanded })
118 | nodes.forEach({ apply($0.children, to: $0.item) })
119 | }
120 | }
121 |
122 | extension OutlineNode {
123 | /// A function builder type that produces an array of nodes.
124 | @resultBuilder
125 | public enum Builder {
126 | public static func buildBlock(_ block: [OutlineNode]...) -> [OutlineNode] {
127 | block.flatMap { $0 }
128 | }
129 |
130 | public static func buildOptional(_ item: [OutlineNode]?) -> [OutlineNode] {
131 | item ?? []
132 | }
133 |
134 | public static func buildEither(first: [OutlineNode]?) -> [OutlineNode] {
135 | first ?? []
136 | }
137 |
138 | public static func buildEither(second: [OutlineNode]?) -> [OutlineNode] {
139 | second ?? []
140 | }
141 |
142 | public static func buildArray(_ components: [[OutlineNode]]) -> [OutlineNode] {
143 | components.flatMap { $0 }
144 | }
145 |
146 | public static func buildExpression(_ expr: [OutlineNode]?) -> [OutlineNode] {
147 | expr ?? []
148 | }
149 |
150 | public static func buildExpression(_ expr: OutlineNode?) -> [OutlineNode] {
151 | expr.map { [$0] } ?? []
152 | }
153 |
154 | public static func buildExpression(_ expr: Item?) -> [OutlineNode] {
155 | if let item = expr {
156 | return [.init(item)]
157 | }
158 | return []
159 | }
160 |
161 | public static func buildExpression(_ expr: [Item]?) -> [OutlineNode] {
162 | expr?.compactMap({.init($0)}) ?? []
163 | }
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/DiffableDataSource/NSOutlineView/Snapshot/OutlineViewDiffableDataSourceTransaction.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OutlineViewDiffableDataSourceTransaction.swift
3 | //
4 | //
5 | // Created by Florian Zand on 09.01.25.
6 | //
7 |
8 | import Foundation
9 |
10 | /// A transaction that describes the changes after reordering the items of a outline view..
11 | public struct OutlineViewDiffableDataSourceTransaction {
12 | /// The section snapshot before the transaction occured.
13 | public let initialSnapshot: OutlineViewDiffableDataSourceSnapshot
-
14 |
15 | /// The section snapshot after the transaction occured.
16 | public let finalSnapshot: OutlineViewDiffableDataSourceSnapshot
-
17 |
18 | /// A collection of insertions and removals that describe the difference between initial and final snapshots.
19 | public let difference: CollectionDifference
-
20 | }
21 |
22 | extension OutlineViewDiffableDataSourceTransaction {
23 | init(initial: OutlineViewDiffableDataSourceSnapshot
- , final: OutlineViewDiffableDataSourceSnapshot
- ) {
24 | initialSnapshot = initial
25 | finalSnapshot = final
26 | difference = initial.items.difference(from: final.items)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/DiffableDataSource/NSTableView/TableViewDiffableDataSource+Delegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TableViewDiffableDataSource+Delegate.swift
3 | //
4 | //
5 | // Created by Florian Zand on 31.08.23.
6 | //
7 |
8 | import AppKit
9 | import FZUIKit
10 |
11 | extension TableViewDiffableDataSource {
12 | class Delegate: NSObject, NSTableViewDelegate {
13 | weak var dataSource: TableViewDiffableDataSource!
14 | var previousSelectedIDs: [Item.ID] = []
15 |
16 | init(_ dataSource: TableViewDiffableDataSource) {
17 | self.dataSource = dataSource
18 | super.init()
19 | dataSource.tableView.delegate = self
20 | }
21 |
22 | // MARK: - Selecting
23 |
24 | public func tableViewSelectionDidChange(_: Notification) {
25 | guard dataSource.selectionHandlers.didSelect != nil || dataSource.selectionHandlers.didDeselect != nil else {
26 | previousSelectedIDs = dataSource.selectedItems.ids
27 | return
28 | }
29 | let selectedIDs = dataSource.selectedItems.ids
30 | let diff = previousSelectedIDs.difference(to: selectedIDs)
31 |
32 | if diff.added.isEmpty == false, let didSelect = dataSource.selectionHandlers.didSelect {
33 | let selectedItems = dataSource.items[ids: diff.added]
34 | didSelect(selectedItems)
35 | }
36 |
37 | if diff.removed.isEmpty == false, let didDeselect = dataSource.selectionHandlers.didDeselect {
38 | let deselectedItems = dataSource.items[ids: diff.removed]
39 | if deselectedItems.isEmpty == false {
40 | didDeselect(deselectedItems)
41 | }
42 | }
43 | previousSelectedIDs = selectedIDs
44 | }
45 |
46 | public func tableView(_ tableView: NSTableView, selectionIndexesForProposedSelection proposedSelectionIndexes: IndexSet) -> IndexSet {
47 | var proposedSelectionIndexes = proposedSelectionIndexes
48 | let isEmpty = proposedSelectionIndexes.isEmpty
49 | dataSource.sectionRowIndexes.forEach { proposedSelectionIndexes.remove($0) }
50 | if !isEmpty, proposedSelectionIndexes.isEmpty {
51 | return dataSource.tableView.selectedRowIndexes
52 | }
53 | guard dataSource.selectionHandlers.shouldSelect != nil || dataSource.selectionHandlers.shouldDeselect != nil else {
54 | return proposedSelectionIndexes
55 | }
56 | let selectedRows = Array(dataSource.tableView.selectedRowIndexes)
57 | let proposedRows = Array(proposedSelectionIndexes)
58 |
59 | let diff = selectedRows.difference(to: proposedRows)
60 | let selectedItems = diff.added.compactMap { dataSource.item(forRow: $0) }
61 | let deselectedItems = diff.removed.compactMap { dataSource.item(forRow: $0) }
62 |
63 | var selections: [Item] = selectedItems
64 | if !selectedItems.isEmpty, let shouldSelectRows = dataSource.selectionHandlers.shouldSelect?(selectedItems) {
65 | selections = selectedItems.filter({ shouldSelectRows.contains($0) })
66 | }
67 |
68 | if !deselectedItems.isEmpty, let shouldDeselectRows = dataSource.selectionHandlers.shouldDeselect?(deselectedItems) {
69 | selections += deselectedItems.filter({ !shouldDeselectRows.contains($0) })
70 | }
71 |
72 | return IndexSet(selections.compactMap { dataSource.row(for: $0) })
73 | }
74 |
75 | // MARK: - View
76 |
77 | public func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
78 | dataSource.dataSource.tableView(tableView, viewFor: tableColumn, row: row)
79 | }
80 |
81 | public func tableView(_ tableView: NSTableView, isGroupRow row: Int) -> Bool {
82 | dataSource.dataSource.tableView(tableView, isGroupRow: row)
83 | }
84 |
85 | public func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? {
86 | dataSource.dataSource.tableView(tableView, rowViewForRow: row)
87 | }
88 |
89 | // MARK: - Row Action
90 |
91 | public func tableView(_: NSTableView, rowActionsForRow row: Int, edge: NSTableView.RowActionEdge) -> [NSTableViewRowAction] {
92 | if let rowActionProvider = dataSource.rowActionProvider, let item = dataSource.item(forRow: row) {
93 | return rowActionProvider(item, edge)
94 | }
95 | return []
96 | }
97 |
98 | // MARK: - Column
99 |
100 | func tableView(_ tableView: NSTableView, userCanChangeVisibilityOf column: NSTableColumn) -> Bool {
101 | dataSource.columnHandlers.userCanChangeVisibility?(column) ?? false
102 | }
103 |
104 | func tableView(_ tableView: NSTableView, userDidChangeVisibilityOf columns: [NSTableColumn]) {
105 | dataSource.columnHandlers.userDidChangeVisibility?(columns)
106 | }
107 |
108 | public func tableViewColumnDidMove(_ notification: Notification) {
109 | guard let didReorder = dataSource.columnHandlers.didReorder else { return }
110 | guard let oldPos = notification.userInfo?["NSOldColumn"] as? Int,
111 | let newPos = notification.userInfo?["NSNewColumn"] as? Int,
112 | let tableColumn = dataSource.tableView.tableColumns[safe: newPos] else { return }
113 | didReorder(tableColumn, oldPos, newPos)
114 | }
115 |
116 | public func tableViewColumnDidResize(_ notification: Notification) {
117 | guard let didResize = dataSource.columnHandlers.didResize else { return }
118 | guard let tableColumn = notification.userInfo?["NSTableColumn"] as? NSTableColumn, let oldWidth = notification.userInfo?["NSOldWidth"] as? CGFloat else { return }
119 | didResize(tableColumn, oldWidth)
120 | }
121 |
122 | public func tableView(_: NSTableView, shouldReorderColumn columnIndex: Int, toColumn newColumnIndex: Int) -> Bool {
123 | guard let tableColumn = dataSource.tableView.tableColumns[safe: columnIndex] else { return true }
124 | return dataSource.columnHandlers.shouldReorder?(tableColumn, newColumnIndex) ?? true
125 | }
126 |
127 | func tableView(_ tableView: NSTableView, didClick tableColumn: NSTableColumn) {
128 | dataSource.columnHandlers.didClick?(tableColumn)
129 | }
130 |
131 | func tableView(_ tableView: NSTableView, mouseDownInHeaderOf tableColumn: NSTableColumn) {
132 | dataSource.columnHandlers.didClickHeader?(tableColumn)
133 | }
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/DiffableDataSource/Shared/DiffableDataSourceTransaction.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DiffableDataSourceTransaction.swift
3 | //
4 | //
5 | // Created by Florian Zand on 16.09.23.
6 | //
7 |
8 | import AppKit
9 |
10 | /// A transaction that describes the changes after reordering the items in the view.
11 | public struct DiffableDataSourceTransaction where Section: Hashable, Item: Hashable {
12 | /// The snapshot before the transaction occured.
13 | public let initialSnapshot: NSDiffableDataSourceSnapshot
14 |
15 | /// The snapshot after the transaction occured.
16 | public let finalSnapshot: NSDiffableDataSourceSnapshot
17 |
18 | /// A collection of insertions and removals that describe the difference between initial and final snapshots.
19 | public let difference: CollectionDifference
-
20 | }
21 |
22 | extension DiffableDataSourceTransaction {
23 | init(initial: NSDiffableDataSourceSnapshot, final: NSDiffableDataSourceSnapshot) {
24 | initialSnapshot = initial
25 | finalSnapshot = final
26 | difference = initial.itemIdentifiers.difference(from: final.itemIdentifiers)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/DiffableDataSource/Shared/EmptyView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EmptyView.swift
3 | //
4 | //
5 | // Created by Florian Zand on 25.10.24.
6 | //
7 |
8 | import AppKit
9 | import FZUIKit
10 |
11 | /// View that is displayed if a table or collection view is empty.
12 | class EmptyView: NSView {
13 |
14 | var view: NSView? {
15 | didSet {
16 | guard oldValue != view else { return }
17 | oldValue?.removeFromSuperview()
18 | if let view = view {
19 | view.frame.size = bounds.size
20 | addSubview(view)
21 | }
22 | }
23 | }
24 |
25 | var configuration: NSContentConfiguration? {
26 | get { (view as? NSContentView)?.configuration }
27 | set {
28 | if let newValue = newValue {
29 | if let view = view as? NSContentView, view.supports(newValue) {
30 | view.configuration = newValue
31 | } else {
32 | view = newValue.makeContentView()
33 | }
34 | } else {
35 | view = nil
36 | }
37 | }
38 | }
39 |
40 | public init(view: NSView) {
41 | super.init(frame: .zero)
42 | defer { self.view = view }
43 | }
44 |
45 | public init(configuration: NSContentConfiguration) {
46 | super.init(frame: .zero)
47 | defer { self.configuration = configuration }
48 | }
49 |
50 | required init?(coder: NSCoder) {
51 | fatalError("init(coder:) has not been implemented")
52 | }
53 |
54 | override func layout() {
55 | super.layout()
56 | view?.frame.size = bounds.size
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/DiffableDataSource/Shared/QuicklookPreviewItem.swift:
--------------------------------------------------------------------------------
1 | //
2 | // QuicklookPreviewItem.swift
3 | //
4 | //
5 | // Created by Florian Zand on 15.09.23.
6 | //
7 |
8 | import AppKit
9 | import FZQuicklook
10 | import QuickLookUI
11 |
12 | /// Quicklook item for selected collection view items and table view cells.
13 | class QuicklookPreviewItem: NSObject, QLPreviewItem, QuicklookPreviewable {
14 | let preview: QuicklookPreviewable
15 | weak var view: NSView?
16 |
17 | public var previewItemURL: URL? {
18 | preview.previewItemURL
19 | }
20 |
21 | public var previewItemFrame: CGRect? {
22 | if let view = self.view {
23 | if let collectionViewItem = view.parentController as? NSCollectionViewItem {
24 | if let view = view as? NSItemContentView, view.appliedConfiguration.image != nil {
25 | let imageView = view.contentView.imageView
26 | if collectionViewItem.collectionView?.visibleRect.intersects(imageView.frame) == true {
27 | return imageView.frameOnScreen
28 | }
29 | } else if collectionViewItem.collectionView?.visibleRect.intersects(view.frame) == true {
30 | return view.frameOnScreen
31 | }
32 | } else if let view = view as? NSTableRowView {
33 | if view.tableView?.visibleRect.intersects(view.frame) == true {
34 | return view.frameOnScreen
35 | }
36 | } else {
37 | return view.frameOnScreen
38 | }
39 | return .zero
40 | }
41 | return preview.previewItemFrame
42 | }
43 |
44 | public var previewItemTitle: String? {
45 | preview.previewItemTitle
46 | }
47 |
48 | public var previewItemTransitionImage: NSImage? {
49 | if let view = view as? NSItemContentView, view.appliedConfiguration.image != nil {
50 | return view.contentView.imageView.renderedImage
51 | }
52 | return view?.renderedImage ?? preview.previewItemTransitionImage
53 | }
54 |
55 | init(_ preview: QuicklookPreviewable, view: NSView? = nil) {
56 | self.preview = preview
57 | self.view = view
58 | }
59 | }
60 |
61 | extension NSPasteboard.PasteboardType {
62 | /// Collection view and table view drag & drop type.
63 | static let itemID: NSPasteboard.PasteboardType = .init("DiffableDataSource.ItemID")
64 | }
65 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/AdvancedCollectionTableView.md:
--------------------------------------------------------------------------------
1 | # ``AdvancedCollectionTableView``
2 |
3 | A collection of classes and extensions for `NSCollectionView` and `NSTableView`, many of them ported from modern `UIKit`.
4 |
5 | ## Overview
6 |
7 | `AdvancedCollectionTableView` provides a collection of classes and extensions for `NSCollectionView` and `NSTableView`.
8 |
9 | ## Topics
10 |
11 | ### Collection- & TableView Data Sources
12 |
13 | - ``CollectionViewDiffableDataSource``
14 | - ``TableViewDiffableDataSource``
15 | - ``DiffableDataSourceTransaction``
16 | - ``NSDiffableDataSourceSnapshotApplyOption``
17 |
18 | ### OutlineView Data Source
19 |
20 | - ``OutlineViewDiffableDataSource``
21 | - ``OutlineViewDiffableDataSourceSnapshot``
22 | - ``OutlineViewDiffableDataSourceTransaction``
23 |
24 | ### CollectionView Registration
25 |
26 | - ``AppKit/NSCollectionView/ItemRegistration``
27 | - ``AppKit/NSCollectionView/SupplementaryRegistration``
28 |
29 | ### TableView Registration
30 |
31 | - ``AppKit/NSTableView/CellRegistration``
32 | - ``AppKit/NSTableView/RowRegistration``
33 |
34 | ### Item Content Configuration
35 |
36 | -
37 | - ``NSItemContentConfiguration``
38 | - ``NSItemContentView``
39 | - ``NSItemConfigurationState``
40 |
41 | ### List Content Configuration
42 |
43 | -
44 | - ``NSListContentConfiguration``
45 | - ``NSListContentView``
46 | - ``NSListConfigurationState``
47 |
48 | ### Collection View Extensions
49 |
50 | -
51 | -
52 | -
53 | - ``NSCollectionViewSupplementaryRegistration``
54 |
55 | ### Table View Extensions
56 |
57 | -
58 | -
59 | -
60 | -
61 | - ``NSTableViewCellRegistration``
62 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Docs/Configurating Collection Items.md:
--------------------------------------------------------------------------------
1 | # Configurating Collection View Items
2 |
3 | Configurate the content and background of a collection view item.
4 |
5 | ## Overview
6 |
7 | The content and background of a `NSCollectionViewItem` can be configurated by providing a `NSContentConfiguration` to an item's ``AppKit/NSCollectionViewItem/contentConfiguration`` and ``AppKit/NSCollectionViewItem/backgroundConfiguration``.
8 |
9 | ## Item content configuration
10 |
11 | - ``NSItemContentConfiguration``
12 | - ``NSItemContentView``
13 |
14 | ``NSItemContentConfiguration`` is a content configuration suitable for a collection view item. It displays an image and/or view with a text and secondary text.
15 |
16 | 
17 |
18 | ```swift
19 | var content = NSItemContentConfiguration()
20 |
21 | // Configure content.
22 | content.image = NSImage(named: "Mozart")
23 | content.text = "Mozart"
24 | content.secondaryText = "A genius composer"
25 |
26 | // Customize appearance.
27 | content.textProperties.font = .body
28 | content.secondaryTextProperties.font = .caption1
29 |
30 | collectionViewItem.contentConfiguration = content
31 | ```
32 |
33 | ## Managing the content
34 |
35 | To manage the content of the item you provide a `NSContentConfiguration` to items `contentConfiguration`.
36 |
37 | - ``AppKit/NSCollectionViewItem/contentConfiguration``
38 | - ``AppKit/NSCollectionViewItem/automaticallyUpdatesContentConfiguration``
39 |
40 | ## Configuring the background
41 |
42 | To configurate the background of the item you provide a `NSContentConfiguration` to items `backgroundConfiguration`.
43 |
44 | - ``AppKit/NSCollectionViewItem/backgroundConfiguration``
45 | - ``AppKit/NSCollectionViewItem/automaticallyUpdatesContentConfiguration``
46 |
47 | ## Managing the state
48 |
49 | `configurationState` provides the current state of an item (e.g. `isSelected` or `highlightState).
50 |
51 | - ``AppKit/NSCollectionViewItem/configurationState``
52 | - ``AppKit/NSCollectionViewItem/configurationUpdateHandler-swift.property``
53 |
54 | ### Handling updates to the state
55 |
56 | To handle updates of an item’s state, provide a handler to the items `configurationUpdateHandler`.
57 |
58 | - ``NSItemConfigurationState``
59 | - ``AppKit/NSCollectionViewItem/ConfigurationUpdateHandler-swift.typealias``
60 |
61 | **Example usage of the configuration update handler:**
62 |
63 | ```swift
64 | var content = NSItemContentConfiguration()
65 | content.text = "Mozart"
66 | content.image = NSImage(named: "Mozart")
67 |
68 | collectionViewItem.contentConfiguration = content
69 |
70 | collectionViewItem.configurationUpdateHandler = { item, state in
71 | if state.isSelected {
72 | content.contentProperties.borderWidth = 1.0
73 | content.contentProperties.borderColor = .controlAccentColor
74 | } else {
75 | content.contentProperties.borderWidth = 0.0
76 | content.contentProperties.borderColor = nil
77 | }
78 | collectionViewItem.contentConfiguration = content
79 | }
80 | ```
81 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Docs/Configurating Table Cells.md:
--------------------------------------------------------------------------------
1 | # Configurating Table Cells
2 |
3 | Configurate the content of a table view cell.
4 |
5 | ## Overview
6 |
7 | The content of a `NSTableCellView` can be configurated by providing a `NSContentConfiguration` to a table cell's ``AppKit/NSTableCellView/contentConfiguration``.
8 |
9 | ## Topics
10 |
11 | ### Table cell content configuration
12 |
13 | - ``NSListContentConfiguration``
14 | - ``NSListContentView``
15 |
16 | ``NSListContentConfiguration`` is a content configuration suitable for a table row. It can display a text, secondary text, image and view.
17 |
18 | 
19 |
20 | ```swift
21 | var content = tableCell.defaultContentConfiguration()
22 |
23 | // Configure content.
24 | content.image = NSImage(systemSymbolName: "star")
25 | content.text = "Favorites"
26 |
27 | // Customize appearance.
28 | content.imageProperties.tintColor = .purple
29 |
30 | tableCell.contentConfiguration = content
31 | ```
32 |
33 | ### Managing the content
34 |
35 | To manage the content of the cell you provide a `NSContentConfiguration` to cells `contentConfiguration`.
36 |
37 | - ``AppKit/NSTableCellView/contentConfiguration``
38 | - ``AppKit/NSTableCellView/defaultContentConfiguration()``
39 | - ``AppKit/NSTableCellView/automaticallyUpdatesContentConfiguration``
40 |
41 | ### Managing the state
42 |
43 | `configurationState` provides the current state of a table view cell (e.g. `isSelected` or `isHovered`).
44 |
45 | - ``AppKit/NSTableCellView/configurationState``
46 | - ``NSListConfigurationState``
47 | - ``AppKit/NSTableCellView/setNeedsUpdateConfiguration()``
48 | - ``AppKit/NSTableCellView/updateConfiguration(using:)``
49 |
50 | ### Handling updates to the state
51 |
52 | To handle updates of a table view cell’s state, provide a handler to the cells `configurationUpdateHandler`.
53 |
54 | - ``AppKit/NSTableCellView/configurationUpdateHandler-swift.property``
55 | - ``AppKit/NSTableCellView/ConfigurationUpdateHandler-swift.typealias``
56 |
57 | **Example usage of the configuration update handler:**
58 |
59 | ```swift
60 | var content = tableCell.defaultContentConfiguration()
61 | content.image = NSImage(systemSymbolName: "star")
62 | content.imageProperties.tintColor = .black
63 |
64 | tableCell.contentConfiguration = content
65 |
66 | tableCell.configurationUpdateHandler = {
67 | newState in
68 | if newState.isSelected {
69 | content.imageProperties.tintColor = .controlAccentColor
70 | } else {
71 | content.imageProperties.tintColor = .black
72 | }
73 | tableCell.contentConfiguration = content
74 | }
75 | ```
76 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/AppKit Extensions/NSCollecionView/NSCollectionView+.md:
--------------------------------------------------------------------------------
1 | # NSCollectionView
2 |
3 | Extensions for `NSCollectionView`.
4 |
5 | ## Topics
6 |
7 | ### Creating items
8 |
9 | - ``AppKit/NSCollectionView/register(_:)``
10 | - ``AppKit/NSCollectionView/register(_:nib:)``
11 | - ``AppKit/NSCollectionView/makeItem(for:)``
12 |
13 | ### Creating items using item registration
14 |
15 | - ``AppKit/NSCollectionView/ItemRegistration``
16 | - ``AppKit/NSCollectionView/makeItem(using:for:element:)``
17 |
18 | ### Creating supplementary views
19 |
20 | - ``AppKit/NSCollectionView/register(_:forSupplementaryKind:)``
21 | - ``AppKit/NSCollectionView/register(_:nib:forSupplementaryKind:)``
22 | - ``AppKit/NSCollectionView/makeSupplementaryView(ofKind:for:)``
23 |
24 | ### Creating supplementary views using supplementary registration
25 |
26 | - ``AppKit/NSCollectionView/SupplementaryRegistration``
27 | - ``AppKit/NSCollectionView/makeSupplementaryView(using:for:)``
28 |
29 | ### Reloading content
30 |
31 | - ``AppKit/NSCollectionView/reconfigureItems(at:)``
32 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/AppKit Extensions/NSCollecionView/NSCollectionViewDiffableDataSource+.md:
--------------------------------------------------------------------------------
1 | # NSCollectionViewDiffableDataSource
2 |
3 | Extensions for `NSCollectionViewDiffableDataSource`.
4 |
5 | ## Topics
6 |
7 | ### Creating a Diffable Data Source
8 |
9 | - ``AppKit/NSCollectionViewDiffableDataSource/init(collectionView:itemRegistration:)``
10 | - ``AppKit/NSCollectionViewDiffableDataSource/init(collectionView:itemRegistration:supplementaryRegistrations:)``
11 |
12 | ### Creating supplementary views
13 |
14 | - ``AppKit/NSCollectionViewDiffableDataSource/useSupplementaryRegistrations(_:)``
15 |
16 | ### Updating data
17 |
18 | - ``AppKit/NSCollectionViewDiffableDataSource/apply(_:_:completion:)``
19 |
20 | ### Supporting deleting
21 |
22 | - ``AppKit/NSCollectionViewDiffableDataSource/deletingHandlers-swift.property``
23 | - ``AppKit/NSCollectionViewDiffableDataSource/DeletingHandlers-swift.struct``
24 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/AppKit Extensions/NSCollecionView/NSCollectionViewDiffableDataSource+DeletingHandlers.md:
--------------------------------------------------------------------------------
1 | # ``AppKit/NSCollectionViewDiffableDataSource/DeletingHandlers-swift.struct``
2 |
3 | ## Topics
4 |
5 | ### Deleting items
6 |
7 | - ``canDelete``
8 | - ``willDelete``
9 | - ``didDelete``
10 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/AppKit Extensions/NSCollecionView/NSCollectionViewItem+.md:
--------------------------------------------------------------------------------
1 | # NSCollectionViewItem
2 |
3 | Extensions for `NSCollectionViewItem`.
4 |
5 | ## Topics
6 |
7 | ### Configuring the background
8 |
9 | - ``AppKit/NSCollectionViewItem/defaultBackgroundConfiguration()``
10 | - ``AppKit/NSCollectionViewItem/backgroundConfiguration``
11 | - ``AppKit/NSCollectionViewItem/automaticallyUpdatesBackgroundConfiguration``
12 |
13 | ### Managing the content
14 |
15 | - ``AppKit/NSCollectionViewItem/contentConfiguration``
16 | - ``AppKit/NSCollectionViewItem/automaticallyUpdatesContentConfiguration``
17 |
18 | ### Managing the state
19 |
20 | - ``AppKit/NSCollectionViewItem/configurationState``
21 | - ``AppKit/NSCollectionViewItem/setNeedsUpdateConfiguration()``
22 | - ``AppKit/NSCollectionViewItem/updateConfiguration(using:)``
23 | - ``AppKit/NSCollectionViewItem/configurationUpdateHandler-swift.property``
24 | - ``AppKit/NSCollectionViewItem/ConfigurationUpdateHandler-swift.typealias``
25 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/AppKit Extensions/NSCollecionView/NSCollectionViewSupplementaryRegistration.md:
--------------------------------------------------------------------------------
1 | # ``NSCollectionViewSupplementaryRegistration``
2 |
3 | ## Topics
4 |
5 | ### Supplementary kind
6 |
7 | - ``elementKind``
8 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/AppKit Extensions/NSTableView/NSTableCellView+.md:
--------------------------------------------------------------------------------
1 | # NSTableCellView
2 |
3 | Extensions for `NSTableCellView`.
4 |
5 | ## Topics
6 |
7 | ### Managing the content
8 |
9 | - ``AppKit/NSTableCellView/defaultContentConfiguration()``
10 | - ``AppKit/NSTableCellView/contentConfiguration``
11 | - ``AppKit/NSTableCellView/automaticallyUpdatesContentConfiguration``
12 |
13 | ### Managing the state
14 |
15 | - ``AppKit/NSTableCellView/configurationState``
16 | - ``AppKit/NSTableCellView/setNeedsUpdateConfiguration()``
17 | - ``AppKit/NSTableCellView/updateConfiguration(using:)``
18 | - ``AppKit/NSTableCellView/configurationUpdateHandler-swift.property``
19 | - ``AppKit/NSTableCellView/ConfigurationUpdateHandler-swift.typealias``
20 |
21 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/AppKit Extensions/NSTableView/NSTableRowView+.md:
--------------------------------------------------------------------------------
1 | # NSTableRowView
2 |
3 | Extensions for `NSTableRowView`.
4 |
5 | ## Topics
6 |
7 | ### Managing the content
8 |
9 | - ``AppKit/NSTableRowView/contentConfiguration``
10 | - ``AppKit/NSTableRowView/automaticallyUpdatesContentConfiguration``
11 |
12 | ### Managing the state
13 |
14 | - ``AppKit/NSTableRowView/configurationState``
15 | - ``AppKit/NSTableRowView/setNeedsUpdateConfiguration()``
16 | - ``AppKit/NSTableRowView/updateConfiguration(using:)``
17 | - ``AppKit/NSTableRowView/configurationUpdateHandler-swift.property``
18 | - ``AppKit/NSTableRowView/ConfigurationUpdateHandler-swift.typealias``
19 |
20 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/AppKit Extensions/NSTableView/NSTableView+.md:
--------------------------------------------------------------------------------
1 | # NSTableView
2 |
3 | Extensions for `NSTableView`.
4 |
5 | ## Topics
6 |
7 | ### Creating table cell views
8 |
9 | - ``AppKit/NSTableView/register(_:)``
10 | - ``AppKit/NSTableView/makeView(for:)``
11 |
12 | ### Creating table cell views using cell registration
13 |
14 | - ``AppKit/NSTableView/CellRegistration``
15 | - ``AppKit/NSTableView/makeCellView(using:forColumn:row:item:)``
16 |
17 | ### Creating table row views
18 |
19 | - ``AppKit/NSTableView/RowRegistration``
20 | - ``AppKit/NSTableView/makeRowView(using:forRow:item:)``
21 |
22 | ### Reloading content
23 |
24 | - ``AppKit/NSTableView/reconfigureRows(at:)``
25 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/AppKit Extensions/NSTableView/NSTableViewCellRegistration.md:
--------------------------------------------------------------------------------
1 | # ``NSTableViewCellRegistration``
2 |
3 | ## Topics
4 |
5 | ### Configurating columns
6 |
7 | - ``columnIdentifiers``
8 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/AppKit Extensions/NSTableView/NSTableViewDiffableDataSource+.md:
--------------------------------------------------------------------------------
1 | # NSTableViewDiffableDataSource
2 |
3 | Extensions for `NSTableViewDiffableDataSource`.
4 |
5 | ## Topics
6 |
7 | ### Creating a Diffable Data Source
8 |
9 | - ``AppKit/NSTableViewDiffableDataSource/init(tableView:cellRegistration:)``
10 | - ``AppKit/NSTableViewDiffableDataSource/init(tableView:cellRegistration:sectionHeaderRegistration:)``
11 | - ``AppKit/NSTableViewDiffableDataSource/init(tableView:cellRegistrations:)``
12 | - ``AppKit/NSTableViewDiffableDataSource/init(tableView:cellRegistrations:sectionHeaderRegistration:)``
13 |
14 | ### Creating Section Views
15 |
16 | - ``AppKit/NSTableViewDiffableDataSource/applySectionHeaderViewRegistration(_:)``
17 |
18 | ### Updating data
19 |
20 | - ``AppKit/NSTableViewDiffableDataSource/apply(_:_:completion:)``
21 |
22 | ### Supporting deleting
23 |
24 | - ``AppKit/NSTableViewDiffableDataSource/deletingHandlers-swift.property``
25 | - ``AppKit/NSTableViewDiffableDataSource/DeletingHandlers-swift.struct``
26 |
27 | ### Supporting protocol requirements
28 |
29 | - ``AppKit/NSTableViewDiffableDataSource/tableView(_:isGroupRow:)``
30 | - ``AppKit/NSTableViewDiffableDataSource/tableView(_:rowViewForRow:)``
31 | - ``AppKit/NSTableViewDiffableDataSource/tableView(_:viewFor:row:)``
32 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/AppKit Extensions/NSTableView/NSTableViewDiffableDataSource+DeletingHandlers.md:
--------------------------------------------------------------------------------
1 | # ``AppKit/NSTableViewDiffableDataSource/DeletingHandlers-swift.struct``
2 |
3 | ## Topics
4 |
5 | ### Deleting items
6 |
7 | - ``canDelete``
8 | - ``willDelete``
9 | - ``didDelete``
10 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/Configuration/Configurations/ItemContentConfiguration/NSItemContentConfiguration+Badge.md:
--------------------------------------------------------------------------------
1 | # ``NSItemContentConfiguration/Badge-swift.struct``
2 |
3 | ## Topics
4 |
5 | ### Creating a badge
6 |
7 | - ``init()``
8 | - ``text(_:textStyle:textColor:color:shape:type:position:)``
9 | - ``image(_:text:textStyle:color:shape:type:position:)``
10 | - ``symbolImage(_:text:size:color:backgroundColor:shape:type:position:)``
11 | - ``view(_:color:shape:type:position:)``
12 |
13 | ### Configurating badge
14 |
15 | - ``type``
16 | - ``position-swift.property``
17 | - ``BadgeType``
18 | - ``Position-swift.enum``
19 |
20 | ### Customizing content
21 |
22 | - ``text``
23 | - ``attributedText``
24 | - ``image``
25 | - ``view``
26 | - ``toolTip``
27 |
28 | ### Customizing content appearance
29 |
30 | - ``textProperties-swift.property``
31 | - ``imageProperties-swift.property``
32 | - ``TextProperties-swift.struct``
33 | - ``ImageProperties-swift.struct``
34 |
35 | ### Customizing background
36 |
37 | - ``backgroundColor``
38 | - ``backgroundColorTransformer``
39 | - ``resolvedBackgroundColor()``
40 | - ``visualEffect``
41 |
42 | ### Customizing shape
43 |
44 | - ``shape``
45 | - ``Shape``
46 |
47 | ### Customizing border
48 |
49 | - ``border``
50 |
51 | ### Customizing shadow
52 |
53 | - ``shadow``
54 |
55 | ### Customizing layout
56 |
57 | - ``margins``
58 | - ``maxWidth``
59 | - ``imageToTextPadding``
60 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/Configuration/Configurations/ItemContentConfiguration/NSItemContentConfiguration+Content.md:
--------------------------------------------------------------------------------
1 | # ``NSItemContentConfiguration/ContentProperties-swift.struct``
2 |
3 | ## Topics
4 |
5 | ### Configuring content properties
6 |
7 | - ``cornerRadius``
8 | - ``scaleTransform``
9 | - ``rotation``
10 | - ``visualEffect``
11 | - ``toolTip``
12 |
13 | ### Configuring background color
14 |
15 | - ``backgroundColor``
16 | - ``backgroundColorTransformer``
17 | - ``resolvedBackgroundColor()``
18 |
19 | ### Configuring border
20 |
21 | - ``border``
22 | - ``borderTransformer``
23 | - ``resolvedBorder()``
24 |
25 | ### Configuring shadow
26 |
27 | - ``shadow``
28 | - ``shadowTransformer``
29 | - ``resolvedShadow()``
30 |
31 | ### Configuring size
32 |
33 | - ``maximumSize``
34 | - ``ProposedSize``
35 |
36 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/Configuration/Configurations/ItemContentConfiguration/NSItemContentConfiguration+ImageProperties.md:
--------------------------------------------------------------------------------
1 | # ``NSItemContentConfiguration/ImageProperties-swift.struct``
2 |
3 | ## Topics
4 |
5 | ### Customizing appearance
6 |
7 | - ``scaling``
8 | - ``symbolConfiguration``
9 | - ``ImageScaling``
10 |
11 | ### Configuring tint color
12 |
13 | - ``tintColor``
14 | - ``tintColorTransformer``
15 | - ``resolvedTintColor()``
16 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/Configuration/Configurations/ItemContentConfiguration/NSItemContentConfiguration.md:
--------------------------------------------------------------------------------
1 | # ``NSItemContentConfiguration``
2 |
3 | ## Topics
4 |
5 | ### Creating item configurations
6 |
7 | - ``init()``
8 | - ``viewItem(_:text:secondaryText:cornerRadius:)``
9 | - ``imageItem(_:text:secondaryText:cornerRadius:)``
10 | - ``listItem(_:secondaryText:image:)``
11 |
12 | ### Customizing text
13 |
14 | - ``text``
15 | - ``attributedText``
16 | - ``secondaryText``
17 | - ``secondaryAttributedText``
18 |
19 | ### Customizing placeholder
20 |
21 | - ``placeholderText``
22 | - ``attributedPlaceholderText``
23 | - ``secondaryPlaceholderText``
24 | - ``secondaryAttributedPlaceholderText``
25 |
26 | ### Customizing content
27 |
28 | - ``image``
29 | - ``view``
30 | - ``overlayView``
31 |
32 | ### Configurating badges
33 |
34 | - ``badges``
35 | - ``Badge``
36 |
37 | ### Customizing appearance
38 |
39 | - ``textProperties``
40 | - ``secondaryTextProperties``
41 | - ``imageProperties-swift.property``
42 | - ``contentProperties-swift.property``
43 | - ``scaleTransform``
44 | - ``rotation``
45 | - ``alpha``
46 | - ``toolTip``
47 | - ``TextProperties``
48 | - ``ImageProperties-swift.struct``
49 | - ``ContentProperties-swift.struct``
50 |
51 | ### Customizing layout
52 |
53 | - ``contentPosition-swift.property``
54 | - ``contentToTextPadding``
55 | - ``textToSecondaryTextPadding``
56 | - ``margins``
57 | - ``ContentPosition-swift.enum``
58 |
59 | ### Creating a content view
60 |
61 | - ``makeContentView()``
62 |
63 | ### Updating the configuration
64 |
65 | - ``updated(for:)``
66 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/Configuration/Configurations/ItemContentConfiguration/NSItemContentView.md:
--------------------------------------------------------------------------------
1 | # ``NSItemContentView``
2 |
3 | ## Topics
4 |
5 | ### Creating a collection item content view
6 |
7 | - ``init(configuration:)``
8 |
9 | ### Managing the content configuration
10 |
11 | - ``configuration``
12 |
13 | ### Managing the content layout
14 |
15 | - ``textLayoutGuide``
16 | - ``secondaryTextLayoutGuide``
17 | - ``contentLayoutGuide``
18 |
19 | ### Determining configuration support
20 |
21 | - ``supports(_:)``
22 |
23 | ### Handling events
24 |
25 | - ``hitTest(_:)``
26 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/Configuration/Configurations/ListContentConfiguration/NSListContentConfiguration+Badge.md:
--------------------------------------------------------------------------------
1 | # ``NSListContentConfiguration/Badge-swift.struct``
2 |
3 | ## Topics
4 |
5 | ### Creating a badge
6 |
7 | - ``init()``
8 | - ``text(_:font:color:backgroundColor:)``
9 | - ``image(_:backgroundColor:)``
10 | - ``symbolImage(_:textStyle:color:backgroundColor:)``
11 |
12 | ### Configurating badge
13 |
14 | - ``position-swift.property``
15 | - ``Position-swift.enum``
16 |
17 | ### Customizing content
18 |
19 | - ``text``
20 | - ``attributedText``
21 | - ``image``
22 | - ``toolTip``
23 |
24 | ### Customizing content appearance
25 |
26 | - ``font``
27 | - ``imageProperties-swift.property``
28 | - ``ImageProperties-swift.struct``
29 |
30 | ### Customizing color
31 |
32 | - ``color``
33 | - ``colorTransformer``
34 | - ``resolvedColor()``
35 |
36 | ### Customizing background color
37 |
38 | - ``backgroundColor``
39 | - ``backgroundColorTransformer``
40 | - ``resolvedBackgroundColor()``
41 |
42 | ### Customizing border
43 |
44 | - ``border``
45 |
46 | ### Customizing shape
47 |
48 | - ``shape``
49 | - ``Shape``
50 |
51 | ### Customizing shadow
52 |
53 | - ``shadow``
54 |
55 | ### Customizing layout
56 |
57 | - ``alignment-swift.property``
58 | - ``margins``
59 | - ``maxWidth``
60 | - ``imageToTextPadding``
61 | - ``margins``
62 | - ``Alignment-swift.enum``
63 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/Configuration/Configurations/ListContentConfiguration/NSListContentConfiguration+Image.md:
--------------------------------------------------------------------------------
1 | # ``NSListContentConfiguration/ImageProperties-swift.struct``
2 |
3 | ## Topics
4 |
5 | ### Configuring scaling
6 |
7 | - ``scaling-swift.property``
8 | - ``Scaling-swift.enum``
9 |
10 | ### Configuring symbol configuration
11 |
12 | - ``symbolConfiguration``
13 |
14 | ### Configuring tint color
15 |
16 | - ``tintColor``
17 | - ``tintColorTransformer``
18 | - ``resolvedTintColor()``
19 |
20 | ### Configuring background color
21 |
22 | - ``backgroundColor``
23 | - ``backgroundColorTransformer``
24 | - ``resolvedBackgroundColor()``
25 |
26 | ### Configuring border
27 |
28 | - ``border``
29 | - ``borderTransformer``
30 | - ``resolvedBorder()``
31 | - ``cornerRadius``
32 |
33 | ### Configuring shadow
34 |
35 | - ``shadow``
36 | - ``shadowTransformer``
37 | - ``resolvedShadow()``
38 |
39 | ### Configuring tooltip
40 |
41 | - ``toolTip``
42 |
43 | ### Customizing layout
44 |
45 | - ``sizing-swift.property``
46 | - ``Sizing-swift.enum``
47 | - ``position-swift.property``
48 | - ``Position-swift.enum``
49 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/Configuration/Configurations/ListContentConfiguration/NSListContentConfiguration.md:
--------------------------------------------------------------------------------
1 | # ``NSListContentConfiguration``
2 |
3 | ## Topics
4 |
5 | ### Creating item configurations
6 |
7 | - ``init()``
8 | - ``plain(imageColor:)``
9 | - ``sidebar(imageColor:)``
10 | - ``sidebarHeader(imageColor:)``
11 | - ``sidebarLarge(imageColor:)``
12 | - ``text(_:)``
13 | - ``editableText(_:placeholderText:onTextEditEnd:stringValidation:)``
14 |
15 |
16 | ### Customizing content
17 |
18 | - ``text``
19 | - ``attributedText``
20 | - ``secondaryText``
21 | - ``secondaryAttributedText``
22 | - ``image``
23 |
24 | ### Customizing placeholder
25 |
26 | - ``placeholderText``
27 | - ``attributedPlaceholderText``
28 | - ``secondaryPlaceholderText``
29 | - ``secondaryAttributedPlaceholderText``
30 |
31 | ### Configurating badge
32 |
33 | - ``badge-swift.property``
34 | - ``Badge-swift.struct``
35 |
36 | ### Customizing appearance
37 |
38 | - ``textProperties``
39 | - ``secondaryTextProperties``
40 | - ``imageProperties-swift.property``
41 | - ``scaleTransform``
42 | - ``rotation``
43 | - ``alpha``
44 | - ``toolTip``
45 | - ``ImageProperties-swift.struct``
46 | - ``TextProperties``
47 |
48 | ### Customizing layout
49 |
50 | - ``imageToTextPadding``
51 | - ``textToSecondaryTextPadding``
52 | - ``textToBadgePadding``
53 | - ``margins``
54 |
55 | ### Creating a content view
56 |
57 | - ``makeContentView()``
58 |
59 | ### Updating the configuration
60 |
61 | - ``updated(for:)``
62 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/Configuration/Configurations/ListContentConfiguration/NSListContentView.md:
--------------------------------------------------------------------------------
1 | # ``NSListContentView``
2 |
3 | ## Topics
4 |
5 | ### Creating a list content view
6 |
7 | - ``init(configuration:)``
8 |
9 | ### Managing the content configuration
10 |
11 | - ``configuration``
12 |
13 | ### Managing the content layout
14 |
15 | - ``textLayoutGuide``
16 | - ``secondaryTextLayoutGuide``
17 | - ``imageLayoutGuide``
18 |
19 | ### Determining configuration support
20 |
21 | - ``supports(_:)``
22 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/Configuration/Configurations/Shared/TextProperties.md:
--------------------------------------------------------------------------------
1 | # ``TextProperties``
2 |
3 | ## Topics
4 |
5 | ### Creating text properties
6 |
7 | - ``system(_:weight:design:)``
8 | - ``system(size:weight:design:)``
9 | - ``primary``
10 | - ``secondary``
11 | - ``tertiary``
12 | - ``body``
13 | - ``callout``
14 | - ``caption1``
15 | - ``caption2``
16 | - ``footnote``
17 | - ``headline``
18 | - ``subheadline``
19 | - ``largeTitle``
20 | - ``title1``
21 | - ``title2``
22 | - ``title3``
23 |
24 | ### Configurating appearance
25 |
26 | - ``font``
27 | - ``color``
28 | - ``colorTransformer``
29 | - ``resolvedColor()``
30 | - ``alignment``
31 | - ``toolTip``
32 |
33 | ### Configurating lines and layout
34 |
35 | - ``maximumNumberOfLines``
36 | - ``lineBreakMode``
37 | - ``adjustsFontSizeToFitWidth``
38 | - ``minimumScaleFactor``
39 | - ``allowsDefaultTighteningForTruncation``
40 |
41 | ### Configurating interaction
42 |
43 | - ``isSelectable``
44 | - ``isEditable``
45 | - ``onEditEnd``
46 | - ``stringValidation``
47 | - ``editingActionOnEnterKeyDown``
48 | - ``editingActionOnEscapeKeyDown``
49 | - ``EnterKeyAction``
50 | - ``EscapeKeyAction``
51 |
52 | ### Configurating formatting
53 |
54 | - ``numberFormatter``
55 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/Configuration/States/NSItemConfigurationState.md:
--------------------------------------------------------------------------------
1 | # ``NSItemConfigurationState``
2 |
3 | ## Topics
4 |
5 | ### Creating item configuration states
6 |
7 | - ``init(isSelected:highlight:isEditing:activeState:isHovered:isReordering:isDropTarget:)``
8 |
9 | ### Managing item configuration states
10 |
11 | - ``isSelected``
12 | - ``activeState-swift.property``
13 | - ``isEditing``
14 | - ``isHovered``
15 | - ``isDropTarget``
16 | - ``isReordering``
17 | - ``highlight``
18 | - ``ActiveState-swift.enum``
19 |
20 | ### Creating a configuration state manually
21 |
22 | - ``subscript(_:)``
23 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/Configuration/States/NSListConfigurationState.md:
--------------------------------------------------------------------------------
1 | # ``NSListConfigurationState``
2 |
3 | ## Topics
4 |
5 | ### Creating list configuration states
6 |
7 | - ``init(isSelected:isEnabled:isHovered:isEditing:activeState:isReordering:isDropTarget:isNextSelected:isPreviousSelected:)``
8 |
9 | ### Managing list configuration states
10 |
11 | - ``isSelected``
12 | - ``isEditing``
13 | - ``activeState-swift.property``
14 | - ``isPreviousSelected``
15 | - ``isNextSelected``
16 | - ``isEnabled``
17 | - ``isHovered``
18 | - ``isDropTarget``
19 | - ``isReordering``
20 | - ``ActiveState-swift.enum``
21 |
22 |
23 | ### Creating a configuration state manually
24 |
25 | - ``subscript(_:)``
26 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/DiffableDataSource/CollectionViewDiffableData/CollectionViewDiffableDataSource+DeletingHandlers.md:
--------------------------------------------------------------------------------
1 | # ``CollectionViewDiffableDataSource/DeletingHandlers-swift.struct``
2 |
3 | ## Topics
4 |
5 | ### Deleting items
6 |
7 | - ``canDelete``
8 | - ``willDelete``
9 | - ``didDelete``
10 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/DiffableDataSource/CollectionViewDiffableData/CollectionViewDiffableDataSource+DisplayHandlers.md:
--------------------------------------------------------------------------------
1 | # ``CollectionViewDiffableDataSource/DisplayHandlers-swift.struct``
2 |
3 | ## Topics
4 |
5 | ### Displaying items
6 |
7 | - ``isDisplaying``
8 | - ``didEndDisplaying``
9 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/DiffableDataSource/CollectionViewDiffableData/CollectionViewDiffableDataSource+DragDropHandlers.md:
--------------------------------------------------------------------------------
1 | # ``CollectionViewDiffableDataSource/DragDropHandlers-swift.struct``
2 |
3 | ## Topics
4 |
5 | ### Drag and drop items
6 |
7 | - ``canDragOutside``
8 | - ``didDragOutside``
9 | - ``pasteboardValue``
10 | - ``canDragInside``
11 | - ``didDragInside``
12 | - ``draggingImage``
13 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/DiffableDataSource/CollectionViewDiffableData/CollectionViewDiffableDataSource+HighlightHandlers.md:
--------------------------------------------------------------------------------
1 | # ``CollectionViewDiffableDataSource/HighlightHandlers-swift.struct``
2 |
3 | ## Topics
4 |
5 | ### Highlighting items
6 |
7 | - ``shouldChange``
8 | - ``didChange``
9 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/DiffableDataSource/CollectionViewDiffableData/CollectionViewDiffableDataSource+HoverHandlers.md:
--------------------------------------------------------------------------------
1 | # ``CollectionViewDiffableDataSource/HoverHandlers-swift.struct``
2 |
3 | ## Topics
4 |
5 | ### Hovering items by mouse
6 |
7 | - ``isHovering``
8 | - ``didEndHovering``
9 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/DiffableDataSource/CollectionViewDiffableData/CollectionViewDiffableDataSource+PrefetchHandlers.md:
--------------------------------------------------------------------------------
1 | # ``CollectionViewDiffableDataSource/PrefetchHandlers-swift.struct``
2 |
3 | ## Topics
4 |
5 | ### Prefetching items
6 |
7 | - ``willPrefetch``
8 | - ``didCancelPrefetching``
9 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/DiffableDataSource/CollectionViewDiffableData/CollectionViewDiffableDataSource+ReorderingHandlers.md:
--------------------------------------------------------------------------------
1 | # ``CollectionViewDiffableDataSource/ReorderingHandlers-swift.struct``
2 |
3 | ## Topics
4 |
5 | ### Reordering items
6 |
7 | - ``canReorder``
8 | - ``willReorder``
9 | - ``didReorder``
10 | - ``animates``
11 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/DiffableDataSource/CollectionViewDiffableData/CollectionViewDiffableDataSource+SelectionHandlers.md:
--------------------------------------------------------------------------------
1 | # ``CollectionViewDiffableDataSource/SelectionHandlers-swift.struct``
2 |
3 | ## Topics
4 |
5 | ### Selecting items
6 |
7 | - ``shouldSelect``
8 | - ``didSelect``
9 | - ``shouldDeselect``
10 | - ``didDeselect``
11 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/DiffableDataSource/CollectionViewDiffableData/CollectionViewDiffableDataSource-Protocol-Implementations.md:
--------------------------------------------------------------------------------
1 | # Protocol implementations
2 |
3 | Access the diffable data source’s implementations of protocol methods.
4 |
5 | ## Overview
6 |
7 | The diffable data source type conforms to `NSCollectionViewDataSource`.
8 |
9 | ## Topics
10 |
11 | ### Getting element and section metrics
12 |
13 | - ``CollectionViewDiffableDataSource/collectionView(_:numberOfItemsInSection:)``
14 | - ``CollectionViewDiffableDataSource/numberOfSections(in:)``
15 |
16 | ### Getting items for elements
17 |
18 | - ``CollectionViewDiffableDataSource/collectionView(_:itemForRepresentedObjectAt:)``
19 | - ``CollectionViewDiffableDataSource/collectionView(_:viewForSupplementaryElementOfKind:at:)``
20 |
21 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/DiffableDataSource/CollectionViewDiffableData/CollectionViewDiffableDataSource.md:
--------------------------------------------------------------------------------
1 | # ``CollectionViewDiffableDataSource``
2 |
3 | ## Topics
4 |
5 | ### Creating a diffable data source
6 |
7 | - ``init(collectionView:itemProvider:)``
8 | - ``init(collectionView:itemRegistration:)``
9 | - ``ItemProvider``
10 |
11 | ### Creating supplementary views
12 |
13 | - ``supplementaryViewProvider-swift.property``
14 | - ``SupplementaryViewProvider-swift.typealias``
15 | - ``useSupplementaryRegistrations(_:)``
16 |
17 | ### Identifying elements
18 |
19 | - ``elements``
20 | - ``selectedElements``
21 | - ``displayingElements``
22 | - ``element(for:)``
23 | - ``element(at:)``
24 | - ``elements(for:)``
25 | - ``indexPath(for:)``
26 | - ``reloadElements(_:animated:)``
27 | - ``reconfigureElements(_:)``
28 | - ``selectElements(_:byExtendingSelection:scrollPosition:)``
29 | - ``selectElements(in:scrollPosition:)``
30 | - ``deselectElements(_:)``
31 | - ``deselectElements(in:)``
32 | - ``scrollToElements(_:scrollPosition:)``
33 |
34 | ### Identifying sections
35 |
36 | - ``sections``
37 | - ``section(at:)``
38 | - ``index(for:)``
39 | - ``scrollToSection(_:scrollPosition:)``
40 |
41 | ### Updating data
42 |
43 | - ``snapshot()``
44 | - ``emptySnapshot()``
45 | - ``apply(_:_:completion:)``
46 |
47 | ### Configurating user interaction
48 |
49 | - ``menuProvider``
50 | - ``rightClickHandler``
51 |
52 | ### Displaying empty view
53 |
54 | - ``emptyView``
55 | - ``emptyContentConfiguration``
56 | - ``emptyHandler``
57 |
58 | ### Previewing elements
59 |
60 | - ``isQuicklookPreviewable``
61 | - ``quicklookElements(_:current:)``
62 |
63 | ### Supporting prefetching elements
64 |
65 | - ``prefetchHandlers-swift.property``
66 | - ``PrefetchHandlers-swift.struct``
67 |
68 | ### Supporting reordering elements
69 |
70 | - ``reorderingHandlers-swift.property``
71 | - ``ReorderingHandlers-swift.struct``
72 |
73 | ### Supporting deleting elements
74 |
75 | - ``deletingHandlers-swift.property``
76 | - ``DeletingHandlers-swift.struct``
77 |
78 | ### Handling selecting elements
79 |
80 | - ``selectionHandlers-swift.property``
81 | - ``SelectionHandlers-swift.struct``
82 |
83 | ### Handling displaying elements
84 |
85 | - ``displayHandlers-swift.property``
86 | - ``DisplayHandlers-swift.struct``
87 |
88 | ### Handling hovering elements
89 |
90 | - ``hoverHandlers-swift.property``
91 | - ``HoverHandlers-swift.struct``
92 |
93 | ### Handling highlighting elements
94 |
95 | - ``highlightHandlers-swift.property``
96 | - ``HighlightHandlers-swift.struct``
97 |
98 | ### Managing drag interactions
99 |
100 | - ``draggingHandlers-swift.property``
101 | - ``DraggingHandlers-swift.struct``
102 |
103 | ### Managing drop interactions
104 |
105 | - ``droppingHandlers-swift.property``
106 | - ``DroppingHandlers-swift.struct``
107 |
108 | ### Supporting protocol requirements
109 |
110 | -
111 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/DiffableDataSource/OutlineViewDiffableDataSource/OutlineNode.md:
--------------------------------------------------------------------------------
1 | # ``OutlineNode``
2 |
3 | ## Topics
4 |
5 | ### Creating a outline node
6 |
7 | - ``init(_:)``
8 | - ``init(_:_:)``
9 |
10 | ### Accessing items
11 |
12 | - ``item``
13 | - ``children``
14 |
15 | ### Configurating expansion
16 |
17 | - ``isExpanded``
18 | - ``isExpanded(_:)``
19 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/DiffableDataSource/OutlineViewDiffableDataSource/OutlineViewDiffableDataSource+ColumnHandlers.md:
--------------------------------------------------------------------------------
1 | # ``OutlineViewDiffableDataSource/ColumnHandlers-swift.struct``
2 |
3 | ## Topics
4 |
5 | ### Resizing columns
6 |
7 | - ``didResize``
8 |
9 | ### Reordering columns
10 |
11 | - ``shouldReorder``
12 | - ``didReorder``
13 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/DiffableDataSource/OutlineViewDiffableDataSource/OutlineViewDiffableDataSource+DeletingHandlers.md:
--------------------------------------------------------------------------------
1 | # ``OutlineViewDiffableDataSource/DeletingHandlers-swift.struct``
2 |
3 | ## Topics
4 |
5 | ### Deleting items
6 |
7 | - ``canDelete``
8 | - ``willDelete``
9 | - ``didDelete``
10 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/DiffableDataSource/OutlineViewDiffableDataSource/OutlineViewDiffableDataSource+ExpansionHandlers.md:
--------------------------------------------------------------------------------
1 | # ``OutlineViewDiffableDataSource/ExpansionHandlers.md-swift.struct``
2 |
3 | ## Topics
4 |
5 | ### Expanding/Collapsing items
6 |
7 | - ``shouldExpand``
8 | - ``didExpand``
9 | - ``shouldCollapse``
10 | - ``didCollapse``
11 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/DiffableDataSource/OutlineViewDiffableDataSource/OutlineViewDiffableDataSource+HoverHandlers.md:
--------------------------------------------------------------------------------
1 | # ``OutlineViewDiffableDataSource/HoverHandlers-swift.struct``
2 |
3 | ## Topics
4 |
5 | ### Hovering items by mouse
6 |
7 | - ``isHovering``
8 | - ``didEndHovering``
9 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/DiffableDataSource/OutlineViewDiffableDataSource/OutlineViewDiffableDataSource+ReorderingHandlers.md:
--------------------------------------------------------------------------------
1 | # ``OutlineViewDiffableDataSource/ReorderingHandlers-swift.struct``
2 |
3 | ## Topics
4 |
5 | ### Reordering items
6 |
7 | - ``canReorder``
8 | - ``willReorder``
9 | - ``didReorder``
10 | - ``animates``
11 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/DiffableDataSource/OutlineViewDiffableDataSource/OutlineViewDiffableDataSource+SelectionHandlers.md:
--------------------------------------------------------------------------------
1 | # ``OutlineViewDiffableDataSource/SelectionHandlers-swift.struct``
2 |
3 | ## Topics
4 |
5 | ### Selecting items
6 |
7 | - ``shouldSelect``
8 | - ``didSelect``
9 | - ``shouldDeselect``
10 | - ``didDeselect``
11 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/DiffableDataSource/OutlineViewDiffableDataSource/OutlineViewDiffableDataSource-Protocol-Implementations.md:
--------------------------------------------------------------------------------
1 | # Protocol implementations
2 |
3 | Access the diffable data source’s implementations of protocol methods.
4 |
5 | ## Overview
6 |
7 | The diffable data source type conforms to `NSOutlineViewDataSource`.
8 |
9 | ## Topics
10 |
11 | ### Getting item metrics
12 |
13 | - ``OutlineViewDiffableDataSource/outlineView(_:child:ofItem:)``
14 | - ``OutlineViewDiffableDataSource/outlineView(_:numberOfChildrenOfItem:)``
15 | - ``OutlineViewDiffableDataSource/outlineView(_:isItemExpandable:)``
16 |
17 | ### Reordering items
18 |
19 | - ``OutlineViewDiffableDataSource/outlineView(_:draggingSession:willBeginAt:forItems:)``
20 | - ``OutlineViewDiffableDataSource/outlineView(_:draggingSession:endedAt:operation:)``
21 | - ``OutlineViewDiffableDataSource/outlineView(_:validateDrop:proposedItem:proposedChildIndex:)``
22 | - ``OutlineViewDiffableDataSource/outlineView(_:acceptDrop:item:childIndex:)``
23 | - ``OutlineViewDiffableDataSource/outlineView(_:pasteboardWriterForItem:)``
24 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/DiffableDataSource/OutlineViewDiffableDataSource/OutlineViewDiffableDataSource.md:
--------------------------------------------------------------------------------
1 | # ``OutlineViewDiffableDataSource``
2 |
3 | ## Overview
4 |
5 | ## Topics
6 |
7 | ### Creating a diffable data source
8 |
9 | - ``init(outlineView:cellRegistration:)``
10 | - ``init(outlineView:cellRegistrations:)``
11 | - ``init(outlineView:cellProvider:)``
12 | - ``CellProvider``
13 |
14 | ### Creating row views
15 |
16 | - ``rowViewProvider-swift.property``
17 | - ``RowViewProvider-swift.typealias``
18 | - ``applyRowViewRegistration(_:)``
19 |
20 | ### Creating group item cell views.
21 |
22 | - ``groupItemCellProvider-swift.property``
23 | - ``GroupItemCellProvider-swift.typealias``
24 | - ``applyGroupItemCellRegistration(_:)``
25 | - ``groupItemsAreCollapsable``
26 |
27 | ### Identifying items
28 |
29 | - ``items``
30 | - ``selectedItems``
31 | - ``visibleItems``
32 | - ``item(forRow:)``
33 | - ``row(for:)``
34 | - ``item(at:)``
35 | - ``reloadItems(_:reloadChildren:animated:)``
36 | - ``reconfigureItems(_:)``
37 | - ``selectItems(_:byExtendingSelection:)``
38 | - ``deselectItems(_:)``
39 | - ``scrollToItem(_:)``
40 |
41 | ### Updating data
42 |
43 | - ``snapshot()``
44 | - ``snapshot(for:)``
45 | - ``emptySnapshot()``
46 | - ``apply(_:_:completion:)``
47 | - ``defaultRowAnimation``
48 |
49 | ### Configurating user interaction
50 |
51 | - ``menuProvider``
52 | - ``rightClickHandler``
53 |
54 | ### Providing tint configurations
55 |
56 | ``tintConfigurationProvider``
57 |
58 | ### Displaying empty view
59 |
60 | - ``emptyView``
61 | - ``emptyContentConfiguration``
62 | - ``emptyHandler``
63 |
64 | ### Supporting reordering items
65 |
66 | - ``reorderingHandlers-swift.property``
67 | - ``ReorderingHandlers-swift.struct``
68 |
69 | ### Supporting deleting items
70 |
71 | - ``deletingHandlers-swift.property``
72 | - ``DeletingHandlers-swift.struct``
73 |
74 | ### Handling selecting items
75 |
76 | - ``selectionHandlers-swift.property``
77 | - ``SelectionHandlers-swift.struct``
78 |
79 | ### Handling expanding/collapsing items
80 |
81 | - ``expand(_:expandChildren:)-770uz``
82 | - ``expand(_:expandChildren:)-1v68w``
83 | - ``collapse(_:collapseChildren:)-82w6c``
84 | - ``collapse(_:collapseChildren:)-5f5fu``
85 | - ``expanionHandlers-swift.property``
86 | - ``ExpanionHandlers-swift.struct``
87 |
88 | ### Handling hovering items
89 |
90 | - ``hoverHandlers-swift.property``
91 | - ``HoverHandlers-swift.struct``
92 |
93 | ### Handling column changes
94 |
95 | - ``columnHandlers-swift.property``
96 | - ``ColumnHandlers-swift.struct``
97 |
98 | ### Sorting items
99 |
100 | - ``setSortComparator(_:forColumn:activate:)``
101 | - ``setSortComparators(_:forColumn:activate:)``
102 |
103 | ### Previewing items
104 |
105 | - ``isQuicklookPreviewable``
106 | - ``quicklookItems(_:current:)``
107 |
108 | ### Supporting protocol requirements
109 |
110 | -
111 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/DiffableDataSource/OutlineViewDiffableDataSource/OutlineViewDiffableDataSourceSnapshot.md:
--------------------------------------------------------------------------------
1 | # ``OutlineViewDiffableDataSourceSnapshot``
2 |
3 | ## Overview
4 |
5 | ## Topics
6 |
7 | ### Creating a snapshot
8 |
9 | - ``init()``
10 | - ``snapshot(of:includingParent:)``
11 | - ``append(_:to:)``
12 |
13 | ### Accessing items
14 |
15 | - ``items``
16 | - ``rootItems``
17 | - ``visibleItems``
18 | - ``children(of:recursive:)``
19 |
20 | ### Getting item metrics
21 |
22 | - ``index(of:)``
23 | - ``level(of:)``
24 | - ``parent(of:)``
25 | - ``contains(_:)``
26 | - ``isVisible(_:)``
27 | - ``isDescendant(_:of:)``
28 |
29 | ### Inserting items
30 |
31 | - ``insert(_:before:)-5psi5``
32 | - ``insert(_:before:)-3vdz``
33 | - ``insert(_:after:)-97sm4``
34 | - ``insert(_:after:)-9at59``
35 |
36 | ### Moving items
37 |
38 | - ``move(_:before:)``
39 | - ``move(_:after:)``
40 | - ``move(_:toIndex:of:)``
41 |
42 | ### Removing items
43 |
44 | - ``delete(_:)``
45 | - ``deleteAll()``
46 |
47 | ### Replacing items
48 |
49 | - ``replace(childrenOf:using:)``
50 |
51 | ### Expanding and collapsing items
52 |
53 | - ``isExpanded(_:)``
54 | - ``expand(_:)``
55 | - ``collapse(_:)``
56 |
57 | ### Debugging snapshots
58 |
59 | - ``visualDescription()``
60 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/DiffableDataSource/OutlineViewDiffableDataSource/OutlineViewDiffableDataSourceTransaction.md:
--------------------------------------------------------------------------------
1 | # ``OutlineViewDiffableDataSourceTransaction``
2 |
3 | ## Overview
4 |
5 | ## Topics
6 |
7 | ### Accessing a transaction's information
8 |
9 | - ``initialSnapshot``
10 | - ``finalSnapshot``
11 | - ``difference``
12 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/DiffableDataSource/Shared/NSDiffableDataSourceSnapshotApplyOption.md:
--------------------------------------------------------------------------------
1 | # ``NSDiffableDataSourceSnapshotApplyOption``
2 |
3 | ## Topics
4 |
5 | ### Snapshot applying options
6 |
7 | - ``animated``
8 | - ``animated(duration:)``
9 | - ``withoutAnimation``
10 | - ``usingReloadData``
11 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/DiffableDataSource/Shared/NSDiffableDataSourceTransaction.md:
--------------------------------------------------------------------------------
1 | # ``DiffableDataSourceTransaction``
2 |
3 | ## Topics
4 |
5 | ### Accessing a transaction's information
6 |
7 | - ``initialSnapshot``
8 | - ``finalSnapshot``
9 | - ``difference``
10 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/DiffableDataSource/TableViewDiffableDataSource/TableViewDiffableDataSource+ColumnHandlers.md:
--------------------------------------------------------------------------------
1 | # ``TableViewDiffableDataSource/ColumnHandlers-swift.struct``
2 |
3 | ## Topics
4 |
5 | ### Resizing columns
6 |
7 | - ``didResize``
8 |
9 | ### Reordering columns
10 |
11 | - ``shouldReorder``
12 | - ``didReorder``
13 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/DiffableDataSource/TableViewDiffableDataSource/TableViewDiffableDataSource+DeletingHandlers.md:
--------------------------------------------------------------------------------
1 | # ``TableViewDiffableDataSource/DeletingHandlers-swift.struct``
2 |
3 | ## Topics
4 |
5 | ### Deleting items
6 |
7 | - ``canDelete``
8 | - ``willDelete``
9 | - ``didDelete``
10 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/DiffableDataSource/TableViewDiffableDataSource/TableViewDiffableDataSource+DragDropHandlers.md:
--------------------------------------------------------------------------------
1 | # ``TableViewDiffableDataSource/DragDropHandlers-swift.struct``
2 |
3 | ## Topics
4 |
5 | ### Drag and drop items
6 |
7 | - ``canDragOutside``
8 | - ``didDragOutside``
9 | - ``pasteboardValue``
10 | - ``canDragInside``
11 | - ``didDragInside``
12 | - ``draggingImage``
13 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/DiffableDataSource/TableViewDiffableDataSource/TableViewDiffableDataSource+HoverHandlers.md:
--------------------------------------------------------------------------------
1 | # ``TableViewDiffableDataSource/HoverHandlers-swift.struct``
2 |
3 | ## Topics
4 |
5 | ### Hovering items by mouse
6 |
7 | - ``isHovering``
8 | - ``didEndHovering``
9 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/DiffableDataSource/TableViewDiffableDataSource/TableViewDiffableDataSource+ReorderingHandlers.md:
--------------------------------------------------------------------------------
1 | # ``TableViewDiffableDataSource/ReorderingHandlers-swift.struct``
2 |
3 | ## Topics
4 |
5 | ### Reordering items
6 |
7 | - ``canReorder``
8 | - ``willReorder``
9 | - ``didReorder``
10 | - ``animates``
11 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/DiffableDataSource/TableViewDiffableDataSource/TableViewDiffableDataSource+SelectionHandlers.md:
--------------------------------------------------------------------------------
1 | # ``TableViewDiffableDataSource/SelectionHandlers-swift.struct``
2 |
3 | ## Topics
4 |
5 | ### Selecting items
6 |
7 | - ``shouldSelect``
8 | - ``didSelect``
9 | - ``shouldDeselect``
10 | - ``didDeselect``
11 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/DiffableDataSource/TableViewDiffableDataSource/TableViewDiffableDataSource-Protocol Implementations.md:
--------------------------------------------------------------------------------
1 | # Protocol implementations
2 |
3 | Access the diffable data source’s implementations of protocol methods.
4 |
5 | ## Overview
6 |
7 | The diffable data source type conforms to `NSTableViewDataSource`.
8 |
9 | ## Topics
10 |
11 | ### Getting item metrics
12 |
13 | - ``TableViewDiffableDataSource/numberOfRows(in:)``
14 |
15 | ### Reordering items
16 |
17 | - ``TableViewDiffableDataSource/tableView(_:pasteboardWriterForRow:)``
18 | - ``TableViewDiffableDataSource/tableView(_:validateDrop:proposedRow:proposedDropOperation:)``
19 | - ``TableViewDiffableDataSource/tableView(_:draggingSession:willBeginAt:forRowIndexes:)``
20 | - ``TableViewDiffableDataSource/tableView(_:acceptDrop:row:dropOperation:)``
21 | - ``TableViewDiffableDataSource/tableView(_:draggingSession:endedAt:operation:)``
22 | - ``TableViewDiffableDataSource/tableView(_:updateDraggingItemsForDrag:)``
23 |
24 | ### Sorting items
25 |
26 | - ``TableViewDiffableDataSource/tableView(_:sortDescriptorsDidChange:)``
27 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/DiffableDataSource/TableViewDiffableDataSource/TableViewDiffableDataSource.md:
--------------------------------------------------------------------------------
1 | # ``TableViewDiffableDataSource``
2 |
3 | ## Overview
4 |
5 | ## Topics
6 |
7 | ### Creating a diffable data source
8 |
9 | - ``init(tableView:cellRegistration:)``
10 | - ``init(tableView:cellRegistrations:)``
11 | - ``init(tableView:cellProvider:)``
12 | - ``CellProvider``
13 |
14 | ### Creating section header views
15 |
16 | - ``sectionHeaderCellProvider-swift.property``
17 | - ``SectionHeaderCellProvider-swift.typealias``
18 | - ``applySectionHeaderRegistration(_:)``
19 |
20 | ### Creating row views
21 |
22 | - ``rowViewProvider-swift.property``
23 | - ``RowViewProvider-swift.typealias``
24 | - ``applyRowViewRegistration(_:)``
25 |
26 | ### Identifying items
27 |
28 | - ``items``
29 | - ``selectedItems``
30 | - ``visibleItems``
31 | - ``item(forRow:)``
32 | - ``row(for:)-3ouhk``
33 | - ``item(at:)``
34 | - ``reloadItems(_:animated:)``
35 | - ``reconfigureItems(_:)``
36 | - ``selectItems(_:byExtendingSelection:)``
37 | - ``selectItems(in:byExtendingSelection:)``
38 | - ``deselectItems(_:)``
39 | - ``deselectItems(in:)``
40 | - ``scrollToItem(_:)``
41 |
42 | ### Identifying sections
43 |
44 | - ``sections``
45 | - ``row(for:)-3rckc``
46 | - ``scrollToSection(_:)``
47 |
48 | ### Updating data
49 |
50 | - ``snapshot()``
51 | - ``emptySnapshot()``
52 | - ``apply(_:_:completion:)``
53 | - ``defaultRowAnimation``
54 |
55 | ### Configurating user interaction
56 |
57 | - ``menuProvider``
58 | - ``rightClickHandler``
59 | - ``rowActionProvider``
60 |
61 | ### Displaying empty view
62 |
63 | - ``emptyView``
64 | - ``emptyContentConfiguration``
65 | - ``emptyHandler``
66 |
67 | ### Supporting reordering items
68 |
69 | - ``reorderingHandlers-swift.property``
70 | - ``ReorderingHandlers-swift.struct``
71 |
72 | ### Supporting deleting items
73 |
74 | - ``deletingHandlers-swift.property``
75 | - ``DeletingHandlers-swift.struct``
76 |
77 | ### Handling selecting items
78 |
79 | - ``selectionHandlers-swift.property``
80 | - ``SelectionHandlers-swift.struct``
81 |
82 | ### Handling hovering items
83 |
84 | - ``hoverHandlers-swift.property``
85 | - ``HoverHandlers-swift.struct``
86 |
87 | ### Handling column changes
88 |
89 | - ``columnHandlers-swift.property``
90 | - ``ColumnHandlers-swift.struct``
91 |
92 | ### Sorting items
93 |
94 | - ``setSortComparator(_:forColumn:activate:)``
95 | - ``setSortComparators(_:forColumn:activate:)``
96 |
97 | ### Previewing items
98 |
99 | - ``isQuicklookPreviewable``
100 | - ``quicklookItems(_:current:)``
101 |
102 | ### Managing drag interactions
103 |
104 | - ``draggingHandlers-swift.property``
105 | - ``DraggingHandlers-swift.struct``
106 |
107 | ### Managing drop interactions
108 |
109 | - ``droppingHandlers-swift.property``
110 | - ``DroppingHandlers-swift.struct``
111 |
112 | ### Supporting protocol requirements
113 |
114 | -
115 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/Registration/NSCollectionView/ItemRegistration.md:
--------------------------------------------------------------------------------
1 | # ``AppKit/NSCollectionView/ItemRegistration``
2 |
3 | @Metadata {
4 | @DisplayName("ItemRegistration")
5 | }
6 |
7 | ## Topics
8 |
9 | ### Creating an item registration
10 |
11 | - ``init(handler:)``
12 | - ``init(nib:handler:)``
13 | - ``Handler``
14 |
15 | ### Creating an item
16 |
17 | - ``AppKit/NSCollectionView/makeItem(using:for:element:)``
18 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/Registration/NSCollectionView/SupplementaryRegistration.md:
--------------------------------------------------------------------------------
1 | # ``AppKit/NSCollectionView/SupplementaryRegistration``
2 |
3 | @Metadata {
4 | @DisplayName("SupplementaryRegistration")
5 | }
6 |
7 | ## Topics
8 |
9 | ### Creating a supplementary registration
10 |
11 | - ``init(elementKind:handler:)``
12 | - ``init(nib:elementKind:handler:)``
13 | - ``elementKind``
14 | - ``Handler``
15 |
16 | ### Creating a supplementary view
17 |
18 | - ``AppKit/NSCollectionView/makeSupplementaryView(using:for:)``
19 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/Registration/NSTableView/CellRegistration.md:
--------------------------------------------------------------------------------
1 | # ``AppKit/NSTableView/CellRegistration``
2 |
3 | @Metadata {
4 | @DisplayName("CellRegistration")
5 | }
6 |
7 | ## Topics
8 |
9 | ### Creating a table cell registration
10 |
11 | - ``init(columnIdentifiers:handler:)``
12 | - ``init(nib:columnIdentifiers:handler:)``
13 | - ``Handler``
14 |
15 | ### Configurating columns
16 |
17 | - ``columnIdentifiers``
18 |
19 | ### Creating a table cell view
20 |
21 | - ``AppKit/NSTableView/makeCellView(using:forColumn:row:item:)``
22 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/Registration/NSTableView/NSTableSectionHeaderView.md:
--------------------------------------------------------------------------------
1 | # ``NSTableSectionHeaderView``
2 |
3 | ## Topics
4 |
5 | ### Managing the content
6 |
7 | - ``defaultContentConfiguration()``
8 | - ``contentConfiguration``
9 | - ``automaticallyUpdatesContentConfiguration``
10 |
11 | ### Managing the state
12 |
13 | - ``configurationState``
14 | - ``setNeedsUpdateConfiguration()``
15 | - ``updateConfiguration(using:)``
16 | - ``configurationUpdateHandler``
17 | - ``ConfigurationUpdateHandler``
18 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Extensions/Registration/NSTableView/RowViewRegistration.md:
--------------------------------------------------------------------------------
1 | # ``AppKit/NSTableView/RowRegistration``
2 |
3 | @Metadata {
4 | @DisplayName("RowRegistration")
5 | }
6 |
7 | ## Topics
8 |
9 | ### Creating a table row registration
10 |
11 | - ``init(handler:)``
12 | - ``init(nib:handler:)``
13 | - ``Handler``
14 |
15 | ### Creating a table row view
16 |
17 | - ``AppKit/NSTableView/makeRowView(using:forRow:item:)``
18 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Resources/Extension.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Resources/NSItemContentConfiguration.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flocked/AdvancedCollectionTableView/094735ce186ccf19d26eb50cd0eb0d72fa44bcce/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Resources/NSItemContentConfiguration.png
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Resources/NSListContentConfiguration.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flocked/AdvancedCollectionTableView/094735ce186ccf19d26eb50cd0eb0d72fa44bcce/Sources/AdvancedCollectionTableView/Documentation/AdvancedCollectionTableView.docc/Resources/NSListContentConfiguration.png
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Extensions/NSCollectionView/NSCollectionView+DragSessionMove.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSCollectionView+DragSessionMove.swift
3 | //
4 | //
5 | // Created by Florian Zand on 02.03.25.
6 | //
7 |
8 | import AppKit
9 | import FZSwiftUtils
10 |
11 | extension NSCollectionView {
12 | var draggingSessionMoveHandler: ((NSDraggingSession, CGPoint)->())? {
13 | get { getAssociatedValue("draggingSessionMoveHandler") }
14 | set {
15 | setAssociatedValue(newValue, key: "draggingSessionMoveHandler")
16 | let selector = #selector(NSCollectionView.draggingSession(_:movedTo:))
17 | if newValue != nil {
18 | guard !isMethodHooked(selector) else { return }
19 | do {
20 | try hook(selector, closure: { original, object, sel, session, point in
21 | (object as? NSCollectionView)?.draggingSessionMoveHandler?(session, point)
22 | original(object, sel, session, point)
23 | } as @convention(block) (
24 | (AnyObject, Selector, NSDraggingSession, CGPoint) -> Void,
25 | AnyObject, Selector, NSDraggingSession, CGPoint) -> Void)
26 | } catch {
27 | debugPrint(error)
28 | }
29 | } else {
30 | revertHooks(for: selector)
31 | }
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Extensions/NSCollectionView/NSCollectionView+ReconfigureItem.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSCollectionView+ReconfigurateItem.swift
3 | //
4 | //
5 | // Created by Florian Zand on 18.05.22.
6 | //
7 |
8 | import AppKit
9 | import FZSwiftUtils
10 |
11 | extension NSCollectionView {
12 | /**
13 | Updates the data for the items at the index paths you specify, preserving existing items.
14 |
15 | To update the contents of existing (including prefetched) items without replacing them with new items, use this method instead of `reloadItems(at:)`. For optimal performance, choose to reconfigure items instead of reloading items unless you have an explicit need to replace the existing item with a new item.
16 |
17 | Your item provider must dequeue the same type of item for the provided index path, and must return the same existing item for a given index path. Because this method reconfigures existing items, the collection view doesn’t item `prepareForReuse()` for each item dequeued. If you need to return a different type of item for an index path, use reloadItems(at:) instead.
18 |
19 | - Parameters:
20 | - indexPaths: An array of `IndexPath` objects identifying the items you want to update.
21 | */
22 | public func reconfigureItems(at indexPaths: [IndexPath]) {
23 | Self.swizzleMakeItem()
24 | guard let dataSource = dataSource else { return }
25 | isReconfiguratingItems = true
26 | let visibleIndexPaths = indexPathsForVisibleItems()
27 | let indexPaths = indexPaths.filter({visibleIndexPaths.contains($0)})
28 | for indexPath in indexPaths {
29 | dataSource.collectionView(self, itemForRepresentedObjectAt: indexPath)
30 | }
31 | isReconfiguratingItems = false
32 | }
33 |
34 | var isReconfiguratingItems: Bool {
35 | get { getAssociatedValue("isReconfiguratingItems", initialValue: false) }
36 | set { setAssociatedValue(newValue, key: "isReconfiguratingItems")
37 | }
38 | }
39 |
40 | static var didSwizzleMakeItem: Bool {
41 | get { getAssociatedValue("didSwizzleMakeItem", initialValue: false) }
42 | set { setAssociatedValue(newValue, key: "didSwizzleMakeItem") }
43 | }
44 |
45 | @objc static func swizzleMakeItem() {
46 | guard didSwizzleMakeItem == false else { return }
47 | do {
48 | try Swizzle(NSCollectionView.self) {
49 | #selector(makeItem(withIdentifier:for:)) <-> #selector(swizzled_makeItem(withIdentifier:for:))
50 | }
51 | didSwizzleMakeItem = true
52 | } catch {
53 | Swift.debugPrint(error)
54 | }
55 | }
56 |
57 | @objc func swizzled_makeItem(withIdentifier identifier: NSUserInterfaceItemIdentifier, for indexPath: IndexPath) -> NSCollectionViewItem {
58 | if isReconfiguratingItems, let item = self.item(at: indexPath) {
59 | return item
60 | }
61 | return self.swizzled_makeItem(withIdentifier: identifier, for: indexPath)
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Extensions/NSCollectionView/NSCollectionViewDiffableDataSource+.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSCollectionViewDiffableDataSource+.swift
3 | //
4 | //
5 | // Created by Florian Zand on 16.03.25.
6 | //
7 |
8 | import AppKit
9 | import FZSwiftUtils
10 |
11 | extension NSCollectionViewDiffableDataSource {
12 | /// The item provider of the datasource.
13 | public var itemProvider: ItemProvider {
14 | typealias itemProviderBlock = @convention(block) (NSCollectionView, IndexPath, Any) -> NSCollectionViewItem?
15 | guard let object: NSObject = getIvarValue(for: "_impl"), let cellProvider: itemProviderBlock = object.getIvarValue(for: "_collectionViewItemProvider") else { return { _,_,_ in return nil } }
16 | return cellProvider
17 | }
18 |
19 | /// Creates a new collection view item for the specified item identifier using the item provider.
20 | public func createItem(for itemIdentifier: ItemIdentifierType) -> NSCollectionViewItem? {
21 | itemProvider(collectionView, IndexPath(item: 0, section: 0), itemIdentifier)
22 | }
23 |
24 | /// Returns a preview image of the collection view item for the specified item.
25 | public func previewImage(for item: ItemIdentifierType) -> NSImage? {
26 | _previewImage(for: item, size: nil)
27 | }
28 |
29 | /// Returns a preview image of the collection view item for the specified item and item size.
30 | public func previewImage(for item: ItemIdentifierType, size: CGSize) -> NSImage? {
31 | _previewImage(for: item, size: size)
32 | }
33 |
34 | private func _previewImage(for item: ItemIdentifierType, size: CGSize? = nil, width: CGFloat? = nil, height: CGFloat? = nil) -> NSImage? {
35 | guard let item = createItem(for: item) else { return nil }
36 | if width != nil || height != nil {
37 | item.view.frame.size = item.view.systemLayoutSizeFitting(width: width, height: height)
38 | item.view.frame.size.width = width ?? item.view.frame.size.width
39 | item.view.frame.size.height = height ?? item.view.frame.size.height
40 | } else {
41 | item.view.frame.size = size ?? collectionView.frameForItem(at: IndexPath(item: 0, section: 0))?.size ?? CGSize(512, 512)
42 | }
43 | return item.view.renderedImage
44 | }
45 |
46 | private var collectionView: NSCollectionView {
47 | guard let object: NSObject = getIvarValue(for: "_impl"), let collectionView: NSCollectionView = object.getIvarValue(for: "_nsCollectionView") else { return NSCollectionView() }
48 | return collectionView
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Extensions/NSCollectionView/NSCollectionViewDiffableDataSource+Apply.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSCollectionViewDiffableDataSource+Apply.swift
3 | //
4 | //
5 | // Created by Florian Zand on 16.12.22.
6 | //
7 |
8 | import AppKit
9 |
10 | extension NSCollectionViewDiffableDataSource {
11 | /**
12 | Updates the UI to reflect the state of the data in the specified snapshot, optionally animating the UI changes and executing a completion handler.
13 |
14 | It’s safe to call this method from a background queue, but you must do so consistently in your app. Always call this method exclusively from the main queue or from a background queue.
15 |
16 | - Parameters:
17 | - snapshot: The snapshot reflecting the new state of the data in the collection view.
18 | - option: Option how to apply the snapshot to the collection view. The default value is `animated`.
19 | - completion: An optional closure to be executed when the animations are complete. The system calls this closure from the main queue.
20 | */
21 | public func apply(_ snapshot: NSDiffableDataSourceSnapshot, _ option: NSDiffableDataSourceSnapshotApplyOption = .animated, completion: (() -> Void)? = nil) {
22 | switch option {
23 | case .usingReloadData:
24 | applySnapshotUsingReloadData(snapshot, completion: completion)
25 | case .animated:
26 | apply(snapshot, animated: true, animationDuration: option.animationDuration, completion: completion)
27 | case .withoutAnimation:
28 | apply(snapshot, animated: false, completion: completion)
29 | }
30 | }
31 |
32 | private func applySnapshotUsingReloadData(_ snapshot: NSDiffableDataSourceSnapshot, completion: (() -> Void)? = nil) {
33 | apply(snapshot, animatingDifferences: false, completion: completion)
34 | }
35 |
36 | private func apply(_ snapshot: NSDiffableDataSourceSnapshot, animated: Bool = true, animationDuration: TimeInterval? = nil, completion: (() -> Void)? = nil) {
37 | if animated, animationDuration == nil {
38 | apply(snapshot, animatingDifferences: true, completion: completion)
39 | } else {
40 | NSAnimationContext.beginGrouping()
41 | NSAnimationContext.current.duration = animationDuration ?? 0
42 | apply(snapshot, animatingDifferences: true, completion: completion)
43 | NSAnimationContext.endGrouping()
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Extensions/NSCollectionView/NSCollectionViewDiffableDataSource+Registration.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSCollectionViewDiffableDataSource+Registration.swift
3 | //
4 | //
5 | // Created by Florian Zand on 02.09.22.
6 | //
7 |
8 | import AppKit
9 |
10 | public extension NSCollectionViewDiffableDataSource {
11 | /**
12 | Creates a diffable data source with the specified item provider, and connects it to the specified collection view.
13 |
14 | - Parameters:
15 | - collectionView: The initialized collection view object to connect to the diffable data source.
16 | - itemRegistration: A item registration that creates, configurate and returns each of the items for the collection view from the data the diffable data source provides.
17 | */
18 | convenience init(collectionView: NSCollectionView, itemRegistration: NSCollectionView.ItemRegistration) {
19 | self.init(collectionView: collectionView, itemProvider: {
20 | tCollectionView, indexPath, element in
21 | tCollectionView.makeItem(using: itemRegistration, for: indexPath, element: element)
22 | })
23 | }
24 |
25 | /**
26 | Creates a diffable data source with the specified item provider, and connects it to the specified collection view.
27 |
28 | - Parameters:
29 | - collectionView: The initialized collection view object to connect to the diffable data source.
30 | - itemRegistration: A item registration that creates, configurate and returns each of the items for the collection view from the data the diffable data source provides.
31 | - supplementaryRegistrations: An array of collection view’s SupplementaryRegistration that provides supplementary views, such as headers and footers.
32 | */
33 | convenience init(collectionView: NSCollectionView, itemRegistration: NSCollectionView.ItemRegistration, supplementaryRegistrations: [NSCollectionViewSupplementaryRegistration]) {
34 | self.init(collectionView: collectionView, itemRegistration: itemRegistration)
35 | useSupplementaryRegistrations(supplementaryRegistrations)
36 | }
37 |
38 | /// Uses the supplementary registrations to return supplementary views to `supplementaryViewProvider`.
39 | func useSupplementaryRegistrations(_ registrations: [NSCollectionViewSupplementaryRegistration]) {
40 | guard !registrations.isEmpty else { return }
41 | supplementaryViewProvider = { collectionView, elementKind, indexPath in
42 | (registrations.first(where: { $0.elementKind == elementKind }) as? _NSCollectionViewSupplementaryRegistration)?.makeSupplementaryView(collectionView, indexPath)
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Extensions/NSCollectionView/unused/NSCollectionView+SelfSizing.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSCollectionView+SelfSizing.swift
3 | //
4 | //
5 | // Created by Florian Zand on 23.07.23.
6 | //
7 |
8 | /*
9 | import AppKit
10 | import FZSwiftUtils
11 | import FZUIKit
12 |
13 | // Currently not implemented
14 | extension NSCollectionView {
15 | /**
16 | Constants that describe modes for invalidating the size of self-sizing collection view items.
17 |
18 | Use these constants with the `selfSizingInvalidation` property.
19 | */
20 | enum SelfSizingInvalidation: Int {
21 | /// A mode that disables self-sizing invalidation.
22 | case disabled = 0
23 | /// A mode that enables manual self-sizing invalidation.
24 | case enabled = 1
25 | /// A mode that enables automatic self-sizing invalidation after Auto Layout changes.
26 | case enabledIncludingConstraints = 2
27 | }
28 |
29 | /// The mode that the collection view uses for invalidating the size of self-sizing items.
30 | var selfSizingInvalidation: SelfSizingInvalidation {
31 | get { getAssociatedValue("selfSizingInvalidation", initialValue: SelfSizingInvalidation.disabled) }
32 | set {
33 | setAssociatedValue(newValue, key: "selfSizingInvalidation")
34 | if newValue != .disabled {
35 | NSCollectionViewItem.swizzleCollectionViewItemIfNeeded()
36 | }
37 | }
38 | }
39 | }
40 |
41 | extension NSCollectionViewItem {
42 | static var didSwizzleCollectionViewItem: Bool {
43 | get { getAssociatedValue("didSwizzleCollectionViewItemLayoutAttributes", initialValue: false) }
44 | set { setAssociatedValue(newValue, key: "didSwizzleCollectionViewItemLayoutAttributes") }
45 | }
46 |
47 | static func swizzleCollectionViewItemIfNeeded() {
48 | if didSwizzleCollectionViewItem == false {
49 | do {
50 | _ = try Swizzle(NSCollectionViewItem.self) {
51 | #selector(viewDidLayout) <-> #selector(swizzled_viewDidLayout)
52 | #selector(apply(_:)) <-> #selector(swizzled_apply(_:))
53 | #selector(preferredLayoutAttributesFitting(_:)) <-> #selector(swizzled_preferredLayoutAttributesFitting(_:))
54 | }
55 | didSwizzleCollectionViewItem = true
56 | } catch {
57 | Swift.debugPrint(error)
58 | }
59 | }
60 | }
61 |
62 | @objc func swizzled_apply(_ layoutAttributes: NSCollectionViewLayoutAttributes) {
63 | cachedLayoutAttributes = layoutAttributes
64 | }
65 |
66 | @objc func swizzled_preferredLayoutAttributesFitting(_ layoutAttributes: NSCollectionViewLayoutAttributes) -> NSCollectionViewLayoutAttributes {
67 | if backgroundConfiguration != nil || contentConfiguration != nil {
68 | let width = layoutAttributes.size.width
69 | var fittingSize = view.sizeThatFits(CGSize(width: width, height: .infinity))
70 | fittingSize.width = width
71 | layoutAttributes.size = fittingSize
72 | return layoutAttributes
73 | }
74 | return swizzled_preferredLayoutAttributesFitting(layoutAttributes)
75 | }
76 |
77 | @objc func swizzled_viewDidLayout() {
78 | switch collectionView?.selfSizingInvalidation {
79 | case .enabled:
80 | if let cachedLayoutAttributes = cachedLayoutAttributes {
81 | if view.frame != cachedLayoutAttributes.frame {
82 | Swift.debugPrint("Not the same. InvalidateSelfSizing")
83 | invalidateSelfSizing()
84 | }
85 | }
86 | case .enabledIncludingConstraints:
87 | break
88 | default:
89 | break
90 | }
91 | }
92 |
93 | var cachedLayoutAttributes: NSCollectionViewLayoutAttributes? {
94 | get { getAssociatedValue("cachedLayoutAttributes") }
95 | set { setAssociatedValue(newValue, key: "cachedLayoutAttributes") }
96 | }
97 |
98 | var layoutInvalidationContext: NSCollectionViewLayoutInvalidationContext? {
99 | guard let collectionView = collectionView, let indexPath = collectionView.indexPath(for: self) else { return nil }
100 |
101 | let context = InvalidationContext(invalidateEverything: false)
102 | context.invalidateItems(at: [indexPath])
103 | return context
104 | }
105 |
106 | func invalidateSelfSizing() {
107 | guard let invalidationContext = layoutInvalidationContext, let collectionView = collectionView, let collectionViewLayout = collectionView.collectionViewLayout else { return }
108 |
109 | view.invalidateIntrinsicContentSize()
110 |
111 | collectionViewLayout.invalidateLayout(with: invalidationContext)
112 | collectionView.layoutSubtreeIfNeeded()
113 | }
114 |
115 | /// Invalidation of collection view items.
116 | class InvalidationContext: NSCollectionViewLayoutInvalidationContext {
117 | override public var invalidateEverything: Bool {
118 | _invalidateEverything
119 | }
120 |
121 | var _invalidateEverything: Bool
122 |
123 | public init(invalidateEverything: Bool) {
124 | _invalidateEverything = invalidateEverything
125 | }
126 | }
127 | }
128 | */
129 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Extensions/NSTableView/NSTableView+DragSessionMove.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSTableView+DragSessionMove.swift
3 | //
4 | //
5 | // Created by Florian Zand on 02.03.25.
6 | //
7 |
8 | import AppKit
9 | import FZSwiftUtils
10 |
11 | /*
12 | extension NSTableView {
13 | var draggingSessionMovedHandler: ((NSDraggingSession, CGPoint)->())? {
14 | get { getAssociatedValue("draggingSessionMoveHandler") }
15 | set {
16 | setAssociatedValue(newValue, key: "draggingSessionMoveHandler")
17 | let selector = #selector(NSTableView.draggingSession(_:movedTo:))
18 | if newValue != nil, !isMethodReplaced(selector), !isMethodHooked(selector) {
19 | do {
20 | if responds(to: selector) {
21 | try hook(selector, closure: { original, object, sel, session, point in
22 | (object as? NSTableView)?.draggingSessionMovedHandler?(session, point)
23 | original(object, sel, session, point)
24 | } as @convention(block) (
25 | (AnyObject, Selector, NSDraggingSession, CGPoint) -> Void,
26 | AnyObject, Selector, NSDraggingSession, CGPoint) -> Void)
27 | } else {
28 | try addMethod(selector,
29 | methodSignature: (@convention(block) (AnyObject, NSDraggingSession, CGPoint) -> ()).self) { object, session, point in
30 | (object as? NSTableView)?.draggingSessionMovedHandler?(session, point)
31 | }
32 | }
33 | } catch {
34 | debugPrint(error)
35 | }
36 | } else if newValue == nil {
37 | resetMethod(selector)
38 | revertHooks(for: selector)
39 | }
40 | }
41 | }
42 | }
43 | */
44 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Extensions/NSTableView/NSTableView+ReconfigureRows.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSTableView+ReconfigurateRows.swift
3 | //
4 | //
5 | // Created by Florian Zand on 18.05.22.
6 | //
7 |
8 | import AppKit
9 | import FZSwiftUtils
10 |
11 | extension NSTableView {
12 | /**
13 | Updates the data for the rows at the indexes you specify, preserving the existing row views and cells for the rows.
14 |
15 | To update the contents of existing (including prefetched) cells without replacing them with new row views nad cells, use this method instead of `reloadData(forRowIndexes:columnIndexes:)`. For optimal performance, choose to reconfigure rows instead of reloading rows unless you have an explicit need to replace the existing row view or cells with new.
16 |
17 | Your cell provider must dequeue the same type of cell for the provided index path, and must return the same existing cell for a given index path. Because this method reconfigures existing cells, the table view doesn’t call `prepareForReuse()` for each cell dequeued. If you need to return a different type of cell for an index path, use `reloadData(forRowIndexes:columnIndexes:)` instead.
18 |
19 | The same applies to your row view provider.
20 |
21 | - Parameters:
22 | - indexes: The indexes you want to update.
23 | */
24 | public func reconfigureRows(at indexes: IndexSet) {
25 | Self.swizzleViewRegistration()
26 | guard let delegate = delegate else { return }
27 | let indexes = indexes.filter({$0 < numberOfRows})
28 | let columns = tableColumns
29 |
30 | for row in indexes {
31 | if delegate.tableView?(self, isGroupRow: row) ?? false {
32 | if rowView(atRow: row, makeIfNecessary: false) != nil {
33 | reconfigureIndexPath = IndexPath(item: row, section: 0)
34 | _ = delegate.tableView?(self, viewFor: nil, row: row)
35 | }
36 | } else {
37 | for (index, column) in columns.enumerated() {
38 | if view(atColumn: index, row: row, makeIfNecessary: false) != nil {
39 | reconfigureIndexPath = IndexPath(item: row, section: index)
40 | _ = delegate.tableView?(self, viewFor: column, row: row)
41 | }
42 | if rowView(atRow: row, makeIfNecessary: false) != nil {
43 | reconfigureIndexPath = IndexPath(item: row, section: -1)
44 | _ = delegate.tableView?(self, rowViewForRow: row)
45 | }
46 | }
47 | }
48 | }
49 | reconfigureIndexPath = nil
50 | }
51 |
52 | var reconfigureIndexPath: IndexPath? {
53 | get { getAssociatedValue("reconfigureIndexPath") }
54 | set { setAssociatedValue(newValue, key: "reconfigureIndexPath")
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Extensions/NSTableView/NSTableView+Register.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSTableView+Nibless.swift
3 | //
4 | //
5 | // Created by Florian Zand on 10.12.22.
6 | //
7 |
8 | import AppKit
9 | import FZSwiftUtils
10 | import FZUIKit
11 |
12 | extension NSTableView {
13 | /**
14 | Registers a view class for the specified identifier, so that view-based table views can use it to instantiate views.
15 |
16 | Use this method to associate the view class with the specified identifier. When you request a view using ``makeView(for:)``, the table view recycles an existing view with the same class or creates a new one by instantiating your class.
17 |
18 | - Parameter viewClass: The view class to register.
19 | */
20 | public func register(_ viewClass: NSView.Type) {
21 | register(viewClass, forIdentifier: .init(viewClass))
22 | }
23 |
24 | func register(_ viewClass: NSView.Type, forIdentifier identifier: NSUserInterfaceItemIdentifier) {
25 | Self.swizzleViewRegistration()
26 | registeredClassesByIdentifier[identifier] = viewClass
27 | registeredClassesByIdentifier = registeredClassesByIdentifier
28 | }
29 |
30 | /**
31 | Returns a new or existing view with the specified view class.
32 |
33 | To be able to create a reusable view using this method, you have to register it first via ``register(_:)``.
34 |
35 | When this method is called, the table view automatically instantiates the cell view with the specified owner, which is usually the table view’s delegate. (The owner is useful in setting up outlets and target/actions from the view.).
36 |
37 | This method may return a reused view with the same class that is no longer available on screen.
38 |
39 | Note that `awakeFromNib()` is called each time this method is called.
40 |
41 | - Parameter viewClass: The class of the view.
42 |
43 | - Returns:The view, or `nil` if the view class isn't registered or the view couldn't be created.
44 | */
45 | public func makeView(for viewClass: View.Type) -> View? {
46 | makeView(for: viewClass, withIdentifier: .init(viewClass))
47 | }
48 |
49 | func makeView(for _: View.Type, withIdentifier identifier: NSUserInterfaceItemIdentifier) -> View? {
50 | makeView(withIdentifier: identifier, owner: nil) as? View
51 | }
52 |
53 | /**
54 | The dictionary of all registered classes for view-based table view identifiers.
55 |
56 | Each key in the dictionary is the identifier used to register the view class in the ``register(_:)``. The value of each key is the corresponding view class.
57 | */
58 | public private(set) var registeredClassesByIdentifier: [NSUserInterfaceItemIdentifier: NSView.Type] {
59 | get { getAssociatedValue("registeredClassesByIdentifier", initialValue: [:]) }
60 | set { setAssociatedValue(newValue, key: "registeredClassesByIdentifier") }
61 | }
62 |
63 | @objc private func swizzled_register(_ nib: NSNib?, forIdentifier identifier: NSUserInterfaceItemIdentifier) {
64 | if nib == nil {
65 | registeredClassesByIdentifier[identifier] = nil
66 | }
67 | swizzled_register(nib, forIdentifier: identifier)
68 | }
69 |
70 | @objc private func swizzled_makeView(withIdentifier identifier: NSUserInterfaceItemIdentifier, owner: Any?) -> NSView? {
71 | if isEnablingAutomaticRowHeights {
72 | isEnablingAutomaticRowHeights = false
73 | return nil
74 | }
75 | if let reconfigureIndexPath = reconfigureIndexPath {
76 | if reconfigureIndexPath.section != -1, let cell = view(atColumn: reconfigureIndexPath.section, row: reconfigureIndexPath.item, makeIfNecessary: false) {
77 | return cell
78 | } else if reconfigureIndexPath.section == -1, let rowView = rowView(atRow: reconfigureIndexPath.item, makeIfNecessary: false) {
79 | return rowView
80 | }
81 | }
82 | if let registeredViewClass = registeredClassesByIdentifier[identifier] {
83 | if let view = swizzled_makeView(withIdentifier: identifier, owner: owner) {
84 | return view
85 | } else {
86 | let view = registeredViewClass.init(frame: .zero)
87 | view.identifier = identifier
88 | return view
89 | }
90 | }
91 | let view = swizzled_makeView(withIdentifier: identifier, owner: owner)
92 | return view
93 | }
94 |
95 | private static var didSwizzleViewRegistration: Bool {
96 | get { getAssociatedValue("didSwizzleViewRegistration") ?? false }
97 | set { setAssociatedValue(newValue, key: "didSwizzleViewRegistration") }
98 | }
99 |
100 | static func swizzleViewRegistration() {
101 | guard !didSwizzleViewRegistration else { return }
102 | do {
103 | try Swizzle(NSTableView.self) {
104 | #selector(makeView(withIdentifier:owner:)) <-> #selector(swizzled_makeView(withIdentifier:owner:))
105 | #selector((register(_:forIdentifier:)) as (NSTableView) -> (NSNib?, NSUserInterfaceItemIdentifier) -> Void) <-> #selector(swizzled_register(_:forIdentifier:))
106 | }
107 | didSwizzleViewRegistration = true
108 | } catch {
109 | Swift.debugPrint(error)
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Extensions/NSTableView/NSTableViewDiffableDataSource+.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSTableViewDiffableDataSource+.swift
3 | //
4 | //
5 | // Created by Florian Zand on 23.07.23.
6 | //
7 |
8 | import AppKit
9 | import FZSwiftUtils
10 |
11 | // NSTableViewDiffableDataSource hides these tableview delegate functions
12 | public extension NSTableViewDiffableDataSource {
13 | /**
14 | Asks the datasource for a view to display the specified row and column.
15 |
16 | - Parameters:
17 | - tableView: The table view that sent the message.
18 | - tableColumn: The table column. If the row is a group row, `tableColumn` is `nil`.
19 | - row: The row index.
20 |
21 | - Returns: The view to display the specified column and row.
22 | */
23 | func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
24 | let selector = NSSelectorFromString("_tableView:viewForTableColumn:row:")
25 | if let meth = class_getInstanceMethod(object_getClass(self), selector) {
26 | let imp = method_getImplementation(meth)
27 | typealias ClosureType = @convention(c) (AnyObject, Selector, NSTableView, NSTableColumn?, Int) -> NSView?
28 | let method: ClosureType = unsafeBitCast(imp, to: ClosureType.self)
29 | let view = method(self, selector, tableView, tableColumn, row)
30 | return view
31 | }
32 | return nil
33 | }
34 |
35 | /**
36 | Asks the delegate for a view to display the specified row.
37 |
38 | - Parameters:
39 | - tableView: The table view that sent the message.
40 | - row: The row index.
41 |
42 | - Returns: An instance or subclass of `NSTableRowView`. If `nil` is returned, an `NSTableRowView` instance will be created and used.
43 | */
44 | func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? {
45 | let selector = NSSelectorFromString("_tableView:rowViewForRow:")
46 | if let meth = class_getInstanceMethod(object_getClass(self), selector) {
47 | let imp = method_getImplementation(meth)
48 | typealias ClosureType = @convention(c) (AnyObject, Selector, NSTableView, Int) -> NSTableRowView?
49 | let method: ClosureType = unsafeBitCast(imp, to: ClosureType.self)
50 | let view = method(self, selector, tableView, row)
51 | return view
52 | }
53 | return nil
54 | }
55 |
56 | /**
57 | Returns whether the specified row is a group row.
58 |
59 | - Parameters:
60 | - tableView: The table view that sent the message.
61 | - row: The row index.
62 |
63 | - Returns: `true` if the specified row should have the group row style drawn, `false` otherwise.
64 | */
65 | func tableView(_ tableView: NSTableView, isGroupRow row: Int) -> Bool {
66 | let selector = NSSelectorFromString("_tableView:isGroupRow:")
67 | if let meth = class_getInstanceMethod(object_getClass(self), selector) {
68 | let imp = method_getImplementation(meth)
69 | typealias ClosureType = @convention(c) (AnyObject, Selector, NSTableView, Int) -> Bool
70 | let method: ClosureType = unsafeBitCast(imp, to: ClosureType.self)
71 | let value = method(self, selector, tableView, row)
72 | return value
73 | }
74 | return false
75 | }
76 |
77 | /// The cell provider of the datasource.
78 | var cellProvider: ((NSTableView, NSTableColumn, Int, ItemIdentifierType)->(NSView)) {
79 | typealias CellProviderBlock = @convention(block) (_ tableView: NSTableView, _ tableColumn: NSTableColumn, _ row: Int, _ identifier: Any) -> NSView
80 | guard let cellProvider: CellProviderBlock = getIvarValue(for: "_cellProvider") else { return { _,_,_,_ in return NSTableCellView() } }
81 | return cellProvider
82 | }
83 |
84 | /// Creates a new table cell view for the specified item using the cell provider.
85 | func createCellView(for item: ItemIdentifierType, tableColumn: NSTableColumn? = nil, tableView: NSTableView) -> NSView? {
86 | guard let tableColumn = tableColumn ?? tableView.tableColumns.first, tableView.tableColumns.contains(tableColumn) else { return nil }
87 | return cellProvider(tableView, tableColumn, 0, item)
88 | }
89 |
90 | /// Returns a preview image of the table cell for the specified item and table column.
91 | func previewImage(for item: ItemIdentifierType, tableView: NSTableView) -> NSImage? {
92 | let columns = tableView.tableColumns
93 | guard !columns.isEmpty else { return nil }
94 | return NSImage(combineHorizontal: columns.compactMap({ _previewImage(for: item, tableColumn: $0, tableView: tableView, useColumnWidth: $0 !== columns.last!) }), alignment: .top)
95 | }
96 |
97 | /// Returns a preview image of the table row for the specified item.
98 | func previewImage(for item: ItemIdentifierType, tableColumn: NSTableColumn, tableView: NSTableView) -> NSImage? {
99 | _previewImage(for: item, tableColumn: tableColumn, tableView: tableView)
100 | }
101 |
102 | /// Returns a preview image of the table rows for the specified items.
103 | func previewImage(for items: [ItemIdentifierType], tableView: NSTableView) -> NSImage? {
104 | return NSImage(combineVertical: items.compactMap({ previewImage(for: $0, tableView: tableView)}).reversed(), alignment: .left)
105 | }
106 |
107 | private func _previewImage(for item: ItemIdentifierType, tableColumn: NSTableColumn, tableView: NSTableView, useColumnWidth: Bool = true) -> NSImage? {
108 | guard tableView.tableColumns.contains(tableColumn), let view = createCellView(for: item, tableColumn: tableColumn, tableView: tableView) else { return nil }
109 | view.frame.size = view.systemLayoutSizeFitting(width: tableColumn.width)
110 | view.frame.size.width = useColumnWidth ? tableColumn.width : view.frame.size.width
111 | return view.renderedImage
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Extensions/NSTableView/NSTableViewDiffableDataSource+Apply.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSTableViewDiffableDataSource+Apply.swift
3 | //
4 | //
5 | // Created by Florian Zand on 16.12.22.
6 | //
7 |
8 | import AppKit
9 |
10 | extension NSTableViewDiffableDataSource {
11 | /**
12 | Updates the UI to reflect the state of the data in the specified snapshot, optionally animating the UI changes and executing a completion handler.
13 |
14 | It’s safe to call this method from a background queue, but you must do so consistently in your app. Always call this method exclusively from the main queue or from a background queue.
15 |
16 | - Parameters:
17 | - snapshot: The snapshot reflecting the new state of the data in the table view.
18 | - option: Option how to apply the snapshot to the table view. The default value is `animated`.
19 | - completion: An optional closure to be executed when the animations are complete. The system calls this closure from the main queue.
20 | */
21 | public func apply(_ snapshot: NSDiffableDataSourceSnapshot, _ option: NSDiffableDataSourceSnapshotApplyOption = .animated, completion: (() -> Void)? = nil) {
22 | switch option {
23 | case .usingReloadData:
24 | applySnapshotUsingReloadData(snapshot, completion: completion)
25 | case .animated:
26 | apply(snapshot, animated: true, animationDuration: option.animationDuration, completion: completion)
27 | case .withoutAnimation:
28 | apply(snapshot, animated: false, completion: completion)
29 | }
30 | }
31 |
32 | private func applySnapshotUsingReloadData(_ snapshot: NSDiffableDataSourceSnapshot, completion: (() -> Void)? = nil) {
33 | apply(snapshot, animatingDifferences: false, completion: completion)
34 | }
35 |
36 | private func apply(_ snapshot: NSDiffableDataSourceSnapshot, animated: Bool = true, animationDuration: TimeInterval? = nil, completion: (() -> Void)? = nil) {
37 | if animated, animationDuration == nil {
38 | apply(snapshot, animatingDifferences: true, completion: completion)
39 | } else {
40 | NSAnimationContext.beginGrouping()
41 | NSAnimationContext.current.duration = animationDuration ?? 0
42 | apply(snapshot, animatingDifferences: true, completion: completion)
43 | NSAnimationContext.endGrouping()
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Extensions/NSTableView/NSTableViewDiffableDataSource+Registration.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSTableViewDiffableDataSource+Registration.swift
3 | //
4 | //
5 | // Created by Florian Zand on 10.12.22.
6 | //
7 |
8 | import AppKit
9 |
10 | public extension NSTableViewDiffableDataSource {
11 | /**
12 | Creates a diffable data source with the specified cell registration, and connects it to the specified table view.
13 |
14 | - Parameters:
15 | - tableView: The initialized table view object to connect to the diffable data source.
16 | - cellRegistration: A cell registration that creates, configurates and returns each of the cells for the table view from the data the diffable data source provides.
17 | */
18 | convenience init(tableView: NSTableView, cellRegistration: NSTableView.CellRegistration) {
19 | self.init(tableView: tableView, cellProvider: {
20 | _tableView, column, row, item in
21 | _tableView.makeCellView(using: cellRegistration, forColumn: column, row: row, item: item)!
22 | })
23 | }
24 |
25 | /**
26 | Creates a diffable data source with the specified cell registrations, and connects it to the specified table view.
27 |
28 | - Parameters:
29 | - tableView: The initialized table view object to connect to the diffable data source.
30 | - cellRegistrations: Cell registrations that create, configurate and return each of the cells for the table view from the data the diffable data source provides.
31 |
32 | - Important: Each of the cell registrations need to have a column identifier.
33 | */
34 | convenience init(tableView: NSTableView, cellRegistrations: [NSTableViewCellRegistration]) {
35 | self.init(tableView: tableView, cellProvider: {
36 | _, column, row, element in
37 | if let cellRegistration = (cellRegistrations.first(where: { $0.columnIdentifiers.contains(column.identifier) }) ?? cellRegistrations.first(where: { $0.columnIdentifiers.isEmpty })) as? _NSTableViewCellRegistration {
38 | return cellRegistration.makeView(tableView, column, row, element) ?? NSTableCellView()
39 | }
40 | return NSTableCellView()
41 | })
42 | }
43 |
44 | /**
45 | Creates a diffable data source with the specified cell and section view registration, and connects it to the specified table view.
46 |
47 | - Parameters:
48 | - tableView: The initialized table view object to connect to the diffable data source.
49 | - cellRegistration: A cell registration that creates, configurates and returns each of the cells for the table view from the data the diffable data source provides.
50 | - sectionHeaderRegistration: A section view registration that creates, configurates and returns each of the section header views for the table view from the data the diffable data source provides.
51 | */
52 | convenience init(tableView: NSTableView, cellRegistration: NSTableView.CellRegistration, sectionHeaderRegistration: NSTableView.CellRegistration
) {
53 | self.init(tableView: tableView, cellProvider: {
54 | _tableView, column, row, item in
55 | _tableView.makeCellView(using: cellRegistration, forColumn: column, row: row, item: item)!
56 | })
57 | applySectionHeaderViewRegistration(sectionHeaderRegistration)
58 | }
59 |
60 | /**
61 | Creates a diffable data source with the specified cell registrations, and connects it to the specified table view.
62 |
63 | - Parameters:
64 | - tableView: The initialized table view object to connect to the diffable data source.
65 | - cellRegistrations: Cell registrations that create, configurate and return each of the cells for the table view from the data the diffable data source provides.
66 | - sectionHeaderRegistration: A section view registration that creates, configurates and returns each of the section header views for the table view from the data the diffable data source provides.
67 |
68 | - Important: Each of the cell registrations need to have a column identifier.
69 | */
70 | convenience init(tableView: NSTableView, cellRegistrations: [NSTableViewCellRegistration], sectionHeaderRegistration: NSTableView.CellRegistration) {
71 | self.init(tableView: tableView, cellProvider: {
72 | _, column, row, element in
73 | if let cellRegistration = cellRegistrations.first(where: { $0.columnIdentifiers.contains(column.identifier) }) ?? cellRegistrations.first(where: { $0.columnIdentifiers.isEmpty }) {
74 | return (cellRegistration as! _NSTableViewCellRegistration).makeView(tableView, column, row, element)!
75 | }
76 | return NSTableCellView()
77 | })
78 | applySectionHeaderViewRegistration(sectionHeaderRegistration)
79 | }
80 |
81 | /// Uses the specified row view registration to configure and return row views.
82 | func useRowViewRegistration(_ registration: NSTableView.RowRegistration) {
83 | self.rowViewProvider = { tableview, row, item in
84 | if let item = item as? ItemIdentifierType {
85 | return tableview.makeRowView(using: registration, forRow: row, item: item)
86 | }
87 | return NSTableRowView()
88 | }
89 | }
90 |
91 | /// Uses the specified cell registration to configure and return section header views.
92 | func applySectionHeaderViewRegistration(_ registration: NSTableView.CellRegistration) {
93 | sectionHeaderViewProvider = { tableView, row, section in
94 | if let column = tableView.tableColumns.first, let cellView = tableView.makeCellView(using: registration, forColumn: column, row: row, item: section) {
95 | return cellView
96 | }
97 | return NSTableCellView()
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Extensions/Shared/NSDiffableDataSourceSnapshot+.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSDiffableDataSourceSnapshot+.swift
3 | //
4 | //
5 | // Created by Florian Zand on 28.12.23.
6 | //
7 |
8 | import AppKit
9 |
10 | extension NSDiffableDataSourceSnapshot where ItemIdentifierType: Identifiable, SectionIdentifierType: Identifiable {
11 | /// A snapshot from the section and item identifiers.
12 | typealias IdentifiableSnapshot = NSDiffableDataSourceSnapshot
13 | typealias Transaction = DiffableDataSourceTransaction
14 |
15 | /// Creates a snapshot from the section and item identifiers.
16 | func toIdentifiableSnapshot() -> IdentifiableSnapshot {
17 | var identifiableSnapshot = IdentifiableSnapshot()
18 | let sections = sectionIdentifiers
19 | identifiableSnapshot.appendSections(sections.ids)
20 | for section in sections {
21 | let items = itemIdentifiers(inSection: section)
22 | identifiableSnapshot.appendItems(items.ids, toSection: section.id)
23 | }
24 | return identifiableSnapshot
25 | }
26 |
27 | func nextItemForDeleting(_ items: [ItemIdentifierType]) -> ItemIdentifierType? {
28 | guard let delete = items.last, let index = indexOfItem(delete), let item = itemIdentifiers[safe: index+1] ?? itemIdentifiers[safe: index-1] else { return nil }
29 | if sectionIdentifier(containingItem: item) != sectionIdentifier(containingItem: delete), let section = sectionIdentifier(containingItem: delete) {
30 | if let item = itemIdentifiers(inSection: section).reversed().first(where: { !items.contains($0) }) {
31 | return item
32 | } else if let index = indexOfSection(section), let section = sectionIdentifiers[safe: index-1], let item = itemIdentifiers(inSection: section).reversed().first(where: { !items.contains($0) }) {
33 | return item
34 | }
35 | }
36 | return item
37 | }
38 |
39 | func moveTransaction(_ section: SectionIdentifierType, after afterSection: SectionIdentifierType) -> Transaction {
40 | var snapshot = self
41 | snapshot.moveSection(section, afterSection: afterSection)
42 | return Transaction(initial: self, final: snapshot)
43 | }
44 |
45 | func moveTransaction(_ section: SectionIdentifierType, before beforeSection: SectionIdentifierType) -> Transaction {
46 | var snapshot = self
47 | snapshot.moveSection(section, beforeSection: beforeSection)
48 | return Transaction(initial: self, final: snapshot)
49 | }
50 |
51 | func deleteTransaction(_ items: [ItemIdentifierType]) -> Transaction {
52 | var finalSnapshot = self
53 | finalSnapshot.deleteItems(items)
54 | return DiffableDataSourceTransaction(initial: self, final: finalSnapshot)
55 | }
56 |
57 | mutating func insertItemsSaftly(_ identifiers: [ItemIdentifierType], afterItem: ItemIdentifierType) {
58 | if identifiers.contains(afterItem) {
59 | if let section = sectionIdentifier(containingItem: afterItem) {
60 | let identifiers = identifiers.filter({ sectionIdentifier(containingItem: $0) != section })
61 | insertItems(identifiers, afterItem: afterItem)
62 | }
63 | } else {
64 | insertItems(identifiers, afterItem: afterItem)
65 | }
66 | }
67 |
68 | mutating func insertItemsSaftly(_ identifiers: [ItemIdentifierType], beforeItem: ItemIdentifierType) {
69 | if identifiers.contains(beforeItem) {
70 | if let section = sectionIdentifier(containingItem: beforeItem) {
71 | let identifiers = identifiers.filter({ sectionIdentifier(containingItem: $0) != section })
72 | insertItems(identifiers, beforeItem: beforeItem)
73 | }
74 | } else {
75 | insertItems(identifiers, beforeItem: beforeItem)
76 | }
77 | }
78 |
79 | /*
80 | func movingTransaction(_ items: [ItemIdentifierType], item: ItemIdentifierType?, section: SectionIdentifierType?) -> DiffableDataSourceTransaction? {
81 | var newSnapshot = self
82 | if let item = item {
83 | newSnapshot.insertItems(items, beforeItem: item)
84 | } else if let section = section {
85 | item
86 | if let item = item(forRow: row - 1) {
87 | newSnapshot.insertItems(newItems, afterItem: item)
88 | } else {
89 | newSnapshot.appendItems(newItems, toSection: section)
90 | }
91 | } else if let section = sections.last {
92 | newSnapshot.appendItems(newItems, toSection: section)
93 | }
94 | return DiffableDataSourceTransaction(initial: currentSnapshot, final: newSnapshot)
95 |
96 | }
97 | */
98 |
99 | /*
100 | func nextItemForDeleting(_ items: [ItemIdentifierType]) -> ItemIdentifierType? {
101 | guard let delete = items.last, let index = indexOfItem(delete), let item = itemIdentifiers[safe: index-1] else { return nil }
102 | if sectionIdentifier(containingItem: item) != sectionIdentifier(containingItem: delete), let section = sectionIdentifier(containingItem: delete), let item = itemIdentifiers(inSection: section).first(where: { !items.contains($0) }) {
103 | return item
104 | }
105 | return item
106 | }
107 | */
108 | }
109 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Extensions/Shared/NSDiffableDataSourceSnapshot+ApplyOption.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSDiffableDataSourceSnapshot+ApplyOption.swift
3 | //
4 | //
5 | // Created by Florian Zand on 23.07.23.
6 | //
7 |
8 | import AppKit
9 |
10 | /**
11 | Options for applying a snapshot to a diffable data source.
12 |
13 | Apple's `apply(_:animatingDifferences:completion:)` provides two options for applying snapshots to a diffable data source depending on `animatingDifferences`:
14 | - `true` applies a diff of the old and new state and applies the updates to the receiver animated.
15 | - `false` is equivalent to calling `reloadData()`. It reloads every item/cell of the receiver.
16 |
17 | **Non-animated diff**
18 |
19 | `NSDiffableDataSourceSnapshotApplyOption` lets you perform a diff even without animations using `withoutAnimation` for much better performance compared to using Apple's `reloadData()`.
20 |
21 | ```swift
22 | diffableDatasource.apply(snapshot, .withoutAnimation)
23 | ```
24 |
25 | **Animation duration**
26 |
27 | When you want to apply the snapshot animated, you can also change the animation duration using `animated(duration:)`.
28 |
29 | ```swift
30 | diffableDatasource.apply(snapshot, .animated(duration: 1.0))
31 | ```
32 | */
33 | public enum NSDiffableDataSourceSnapshotApplyOption: Hashable, Sendable {
34 | /**
35 | The snapshot gets applied animated.
36 |
37 | The data source computes a diff of the previous and new state and applies the updates to the receiver animated with a default animation duration.
38 | */
39 | public static var animated: Self { .animated(duration: noAnimationDuration) }
40 |
41 | /**
42 | The snapshot gets applied animiated with the specified animation duration.
43 |
44 | The data source computes a diff of the previous and new state and applies the updates to the receiver animated with the specified animation duration.
45 | */
46 | case animated(duration: TimeInterval)
47 |
48 | /**
49 | The snapshot gets applied using `reloadData()`.
50 |
51 | The system resets the UI to reflect the state of the data in the snapshot without computing a diff or animating the changes.
52 | */
53 | case usingReloadData
54 | /**
55 | The snapshot gets applied without any animation.
56 |
57 | The data source computes a diff of the previous and new state and applies the updates to the receiver without any animation.
58 | */
59 | case withoutAnimation
60 |
61 | static var noAnimationDuration: TimeInterval { 2_344_235 }
62 |
63 | var animationDuration: TimeInterval? {
64 | switch self {
65 | case let .animated(duration):
66 | guard duration != Self.noAnimationDuration else { return nil }
67 | guard let currentEvent = NSApplication.shared.currentEvent else { return duration }
68 | let flags = currentEvent.modifierFlags.intersection([.shift, .option, .control, .command])
69 | return duration * (flags == .shift ? 10 : 1)
70 | default:
71 | return nil
72 | }
73 | }
74 |
75 | var isReloadData: Bool {
76 | switch self {
77 | case .usingReloadData: return true
78 | default: return false
79 | }
80 | }
81 |
82 | var isWithoutAbinatuib: Bool {
83 | switch self {
84 | case .withoutAnimation: return true
85 | default: return false
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Extensions/Shared/NSPasteboardItem+.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSPasteboardItem+.swift
3 | //
4 | //
5 | // Created by Florian Zand on 16.03.25.
6 | //
7 |
8 | import AppKit
9 | import FZUIKit
10 |
11 |
12 | extension NSPasteboardItem {
13 | convenience init(for element: Element, content: [PasteboardWriting]? = nil) {
14 | self.init(content: content ?? [])
15 | setString(String(element.id.hashValue), forType: .itemID)
16 | }
17 |
18 | convenience init(forItem item: Item, content: [PasteboardWriting]? = nil) {
19 | self.init(content: content ?? [])
20 | setString(String(describing: item), forType: .itemID)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Extensions/Shared/NSView+Transform.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSView+Transform.swift
3 | //
4 | //
5 | // Created by Florian Zand on 03.03.25.
6 | //
7 |
8 | import AppKit
9 | import FZSwiftUtils
10 | import FZUIKit
11 |
12 | extension NSView {
13 | var _scaleTransform: Scale {
14 | get { getAssociatedValue("_scaleTransform") ?? .none }
15 | set {
16 | guard newValue != _scaleTransform else { return }
17 | setAssociatedValue(newValue, key: "_scaleTransform")
18 | anchorPoint = .center
19 | animatorIfNeeded().scale = newValue
20 | }
21 | }
22 |
23 | var _rotation: Rotation {
24 | get { getAssociatedValue("_rotation") ?? .zero }
25 | set {
26 | guard newValue != _rotation else { return }
27 | setAssociatedValue(newValue, key: "_rotation")
28 | anchorPoint = .center
29 | animatorIfNeeded().rotation = newValue
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Sources/AdvancedCollectionTableView/Registrations/NSTableView/RowRegistration.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RowRegistration.swift
3 | //
4 | //
5 | // Created by Florian Zand on 27.04.22.
6 | //
7 |
8 | import AppKit
9 | import FZSwiftUtils
10 | import FZUIKit
11 |
12 | public extension NSTableView {
13 | /**
14 | A registration for the table view’s row views.
15 |
16 | Use a row registration to register row views with your table view and configure each row for display. You create a row registration with your row type and data row type as the registration’s generic parameters, passing in a registration handler to configure the row. In the registration handler, you specify how to configure the content and appearance of that type of row.
17 |
18 | The following example creates a row registration for row views of type [NSTableRowView](https://developer.apple.com/documentation/appkit/nstablerowview).
19 |
20 | ```swift
21 | let rowRegistration = NSTableView.RowRegistration { row, indexPath, string in
22 |
23 | }
24 | ```
25 |
26 | After you create a row registration, you pass it in to ``makeRowView(using:forRow:item:)``, which you call from your data source’s row provider.
27 |
28 | ```swift
29 | dataSource.rowProvider = { tableView, row, item in
30 | return tableView.makeRowView(using: rowRegistration, forRow: row, item)
31 | }
32 | ```
33 | */
34 | struct RowRegistration where RowView: NSTableRowView {
35 | let identifier: NSUserInterfaceItemIdentifier = .init(UUID().uuidString)
36 | let nib: NSNib?
37 | let handler: Handler
38 |
39 | // MARK: Creating a row registration
40 |
41 | /**
42 | Creates a row registration with the specified registration handler.
43 |
44 | - Parameters:
45 | - handler: The handler to configurate the row view.
46 | */
47 | public init(handler: @escaping Handler) {
48 | self.handler = handler
49 | self.nib = nil
50 | }
51 |
52 | /**
53 | Creates a row registration with the specified registration handler and nib file.
54 |
55 | - Parameters:
56 | - nib: The nib of the row view.
57 | - handler: The handler to configurate the row view.
58 | */
59 | public init(nib: NSNib, handler: @escaping Handler) {
60 | self.handler = handler
61 | self.nib = nib
62 | }
63 |
64 | /// A closure that handles the row registration and configuration.
65 | public typealias Handler = (_ rowView: RowView, _ row: Int, _ item: Item) -> Void
66 |
67 | func makeView(_ tableView: NSTableView, _ row: Int, _ item: Item) -> RowView {
68 | let rowView = (tableView.makeView(withIdentifier: identifier, owner: self) as? RowView) ?? RowView(frame: .zero)
69 | rowView.identifier = identifier
70 | handler(rowView, row, item)
71 | return rowView
72 | }
73 | }
74 | }
75 |
76 | extension NSTableView {
77 | /**
78 | Dequeues a configured reusable row view object.
79 |
80 | - Parameters:
81 | - registration: The row view registration for configuring the rowview object. See ``RowRegistration``.
82 | - row: The index path specifying the row of the row. The data source receives this information when it is asked for the row and should just pass it along. This method uses the row to perform additional configuration based on the row’s position in the table view.
83 | - item: The item that provides data for the row.
84 |
85 | - Returns:A configured reusable row view object.
86 | */
87 | public func makeRowView(using registration: RowRegistration, forRow row: Int, item: Item) -> RowView where RowView: NSTableRowView {
88 | registration.makeView(self, row, item)
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
| |