Skip to content

Commit e001cef

Browse files
authored
Introduce firstDescendant for faster descendants enumeration (#113)
1 parent 5b0f24c commit e001cef

File tree

3 files changed

+28
-33
lines changed

3 files changed

+28
-33
lines changed

MarkEditMac/Modules/Sources/AppKitControls/BackgroundTheming.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public extension BackgroundTheming {
1414
layerBackgroundColor = color
1515
needsDisplay = true
1616

17-
enumerateChildren { (button: NonBezelButton) in
17+
enumerateDescendants { (button: NonBezelButton) in
1818
button.layerBackgroundColor = color
1919
button.needsDisplay = true
2020
}

MarkEditMac/Modules/Sources/AppKitExtensions/UI/NSView+Extension.swift

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public extension NSView {
5757
}
5858
}
5959

60-
/// Check if the view itself or its children is the first responder in a window.
60+
/// Check if the view itself or one of its descendants is the first responder in a window.
6161
func isFirstResponder(in window: NSWindow?) -> Bool {
6262
(window?.firstResponder as? NSView)?.belongs(to: self) ?? false
6363
}
@@ -79,14 +79,30 @@ public extension NSView {
7979
animated ? animator() : self
8080
}
8181

82-
/// Enumerate all children, recursively, self first.
83-
func enumerateChildren<T: NSView>(where: ((T) -> Bool)? = nil, handler: (T) -> Void) {
82+
/// Enumerate all descendants, recursively, self first.
83+
func enumerateDescendants<T: NSView>(where: ((T) -> Bool)? = nil, handler: (T) -> Void) {
8484
if let view = self as? T, `where`?(view) ?? true {
8585
handler(view)
8686
}
8787

8888
subviews.forEach {
89-
$0.enumerateChildren(where: `where`, handler: handler)
89+
$0.enumerateDescendants(where: `where`, handler: handler)
9090
}
9191
}
92+
93+
/// Returns the first descendant that matches a predicate, self is included.
94+
func firstDescendant<T: NSView>(where: ((T) -> Bool)? = nil) -> T? {
95+
var stack = [self]
96+
while !stack.isEmpty {
97+
let node = stack.removeLast()
98+
if let view = node as? T, `where`?(view) ?? true {
99+
return view
100+
}
101+
102+
// Depth-first search
103+
stack.append(contentsOf: node.subviews)
104+
}
105+
106+
return nil
107+
}
92108
}

MarkEditMac/Modules/Sources/AppKitExtensions/UI/NSWindow+Extension.swift

Lines changed: 7 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,13 @@ public extension NSWindow {
1212
}
1313

1414
var toolbarEffectView: NSVisualEffectView? {
15-
guard let rootView else {
16-
assertionFailure("Missing rootView from window to proceed")
17-
return nil
15+
let result = rootView?.firstDescendant { (effectView: NSVisualEffectView) in
16+
// What we want is an NSVisualEffectView child of an NSTitlebarView
17+
effectView.superview?.className.hasPrefix("NSTitlebar") == true
1818
}
1919

20-
var stack = [rootView]
21-
while !stack.isEmpty {
22-
let node = stack.removeLast()
23-
if node is NSVisualEffectView && node.superview?.className.hasPrefix("NSTitlebar") == true {
24-
// What we want is an NSVisualEffectView child of an NSTitlebarView
25-
return node as? NSVisualEffectView
26-
}
27-
28-
// Depth-first search
29-
stack.append(contentsOf: node.subviews)
30-
}
31-
32-
assertionFailure("Failed to find NSVisualEffectView in toolbar")
33-
return nil
20+
assert(result != nil, "Failed to find NSVisualEffectView in toolbar")
21+
return result
3422
}
3523

3624
/// Change the frame size, treat the top-left corner as the anchor point.
@@ -66,18 +54,9 @@ public extension NSWindow {
6654
///
6755
/// There's no public API to programmatically show the menu assigned to an NSToolbarItem.
6856
func popUpButton(with menuIdentifier: NSUserInterfaceItemIdentifier) -> NSPopUpButton? {
69-
guard let view = contentView?.superview else {
70-
return nil
57+
contentView?.superview?.firstDescendant { (button: NSPopUpButton) in
58+
button.menu?.identifier == menuIdentifier
7159
}
72-
73-
var result: NSPopUpButton?
74-
view.enumerateChildren { (button: NSPopUpButton) in
75-
if button.menu?.identifier == menuIdentifier {
76-
result = button
77-
}
78-
}
79-
80-
return result
8160
}
8261
}
8362

0 commit comments

Comments
 (0)