@@ -8,9 +8,11 @@ import Cocoa
8
8
class EditorTableViewController : NSObject , SwitchableTableViewController {
9
9
let contentView : NSStackView
10
10
let scrollView : CustomScrollView
11
+ let bottomBar : BottomBar
11
12
let tableView = NSTableView ( )
12
13
13
14
let allServices : [ BaseService ] = BaseService . all ( ) . sorted ( )
15
+ let allServicesWithoutSubServices : [ BaseService ] = BaseService . allWithoutSubServices ( ) . sorted ( )
14
16
var filteredServices : [ BaseService ]
15
17
var selectedServices : [ BaseService ] = Preferences . shared. selectedServices
16
18
@@ -20,6 +22,64 @@ class EditorTableViewController: NSObject, SwitchableTableViewController {
20
22
21
23
var hidden : Bool = true
22
24
25
+ var savedScrollPosition : CGPoint = . zero
26
+
27
+ var selectedCategory : ServiceCategory ? {
28
+ didSet {
29
+ // Save the scroll position between screens
30
+ let scrollToPosition : CGPoint ?
31
+
32
+ if selectedCategory != nil && oldValue == nil {
33
+ savedScrollPosition = CGPoint ( x: 0 , y: tableView. visibleRect. minY)
34
+ scrollToPosition = . zero
35
+ } else if selectedCategory == nil && oldValue != nil {
36
+ scrollToPosition = savedScrollPosition
37
+ } else {
38
+ scrollToPosition = nil
39
+ }
40
+
41
+ // Adjust UI
42
+ bottomBar. openedCategory ( selectedCategory, backCallback: { [ weak self] in
43
+ self ? . selectedCategory = nil
44
+ } )
45
+
46
+ guard let category = selectedCategory else {
47
+ // Show the unfiltered services
48
+ filteredServices = allServicesWithoutSubServices
49
+ tableView. reloadData ( )
50
+
51
+ if let scrollPosition = scrollToPosition {
52
+ tableView. scroll ( scrollPosition)
53
+ }
54
+
55
+ return
56
+ }
57
+
58
+ // Find the sub services
59
+ var subServices = allServices. filter {
60
+ // Can't check superclass matches without mirror
61
+ Mirror ( reflecting: $0) . superclassMirror? . subjectType == category. subServiceSuperclass
62
+
63
+ // Exclude the category so that we can add it at the top
64
+ && $0 != category as? BaseService
65
+ } . sorted ( )
66
+
67
+ // Add the category as the top item
68
+ ( category as? BaseService ) . flatMap { subServices. insert ( $0, at: 0 ) }
69
+
70
+ filteredServices = subServices
71
+ tableView. reloadData ( )
72
+
73
+ if let scrollPosition = scrollToPosition {
74
+ tableView. scroll ( scrollPosition)
75
+ }
76
+ }
77
+ }
78
+
79
+ var isSearching : Bool {
80
+ settingsView. searchField. stringValue. trimmingCharacters ( in: . whitespacesAndNewlines) != " "
81
+ }
82
+
23
83
private var maxNameWidth : CGFloat ? {
24
84
didSet {
25
85
if oldValue != maxNameWidth {
@@ -28,10 +88,11 @@ class EditorTableViewController: NSObject, SwitchableTableViewController {
28
88
}
29
89
}
30
90
31
- init ( contentView: NSStackView , scrollView: CustomScrollView ) {
91
+ init ( contentView: NSStackView , scrollView: CustomScrollView , bottomBar : BottomBar ) {
32
92
self . contentView = contentView
33
93
self . scrollView = scrollView
34
- self . filteredServices = allServices
94
+ self . filteredServices = allServicesWithoutSubServices
95
+ self . bottomBar = bottomBar
35
96
36
97
super. init ( )
37
98
setup ( )
@@ -56,11 +117,12 @@ class EditorTableViewController: NSObject, SwitchableTableViewController {
56
117
settingsView. searchCallback = { [ weak self] searchString in
57
118
guard
58
119
let strongSelf = self ,
59
- let allServices = strongSelf. allServices as? [ Service ]
120
+ let allServices = strongSelf. allServices as? [ Service ] ,
121
+ let allServicesWithoutSubServices = strongSelf. allServicesWithoutSubServices as? [ Service ]
60
122
else { return }
61
123
62
124
if searchString. trimmingCharacters ( in: . whitespacesAndNewlines) == " " {
63
- strongSelf. filteredServices = allServices
125
+ strongSelf. filteredServices = allServicesWithoutSubServices
64
126
} else {
65
127
// Can't filter array with NSPredicate without making Service inherit KVO from NSObject, therefore we create
66
128
// an array of service names that we can run the predicate on
@@ -71,6 +133,10 @@ class EditorTableViewController: NSObject, SwitchableTableViewController {
71
133
strongSelf. filteredServices = allServices. filter { filteredServiceNames. contains ( $0. name) }
72
134
}
73
135
136
+ if strongSelf. selectedCategory != nil {
137
+ strongSelf. selectedCategory = nil
138
+ }
139
+
74
140
strongSelf. tableView. reloadData ( )
75
141
}
76
142
@@ -122,6 +188,10 @@ class EditorTableViewController: NSObject, SwitchableTableViewController {
122
188
func willHide( ) {
123
189
settingsView. isHidden = true
124
190
}
191
+
192
+ @objc func deselectCategory( ) {
193
+ selectedCategory = nil
194
+ }
125
195
}
126
196
127
197
extension EditorTableViewController : NSTableViewDataSource {
@@ -132,6 +202,19 @@ extension EditorTableViewController: NSTableViewDataSource {
132
202
func tableView( _ tableView: NSTableView , objectValueFor tableColumn: NSTableColumn ? , row: Int ) -> Any ? {
133
203
return nil
134
204
}
205
+
206
+ func tableViewSelectionDidChange( _ notification: Notification ) {
207
+ guard tableView. selectedRow != - 1 else { return }
208
+
209
+ // We're only interested in selections of categories
210
+ guard
211
+ selectedCategory == nil ,
212
+ let category = filteredServices [ tableView. selectedRow] as? ServiceCategory
213
+ else { return }
214
+
215
+ // Change the selected category
216
+ selectedCategory = category
217
+ }
135
218
}
136
219
137
220
extension EditorTableViewController : NSTableViewDelegate {
@@ -151,22 +234,35 @@ extension EditorTableViewController: NSTableViewDelegate {
151
234
guard let view = cell as? EditorTableCell else { return nil }
152
235
guard let service = filteredServices [ row] as? Service else { return nil }
153
236
154
- view. textField? . stringValue = service. name
155
- view. selected = selectedServices. contains ( service)
156
- view. toggleCallback = { [ weak self] in
157
- guard let strongSelf = self else { return }
158
-
159
- strongSelf. selectionChanged = true
237
+ if isSearching || selectedCategory != nil {
238
+ view. type = . service
239
+ } else {
240
+ view. type = ( service is ServiceCategory ) ? . category : . service
241
+ }
160
242
161
- if view. selected {
162
- self ? . selectedServices. append ( service)
163
- } else {
164
- if let index = self ? . selectedServices. firstIndex ( of: service) {
165
- self ? . selectedServices. remove ( at: index)
243
+ switch view. type {
244
+ case . service:
245
+ view. textField? . stringValue = service. name
246
+ view. selected = selectedServices. contains ( service)
247
+ view. toggleCallback = { [ weak self] in
248
+ guard let strongSelf = self else { return }
249
+
250
+ strongSelf. selectionChanged = true
251
+
252
+ if view. selected {
253
+ self ? . selectedServices. append ( service)
254
+ } else {
255
+ if let index = self ? . selectedServices. firstIndex ( of: service) {
256
+ self ? . selectedServices. remove ( at: index)
257
+ }
166
258
}
167
- }
168
259
169
- Preferences . shared. selectedServices = strongSelf. selectedServices
260
+ Preferences . shared. selectedServices = strongSelf. selectedServices
261
+ }
262
+ case . category:
263
+ view. textField? . stringValue = ( service as? ServiceCategory ) ? . categoryName ?? service. name
264
+ view. selected = false
265
+ view. toggleCallback = { }
170
266
}
171
267
172
268
maxNameWidth = EditorTableCell . maxNameWidth ( for: tableView)
0 commit comments