From 2e74a0f9d4f9649c4b7e22b35bbeca9d2dc85c70 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 17 Dec 2023 15:37:23 -0800 Subject: [PATCH] macos: custom about window so we can be a first responder Fixes #1052 This implements the about window as a custom window with a view controller. This lets us implement the proper responder chain so that our custom close window IBActions do the right thing. This has an additional benefit that we can easily customize this window going forward. --- macos/Ghostty.xcodeproj/project.pbxproj | 20 ++++++++++ macos/Sources/AppDelegate.swift | 4 ++ macos/Sources/Features/About/About.xib | 31 ++++++++++++++++ .../Features/About/AboutController.swift | 37 +++++++++++++++++++ macos/Sources/Features/About/AboutView.swift | 30 +++++++++++++++ macos/Sources/Features/Terminal/Terminal.xib | 4 +- macos/Sources/MainMenu.xib | 6 +-- 7 files changed, 127 insertions(+), 5 deletions(-) create mode 100644 macos/Sources/Features/About/About.xib create mode 100644 macos/Sources/Features/About/AboutController.swift create mode 100644 macos/Sources/Features/About/AboutView.swift diff --git a/macos/Ghostty.xcodeproj/project.pbxproj b/macos/Ghostty.xcodeproj/project.pbxproj index 1e2e3f612..87033b409 100644 --- a/macos/Ghostty.xcodeproj/project.pbxproj +++ b/macos/Ghostty.xcodeproj/project.pbxproj @@ -10,6 +10,9 @@ 8503D7C72A549C66006CFF3D /* FullScreenHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8503D7C62A549C66006CFF3D /* FullScreenHandler.swift */; }; 857F63812A5E64F200CA4815 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 857F63802A5E64F200CA4815 /* MainMenu.xib */; }; A51B78472AF4B58B00F3EDB9 /* TerminalWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51B78462AF4B58B00F3EDB9 /* TerminalWindow.swift */; }; + A51BFC1E2B2FB5CE00E92F16 /* About.xib in Resources */ = {isa = PBXBuildFile; fileRef = A51BFC1D2B2FB5CE00E92F16 /* About.xib */; }; + A51BFC202B2FB64F00E92F16 /* AboutController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51BFC1F2B2FB64F00E92F16 /* AboutController.swift */; }; + A51BFC222B2FB6B400E92F16 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51BFC212B2FB6B400E92F16 /* AboutView.swift */; }; A5278A9B2AA05B2600CD3039 /* Ghostty.Input.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5278A9A2AA05B2600CD3039 /* Ghostty.Input.swift */; }; A53426352A7DA53D00EBB7A2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A53426342A7DA53D00EBB7A2 /* AppDelegate.swift */; }; A535B9DA299C569B0017E2E4 /* ErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A535B9D9299C569B0017E2E4 /* ErrorView.swift */; }; @@ -51,6 +54,9 @@ 8503D7C62A549C66006CFF3D /* FullScreenHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullScreenHandler.swift; sourceTree = ""; }; 857F63802A5E64F200CA4815 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = ""; }; A51B78462AF4B58B00F3EDB9 /* TerminalWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalWindow.swift; sourceTree = ""; }; + A51BFC1D2B2FB5CE00E92F16 /* About.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = About.xib; sourceTree = ""; }; + A51BFC1F2B2FB64F00E92F16 /* AboutController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutController.swift; sourceTree = ""; }; + A51BFC212B2FB6B400E92F16 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = ""; }; A5278A9A2AA05B2600CD3039 /* Ghostty.Input.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ghostty.Input.swift; sourceTree = ""; }; A53426342A7DA53D00EBB7A2 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; A535B9D9299C569B0017E2E4 /* ErrorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorView.swift; sourceTree = ""; }; @@ -104,6 +110,16 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + A51BFC1C2B2FB5AB00E92F16 /* About */ = { + isa = PBXGroup; + children = ( + A51BFC1D2B2FB5CE00E92F16 /* About.xib */, + A51BFC1F2B2FB64F00E92F16 /* AboutController.swift */, + A51BFC212B2FB6B400E92F16 /* AboutView.swift */, + ); + path = About; + sourceTree = ""; + }; A53426362A7DC53000EBB7A2 /* Features */ = { isa = PBXGroup; children = ( @@ -111,6 +127,7 @@ A59630982AEE1C4400D64628 /* Terminal */, A5E112912AF73E4D00C6E0C2 /* ClipboardConfirmation */, A534263E2A7DCC5800EBB7A2 /* Settings */, + A51BFC1C2B2FB5AB00E92F16 /* About */, ); path = Features; sourceTree = ""; @@ -305,6 +322,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + A51BFC1E2B2FB5CE00E92F16 /* About.xib in Resources */, A5CB04382B0F1C1C009ED217 /* themes in Resources */, A545D1A22A5772CE006E0AE4 /* shell-integration in Resources */, A596309A2AEE1C6400D64628 /* Terminal.xib in Resources */, @@ -327,6 +345,7 @@ A59FB5CF2AE0DB50009128F3 /* InspectorView.swift in Sources */, A596309C2AEE1C9E00D64628 /* TerminalController.swift in Sources */, A56D58892ACDE6CA00508D2C /* ServiceProvider.swift in Sources */, + A51BFC222B2FB6B400E92F16 /* AboutView.swift in Sources */, A5278A9B2AA05B2600CD3039 /* Ghostty.Input.swift in Sources */, A59630972AEE163600D64628 /* HostingWindow.swift in Sources */, A59630A02AEF6AEB00D64628 /* TerminalManager.swift in Sources */, @@ -345,6 +364,7 @@ A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */, A55685E029A03A9F004303CE /* AppError.swift in Sources */, A535B9DA299C569B0017E2E4 /* ErrorView.swift in Sources */, + A51BFC202B2FB64F00E92F16 /* AboutController.swift in Sources */, A5CEAFFF29C2410700646FDA /* Backport.swift in Sources */, A5E112952AF73E8A00C6E0C2 /* ClipboardConfirmationController.swift in Sources */, 8503D7C72A549C66006CFF3D /* FullScreenHandler.swift in Sources */, diff --git a/macos/Sources/AppDelegate.swift b/macos/Sources/AppDelegate.swift index b99d45ba8..4b0398767 100644 --- a/macos/Sources/AppDelegate.swift +++ b/macos/Sources/AppDelegate.swift @@ -360,6 +360,10 @@ class AppDelegate: NSObject, ObservableObject, NSApplicationDelegate, UNUserNoti NSApp.activate(ignoringOtherApps: true) } + @IBAction func showAbout(_ sender: Any?) { + AboutController.shared.show() + } + @IBAction func showHelp(_ sender: Any) { guard let url = URL(string: "https://github.com/mitchellh/ghostty") else { return } NSWorkspace.shared.open(url) diff --git a/macos/Sources/Features/About/About.xib b/macos/Sources/Features/About/About.xib new file mode 100644 index 000000000..fe339493a --- /dev/null +++ b/macos/Sources/Features/About/About.xib @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/Sources/Features/About/AboutController.swift b/macos/Sources/Features/About/AboutController.swift new file mode 100644 index 000000000..2c6882eea --- /dev/null +++ b/macos/Sources/Features/About/AboutController.swift @@ -0,0 +1,37 @@ +import Foundation +import Cocoa +import SwiftUI + +class AboutController: NSWindowController, NSWindowDelegate { + static let shared: AboutController = AboutController() + + override var windowNibName: NSNib.Name? { "About" } + + override func windowDidLoad() { + guard let window = window else { return } + window.center() + window.contentView = NSHostingView(rootView: AboutView()) + } + + // MARK: - Functions + + func show() { + guard let window = window else { return } + window.makeKeyAndOrderFront(nil) + } + + //MARK: - First Responder + + @IBAction func close(_ sender: Any) { + self.window?.performClose(sender) + } + + @IBAction func closeWindow(_ sender: Any) { + self.window?.performClose(sender) + } + + // This is called when "escape" is pressed. + @objc func cancel(_ sender: Any?) { + close() + } +} diff --git a/macos/Sources/Features/About/AboutView.swift b/macos/Sources/Features/About/AboutView.swift new file mode 100644 index 000000000..7232f3ba0 --- /dev/null +++ b/macos/Sources/Features/About/AboutView.swift @@ -0,0 +1,30 @@ +import SwiftUI + +struct AboutView: View { + /// Read the commit from the bundle. + var commit: String { + guard let valueAny = Bundle.main.infoDictionary?["CFBundleVersion"], + let version = valueAny as? String else { + return "unknown" + } + + return version + } + + var body: some View { + VStack(alignment: .center) { + Image("AppIconImage") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(maxHeight: 96) + + Text("Ghostty") + .font(.title3) + + Text("Commit: \(commit)") + .font(.body) + } + .frame(minWidth: 300) + .padding() + } +} diff --git a/macos/Sources/Features/Terminal/Terminal.xib b/macos/Sources/Features/Terminal/Terminal.xib index a086ffe89..4078fa2c6 100644 --- a/macos/Sources/Features/Terminal/Terminal.xib +++ b/macos/Sources/Features/Terminal/Terminal.xib @@ -1,8 +1,8 @@ - + - + diff --git a/macos/Sources/MainMenu.xib b/macos/Sources/MainMenu.xib index c9317efbb..69ee9fe38 100644 --- a/macos/Sources/MainMenu.xib +++ b/macos/Sources/MainMenu.xib @@ -1,8 +1,8 @@ - + - + @@ -53,7 +53,7 @@ - +