-
-
Notifications
You must be signed in to change notification settings - Fork 3
Getting Started
- Java 25 or later.
- A platform capable of opening an LWJGL/OpenGL window for the native examples.
- No separate Gradle installation: use the repository wrapper.
Clone the repository and run the smallest windowed example:
.\\gradlew.bat :spinygui.demo.simple:runHelloWorldExampleOn macOS or Linux, use ./gradlew instead. The example opens a native GLFW window, so it is not
a headless test and must be run in a desktop session.
NvgLwjglApplication is the convenience base for an application that owns its window, the normal
frame pipeline, NanoVG renderer, input bridge, and teardown. Add DOM-like nodes to its Frame,
then attach a stylesheet.
import static com.spinyowl.spinygui.core.node.NodeBuilder.div;
import static com.spinyowl.spinygui.core.node.NodeBuilder.text;
import com.spinyowl.spinygui.core.backend.renderer.lwjgl.LwjglApplicationConfiguration;
import com.spinyowl.spinygui.core.backend.renderer.lwjgl.NvgLwjglApplication;
import java.util.Map;
public final class Starter extends NvgLwjglApplication {
public Starter() {
super(LwjglApplicationConfiguration.windowed(720, 420, "My SpinyGUI app"));
}
@Override
protected void initializeGui() {
frame().addChild(div(Map.of("class", "card"), text("Hello, SpinyGUI")));
addStyleSheet("""
winframe { background-color: #eef2ff; padding: 48px; }
.card { background-color: white; color: #172554; padding: 24px; }
""");
}
public static void main(String[] args) {
new Starter().run();
}
}The checked-in HelloWorldExample is the canonical runnable version.
Choose the owned host when SpinyGUI should create the window, standard services, callback bridge, and renderer. The navigator is available to the lifecycle after initialization, so UI callbacks can move between frames or show a modal on the current frame.
Frame initialFrame = new Frame();
try (LwjglApplicationHost host = LwjglApplicationHost.owned(
LwjglApplicationConfiguration.windowed(760, 520, "My application"),
initialFrame,
8,
lifecycle)) {
host.run();
}The runnable NavigationModalHostExample
shows navigator.navigate(...) and frame.topLayer().showModal(...), including nested modals.
Use the injected host when the caller already owns the native window, renderer, services, and loop resources. The host uses those dependencies but does not close them; its lifecycle must therefore follow the embedding application's teardown rules.
LwjglApplicationHost host = LwjglApplicationHost.injected(
navigator, pipeline, renderer, window, services, clock, lifecycle);
host.run(); // caller still owns renderer, services, window, and GLFWFor an already-installed callback chain, use an attach-only bridge and retain its registration:
GlfwSystemEventBridge bridge = GlfwSystemEventBridge.attached(
navigator, systemEvents, keyPolicy, callerOwnedChains);
try (LwjglCallbackInstaller.Registration registration = bridge.install(nativeWindow)) {
// Run the caller-owned event loop. Closing registration removes only bridge handlers.
}The embedded callback smoke demonstrates callback coexistence and exact detachment. Read LWJGL Application Host before composing this path.
Wrap the normal parser only when parsed XML should refer to caller-owned named handlers. The registry remains explicit; this is not reflection-based controller discovery or data binding.
HandlerRegistry handlers = new HandlerRegistry();
handlers.register("save", ActionEvent.class, event -> saveDocument());
NodeParser parser = new XmlEventBindingLoader(
new DefaultNodeParser(), handlers, XmlEventBindingOptions.defaults());
Frame frame = parser.fromHtml(
"<winframe><button on-action=\"save\">Save</button></winframe>").frame();See XML Event Handlers for the supported attributes and missing-handler policy.