FountainGUIKit is a small, NSView‑only GUI framework designed to host MetalViewKit renderers and MIDI 2.0 “instruments” without depending on SwiftUI or AppKit’s responder chain. It provides a custom event model, a pure‑Swift node graph, and a single root NSView that you can embed into existing macOS apps.
This repository follows an agent‑driven design: AGENTS.md is the primary design narrative, and PLAN.md tracks milestones and Definition of Done. The conceptual foundations are described in the design note Doc/Building-a-Custom-GUI-Framework-with-MetalViewKit-and-MIDI2-Integration.md, which explains why we want predictable, MIDI‑friendly UI surfaces.
-
NSView‑only host
FGKRootView: NSViewis the single entry point. It receivesNSEvents and forwards them into a framework‑owned event system instead of using AppKit’s responder chain.- The package does not expose any SwiftUI types; apps can wrap
FGKRootViewin SwiftUI themselves if desired. FGKRootView,FGKEventTarget, andFGKPropertyConsumerare main‑actor aware so UI code remains Swift 6 concurrency safe by default.
-
Explicit event model
FGKEvent,FGKKeyEvent,FGKMouseEventcapture keyboard and mouse input as simple Swift types.- Events are created from
NSEventinFGKRootViewbut can also be constructed directly in tests.
-
Custom responder chain and node graph
FGKEventTargetis a protocol for anything that wants to receive events:func handle(event: FGKEvent) -> Bool.FGKNodeis a pure‑Swift tree node (no NSView base class) with:parentandchildrento represent the UI hierarchy.- an optional
instrumentIdfor future MIDI 2.0/CI integration. - a
bubble(event:)method that walksself → parent → …until a target handles the event (an explicit, testable responder chain).
-
Future integration points
- MetalViewKit: planned adapters will attach MetalViewKit renderers to
FGKNodeinstances and treat them as instrumented views. - MIDI 2.0 CI/PE: nodes with an
instrumentIdwill eventually map their property schema into MIDI 2.0 Property Exchange, reusing FountainTelemetryKit transports (loopback/RTP/BLE) without CoreMIDI.
- MetalViewKit: planned adapters will attach MetalViewKit renderers to
FountainGUIKit is shaped by three main sources:
AGENTS.md— the authoritative design document for this repo. It defines:- scope (NSView‑only host, no SwiftUI, no CoreMIDI),
- current API layers (
FGKEvent,FGKNode,FGKRootView), - and how the framework should integrate with MetalViewKit and MIDI 2.0 over time.
PLAN.md— the implementation plan and Definition of Done:- M1: core event model and root view.
- M2: layout and hit‑testing.
- M3: MetalViewKit adapter.
- M4: MIDI 2.0 CI/PE integration.
- M5: consumers and documentation.
- M6: testing, MRTS, and PB‑VRT integration.
Doc/Building-a-Custom-GUI-Framework-with-MetalViewKit-and-MIDI2-Integration.md- A design note (transcribed from the original PDF) that motivates the framework:
- treat every interactive element as a MIDI 2.0 instrument,
- replace “mystery” responder chains with explicit, testable event flows,
- and make UI behaviour controllable via MIDI messages and property changes.
- A design note (transcribed from the original PDF) that motivates the framework:
When you change the framework, update AGENTS.md first, then PLAN.md, then this README.
FountainGUIKit is intended to be consumed by the main FountainKit workspace, not to replace it:
- MetalViewKit lives in
FountainKit/Packages/FountainApps/Sources/MetalViewKit. - MIDI 2.0 transports live in
FountainTelemetryKit. - PB‑VRT and MRTS harnesses live under
Packages/FountainApps/Testsand related scripts.
FountainGUIKit’s role is to:
- provide a predictable, instrument‑friendly NSView host and event system; and
- make it easy for apps like PatchBay, Composer Studio, and future tools to expose their UI surfaces as MIDI 2.0 instruments and PB‑VRT scenes.
In a consuming package’s Package.swift:
- Add FountainGUIKit as a dependency:
- For development, point to the local checkout or the GitHub URL:
.package(url: "https://github.com/Fountain-Coach/FountainGUIKit.git", from: "0.1.0")
- For development, point to the local checkout or the GitHub URL:
- Add
"FountainGUIKit"to the target’s dependencies.
This package ships a small AppKit demo executable that hosts an FGKRootView and logs events:
- Build and run it with:
swift run -c debug fountain-gui-demo
- The window surface is fully instrument‑ready: typing and clicking will generate
FGKEvents that are routed through anFGKNodetree and logged via a simpleFGKEventTarget.
A minimal usage pattern in an AppKit app looks like:
import AppKit
import FountainGUIKit
final class MyEventTarget: FGKEventTarget {
func handle(event: FGKEvent) -> Bool {
// Decide which events to consume.
return false
}
}
let rootNode = FGKNode(instrumentId: "my.app.surface", target: MyEventTarget())
let rootView = FGKRootView(frame: .zero, rootNode: rootNode)
// Attach rootView to your NSWindow contentView as usual.From here you can:
- add child nodes to
rootNodeto build a logical hierarchy; and - implement
handle(event:)on your targets to respond to keyboard and mouse input.
MetalViewKit integration and MIDI 2.0 adapters will build on top of this structure without changing the embedding model.
The framework is designed to integrate with FountainKit’s test infrastructure:
-
Local XCTest
FountainGUIKitTestsfocuses on:- mapping
NSEventtoFGKEventinFGKRootView, - event bubbling behaviour in
FGKNode, - and layout and hit‑testing.
- mapping
-
MRTS (MIDI Robot Test Script) readiness
- Nodes can carry an
instrumentIdaligned with the identities used by FountainKit’s MIDI 2.0 instrument host. - Consuming apps can host their MetalViewKit views inside FountainGUIKit and drive them via MIDI 2.0 PE, asserting numeric invariants as they already do for PatchBay and other surfaces.
- Nodes can carry an
-
PB‑VRT readiness
- Because
FGKRootViewis an NSView, FountainApps can render it into images and feed those into the existing PB‑VRT service as baselines and regression checks. - FountainGUIKit may eventually expose small snapshot helpers, but PB‑VRT orchestration remains in FountainKit.
- Because
- Start by reading
AGENTS.mdto understand the design rules and intended integration points. - Check
PLAN.mdand the gap checklist before adding new APIs. - Keep AGENTS, PLAN, and README in sync when you modify the public surface.
FountainGUIKit is intentionally small and focused: its job is to make MetalViewKit and MIDI 2.0 easier to use in deterministic, testable GUIs, not to replace full UI frameworks. Design first in AGENTS, plan in PLAN, then implement.