mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
Merge branch 'NSToolbar-Deprication-Warning-Fix' of github.com:johnseth97/ghostty into NSToolbar-Deprication-Warning-Fix
This commit is contained in:
@ -61,6 +61,7 @@
|
|||||||
A59FB5CF2AE0DB50009128F3 /* InspectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59FB5CE2AE0DB50009128F3 /* InspectorView.swift */; };
|
A59FB5CF2AE0DB50009128F3 /* InspectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59FB5CE2AE0DB50009128F3 /* InspectorView.swift */; };
|
||||||
A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59FB5D02AE0DEA7009128F3 /* MetalView.swift */; };
|
A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59FB5D02AE0DEA7009128F3 /* MetalView.swift */; };
|
||||||
A5A1F8852A489D6800D1E8BC /* terminfo in Resources */ = {isa = PBXBuildFile; fileRef = A5A1F8842A489D6800D1E8BC /* terminfo */; };
|
A5A1F8852A489D6800D1E8BC /* terminfo in Resources */ = {isa = PBXBuildFile; fileRef = A5A1F8842A489D6800D1E8BC /* terminfo */; };
|
||||||
|
A5A6F72A2CC41B8900B232A5 /* Xcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5A6F7292CC41B8700B232A5 /* Xcode.swift */; };
|
||||||
A5B30539299BEAAB0047F10C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A5B30538299BEAAB0047F10C /* Assets.xcassets */; };
|
A5B30539299BEAAB0047F10C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A5B30538299BEAAB0047F10C /* Assets.xcassets */; };
|
||||||
A5CBD0562C9E65B80017A1AE /* DraggableWindowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CBD0552C9E65A50017A1AE /* DraggableWindowView.swift */; };
|
A5CBD0562C9E65B80017A1AE /* DraggableWindowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CBD0552C9E65A50017A1AE /* DraggableWindowView.swift */; };
|
||||||
A5CBD0582C9F30960017A1AE /* Cursor.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CBD0572C9F30860017A1AE /* Cursor.swift */; };
|
A5CBD0582C9F30960017A1AE /* Cursor.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CBD0572C9F30860017A1AE /* Cursor.swift */; };
|
||||||
@ -139,6 +140,7 @@
|
|||||||
A59FB5CE2AE0DB50009128F3 /* InspectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorView.swift; sourceTree = "<group>"; };
|
A59FB5CE2AE0DB50009128F3 /* InspectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorView.swift; sourceTree = "<group>"; };
|
||||||
A59FB5D02AE0DEA7009128F3 /* MetalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetalView.swift; sourceTree = "<group>"; };
|
A59FB5D02AE0DEA7009128F3 /* MetalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetalView.swift; sourceTree = "<group>"; };
|
||||||
A5A1F8842A489D6800D1E8BC /* terminfo */ = {isa = PBXFileReference; lastKnownFileType = folder; name = terminfo; path = "../zig-out/share/terminfo"; sourceTree = "<group>"; };
|
A5A1F8842A489D6800D1E8BC /* terminfo */ = {isa = PBXFileReference; lastKnownFileType = folder; name = terminfo; path = "../zig-out/share/terminfo"; sourceTree = "<group>"; };
|
||||||
|
A5A6F7292CC41B8700B232A5 /* Xcode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Xcode.swift; sourceTree = "<group>"; };
|
||||||
A5B30531299BEAAA0047F10C /* Ghostty.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Ghostty.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
A5B30531299BEAAA0047F10C /* Ghostty.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Ghostty.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
A5B30538299BEAAB0047F10C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
A5B30538299BEAAB0047F10C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
A5B3053D299BEAAB0047F10C /* Ghostty.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Ghostty.entitlements; sourceTree = "<group>"; };
|
A5B3053D299BEAAB0047F10C /* Ghostty.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Ghostty.entitlements; sourceTree = "<group>"; };
|
||||||
@ -233,6 +235,7 @@
|
|||||||
A534263D2A7DCBB000EBB7A2 /* Helpers */ = {
|
A534263D2A7DCBB000EBB7A2 /* Helpers */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
A5A6F7292CC41B8700B232A5 /* Xcode.swift */,
|
||||||
A5CEAFFE29C2410700646FDA /* Backport.swift */,
|
A5CEAFFE29C2410700646FDA /* Backport.swift */,
|
||||||
A5333E1B2B5A1CE3008AEFF7 /* CrossKit.swift */,
|
A5333E1B2B5A1CE3008AEFF7 /* CrossKit.swift */,
|
||||||
A5CBD0572C9F30860017A1AE /* Cursor.swift */,
|
A5CBD0572C9F30860017A1AE /* Cursor.swift */,
|
||||||
@ -582,6 +585,7 @@
|
|||||||
A52FFF5D2CAB4D08000C6A5B /* NSScreen+Extension.swift in Sources */,
|
A52FFF5D2CAB4D08000C6A5B /* NSScreen+Extension.swift in Sources */,
|
||||||
A53426352A7DA53D00EBB7A2 /* AppDelegate.swift in Sources */,
|
A53426352A7DA53D00EBB7A2 /* AppDelegate.swift in Sources */,
|
||||||
A5CBD0582C9F30960017A1AE /* Cursor.swift in Sources */,
|
A5CBD0582C9F30960017A1AE /* Cursor.swift in Sources */,
|
||||||
|
A5A6F72A2CC41B8900B232A5 /* Xcode.swift in Sources */,
|
||||||
A52FFF5B2CAA54B1000C6A5B /* FullscreenMode+Extension.swift in Sources */,
|
A52FFF5B2CAA54B1000C6A5B /* FullscreenMode+Extension.swift in Sources */,
|
||||||
A5333E222B5A2128008AEFF7 /* SurfaceView_AppKit.swift in Sources */,
|
A5333E222B5A2128008AEFF7 /* SurfaceView_AppKit.swift in Sources */,
|
||||||
A5CDF1952AAFA19600513312 /* ConfigurationErrorsView.swift in Sources */,
|
A5CDF1952AAFA19600513312 /* ConfigurationErrorsView.swift in Sources */,
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||||
<window allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" titlebarAppearsTransparent="YES" id="QvC-M9-y7g">
|
<window allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" titlebarAppearsTransparent="YES" id="QvC-M9-y7g">
|
||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" fullSizeContentView="YES"/>
|
||||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||||
<rect key="contentRect" x="196" y="240" width="300" height="172"/>
|
<rect key="contentRect" x="196" y="240" width="300" height="172"/>
|
||||||
<rect key="screenRect" x="0.0" y="0.0" width="3008" height="1667"/>
|
<rect key="screenRect" x="0.0" y="0.0" width="3008" height="1667"/>
|
||||||
|
@ -10,6 +10,7 @@ class AboutController: NSWindowController, NSWindowDelegate {
|
|||||||
override func windowDidLoad() {
|
override func windowDidLoad() {
|
||||||
guard let window = window else { return }
|
guard let window = window else { return }
|
||||||
window.center()
|
window.center()
|
||||||
|
window.isMovableByWindowBackground = true
|
||||||
window.contentView = NSHostingView(rootView: AboutView())
|
window.contentView = NSHostingView(rootView: AboutView())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,35 +1,136 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct AboutView: View {
|
struct AboutView: View {
|
||||||
|
@Environment(\.openURL) var openURL
|
||||||
|
|
||||||
|
private let githubLink = URL(string: "https://github.com/ghostty-org/ghostty")
|
||||||
|
|
||||||
/// Read the commit from the bundle.
|
/// Read the commit from the bundle.
|
||||||
var build: String? { Bundle.main.infoDictionary?["CFBundleVersion"] as? String }
|
private var build: String? { Bundle.main.infoDictionary?["CFBundleVersion"] as? String }
|
||||||
var commit: String? { Bundle.main.infoDictionary?["GhosttyCommit"] as? String }
|
private var commit: String? { Bundle.main.infoDictionary?["GhosttyCommit"] as? String }
|
||||||
var version: String? { Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String }
|
private var version: String? { Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String }
|
||||||
|
private var copyright: String? { Bundle.main.infoDictionary?["NSHumanReadableCopyright"] as? String }
|
||||||
|
|
||||||
|
private var properties: [KeyValue<String>] {
|
||||||
|
let list: [KeyValue<String?>] = [
|
||||||
|
.init(key: "Version", value: version),
|
||||||
|
.init(key: "Build", value: build),
|
||||||
|
.init(key: "Commit", value: commit == "" ? nil : commit)
|
||||||
|
]
|
||||||
|
|
||||||
|
return list.compactMap {
|
||||||
|
guard let value = $0.value else { return nil }
|
||||||
|
return .init(key: $0.key, value: value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct KeyValue<Value: Equatable>: Identifiable {
|
||||||
|
var id = UUID()
|
||||||
|
public let key: LocalizedStringResource
|
||||||
|
public let value: Value
|
||||||
|
}
|
||||||
|
|
||||||
|
#if os(macOS)
|
||||||
|
// This creates a background style similar to the Apple "About My Mac" Window
|
||||||
|
private struct VisualEffectBackground: NSViewRepresentable {
|
||||||
|
let material: NSVisualEffectView.Material
|
||||||
|
let blendingMode: NSVisualEffectView.BlendingMode
|
||||||
|
let isEmphasized: Bool
|
||||||
|
|
||||||
|
init(material: NSVisualEffectView.Material,
|
||||||
|
blendingMode: NSVisualEffectView.BlendingMode = .behindWindow,
|
||||||
|
isEmphasized: Bool = false)
|
||||||
|
{
|
||||||
|
self.material = material
|
||||||
|
self.blendingMode = blendingMode
|
||||||
|
self.isEmphasized = isEmphasized
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateNSView(_ nsView: NSVisualEffectView, context: Context) {
|
||||||
|
nsView.material = material
|
||||||
|
nsView.blendingMode = blendingMode
|
||||||
|
nsView.isEmphasized = isEmphasized
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeNSView(context: Context) -> NSVisualEffectView {
|
||||||
|
let visualEffect = NSVisualEffectView()
|
||||||
|
visualEffect.autoresizingMask = [.width, .height]
|
||||||
|
return visualEffect
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .center) {
|
VStack(alignment: .center) {
|
||||||
Image("AppIconImage")
|
Image("AppIconImage")
|
||||||
.resizable()
|
.resizable()
|
||||||
.aspectRatio(contentMode: .fit)
|
.aspectRatio(contentMode: .fit)
|
||||||
.frame(maxHeight: 96)
|
.frame(height: 128)
|
||||||
|
|
||||||
|
VStack(alignment: .center, spacing: 32) {
|
||||||
|
VStack(alignment: .center, spacing: 8) {
|
||||||
Text("Ghostty")
|
Text("Ghostty")
|
||||||
.font(.title3)
|
.bold()
|
||||||
|
.font(.title)
|
||||||
|
Text("Fast, native, feature-rich terminal \nemulator pushing modern features.")
|
||||||
|
.multilineTextAlignment(.center)
|
||||||
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
|
.font(.caption)
|
||||||
|
.tint(.secondary)
|
||||||
|
.opacity(0.8)
|
||||||
|
}
|
||||||
.textSelection(.enabled)
|
.textSelection(.enabled)
|
||||||
|
VStack(spacing: 2) {
|
||||||
|
ForEach(properties) { item in
|
||||||
|
HStack(spacing: 4) {
|
||||||
|
Text(item.key)
|
||||||
|
.frame(width: 126, alignment: .trailing)
|
||||||
|
.padding(.trailing, 2)
|
||||||
|
Text(item.value)
|
||||||
|
.frame(width: 125, alignment: .leading)
|
||||||
|
.padding(.leading, 2)
|
||||||
|
.tint(.secondary)
|
||||||
|
.opacity(0.8)
|
||||||
|
}
|
||||||
|
.font(.callout)
|
||||||
|
.textSelection(.enabled)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
|
||||||
if let version = self.version {
|
HStack(spacing: 8) {
|
||||||
Text("Version: \(version)")
|
if let url = githubLink {
|
||||||
.font(.body)
|
Button("GitHub") {
|
||||||
.textSelection(.enabled)
|
openURL(url)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let build = self.build {
|
}
|
||||||
Text("Build: \(build)")
|
|
||||||
.font(.body)
|
if let copy = self.copyright {
|
||||||
|
Text(copy)
|
||||||
|
.font(.caption)
|
||||||
.textSelection(.enabled)
|
.textSelection(.enabled)
|
||||||
|
.tint(.secondary)
|
||||||
|
.opacity(0.8)
|
||||||
|
.multilineTextAlignment(.center)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.frame(minWidth: 300)
|
.frame(maxWidth: .infinity)
|
||||||
.padding()
|
}
|
||||||
|
.padding(.top, 8)
|
||||||
|
.padding(32)
|
||||||
|
.frame(minWidth: 256)
|
||||||
|
#if os(macOS)
|
||||||
|
.background(VisualEffectBackground(material: .underWindowBackground).ignoresSafeArea())
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AboutView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
AboutView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,6 +57,14 @@ class BaseTerminalController: NSWindowController,
|
|||||||
/// Event monitor (see individual events for why)
|
/// Event monitor (see individual events for why)
|
||||||
private var eventMonitor: Any? = nil
|
private var eventMonitor: Any? = nil
|
||||||
|
|
||||||
|
/// The previous frame information from the window
|
||||||
|
private var savedFrame: SavedFrame? = nil
|
||||||
|
|
||||||
|
struct SavedFrame {
|
||||||
|
let window: NSRect
|
||||||
|
let screen: NSRect
|
||||||
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder: NSCoder) {
|
||||||
fatalError("init(coder:) is not supported for this view")
|
fatalError("init(coder:) is not supported for this view")
|
||||||
}
|
}
|
||||||
@ -80,6 +88,11 @@ class BaseTerminalController: NSWindowController,
|
|||||||
selector: #selector(onConfirmClipboardRequest),
|
selector: #selector(onConfirmClipboardRequest),
|
||||||
name: Ghostty.Notification.confirmClipboard,
|
name: Ghostty.Notification.confirmClipboard,
|
||||||
object: nil)
|
object: nil)
|
||||||
|
center.addObserver(
|
||||||
|
self,
|
||||||
|
selector: #selector(didChangeScreenParametersNotification),
|
||||||
|
name: NSApplication.didChangeScreenParametersNotification,
|
||||||
|
object: nil)
|
||||||
|
|
||||||
// Listen for local events that we need to know of outside of
|
// Listen for local events that we need to know of outside of
|
||||||
// single surface handlers.
|
// single surface handlers.
|
||||||
@ -89,6 +102,8 @@ class BaseTerminalController: NSWindowController,
|
|||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
|
NotificationCenter.default.removeObserver(self)
|
||||||
|
|
||||||
if let eventMonitor {
|
if let eventMonitor {
|
||||||
NSEvent.removeMonitor(eventMonitor)
|
NSEvent.removeMonitor(eventMonitor)
|
||||||
}
|
}
|
||||||
@ -121,6 +136,57 @@ class BaseTerminalController: NSWindowController,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Call this whenever the frame changes
|
||||||
|
private func windowFrameDidChange() {
|
||||||
|
// We need to update our saved frame information in case of monitor
|
||||||
|
// changes (see didChangeScreenParameters notification).
|
||||||
|
savedFrame = nil
|
||||||
|
guard let window, let screen = window.screen else { return }
|
||||||
|
savedFrame = .init(window: window.frame, screen: screen.visibleFrame)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Notifications
|
||||||
|
|
||||||
|
@objc private func didChangeScreenParametersNotification(_ notification: Notification) {
|
||||||
|
// If we have a window that is visible and it is outside the bounds of the
|
||||||
|
// screen then we clamp it back to within the screen.
|
||||||
|
guard let window else { return }
|
||||||
|
guard window.isVisible else { return }
|
||||||
|
guard let screen = window.screen else { return }
|
||||||
|
|
||||||
|
let visibleFrame = screen.visibleFrame
|
||||||
|
var newFrame = window.frame
|
||||||
|
|
||||||
|
// Clamp width/height
|
||||||
|
if newFrame.size.width > visibleFrame.size.width {
|
||||||
|
newFrame.size.width = visibleFrame.size.width
|
||||||
|
}
|
||||||
|
if newFrame.size.height > visibleFrame.size.height {
|
||||||
|
newFrame.size.height = visibleFrame.size.height
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the window is on-screen. We only do this if the previous frame
|
||||||
|
// was also on screen. If a user explicitly wanted their window off screen
|
||||||
|
// then we let it stay that way.
|
||||||
|
x: if newFrame.origin.x < visibleFrame.origin.x {
|
||||||
|
if let savedFrame, savedFrame.window.origin.x < savedFrame.screen.origin.x {
|
||||||
|
break x;
|
||||||
|
}
|
||||||
|
|
||||||
|
newFrame.origin.x = visibleFrame.origin.x
|
||||||
|
}
|
||||||
|
y: if newFrame.origin.y < visibleFrame.origin.y {
|
||||||
|
if let savedFrame, savedFrame.window.origin.y < savedFrame.screen.origin.y {
|
||||||
|
break y;
|
||||||
|
}
|
||||||
|
|
||||||
|
newFrame.origin.y = visibleFrame.origin.y
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the new window frame
|
||||||
|
window.setFrame(newFrame, display: true)
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: Local Events
|
// MARK: Local Events
|
||||||
|
|
||||||
private func localEventHandler(_ event: NSEvent) -> NSEvent? {
|
private func localEventHandler(_ event: NSEvent) -> NSEvent? {
|
||||||
@ -371,6 +437,14 @@ class BaseTerminalController: NSWindowController,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func windowDidResize(_ notification: Notification) {
|
||||||
|
windowFrameDidChange()
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowDidMove(_ notification: Notification) {
|
||||||
|
windowFrameDidChange()
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: First Responder
|
// MARK: First Responder
|
||||||
|
|
||||||
@IBAction func close(_ sender: Any) {
|
@IBAction func close(_ sender: Any) {
|
||||||
|
@ -358,7 +358,8 @@ class TerminalController: BaseTerminalController {
|
|||||||
self.fixTabBar()
|
self.fixTabBar()
|
||||||
}
|
}
|
||||||
|
|
||||||
func windowDidMove(_ notification: Notification) {
|
override func windowDidMove(_ notification: Notification) {
|
||||||
|
super.windowDidMove(notification)
|
||||||
self.fixTabBar()
|
self.fixTabBar()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,8 +43,8 @@ class TerminalToolbar: NSToolbar, NSToolbarDelegate {
|
|||||||
item.view = self.titleTextField
|
item.view = self.titleTextField
|
||||||
item.visibilityPriority = .user
|
item.visibilityPriority = .user
|
||||||
|
|
||||||
|
// This ensures the title text field doesn't disappear when shrinking the view
|
||||||
self.titleTextField.translatesAutoresizingMaskIntoConstraints = false
|
self.titleTextField.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
|
||||||
self.titleTextField.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
self.titleTextField.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
||||||
self.titleTextField.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
|
self.titleTextField.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
|
||||||
|
|
||||||
@ -52,7 +52,6 @@ class TerminalToolbar: NSToolbar, NSToolbarDelegate {
|
|||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
// Set the height constraint to match the toolbar's height
|
// Set the height constraint to match the toolbar's height
|
||||||
self.titleTextField.heightAnchor.constraint(equalToConstant: 22), // Adjust as needed
|
self.titleTextField.heightAnchor.constraint(equalToConstant: 22), // Adjust as needed
|
||||||
|
|
||||||
])
|
])
|
||||||
|
|
||||||
item.isEnabled = true
|
item.isEnabled = true
|
||||||
@ -82,7 +81,6 @@ class TerminalToolbar: NSToolbar, NSToolbarDelegate {
|
|||||||
fileprivate class CenteredDynamicLabel: NSTextField {
|
fileprivate class CenteredDynamicLabel: NSTextField {
|
||||||
override func viewDidMoveToSuperview() {
|
override func viewDidMoveToSuperview() {
|
||||||
// Configure the text field
|
// Configure the text field
|
||||||
|
|
||||||
isEditable = false
|
isEditable = false
|
||||||
isBordered = false
|
isBordered = false
|
||||||
drawsBackground = false
|
drawsBackground = false
|
||||||
|
@ -56,7 +56,13 @@ extension Ghostty {
|
|||||||
// same filesystem concept.
|
// same filesystem concept.
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
ghostty_config_load_default_files(cfg);
|
ghostty_config_load_default_files(cfg);
|
||||||
|
|
||||||
|
// We only load CLI args when not running in Xcode because in Xcode we
|
||||||
|
// pass some special parameters to control the debugger.
|
||||||
|
if !isRunningInXcode() {
|
||||||
ghostty_config_load_cli_args(cfg);
|
ghostty_config_load_cli_args(cfg);
|
||||||
|
}
|
||||||
|
|
||||||
ghostty_config_load_recursive_files(cfg);
|
ghostty_config_load_recursive_files(cfg);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
10
macos/Sources/Helpers/Xcode.swift
Normal file
10
macos/Sources/Helpers/Xcode.swift
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// True if we appear to be running in Xcode.
|
||||||
|
func isRunningInXcode() -> Bool {
|
||||||
|
if let _ = ProcessInfo.processInfo.environment["__XCODE_BUILT_PRODUCTS_DIR_PATHS"] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
@ -500,6 +500,7 @@ pub fn init(
|
|||||||
|
|
||||||
try termio.Termio.init(&self.io, alloc, .{
|
try termio.Termio.init(&self.io, alloc, .{
|
||||||
.grid_size = grid_size,
|
.grid_size = grid_size,
|
||||||
|
.cell_size = cell_size,
|
||||||
.screen_size = screen_size,
|
.screen_size = screen_size,
|
||||||
.padding = padding,
|
.padding = padding,
|
||||||
.full_config = config,
|
.full_config = config,
|
||||||
@ -1331,6 +1332,7 @@ fn setCellSize(self: *Surface, size: renderer.CellSize) !void {
|
|||||||
self.io.queueMessage(.{
|
self.io.queueMessage(.{
|
||||||
.resize = .{
|
.resize = .{
|
||||||
.grid_size = self.grid_size,
|
.grid_size = self.grid_size,
|
||||||
|
.cell_size = self.cell_size,
|
||||||
.screen_size = self.screen_size,
|
.screen_size = self.screen_size,
|
||||||
.padding = self.padding,
|
.padding = self.padding,
|
||||||
},
|
},
|
||||||
@ -1435,6 +1437,7 @@ fn resize(self: *Surface, size: renderer.ScreenSize) !void {
|
|||||||
self.io.queueMessage(.{
|
self.io.queueMessage(.{
|
||||||
.resize = .{
|
.resize = .{
|
||||||
.grid_size = self.grid_size,
|
.grid_size = self.grid_size,
|
||||||
|
.cell_size = self.cell_size,
|
||||||
.screen_size = self.screen_size,
|
.screen_size = self.screen_size,
|
||||||
.padding = self.padding,
|
.padding = self.padding,
|
||||||
},
|
},
|
||||||
|
@ -23,7 +23,7 @@ pub fn run(alloc: Allocator) !u8 {
|
|||||||
defer opts.deinit();
|
defer opts.deinit();
|
||||||
|
|
||||||
{
|
{
|
||||||
var iter = try std.process.argsWithAllocator(alloc);
|
var iter = try args.argsIterator(alloc);
|
||||||
defer iter.deinit();
|
defer iter.deinit();
|
||||||
try args.parse(Options, alloc, &opts, &iter);
|
try args.parse(Options, alloc, &opts, &iter);
|
||||||
}
|
}
|
||||||
|
@ -1456,8 +1456,13 @@ keybind: Keybinds = .{},
|
|||||||
/// Note that if an *Option*-sequence doesn't produce a printable character, it
|
/// Note that if an *Option*-sequence doesn't produce a printable character, it
|
||||||
/// will be treated as *Alt* regardless of this setting. (i.e. `alt+ctrl+a`).
|
/// will be treated as *Alt* regardless of this setting. (i.e. `alt+ctrl+a`).
|
||||||
///
|
///
|
||||||
|
/// The default value is `left`. This allows alt-based bindings to work
|
||||||
|
/// with the left *Option* key while still allowing the right *Option* key
|
||||||
|
/// to be used for Unicode input. This is a common setup for users of
|
||||||
|
/// certain keyboard layouts.
|
||||||
|
///
|
||||||
/// This does not work with GLFW builds.
|
/// This does not work with GLFW builds.
|
||||||
@"macos-option-as-alt": OptionAsAlt = .false,
|
@"macos-option-as-alt": OptionAsAlt = .left,
|
||||||
|
|
||||||
/// Whether to enable the macOS window shadow. The default value is true.
|
/// Whether to enable the macOS window shadow. The default value is true.
|
||||||
/// With some window managers and window transparency settings, you may
|
/// With some window managers and window transparency settings, you may
|
||||||
|
@ -54,8 +54,9 @@ pub fn add(self: *CodepointMap, alloc: Allocator, entry: Entry) !void {
|
|||||||
/// Get a descriptor for a codepoint.
|
/// Get a descriptor for a codepoint.
|
||||||
pub fn get(self: *const CodepointMap, cp: u21) ?discovery.Descriptor {
|
pub fn get(self: *const CodepointMap, cp: u21) ?discovery.Descriptor {
|
||||||
const items = self.list.items(.range);
|
const items = self.list.items(.range);
|
||||||
for (items, 0..) |range, forward_i| {
|
for (0..items.len) |forward_i| {
|
||||||
const i = items.len - forward_i - 1;
|
const i = items.len - forward_i - 1;
|
||||||
|
const range = items[i];
|
||||||
if (range[0] <= cp and cp <= range[1]) {
|
if (range[0] <= cp and cp <= range[1]) {
|
||||||
const descs = self.list.items(.descriptor);
|
const descs = self.list.items(.descriptor);
|
||||||
return descs[i];
|
return descs[i];
|
||||||
@ -110,4 +111,15 @@ test "codepointmap" {
|
|||||||
// Non-matching
|
// Non-matching
|
||||||
try testing.expect(m.get(0) == null);
|
try testing.expect(m.get(0) == null);
|
||||||
try testing.expect(m.get(3) == null);
|
try testing.expect(m.get(3) == null);
|
||||||
|
|
||||||
|
try m.add(alloc, .{ .range = .{ 3, 4 }, .descriptor = .{ .family = "C" } });
|
||||||
|
try m.add(alloc, .{ .range = .{ 5, 6 }, .descriptor = .{ .family = "D" } });
|
||||||
|
{
|
||||||
|
const d = m.get(3).?;
|
||||||
|
try testing.expectEqualStrings("C", d.family.?);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const d = m.get(1).?;
|
||||||
|
try testing.expectEqualStrings("B", d.family.?);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,9 @@ const termio = @import("../termio.zig");
|
|||||||
/// The size of the terminal grid.
|
/// The size of the terminal grid.
|
||||||
grid_size: renderer.GridSize,
|
grid_size: renderer.GridSize,
|
||||||
|
|
||||||
|
/// The size of a single cell, in pixels.
|
||||||
|
cell_size: renderer.CellSize,
|
||||||
|
|
||||||
/// The size of the viewport in pixels.
|
/// The size of the viewport in pixels.
|
||||||
screen_size: renderer.ScreenSize,
|
screen_size: renderer.ScreenSize,
|
||||||
|
|
||||||
|
@ -60,6 +60,9 @@ surface_mailbox: apprt.surface.Mailbox,
|
|||||||
/// The cached grid size whenever a resize is called.
|
/// The cached grid size whenever a resize is called.
|
||||||
grid_size: renderer.GridSize,
|
grid_size: renderer.GridSize,
|
||||||
|
|
||||||
|
/// The size of a single cell. Used for size reports.
|
||||||
|
cell_size: renderer.CellSize,
|
||||||
|
|
||||||
/// The mailbox implementation to use.
|
/// The mailbox implementation to use.
|
||||||
mailbox: termio.Mailbox,
|
mailbox: termio.Mailbox,
|
||||||
|
|
||||||
@ -171,9 +174,8 @@ pub fn init(self: *Termio, alloc: Allocator, opts: termio.Options) !void {
|
|||||||
backend.initTerminal(&term);
|
backend.initTerminal(&term);
|
||||||
|
|
||||||
// Setup our terminal size in pixels for certain requests.
|
// Setup our terminal size in pixels for certain requests.
|
||||||
const screen_size = opts.screen_size.subPadding(opts.padding);
|
term.width_px = opts.grid_size.columns * opts.cell_size.width;
|
||||||
term.width_px = screen_size.width;
|
term.height_px = opts.grid_size.rows * opts.cell_size.height;
|
||||||
term.height_px = screen_size.height;
|
|
||||||
|
|
||||||
// Create our stream handler. This points to memory in self so it
|
// Create our stream handler. This points to memory in self so it
|
||||||
// isn't safe to use until self.* is set.
|
// isn't safe to use until self.* is set.
|
||||||
@ -214,6 +216,7 @@ pub fn init(self: *Termio, alloc: Allocator, opts: termio.Options) !void {
|
|||||||
.renderer_mailbox = opts.renderer_mailbox,
|
.renderer_mailbox = opts.renderer_mailbox,
|
||||||
.surface_mailbox = opts.surface_mailbox,
|
.surface_mailbox = opts.surface_mailbox,
|
||||||
.grid_size = opts.grid_size,
|
.grid_size = opts.grid_size,
|
||||||
|
.cell_size = opts.cell_size,
|
||||||
.backend = opts.backend,
|
.backend = opts.backend,
|
||||||
.mailbox = opts.mailbox,
|
.mailbox = opts.mailbox,
|
||||||
.terminal_stream = .{
|
.terminal_stream = .{
|
||||||
@ -348,6 +351,7 @@ pub fn resize(
|
|||||||
self: *Termio,
|
self: *Termio,
|
||||||
td: *ThreadData,
|
td: *ThreadData,
|
||||||
grid_size: renderer.GridSize,
|
grid_size: renderer.GridSize,
|
||||||
|
cell_size: renderer.CellSize,
|
||||||
screen_size: renderer.ScreenSize,
|
screen_size: renderer.ScreenSize,
|
||||||
padding: renderer.Padding,
|
padding: renderer.Padding,
|
||||||
) !void {
|
) !void {
|
||||||
@ -357,6 +361,7 @@ pub fn resize(
|
|||||||
|
|
||||||
// Update our cached grid size
|
// Update our cached grid size
|
||||||
self.grid_size = grid_size;
|
self.grid_size = grid_size;
|
||||||
|
self.cell_size = cell_size;
|
||||||
|
|
||||||
// Enter the critical area that we want to keep small
|
// Enter the critical area that we want to keep small
|
||||||
{
|
{
|
||||||
@ -371,8 +376,8 @@ pub fn resize(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Update our pixel sizes
|
// Update our pixel sizes
|
||||||
self.terminal.width_px = padded_size.width;
|
self.terminal.width_px = self.grid_size.columns * self.cell_size.width;
|
||||||
self.terminal.height_px = padded_size.height;
|
self.terminal.height_px = self.grid_size.rows * self.cell_size.height;
|
||||||
|
|
||||||
// Disable synchronized output mode so that we show changes
|
// Disable synchronized output mode so that we show changes
|
||||||
// immediately for a resize. This is allowed by the spec.
|
// immediately for a resize. This is allowed by the spec.
|
||||||
@ -412,24 +417,24 @@ fn sizeReportLocked(self: *Termio, td: *ThreadData, style: termio.Message.SizeRe
|
|||||||
.{
|
.{
|
||||||
self.grid_size.rows,
|
self.grid_size.rows,
|
||||||
self.grid_size.columns,
|
self.grid_size.columns,
|
||||||
self.terminal.height_px,
|
self.grid_size.rows * self.cell_size.height,
|
||||||
self.terminal.width_px,
|
self.grid_size.columns * self.cell_size.width,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
.csi_14_t => try std.fmt.bufPrint(
|
.csi_14_t => try std.fmt.bufPrint(
|
||||||
&buf,
|
&buf,
|
||||||
"\x1b[4;{};{}t",
|
"\x1b[4;{};{}t",
|
||||||
.{
|
.{
|
||||||
self.terminal.height_px,
|
self.grid_size.rows * self.cell_size.height,
|
||||||
self.terminal.width_px,
|
self.grid_size.columns * self.cell_size.width,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
.csi_16_t => try std.fmt.bufPrint(
|
.csi_16_t => try std.fmt.bufPrint(
|
||||||
&buf,
|
&buf,
|
||||||
"\x1b[6;{};{}t",
|
"\x1b[6;{};{}t",
|
||||||
.{
|
.{
|
||||||
self.terminal.height_px / self.grid_size.rows,
|
self.cell_size.height,
|
||||||
self.terminal.width_px / self.grid_size.columns,
|
self.cell_size.width,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
.csi_18_t => try std.fmt.bufPrint(
|
.csi_18_t => try std.fmt.bufPrint(
|
||||||
|
@ -383,6 +383,7 @@ fn coalesceCallback(
|
|||||||
cb.io.resize(
|
cb.io.resize(
|
||||||
&cb.data,
|
&cb.data,
|
||||||
v.grid_size,
|
v.grid_size,
|
||||||
|
v.cell_size,
|
||||||
v.screen_size,
|
v.screen_size,
|
||||||
v.padding,
|
v.padding,
|
||||||
) catch |err| {
|
) catch |err| {
|
||||||
|
@ -20,6 +20,9 @@ pub const Message = union(enum) {
|
|||||||
/// The grid size for the given screen size with padding applied.
|
/// The grid size for the given screen size with padding applied.
|
||||||
grid_size: renderer.GridSize,
|
grid_size: renderer.GridSize,
|
||||||
|
|
||||||
|
/// The updated cell size.
|
||||||
|
cell_size: renderer.CellSize,
|
||||||
|
|
||||||
/// The full screen (drawable) size. This does NOT include padding.
|
/// The full screen (drawable) size. This does NOT include padding.
|
||||||
/// This should be sent on to the renderer.
|
/// This should be sent on to the renderer.
|
||||||
screen_size: renderer.ScreenSize,
|
screen_size: renderer.ScreenSize,
|
||||||
|
Reference in New Issue
Block a user