macos: use a MTKView subclass for the inspector

This commit is contained in:
Mitchell Hashimoto
2023-10-18 22:24:21 -07:00
parent 69cc6d11dc
commit f15aaf3b9e
2 changed files with 63 additions and 45 deletions

View File

@ -13,58 +13,83 @@ extension Ghostty {
SplitView(.vertical, left: { SplitView(.vertical, left: {
SurfaceWrapper(surfaceView: surfaceView, isSplit: isSplit) SurfaceWrapper(surfaceView: surfaceView, isSplit: isSplit)
}, right: { }, right: {
InspectorView() SurfaceInspector()
}) })
} }
} }
struct InspectorView: View { struct SurfaceInspector: View {
var body: some View { var body: some View {
MetalView<Renderer>() MetalView<InspectorView>()
} }
} }
class Renderer: NSObject, MetalViewRenderer { class InspectorView: MTKView {
let device: MTLDevice
let commandQueue: MTLCommandQueue let commandQueue: MTLCommandQueue
required init(metalView: MTKView) { override init(frame: CGRect, device: MTLDevice?) {
// Initialize our Metal primitives // Initialize our Metal primitives
guard guard
let device = MTLCreateSystemDefaultDevice(), let device = device ?? MTLCreateSystemDefaultDevice(),
let commandQueue = device.makeCommandQueue() else { let commandQueue = device.makeCommandQueue() else {
fatalError("GPU not available") fatalError("GPU not available")
} }
self.device = device // Setup our properties before initializing the parent
self.commandQueue = commandQueue self.commandQueue = commandQueue
super.init() super.init(frame: frame, device: device)
// Setup the view to point to this renderer // After initializing the parent we can set our own properties
metalView.device = device self.device = MTLCreateSystemDefaultDevice()
metalView.delegate = self self.clearColor = MTLClearColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)
metalView.clearColor = MTLClearColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)
// Setup our tracking areas for mouse events
updateTrackingAreas()
}
required init(coder: NSCoder) {
fatalError("init(coder:) is not supported for this view")
}
deinit {
trackingAreas.forEach { removeTrackingArea($0) }
}
override func updateTrackingAreas() {
// To update our tracking area we just recreate it all.
trackingAreas.forEach { removeTrackingArea($0) }
// This tracking area is across the entire frame to notify us of mouse movements.
addTrackingArea(NSTrackingArea(
rect: frame,
options: [
.mouseMoved,
// Only send mouse events that happen in our visible (not obscured) rect
.inVisibleRect,
// We want active always because we want to still send mouse reports
// even if we're not focused or key.
.activeAlways,
],
owner: self,
userInfo: nil))
}
override func draw(_ dirtyRect: NSRect) {
guard
let commandBuffer = self.commandQueue.makeCommandBuffer(),
let descriptor = self.currentRenderPassDescriptor,
let renderEncoder =
commandBuffer.makeRenderCommandEncoder(
descriptor: descriptor) else {
return
}
renderEncoder.endEncoding()
guard let drawable = self.currentDrawable else { return }
commandBuffer.present(drawable)
commandBuffer.commit()
} }
} }
} }
extension Ghostty.Renderer: MTKViewDelegate {
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
}
func draw(in view: MTKView) {
guard
let commandBuffer = self.commandQueue.makeCommandBuffer(),
let descriptor = view.currentRenderPassDescriptor,
let renderEncoder =
commandBuffer.makeRenderCommandEncoder(
descriptor: descriptor) else {
return
}
renderEncoder.endEncoding()
guard let drawable = view.currentDrawable else { return }
commandBuffer.present(drawable)
commandBuffer.commit()
}
}

View File

@ -1,24 +1,17 @@
import SwiftUI import SwiftUI
import MetalKit import MetalKit
/// Implements the logic for a metal view used by MetalView.
protocol MetalViewRenderer: MTKViewDelegate {
init(metalView: MTKView)
}
/// Renders an MTKView with the given renderer class. /// Renders an MTKView with the given renderer class.
struct MetalView<R: MetalViewRenderer>: View { struct MetalView<V: MTKView>: View {
@State private var metalView = MTKView() @State private var metalView = V()
@State private var renderer: R?
var body: some View { var body: some View {
MetalViewRepresentable(metalView: $metalView) MetalViewRepresentable(metalView: $metalView)
.onAppear { renderer = R(metalView: metalView) }
} }
} }
fileprivate struct MetalViewRepresentable: NSViewRepresentable { fileprivate struct MetalViewRepresentable<V: MTKView>: NSViewRepresentable {
@Binding var metalView: MTKView @Binding var metalView: V
func makeNSView(context: Context) -> some NSView { func makeNSView(context: Context) -> some NSView {
metalView metalView