Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,6 @@ logs/
build/
out/
!gradle/wrapper/*.jar

# Dear ImGui runtime layout file (written by ImGui on exit)
imgui.ini
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ consuming them as a dependency only needs JDK 8+ at runtime.
git clone --recurse-submodules https://github.com/SpaiR/imgui-java.git
cd imgui-java
./gradlew :example:run
# SDL3 + SDL_GPU backend smoke test:
./gradlew :example:run -PmainClass=MainSdl
```

`example/src/main/java/` contains a runnable showcase for every widget and extension — start with `Main.java`, then
Expand All @@ -64,6 +66,30 @@ Pick a module based on how much control you need:
On Apple Silicon nothing extra is needed: the published macOS native is a universal binary covering both `x86_64` and
`aarch64`.

### SDL3 backend (imgui-app)

`imgui-app` now supports two backends via `Configuration#setBackend(...)`:

- `Backend.GLFW` (default)
- `Backend.SDL` (SDL3 + SDL_GPU)

Use SDL3 by switching backend in `configure(...)`:

```
@Override
protected void configure(final Configuration config) {
config.setBackend(Backend.SDL);
}
```

For a ready smoke-test entry point, run `MainSdl`:

```
./gradlew :example:run -PmainClass=MainSdl
```

At the moment, SDL backend in `imgui-app` is focused on single-window flow (multi-viewport is not supported).

## Application

If you don't care about OpenGL and other low-level stuff, then you can use application layer from `imgui-app` module.
Expand Down
4 changes: 2 additions & 2 deletions example/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ java {
}

application {
mainClass = 'Main'
mainClass = (findProperty('mainClass') ?: 'Main') as String
try {
if (libPath) {
applicationDefaultJvmArgs = ["-Dimgui.library.path=$libPath"]
Expand All @@ -23,7 +23,7 @@ dependencies {
implementation project(':imgui-app')
}

run {
tasks.withType(JavaExec).configureEach {
if (org.gradle.internal.os.OperatingSystem.current().isMacOsX()) {
jvmArgs += ['-XstartOnFirstThread', '-Djava.awt.headless=true']
}
Expand Down
29 changes: 29 additions & 0 deletions example/src/main/java/MainSdl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import imgui.ImGui;
import imgui.app.Application;
import imgui.app.Backend;
import imgui.app.Configuration;

/**
* Smoke-test entry point for the SDL3 + SDL_GPU backend. Run with:
* <pre>
* ./gradlew :example:run -PmainClass=MainSdl
* </pre>
*/
public class MainSdl extends Application {
@Override
protected void configure(final Configuration config) {
config.setTitle("Example Application — SDL3 + SDL_GPU");
config.setBackend(Backend.SDL);
}

@Override
public void process() {
ImGui.begin("SDL3 + SDL_GPU");
ImGui.text("Hello from the SDL backend!");
ImGui.end();
}

public static void main(final String[] args) {
launch(new MainSdl());
}
}
2 changes: 2 additions & 0 deletions imgui-app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ dependencies {
api 'org.lwjgl:lwjgl'
api 'org.lwjgl:lwjgl-glfw'
api 'org.lwjgl:lwjgl-opengl'
api 'org.lwjgl:lwjgl-sdl'

['natives-linux', 'natives-windows', 'natives-macos', 'natives-macos-arm64'].each {
api "org.lwjgl:lwjgl::$it"
api "org.lwjgl:lwjgl-glfw::$it"
api "org.lwjgl:lwjgl-opengl::$it"
api "org.lwjgl:lwjgl-sdl::$it"
}

api project(':imgui-binding')
Expand Down
79 changes: 65 additions & 14 deletions imgui-app/src/main/java/imgui/app/Application.java
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
package imgui.app;

import imgui.ImGui;

/**
* Application class from which ImGui applications extend.
* Serves as an abstraction layer to hide all low-level details about window creation and rendering routine.
*
* <h2>Life-cycle</h2>
* <p>The entry point for ImGui applications is the Application class and {@link #launch(Application)} method.
* It initializes application instance and starts the main application loop.
* It builds a {@link Configuration}, lets the user customize it (including selecting a {@link Backend}), then
* instantiates the matching {@link Window} subclass ({@link WindowGlfw} or {@link WindowSdl}) and drives the
* application loop:
*
* <ol>
* <li>{@link #configure(Configuration)} It's called before window creation, so only basic application setups are expected.</li>
* <li>{@link #initWindow(Configuration)} Method creates application window.</li>
* <li>{@link #initImGui(Configuration)} Method initializes Dear ImGui context. Could be used to do Dear ImGui setup as well.</li>
* <li>{@link Window#init(Configuration)} The window's {@code init} creates the OS window and triggers
* {@link #initImGui(Configuration)} to set up the Dear ImGui context.</li>
* <li>{@link #preRun()} Method called once, before application loop.</li>
* <li>{@link #preProcess()} Method called every frame, before {@link #process()}.</li>
* <li>{@link #process()} Method is meant to be overridden with user application logic.</li>
* <li>{@link #postProcess()} Method called every frame, after {@link #process()}.</li>
* <li>{@link #postRun()} Method called once, after application loop.</li>
* <li>{@link #disposeImGui()} Destroys Dear ImGui context.</li>
* <li>{@link #disposeWindow()} Destroys application window.</li>
* <li>{@link Window#dispose()} The window's {@code dispose} releases platform/renderer resources.</li>
* </ol>
*
* <p>As it could be seen, ImGui application differs from the classic one in the way of its life-cycle flow.
Expand Down Expand Up @@ -52,7 +56,9 @@
* For example, large list of computations could be separated between application ticks. {@link #process()} method is called constantly.
* Use that wisely and remember that all GUI should be in the main thread.
*/
public abstract class Application extends Window {
public abstract class Application {
private Window window;

/**
* Method called before window creation. Could be used to provide basic window information, like title name etc.
*
Expand All @@ -61,6 +67,32 @@ public abstract class Application extends Window {
protected void configure(final Configuration config) {
}

/**
* Method to initialize Dear ImGui context. Could be overridden to do custom Dear ImGui setup before application start.
*
* @param config configuration object with basic window information
*/
protected void initImGui(final Configuration config) {
ImGui.createContext();
}

/**
* Method called every frame, before calling {@link #process()} method.
*/
protected void preProcess() {
}

/**
* Method called every frame, after calling {@link #process()} method.
*/
protected void postProcess() {
}

/**
* Method to be overridden by user to provide main application logic.
*/
public abstract void process();

/**
* Method called once, before application run loop.
*/
Expand All @@ -73,22 +105,41 @@ protected void preRun() {
protected void postRun() {
}

/**
* Method to destroy Dear ImGui context.
*/
protected void disposeImGui() {
ImGui.destroyContext();
}

/**
* @return the {@link Window} backing this application
*/
public final Window getWindow() {
return window;
}

/**
* @return {@link Color} instance, which represents background color for the window
*/
public final Color getColorBg() {
return window.getColorBg();
}

/**
* Entry point of any ImGui application. Use it to start the application loop.
*
* @param app application instance to run
*/
public static void launch(final Application app) {
initialize(app);
app.preRun();
app.run();
app.postRun();
app.dispose();
}

private static void initialize(final Application app) {
final Configuration config = new Configuration();
app.configure(config);
app.init(config);
app.window = (config.getBackend() == Backend.SDL) ? new WindowSdl() : new WindowGlfw();
app.window.setOwner(app);
app.window.init(config);
app.preRun();
app.window.run();
app.postRun();
app.window.dispose();
}
}
15 changes: 15 additions & 0 deletions imgui-app/src/main/java/imgui/app/Backend.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package imgui.app;

/**
* Backend selection used by {@link Configuration} to choose which {@link Window} subclass {@link Application} instantiates.
*/
public enum Backend {
/**
* GLFW platform + OpenGL 3 renderer. Default.
*/
GLFW,
/**
* SDL3 platform + SDL_GPU renderer.
*/
SDL
}
12 changes: 12 additions & 0 deletions imgui-app/src/main/java/imgui/app/Configuration.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ public class Configuration {
* When true, application will be maximized by default.
*/
private boolean fullScreen = false;
/**
* Backend to use for window/platform and rendering.
*/
private Backend backend = Backend.GLFW;

public String getTitle() {
return title;
Expand Down Expand Up @@ -52,4 +56,12 @@ public boolean isFullScreen() {
public void setFullScreen(final boolean fullScreen) {
this.fullScreen = fullScreen;
}

public Backend getBackend() {
return backend;
}

public void setBackend(final Backend backend) {
this.backend = backend;
}
}
Loading