Created
March 6, 2023 06:23
-
-
Save bacongravy/92a0822e06338599fe12ea6dca3b84a8 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct InjectedNSView<Content: View, Injected: NSView>: NSViewRepresentable { | |
let content: Content | |
let injected: () -> Injected | |
func makeNSView(context: Context) -> Injected { | |
let injected = injected() | |
let hostingView = NSHostingView(rootView: content) | |
hostingView.translatesAutoresizingMaskIntoConstraints = false | |
injected.addSubview(hostingView) | |
hostingView.topAnchor.constraint(equalTo: injected.topAnchor).isActive = true | |
hostingView.bottomAnchor.constraint(equalTo: injected.bottomAnchor).isActive = true | |
hostingView.leftAnchor.constraint(equalTo: injected.leftAnchor).isActive = true | |
hostingView.rightAnchor.constraint(equalTo: injected.rightAnchor).isActive = true | |
return injected | |
} | |
func updateNSView(_ nsView: Injected, context: Context) { | |
if let view = nsView.subviews.first as? NSHostingView<Content> { | |
view.rootView = content | |
} | |
} | |
} | |
// Example usage | |
extension View { | |
func onKeyDown(perform action: @escaping OnKeyDown.Action) -> some View { | |
return InjectedNSView(content: self) { OnKeyDown(action: action) } | |
} | |
} | |
class OnKeyDown: EventHandlerView { | |
override func keyDown(with event: NSEvent) { | |
if !action(event) { | |
super.keyDown(with: event) | |
} | |
} | |
} | |
class EventHandlerView: NSView { | |
typealias Action = (NSEvent) -> Bool | |
let action: Action | |
init(action: @escaping Action) { | |
self.action = action | |
super.init(frame: .zero) | |
} | |
@objc required dynamic init?(coder aDecoder: NSCoder) { | |
fatalError("init(coder:) has not been implemented") | |
} | |
} | |
private let characterSet = CharacterSet.init() | |
.union(.alphanumerics) | |
.union(.punctuationCharacters) | |
.union(.symbols) | |
.union(.whitespacesAndNewlines) | |
.subtracting(.init(charactersIn: "\t")) | |
struct BasicTextInput: View { | |
@Binding var text: String | |
var body: some View { | |
ZStack(alignment: .topLeading) { | |
RoundedRectangle(cornerRadius: 8.0) | |
.strokeBorder(style: .init(lineWidth: 2)) | |
.frame(maxWidth: .infinity, maxHeight: .infinity) | |
Text(text) | |
.padding(8) | |
} | |
.padding(.horizontal, -0.5) | |
.padding(.bottom, -0.25) | |
.contentShape(RoundedRectangle(cornerRadius: 7.0)) | |
.focusable() | |
.padding(2) | |
// ******************************************************* | |
.onKeyDown { event in | |
if let specialKey = event.specialKey, | |
specialKey == .delete, | |
text.count > 0 { | |
let index = text.index(text.endIndex, offsetBy: -1) | |
text = String(text.prefix(upTo: index)) | |
return true | |
} | |
else if let characters = event.characters, | |
let unicodeScalar = characters.unicodeScalars.first { | |
if characterSet.contains(unicodeScalar) { | |
text.append(characters) | |
return true | |
} | |
} | |
return false | |
} | |
// ******************************************************* | |
.padding(-2) | |
} | |
} | |
struct BasicTextInput_PreviewContainer: View { | |
@State private var text: String = "" | |
var body: some View { | |
VStack { | |
Spacer() | |
Text("Enter some text:").font(.title).bold() | |
.frame(maxWidth: .infinity, alignment: .leading) | |
BasicTextInput(text: $text) | |
Button("Clear") { | |
text = "" | |
} | |
.frame(maxWidth: .infinity, alignment: .trailing) | |
Spacer() | |
} | |
.padding() | |
} | |
} | |
struct BasicTextInput_Previews: PreviewProvider { | |
static var previews: some View { | |
BasicTextInput_PreviewContainer() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment