From e00197c1bd508920f465b201542f4144ce21b3e3 Mon Sep 17 00:00:00 2001 From: Pete Schaffner Date: Wed, 7 Feb 2024 11:10:43 +0100 Subject: [PATCH 1/4] Add extensions to help finding private subviews --- macos/Ghostty.xcodeproj/project.pbxproj | 4 +++ .../Features/Terminal/TerminalWindow.swift | 15 +++++----- macos/Sources/Helpers/NSView+Extension.swift | 29 +++++++++++++++++++ 3 files changed, 41 insertions(+), 7 deletions(-) create mode 100644 macos/Sources/Helpers/NSView+Extension.swift diff --git a/macos/Ghostty.xcodeproj/project.pbxproj b/macos/Ghostty.xcodeproj/project.pbxproj index eb8c236e7e..d0942cad25 100644 --- a/macos/Ghostty.xcodeproj/project.pbxproj +++ b/macos/Ghostty.xcodeproj/project.pbxproj @@ -68,6 +68,7 @@ AEF9CE242B6AD07A0017E195 /* TerminalToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEF9CE232B6AD07A0017E195 /* TerminalToolbar.swift */; }; C159E81D2B66A06B00FDFE9C /* OSColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C159E81C2B66A06B00FDFE9C /* OSColor+Extension.swift */; }; C159E89D2B69A2EF00FDFE9C /* OSColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C159E81C2B66A06B00FDFE9C /* OSColor+Extension.swift */; }; + C1F26EA72B738B9900404083 /* NSView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1F26EA62B738B9900404083 /* NSView+Extension.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -128,6 +129,7 @@ A5FEB2FF2ABB69450068369E /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; AEF9CE232B6AD07A0017E195 /* TerminalToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalToolbar.swift; sourceTree = ""; }; C159E81C2B66A06B00FDFE9C /* OSColor+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OSColor+Extension.swift"; sourceTree = ""; }; + C1F26EA62B738B9900404083 /* NSView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSView+Extension.swift"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -193,6 +195,7 @@ A59630962AEE163600D64628 /* HostingWindow.swift */, A59FB5D02AE0DEA7009128F3 /* MetalView.swift */, C159E81C2B66A06B00FDFE9C /* OSColor+Extension.swift */, + C1F26EA62B738B9900404083 /* NSView+Extension.swift */, A5CEAFDA29B8005900646FDA /* SplitView */, ); path = Helpers; @@ -468,6 +471,7 @@ A59FB5CF2AE0DB50009128F3 /* InspectorView.swift in Sources */, A5D0AF3D2B37804400D21823 /* CodableBridge.swift in Sources */, A5D0AF3B2B36A1DE00D21823 /* TerminalRestorable.swift in Sources */, + C1F26EA72B738B9900404083 /* NSView+Extension.swift in Sources */, A596309C2AEE1C9E00D64628 /* TerminalController.swift in Sources */, A56D58892ACDE6CA00508D2C /* ServiceProvider.swift in Sources */, A51BFC222B2FB6B400E92F16 /* AboutView.swift in Sources */, diff --git a/macos/Sources/Features/Terminal/TerminalWindow.swift b/macos/Sources/Features/Terminal/TerminalWindow.swift index e2d14ad1ff..db76bf98b1 100644 --- a/macos/Sources/Features/Terminal/TerminalWindow.swift +++ b/macos/Sources/Features/Terminal/TerminalWindow.swift @@ -70,10 +70,10 @@ class TerminalWindow: NSWindow { func setTitlebarBackground(_ color: CGColor) { storedTitlebarBackgroundColor = color - guard let titlebarContainer = contentView?.superview?.subviews.first(where: { - $0.className == "NSTitlebarContainerView" - }) else { return } - + guard let titlebarContainer = contentView?.superview?.firstSubview(withClassName: "NSTitlebarContainerView") else { + return + } + titlebarContainer.wantsLayer = true titlebarContainer.layer?.backgroundColor = color } @@ -134,9 +134,10 @@ class TerminalWindow: NSWindow { guard let accessoryClipView = accessoryView.superview else { return } guard let titlebarView = accessoryClipView.superview else { return } guard titlebarView.className == "NSTitlebarView" else { return } - guard let toolbarView = titlebarView.subviews.first(where: { - $0.className == "NSToolbarView" - }) else { return } + + guard let toolbarView = titlebarView.firstSubview(withClassName: "NSToolbarView") else { + return + } addWindowButtonsBackdrop(titlebarView: titlebarView, toolbarView: toolbarView) guard let windowButtonsBackdrop = windowButtonsBackdrop else { return } diff --git a/macos/Sources/Helpers/NSView+Extension.swift b/macos/Sources/Helpers/NSView+Extension.swift new file mode 100644 index 0000000000..94cbba3a76 --- /dev/null +++ b/macos/Sources/Helpers/NSView+Extension.swift @@ -0,0 +1,29 @@ +import AppKit + +extension NSView { + func firstSubview(withClassName name: String) -> NSView? { + for subview in subviews { + if String(describing: type(of: subview)) == name { + return subview + } else if let found = subview.firstSubview(withClassName: name) { + return found + } + } + + return nil + } + + func subviews(withClassName name: String) -> [NSView] { + var result = [NSView]() + + for subview in subviews { + if String(describing: type(of: subview)) == name { + result.append(subview) + } + + result += subview.subviews(withClassName: name) + } + + return result + } +} From bc946109b7e6fcbbfbe2044c3eddae5acbd5b978 Mon Sep 17 00:00:00 2001 From: Pete Schaffner Date: Wed, 7 Feb 2024 11:11:34 +0100 Subject: [PATCH 2/4] Hide tab bar shadows which improves light themes In a light appearance there was a shadow cast on top of the inactive tabs that looked bad. In a dark appearance the shadow manifests as a solid line that required a faux border to look right. This removes both and improves the aesthetics of the tab bar in light themes. --- .../Features/Terminal/TerminalWindow.swift | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/macos/Sources/Features/Terminal/TerminalWindow.swift b/macos/Sources/Features/Terminal/TerminalWindow.swift index db76bf98b1..c707557271 100644 --- a/macos/Sources/Features/Terminal/TerminalWindow.swift +++ b/macos/Sources/Features/Terminal/TerminalWindow.swift @@ -33,7 +33,19 @@ class TerminalWindow: NSWindow { // The tab bar controller ID from macOS static private let TabBarController = NSUserInterfaceItemIdentifier("_tabBarController") - + + override func updateConstraintsIfNeeded() { + super.updateConstraintsIfNeeded() + + guard let titlebarContainer = contentView?.superview?.firstSubview(withClassName: "NSTitlebarContainerView") else { + return + } + + for v in titlebarContainer.subviews(withClassName: "NSTitlebarSeparatorView") { + v.isHidden = true + } + } + /// This is called by titlebarTabs changing so that we can setup the rest of our window private func changedTitlebarTabs(to newValue: Bool) { self.titlebarAppearsTransparent = newValue @@ -191,24 +203,13 @@ class TerminalWindow: NSWindow { view.heightAnchor.constraint(equalTo: toolbarView.heightAnchor).isActive = true view.wantsLayer = true - let topBorder = NSView() - view.addSubview(topBorder) - topBorder.translatesAutoresizingMaskIntoConstraints = false - topBorder.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true - topBorder.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true - topBorder.topAnchor.constraint(equalTo: view.topAnchor).isActive = true - topBorder.bottomAnchor.constraint(equalTo: view.topAnchor, constant: 1).isActive = true - topBorder.wantsLayer = true - // This is jank but this makes the background color for light themes on the button // backdrop look MUCH better. I couldn't figure out a perfect color to use that works // for both so we just check the appearance. if effectiveAppearance.name == .aqua { view.layer?.backgroundColor = CGColor(genericGrayGamma2_2Gray: 0.95, alpha: 1) - topBorder.layer?.backgroundColor = CGColor(genericGrayGamma2_2Gray: 0.0, alpha: 0.2) } else { view.layer?.backgroundColor = CGColor(genericGrayGamma2_2Gray: 0.0, alpha: 0.45) - topBorder.layer?.backgroundColor = CGColor(genericGrayGamma2_2Gray: 0.0, alpha: 0.85) } windowButtonsBackdrop = view From 591c05641b436f5d6c308a2ee79f9689ecd54d35 Mon Sep 17 00:00:00 2001 From: Pete Schaffner Date: Wed, 7 Feb 2024 21:39:36 +0100 Subject: [PATCH 3/4] Use shallow search to improve performance --- .../Features/Terminal/TerminalWindow.swift | 19 +++++++++---------- macos/Sources/Helpers/NSView+Extension.swift | 11 ----------- 2 files changed, 9 insertions(+), 21 deletions(-) diff --git a/macos/Sources/Features/Terminal/TerminalWindow.swift b/macos/Sources/Features/Terminal/TerminalWindow.swift index c707557271..050fbce692 100644 --- a/macos/Sources/Features/Terminal/TerminalWindow.swift +++ b/macos/Sources/Features/Terminal/TerminalWindow.swift @@ -37,9 +37,9 @@ class TerminalWindow: NSWindow { override func updateConstraintsIfNeeded() { super.updateConstraintsIfNeeded() - guard let titlebarContainer = contentView?.superview?.firstSubview(withClassName: "NSTitlebarContainerView") else { - return - } + guard let titlebarContainer = contentView?.superview?.subviews.first(where: { + $0.className == "NSTitlebarContainerView" + }) else { return } for v in titlebarContainer.subviews(withClassName: "NSTitlebarSeparatorView") { v.isHidden = true @@ -82,9 +82,9 @@ class TerminalWindow: NSWindow { func setTitlebarBackground(_ color: CGColor) { storedTitlebarBackgroundColor = color - guard let titlebarContainer = contentView?.superview?.firstSubview(withClassName: "NSTitlebarContainerView") else { - return - } + guard let titlebarContainer = contentView?.superview?.subviews.first(where: { + $0.className == "NSTitlebarContainerView" + }) else { return } titlebarContainer.wantsLayer = true titlebarContainer.layer?.backgroundColor = color @@ -146,11 +146,10 @@ class TerminalWindow: NSWindow { guard let accessoryClipView = accessoryView.superview else { return } guard let titlebarView = accessoryClipView.superview else { return } guard titlebarView.className == "NSTitlebarView" else { return } + guard let toolbarView = titlebarView.subviews.first(where: { + $0.className == "NSToolbarView" + }) else { return } - guard let toolbarView = titlebarView.firstSubview(withClassName: "NSToolbarView") else { - return - } - addWindowButtonsBackdrop(titlebarView: titlebarView, toolbarView: toolbarView) guard let windowButtonsBackdrop = windowButtonsBackdrop else { return } diff --git a/macos/Sources/Helpers/NSView+Extension.swift b/macos/Sources/Helpers/NSView+Extension.swift index 94cbba3a76..744517c238 100644 --- a/macos/Sources/Helpers/NSView+Extension.swift +++ b/macos/Sources/Helpers/NSView+Extension.swift @@ -1,17 +1,6 @@ import AppKit extension NSView { - func firstSubview(withClassName name: String) -> NSView? { - for subview in subviews { - if String(describing: type(of: subview)) == name { - return subview - } else if let found = subview.firstSubview(withClassName: name) { - return found - } - } - - return nil - } func subviews(withClassName name: String) -> [NSView] { var result = [NSView]() From 93fb852d9b24c76ce7f7bb7ad5e30c7ecd53a496 Mon Sep 17 00:00:00 2001 From: Pete Schaffner Date: Wed, 7 Feb 2024 21:44:44 +0100 Subject: [PATCH 4/4] Add comments/docs and make method name clearer --- macos/Sources/Features/Terminal/TerminalWindow.swift | 5 ++++- macos/Sources/Helpers/NSView+Extension.swift | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/macos/Sources/Features/Terminal/TerminalWindow.swift b/macos/Sources/Features/Terminal/TerminalWindow.swift index 050fbce692..457b3bd08b 100644 --- a/macos/Sources/Features/Terminal/TerminalWindow.swift +++ b/macos/Sources/Features/Terminal/TerminalWindow.swift @@ -34,6 +34,9 @@ class TerminalWindow: NSWindow { // The tab bar controller ID from macOS static private let TabBarController = NSUserInterfaceItemIdentifier("_tabBarController") + // Look through the titlebar's view hierarchy and hide any of the internal + // views used to create a separator between the title/toolbar and unselected + // tabs in the tab bar. override func updateConstraintsIfNeeded() { super.updateConstraintsIfNeeded() @@ -41,7 +44,7 @@ class TerminalWindow: NSWindow { $0.className == "NSTitlebarContainerView" }) else { return } - for v in titlebarContainer.subviews(withClassName: "NSTitlebarSeparatorView") { + for v in titlebarContainer.descendants(withClassName: "NSTitlebarSeparatorView") { v.isHidden = true } } diff --git a/macos/Sources/Helpers/NSView+Extension.swift b/macos/Sources/Helpers/NSView+Extension.swift index 744517c238..8612c0417d 100644 --- a/macos/Sources/Helpers/NSView+Extension.swift +++ b/macos/Sources/Helpers/NSView+Extension.swift @@ -1,8 +1,8 @@ import AppKit extension NSView { - - func subviews(withClassName name: String) -> [NSView] { + /// Recursively finds and returns descendant views that have the given class name. + func descendants(withClassName name: String) -> [NSView] { var result = [NSView]() for subview in subviews { @@ -10,7 +10,7 @@ extension NSView { result.append(subview) } - result += subview.subviews(withClassName: name) + result += subview.descendants(withClassName: name) } return result