mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-05-26 14:58:35 +00:00
180 lines
4.9 KiB
Swift
180 lines
4.9 KiB
Swift
import SwiftUI
|
|
|
|
// All backport view/scene modifiers go as an extension on this. We use this
|
|
// so we can easily track and centralize all backports.
|
|
struct Backport<Content> {
|
|
let content: Content
|
|
}
|
|
|
|
extension View {
|
|
var backport: Backport<Self> { Backport(content: self) }
|
|
}
|
|
|
|
extension Scene {
|
|
var backport: Backport<Self> { Backport(content: self) }
|
|
}
|
|
|
|
extension Backport where Content: Scene {
|
|
// None currently
|
|
}
|
|
|
|
/// Result type for backported onKeyPress handler
|
|
enum BackportKeyPressResult {
|
|
case handled
|
|
case ignored
|
|
}
|
|
|
|
extension Backport where Content: View {
|
|
func pointerVisibility(_ v: BackportVisibility) -> some View {
|
|
#if canImport(AppKit)
|
|
if #available(macOS 15, *) {
|
|
return content.pointerVisibility(v.official)
|
|
} else {
|
|
return content
|
|
}
|
|
#else
|
|
return content
|
|
#endif
|
|
}
|
|
|
|
func pointerStyle(_ style: BackportPointerStyle?) -> some View {
|
|
#if canImport(AppKit)
|
|
if #available(macOS 15, *) {
|
|
return content.pointerStyle(style?.official)
|
|
} else {
|
|
return content
|
|
}
|
|
#else
|
|
return content
|
|
#endif
|
|
}
|
|
|
|
/// Backported onKeyPress that works on macOS 14+ and is a no-op on macOS 13.
|
|
func onKeyPress(_ key: KeyEquivalent, action: @escaping (EventModifiers) -> BackportKeyPressResult) -> some View {
|
|
#if canImport(AppKit)
|
|
if #available(macOS 14, *) {
|
|
return content.onKeyPress(key, phases: .down, action: { keyPress in
|
|
switch action(keyPress.modifiers) {
|
|
case .handled: return .handled
|
|
case .ignored: return .ignored
|
|
}
|
|
})
|
|
} else {
|
|
return content
|
|
}
|
|
#else
|
|
return content
|
|
#endif
|
|
}
|
|
}
|
|
|
|
enum BackportVisibility {
|
|
case automatic
|
|
case visible
|
|
case hidden
|
|
|
|
@available(macOS 15, *)
|
|
var official: Visibility {
|
|
switch self {
|
|
case .automatic: return .automatic
|
|
case .visible: return .visible
|
|
case .hidden: return .hidden
|
|
}
|
|
}
|
|
}
|
|
|
|
enum BackportPointerStyle {
|
|
case `default`
|
|
case grabIdle
|
|
case grabActive
|
|
case horizontalText
|
|
case verticalText
|
|
case link
|
|
case resizeLeft
|
|
case resizeRight
|
|
case resizeUp
|
|
case resizeDown
|
|
case resizeUpDown
|
|
case resizeLeftRight
|
|
|
|
#if canImport(AppKit)
|
|
@available(macOS 15, *)
|
|
var official: PointerStyle {
|
|
switch self {
|
|
case .default: return .default
|
|
case .grabIdle: return .grabIdle
|
|
case .grabActive: return .grabActive
|
|
case .horizontalText: return .horizontalText
|
|
case .verticalText: return .verticalText
|
|
case .link: return .link
|
|
case .resizeLeft: return .frameResize(position: .trailing, directions: [.inward])
|
|
case .resizeRight: return .frameResize(position: .leading, directions: [.inward])
|
|
case .resizeUp: return .frameResize(position: .bottom, directions: [.inward])
|
|
case .resizeDown: return .frameResize(position: .top, directions: [.inward])
|
|
case .resizeUpDown: return .frameResize(position: .top)
|
|
case .resizeLeftRight: return .frameResize(position: .trailing)
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
enum BackportNSGlassStyle {
|
|
case regular, clear
|
|
|
|
#if canImport(AppKit)
|
|
@available(macOS 26, *)
|
|
var official: NSGlassEffectView.Style {
|
|
switch self {
|
|
case .regular: return .regular
|
|
case .clear: return .clear
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/// Backported `TextField` that supports text selection on macOS 15/iOS 18 and up. The `selection`
|
|
/// has no effect on versions below macOS 15/iOS 18.
|
|
struct BackportSelectionTextField: View {
|
|
private let titleKey: LocalizedStringKey
|
|
@Binding private var text: String
|
|
@Binding private var textSelection: Range<String.Index>?
|
|
|
|
init(
|
|
_ titleKey: LocalizedStringKey,
|
|
text: Binding<String>,
|
|
selection: Binding<Range<String.Index>?>
|
|
) {
|
|
self.titleKey = titleKey
|
|
self._text = text
|
|
self._textSelection = selection
|
|
}
|
|
|
|
var body: some View {
|
|
if #available(iOS 18.0, macOS 15, *) {
|
|
TextField(
|
|
titleKey,
|
|
text: _text,
|
|
selection: Binding(
|
|
get: {
|
|
if let textSelection {
|
|
TextSelection(range: textSelection)
|
|
} else {
|
|
nil
|
|
}
|
|
},
|
|
set: { selection in
|
|
if let selection,
|
|
case .selection(let range) = selection.indices {
|
|
self.textSelection = range
|
|
} else {
|
|
self.textSelection = nil
|
|
}
|
|
}
|
|
)
|
|
)
|
|
} else {
|
|
TextField(titleKey, text: _text)
|
|
}
|
|
}
|
|
}
|