Atomis Development Blog

jdBasic Update: Building Modern UIs with Immediate Mode GUI (ImGui)

jdBasic ImGui Demo

Introduction

When I started building jdBasic, my goal was to create a modern interpreter that felt like the classic BASICs of the 80s but with the power of C++ under the hood. It started with simple console I/O, then gained sprite graphics and sound with SDL3.

But there was one thing missing: User Interfaces.

If you wanted to build a level editor, a database manager, or a debugging tool in jdBasic, you had to draw your own buttons and handle your own mouse clicks. That changes today.

I have integrated Dear ImGui directly into the language. This means you can now create professional-grade windows, menus, inputs, and plots using simple, declarative BASIC commands.

Why Immediate Mode?

For an interpreter like jdBasic, "Immediate Mode" GUI is a perfect fit. Instead of creating objects, setting callbacks, and managing complex state (Retained Mode), you just describe your UI every frame inside your main loop.

If you want a button, you just ask for it:

IF GUI.BUTTON("Click Me") THEN
    PRINT "Button was clicked!"
ENDIF

This stateless approach aligns perfectly with the procedural nature of BASIC.

New Features Overview

The new GUI command set covers almost everything you need to build functional applications.

1. Windows and Layouts

You aren't stuck with a single graphics window anymore. You can create movable, resizable windows, tabs, and child regions.

WindowOpen = GUI.BEGIN("My Tool", 50, 50, 300, 200, WindowOpen)
    GUI.TEXT "This is a floating window inside jdBasic!"
    GUI.SEPARATOR
    GUI.TEXT "It handles its own state."
GUI.END

2. Rich Input Widgets

We've gone beyond INPUT. You now have access to:

3. Data Visualization

Need to debug a sensor or show game stats? You can plot data arrays directly.

' Visualize an array of data as a histogram
GUI.PLOT_HISTOGRAM("CPU Load", LoadHistoryArray, "Usage", 0.0, 100.0)

4. Menus and Popups

You can add a full menu bar to your main window or create right-click context menus and modal popups for dialogs.

IF GUI.BEGIN_MAIN_MENU_BAR() THEN
    IF GUI.BEGIN_MENU("File") THEN
        IF GUI.MENU_ITEM("Exit") THEN STOP
        GUI.END_MENU()
    ENDIF
    GUI.END_MAIN_MENU_BAR()
ENDIF

Real-World Example: A SQLite Database Manager

To prove the power of this integration, I built a fully functional Database CRUD (Create, Read, Update, Delete) application entirely in jdBasic.

It uses the sqlitefunc module for backend logic and the new GUI commands for the frontend.

Here is a snippet of the main loop:

' ... setup code ...

DO
    CLS
    WindowOpen = GUI.BEGIN("User Manager", 50, 50, 800, 600, WindowOpen, GUI.FLAG("MENUBAR"))
    
    ' Render the list of users
    GUI.BEGIN_CHILD("ScrollingRegion", 0, 300, TRUE)
        FOR i = 0 TO LEN(UserList) - 1
            ' Create a unique ID scope for buttons in this loop
            GUI.PUSH_ID(i) 
            
            ' Draw the row
            line$ = FORMAT$("{:3} | {:20} | {:5}", id, name$, score)
            IF GUI.SELECTABLE(line$, SelectedID = id) THEN SelectedID = id
            
            GUI.SAME_LINE
            IF GUI.BUTTON("Edit") THEN OpenEditPopup = TRUE
            
            GUI.POP_ID()
        NEXT i
    GUI.END_CHILD()

    ' ... popup handling ...
    
    GUI.END
    SCREENFLIP
LOOP

This script creates a scrollable list of users from a SQLite database, complete with selectable rows and action buttons for each entry.

Under the Hood: C++ Integration

Binding ImGui to an interpreter involves mapping the C++ types to jdBasic's BasicValue variant system.

One interesting challenge was handling ImGui::Begin. In C++, it takes a bool* p_open to handle the close button. Since jdBasic passes arguments by value, I couldn't just modify the variable.

The solution was to make GUI.BEGIN return the new state of the window:

// C++ Binding Implementation
BasicValue gui_begin(NeReLaBasic& vm, const std::vector<BasicValue>& args) {
    // ... setup args ...
    bool is_open = to_bool(args.back());
    
    // ImGui modifies this boolean if the X is clicked
    ImGui::Begin(title.c_str(), &is_open, flags);

    // We return the new state to the interpreter
    return is_open;
}

This requires a slightly different pattern in BASIC, but it keeps the logic explicit:

' Update the variable with the return value
IsWindowOpen = GUI.BEGIN("Title", ..., IsWindowOpen)

What's Next?

With these tools, jdBasic is now capable of building its own IDE features inside itself! I plan to use these features to build a visual debugger and a tilemap editor for the game engine component.

If you are interested in interpreter development or just love BASIC, check out the project on GitHub!


Thanks for reading! Let me know in the comments if you prefer Immediate Mode or Retained Mode GUIs.