mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
macos: UI for configuration errors
This commit is contained in:
@ -26,6 +26,9 @@
|
|||||||
A59444F729A2ED5200725BBA /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59444F629A2ED5200725BBA /* SettingsView.swift */; };
|
A59444F729A2ED5200725BBA /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59444F629A2ED5200725BBA /* SettingsView.swift */; };
|
||||||
A5A1F8852A489D6800D1E8BC /* terminfo in Resources */ = {isa = PBXBuildFile; fileRef = A5A1F8842A489D6800D1E8BC /* terminfo */; };
|
A5A1F8852A489D6800D1E8BC /* terminfo in Resources */ = {isa = PBXBuildFile; fileRef = A5A1F8842A489D6800D1E8BC /* terminfo */; };
|
||||||
A5B30539299BEAAB0047F10C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A5B30538299BEAAB0047F10C /* Assets.xcassets */; };
|
A5B30539299BEAAB0047F10C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A5B30538299BEAAB0047F10C /* Assets.xcassets */; };
|
||||||
|
A5CDF1912AAF9A5800513312 /* ConfigurationErrors.xib in Resources */ = {isa = PBXBuildFile; fileRef = A5CDF1902AAF9A5800513312 /* ConfigurationErrors.xib */; };
|
||||||
|
A5CDF1932AAF9E0800513312 /* ConfigurationErrorsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CDF1922AAF9E0800513312 /* ConfigurationErrorsController.swift */; };
|
||||||
|
A5CDF1952AAFA19600513312 /* ConfigurationErrorsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CDF1942AAFA19600513312 /* ConfigurationErrorsView.swift */; };
|
||||||
A5CEAFDC29B8009000646FDA /* SplitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CEAFDB29B8009000646FDA /* SplitView.swift */; };
|
A5CEAFDC29B8009000646FDA /* SplitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CEAFDB29B8009000646FDA /* SplitView.swift */; };
|
||||||
A5CEAFDE29B8058B00646FDA /* SplitView.Divider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CEAFDD29B8058B00646FDA /* SplitView.Divider.swift */; };
|
A5CEAFDE29B8058B00646FDA /* SplitView.Divider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CEAFDD29B8058B00646FDA /* SplitView.Divider.swift */; };
|
||||||
A5CEAFFF29C2410700646FDA /* Backport.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CEAFFE29C2410700646FDA /* Backport.swift */; };
|
A5CEAFFF29C2410700646FDA /* Backport.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CEAFFE29C2410700646FDA /* Backport.swift */; };
|
||||||
@ -55,6 +58,9 @@
|
|||||||
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>"; };
|
||||||
|
A5CDF1902AAF9A5800513312 /* ConfigurationErrors.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ConfigurationErrors.xib; sourceTree = "<group>"; };
|
||||||
|
A5CDF1922AAF9E0800513312 /* ConfigurationErrorsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationErrorsController.swift; sourceTree = "<group>"; };
|
||||||
|
A5CDF1942AAFA19600513312 /* ConfigurationErrorsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationErrorsView.swift; sourceTree = "<group>"; };
|
||||||
A5CEAFDB29B8009000646FDA /* SplitView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitView.swift; sourceTree = "<group>"; };
|
A5CEAFDB29B8009000646FDA /* SplitView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitView.swift; sourceTree = "<group>"; };
|
||||||
A5CEAFDD29B8058B00646FDA /* SplitView.Divider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitView.Divider.swift; sourceTree = "<group>"; };
|
A5CEAFDD29B8058B00646FDA /* SplitView.Divider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitView.Divider.swift; sourceTree = "<group>"; };
|
||||||
A5CEAFFE29C2410700646FDA /* Backport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Backport.swift; sourceTree = "<group>"; };
|
A5CEAFFE29C2410700646FDA /* Backport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Backport.swift; sourceTree = "<group>"; };
|
||||||
@ -112,6 +118,9 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
A59444F629A2ED5200725BBA /* SettingsView.swift */,
|
A59444F629A2ED5200725BBA /* SettingsView.swift */,
|
||||||
|
A5CDF1902AAF9A5800513312 /* ConfigurationErrors.xib */,
|
||||||
|
A5CDF1922AAF9E0800513312 /* ConfigurationErrorsController.swift */,
|
||||||
|
A5CDF1942AAFA19600513312 /* ConfigurationErrorsView.swift */,
|
||||||
);
|
);
|
||||||
path = Settings;
|
path = Settings;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -249,6 +258,7 @@
|
|||||||
files = (
|
files = (
|
||||||
A545D1A22A5772CE006E0AE4 /* shell-integration in Resources */,
|
A545D1A22A5772CE006E0AE4 /* shell-integration in Resources */,
|
||||||
A5A1F8852A489D6800D1E8BC /* terminfo in Resources */,
|
A5A1F8852A489D6800D1E8BC /* terminfo in Resources */,
|
||||||
|
A5CDF1912AAF9A5800513312 /* ConfigurationErrors.xib in Resources */,
|
||||||
A5B30539299BEAAB0047F10C /* Assets.xcassets in Resources */,
|
A5B30539299BEAAB0047F10C /* Assets.xcassets in Resources */,
|
||||||
857F63812A5E64F200CA4815 /* MainMenu.xib in Resources */,
|
857F63812A5E64F200CA4815 /* MainMenu.xib in Resources */,
|
||||||
);
|
);
|
||||||
@ -265,6 +275,7 @@
|
|||||||
85DE1C922A6A3DCA00493853 /* PrimaryWindow.swift in Sources */,
|
85DE1C922A6A3DCA00493853 /* PrimaryWindow.swift in Sources */,
|
||||||
A5278A9B2AA05B2600CD3039 /* Ghostty.Input.swift in Sources */,
|
A5278A9B2AA05B2600CD3039 /* Ghostty.Input.swift in Sources */,
|
||||||
A53426352A7DA53D00EBB7A2 /* AppDelegate.swift in Sources */,
|
A53426352A7DA53D00EBB7A2 /* AppDelegate.swift in Sources */,
|
||||||
|
A5CDF1952AAFA19600513312 /* ConfigurationErrorsView.swift in Sources */,
|
||||||
A55B7BBC29B6FC330055DE60 /* SurfaceView.swift in Sources */,
|
A55B7BBC29B6FC330055DE60 /* SurfaceView.swift in Sources */,
|
||||||
A59444F729A2ED5200725BBA /* SettingsView.swift in Sources */,
|
A59444F729A2ED5200725BBA /* SettingsView.swift in Sources */,
|
||||||
A5FECBD729D1FC3900022361 /* PrimaryView.swift in Sources */,
|
A5FECBD729D1FC3900022361 /* PrimaryView.swift in Sources */,
|
||||||
@ -272,6 +283,7 @@
|
|||||||
A55B7BBE29B701360055DE60 /* Ghostty.SplitView.swift in Sources */,
|
A55B7BBE29B701360055DE60 /* Ghostty.SplitView.swift in Sources */,
|
||||||
A55B7BB629B6F47F0055DE60 /* AppState.swift in Sources */,
|
A55B7BB629B6F47F0055DE60 /* AppState.swift in Sources */,
|
||||||
A5CEAFDC29B8009000646FDA /* SplitView.swift in Sources */,
|
A5CEAFDC29B8009000646FDA /* SplitView.swift in Sources */,
|
||||||
|
A5CDF1932AAF9E0800513312 /* ConfigurationErrorsController.swift in Sources */,
|
||||||
A55685E029A03A9F004303CE /* AppError.swift in Sources */,
|
A55685E029A03A9F004303CE /* AppError.swift in Sources */,
|
||||||
A5FECBD929D2010400022361 /* WindowAccessor.swift in Sources */,
|
A5FECBD929D2010400022361 /* WindowAccessor.swift in Sources */,
|
||||||
A535B9DA299C569B0017E2E4 /* ErrorView.swift in Sources */,
|
A535B9DA299C569B0017E2E4 /* ErrorView.swift in Sources */,
|
||||||
|
@ -60,12 +60,12 @@ class AppDelegate: NSObject, ObservableObject, NSApplicationDelegate, GhosttyApp
|
|||||||
"ApplePressAndHoldEnabled": false,
|
"ApplePressAndHoldEnabled": false,
|
||||||
])
|
])
|
||||||
|
|
||||||
// Sync our menu shortcuts with our Ghostty config
|
|
||||||
syncMenuShortcuts()
|
|
||||||
|
|
||||||
// Let's launch our first window.
|
// Let's launch our first window.
|
||||||
// TODO: we should detect if we restored windows and if so not launch a new window.
|
// TODO: we should detect if we restored windows and if so not launch a new window.
|
||||||
windowManager.addInitialWindow()
|
windowManager.addInitialWindow()
|
||||||
|
|
||||||
|
// Initial config loading
|
||||||
|
configDidReload(ghostty)
|
||||||
}
|
}
|
||||||
|
|
||||||
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
|
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
|
||||||
@ -180,7 +180,13 @@ class AppDelegate: NSObject, ObservableObject, NSApplicationDelegate, GhosttyApp
|
|||||||
//MARK: - GhosttyAppStateDelegate
|
//MARK: - GhosttyAppStateDelegate
|
||||||
|
|
||||||
func configDidReload(_ state: Ghostty.AppState) {
|
func configDidReload(_ state: Ghostty.AppState) {
|
||||||
|
// Config could change keybindings, so update our menu
|
||||||
syncMenuShortcuts()
|
syncMenuShortcuts()
|
||||||
|
|
||||||
|
// If we have configuration errors, we need to show them.
|
||||||
|
let c = ConfigurationErrorsController.sharedInstance
|
||||||
|
c.model.errors = state.configErrors()
|
||||||
|
if (c.model.errors.count > 0) { c.showWindow(self) }
|
||||||
}
|
}
|
||||||
|
|
||||||
//MARK: - Dock Menu
|
//MARK: - Dock Menu
|
||||||
|
31
macos/Sources/Features/Settings/ConfigurationErrors.xib
Normal file
31
macos/Sources/Features/Settings/ConfigurationErrors.xib
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21701" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="macosx"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21701"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<objects>
|
||||||
|
<customObject id="-2" userLabel="File's Owner" customClass="ConfigurationErrorsController" customModule="Ghostty" customModuleProvider="target">
|
||||||
|
<connections>
|
||||||
|
<outlet property="window" destination="QvC-M9-y7g" id="H7g-uf-37u"/>
|
||||||
|
</connections>
|
||||||
|
</customObject>
|
||||||
|
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||||
|
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||||
|
<window title="Configuration Errors" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="QvC-M9-y7g" userLabel="Configuration Errors">
|
||||||
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||||
|
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||||
|
<rect key="contentRect" x="196" y="240" width="480" height="270"/>
|
||||||
|
<rect key="screenRect" x="0.0" y="0.0" width="3008" height="1667"/>
|
||||||
|
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="480" height="270"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="delegate" destination="-2" id="vqU-UH-2Ks"/>
|
||||||
|
</connections>
|
||||||
|
<point key="canvasLocation" x="127.5" y="137.5"/>
|
||||||
|
</window>
|
||||||
|
</objects>
|
||||||
|
</document>
|
@ -0,0 +1,49 @@
|
|||||||
|
import Foundation
|
||||||
|
import Cocoa
|
||||||
|
import SwiftUI
|
||||||
|
import Combine
|
||||||
|
|
||||||
|
class ConfigurationErrorsController: NSWindowController, NSWindowDelegate {
|
||||||
|
/// Singleton for the errors view.
|
||||||
|
static let sharedInstance = ConfigurationErrorsController()
|
||||||
|
|
||||||
|
override var windowNibName: NSNib.Name? { "ConfigurationErrors" }
|
||||||
|
|
||||||
|
/// The data model for this view. Update this directly and the associated view will be updated, too.
|
||||||
|
let model = ConfigurationErrorsView.Model()
|
||||||
|
|
||||||
|
private var cancellable: AnyCancellable?
|
||||||
|
|
||||||
|
//MARK: - NSWindowController
|
||||||
|
|
||||||
|
override func windowWillLoad() {
|
||||||
|
shouldCascadeWindows = false
|
||||||
|
|
||||||
|
if let c = cancellable { c.cancel() }
|
||||||
|
cancellable = model.objectWillChange.sink {
|
||||||
|
if (self.model.errors.count == 0) {
|
||||||
|
self.window?.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func windowDidLoad() {
|
||||||
|
guard let window = window else { return }
|
||||||
|
window.center()
|
||||||
|
window.level = .popUpMenu
|
||||||
|
window.contentView = NSHostingView(rootView: ConfigurationErrorsView(model: model))
|
||||||
|
window.makeKeyAndOrderFront(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: - NSWindowDelegate
|
||||||
|
|
||||||
|
func windowWillClose(_ notification: Notification) {
|
||||||
|
guard let window = window else { return }
|
||||||
|
window.contentView = nil
|
||||||
|
|
||||||
|
if let cancellable = cancellable {
|
||||||
|
cancellable.cancel()
|
||||||
|
self.cancellable = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct ConfigurationErrorsView: View {
|
||||||
|
class Model: ObservableObject {
|
||||||
|
@Published var errors: [String] = []
|
||||||
|
}
|
||||||
|
|
||||||
|
var model: Model
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack {
|
||||||
|
HStack {
|
||||||
|
Image(systemName: "exclamationmark.triangle.fill")
|
||||||
|
.foregroundColor(.yellow)
|
||||||
|
.font(.system(size: 52))
|
||||||
|
.padding()
|
||||||
|
.frame(alignment: .center)
|
||||||
|
|
||||||
|
Text("""
|
||||||
|
^[\(model.errors.count) error was](inflect: true) found while loading the configuration. \
|
||||||
|
Please review the errors below and reload your configuration.
|
||||||
|
""")
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
|
||||||
|
GeometryReader { geo in
|
||||||
|
ScrollView {
|
||||||
|
VStack {
|
||||||
|
ForEach(model.errors, id: \.self) { error in
|
||||||
|
Text(error)
|
||||||
|
.lineLimit(nil)
|
||||||
|
.font(.system(size: 12).monospaced())
|
||||||
|
.textSelection(.enabled)
|
||||||
|
.padding(.all)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .topLeading)
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.frame(height: geo.size.height)
|
||||||
|
.background(Color.white)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(minWidth: 480, maxWidth: 960, minHeight: 270)
|
||||||
|
}
|
||||||
|
}
|
@ -145,6 +145,21 @@ extension Ghostty {
|
|||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the configuration errors (if any).
|
||||||
|
func configErrors() -> [String] {
|
||||||
|
guard let cfg = self.config else { return [] }
|
||||||
|
|
||||||
|
var errors: [String] = [];
|
||||||
|
let errCount = ghostty_config_errors_count(cfg)
|
||||||
|
for i in 0..<errCount {
|
||||||
|
let err = ghostty_config_get_error(cfg, UInt32(i))
|
||||||
|
let message = String(cString: err.message)
|
||||||
|
errors.append(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
|
||||||
func appTick() {
|
func appTick() {
|
||||||
guard let app = self.app else { return }
|
guard let app = self.app else { return }
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user