8.19.33. file_dialog

A native, reusable New / Open / Save / Pick-Folder file dialog, built entirely from the boost DSL and living in imgui/imgui_file_dialog. Because it is composed from boost widgets (popup_modal / data_table / selectable_label / input_text / combo / button) rather than a bound C++ file-dialog library, every element registers on the snapshot registry — the whole dialog is imgui_playwright-drivable (a bound C++ dialog renders raw ImGui and would be invisible to it).

It mirrors the core UX of a desktop file picker: a clickable directory breadcrumb plus .. and an editable path bar (type a path or a different drive, press Enter), a New Folder button, a sortable Name / Size / Type / Modified table, a multi-extension filter dropdown, multi-select in open mode, a filename field (with an optional pre-filled default) in save / create mode, an overwrite-confirm step, a directory-pick mode, and keyboard shortcuts (Enter confirms, Backspace goes up a directory).

Source: modules/dasImgui/examples/tutorial/file_dialog.das.

The recording opens the dialog in Save mode: the name field is pre-filled from default_name, the breadcrumb and editable path bar sit above a sortable Name / Size / Type / Modified table, the Images filter matches several extensions at once, and New Folder makes a directory in place.

8.19.33.1. Walkthrough

The dialog is immediate-mode — there are no stored callbacks (daslang blocks can’t be stored across frames). The flow is three calls:

  1. a trigger (button / menu item) calls file_dialog_open(cfg) with a mode, a start directory and a list of extension filters;

  2. file_dialog_draw() is called every frame, at the top level of update() (outside any window, so the registry path stays a clean FILEDLG/...). It renders the modal when open and returns this frame’s FileDialogResult;

  3. on confirmed, the chosen path(s) are read with file_dialog_selection() / file_dialog_selected_path().

  1options gen2
  2options _comment_hygiene = true
  3options gc
  4
  5require imgui/imgui_harness
  6require imgui/imgui_file_dialog
  7
  8// =============================================================================
  9// TUTORIAL: file_dialog — native New / Open / Save dialog (boost DSL).
 10//
 11// `imgui/imgui_file_dialog` is a reusable, fully `imgui_playwright`-drivable
 12// file dialog built entirely from boost widgets (popup_modal + data_table +
 13// selectable_label + input_text + combo + buttons) — no raw-ImGui C++ library,
 14// so every element shows up in the snapshot registry.
 15//
 16// Immediate-mode usage:
 17//   1. a trigger (button / menu) calls `file_dialog_open(cfg)` with a mode,
 18//      a start directory, and a list of extension filters;
 19//   2. `file_dialog_draw()` runs EVERY frame at the top level of update()
 20//      (outside any window, so the registry path stays a clean `FILEDLG/...`)
 21//      and returns this frame's `FileDialogResult`;
 22//   3. on `confirmed`, read `file_dialog_selection()` / `file_dialog_selected_path()`.
 23//
 24// open mode multi-selects (Ctrl / Shift, `max_selection`); save / create modes
 25// take a typed name and confirm-overwrite if it already exists.
 26//
 27// STANDALONE: daslang.exe modules/dasImgui/examples/tutorial/file_dialog.das
 28// HEADLESS:   daslang.exe modules/dasImgui/examples/tutorial/file_dialog.das -- --headless --headless-frames=60
 29// LIVE:       daslang-live modules/dasImgui/examples/tutorial/file_dialog.das
 30// =============================================================================
 31
 32var private g_status = "(nothing selected yet)"
 33
 34// Two equivalent ways to build the filter list. The typed array is explicit;
 35// `file_filters` parses the terse C#-style "caption|ext;ext|caption|ext|..."
 36// pipe spec (';'-separated extensions = a multi-extension filter, "" = all).
 37def private das_filters() : array<FileDialogFilter> {
 38    return <- file_filters("daslang (*.das)|das|Images (*.png *.jpg)|png;jpg;jpeg|All files|")
 39}
 40
 41[export]
 42def init() {
 43    harness_init("file_dialog - native New / Open / Save", 760, 560)
 44}
 45
 46[export]
 47def update() {
 48    if (!harness_begin_frame()) {
 49        return
 50    }
 51    harness_new_frame()
 52
 53    SetNextWindowSize(ImVec2(520.0f, 200.0f), ImGuiCond.FirstUseEver)
 54    window(FDLG_WIN, (text = "file_dialog demo", closable = false, flags = ImGuiWindowFlags.None)) {
 55        text(LEAD, (text = "Open the native file dialog in each mode:"))
 56        if (button(BTN_OPEN, (text = "Open..."))) {
 57            file_dialog_open(FileDialogConfig(title = "Open File", start_dir = "",
 58                filters <- das_filters(), mode = FileDialogMode.open, max_selection = 0))
 59        }
 60        same_line()
 61        if (button(BTN_SAVE, (text = "Save As..."))) {
 62            // default_name pre-fills the filename field
 63            file_dialog_open(FileDialogConfig(title = "Save File As", start_dir = "",
 64                filters <- das_filters(), mode = FileDialogMode.save, max_selection = 1,
 65                default_name = "untitled.das"))
 66        }
 67        same_line()
 68        if (button(BTN_CREATE, (text = "New..."))) {
 69            file_dialog_open(FileDialogConfig(title = "New File", start_dir = "",
 70                filters <- das_filters(), mode = FileDialogMode.create, max_selection = 1))
 71        }
 72        same_line()
 73        if (button(BTN_PICKDIR, (text = "Pick Folder..."))) {
 74            file_dialog_open(FileDialogConfig(title = "Select Folder", start_dir = "",
 75                mode = FileDialogMode.pick_dir))
 76        }
 77        separator(SEP)
 78        text(RESULT, (text = g_status))
 79    }
 80
 81    // The dialog renders at the top level so its registry path is `FILEDLG/...`
 82    // independent of any window. Poll the result every frame.
 83    let r = file_dialog_draw()
 84    if (r == FileDialogResult.confirmed) {
 85        var sel <- file_dialog_selection()
 86        g_status = "picked {length(sel)} item(s): {file_dialog_selected_path()}"
 87        delete sel
 88    } elif (r == FileDialogResult.cancelled) {
 89        g_status = "cancelled"
 90    }
 91
 92    harness_end_frame()
 93}
 94
 95[export]
 96def shutdown() {
 97    harness_shutdown()
 98}
 99
100[export]
101def main() {
102    init()
103    while (!exit_requested()) {
104        update()
105        harness_maybe_collect_gc()
106    }
107    shutdown()
108}

8.19.33.1.1. Requires

One module on top of the harness:

  • imgui/imgui_file_dialog — the dialog component. It pulls in imgui_table_builtin (the sortable data_table), imgui_widgets_builtin and imgui_containers_builtin (the leaf widgets + popup_modal), and daslib/fio (directory listing) transitively.

8.19.33.1.2. Public API

enum FileDialogResult { none; confirmed; cancelled }
enum FileDialogMode   { open; save; create; pick_dir }   // 'new' is a reserved keyword

struct FileDialogFilter { caption : string; ext : string }  // ext: ';'-separated ("png;jpg"); "" = all
struct FileDialogConfig {
    title : string = "Select File"
    start_dir : string = ""                   // "" => current working directory
    filters : array<FileDialogFilter>         // empty => all files
    mode : FileDialogMode = FileDialogMode.open
    max_selection : int = 1                   // open mode; 0 = unlimited
    default_name : string = ""                // save/create: pre-filled filename
}

def public file_dialog_open(cfg : FileDialogConfig) : void
def public file_dialog_draw() : FileDialogResult
def public file_dialog_selection() : array<string>   // confirmed absolute path(s)
def public file_dialog_selected_path() : string      // first selection (convenience)
def public file_dialog_current_dir() : string
def public file_dialog_is_open() : bool
def public file_dialog_last_dir() : string                 // last confirmed folder this session
def public file_dialog_set_last_dir(dir : string) : void   // seed it (persist across runs)
// convenience: parse a C#-style "caption|ext;ext|caption|ext|..." pipe spec
def public file_filters(spec : string) : array<FileDialogFilter>

The result is polled, not pushed: file_dialog_draw() returns confirmed exactly on the frame the user commits, cancelled on the frame they close / press Escape, and none otherwise. The accessors stay valid from the confirmed frame until the dialog is reopened.

A blank start_dir opens the dialog at the last folder a selection was confirmed in this session, then the working directory. file_dialog_set_last_dir seeds that folder (e.g. from saved app config on startup) and file_dialog_last_dir reads it back to persist across runs; an explicit start_dir always wins.

8.19.33.1.3. Modes

  • open — pick one or more existing files. Selection follows the usual modifiers: plain click selects, Ctrl+click toggles, Shift+click selects a range, clamped to max_selection (0 = unlimited). Double-clicking a file confirms it; double-clicking a directory enters it.

  • save / create — type a name in the filename field (clicking a row fills it; default_name pre-fills it on open). The active filter’s first extension is appended if missing. If the name already exists, an overwrite-confirm sub-modal (FILEDLG/OVERWRITE) gates the commit. (create is the “New” flavor — same name-entry path; new itself is a reserved keyword, hence the enum name.)

  • pick_dir — choose a directory. The list shows folders only, the filter and name field are hidden, and Select Folder confirms the highlighted folder (or, with nothing selected, the directory you are browsing).

A filter’s ext is a ;-separated pattern list, so "png;jpg;jpeg" is one “Images” filter matching all three. file_filters(...) builds the typed list from the terse C# pipe form: "daslang|das|Images|png;jpg|All files|".

Keyboard: Enter / Keypad-Enter confirms (or, when the path bar has focus, navigates to it); Backspace goes up a directory when no text field is being edited; Escape cancels.

8.19.33.1.5. Drivability

Every interactive element is a registry-backed boost widget, so a driver can operate the dialog by id:

Element

Path

Verb

Modal

FILEDLG

snapshot / imgui_open

Parent / breadcrumb

FILEDLG/UP · FILEDLG/CRUMB[i]

imgui_click

Path bar / Go

FILEDLG/PATH_FIELD · FILEDLG/GO

imgui_force_set {"value": "path"} then click GO

New Folder

FILEDLG/NEWFOLDERFILEDLG/NEWDIR/NEWDIR_NAME · .../NEWDIR_OK · .../NEWDIR_CANCEL

imgui_click / imgui_force_set

Filter dropdown

FILEDLG/FILTER_COMBO

imgui_force_set {"value": N}

File table

FILEDLG/FILE_TABLE

sort via header click

File row i

FILEDLG/FILE_TABLE/ROW[i]

imgui_click (select) · double_click (enter dir / open file)

Filename field

FILEDLG/NAME_FIELD

imgui_force_set {"value": "name"}

OK / Cancel

FILEDLG/BTN_OK · FILEDLG/BTN_CANCEL

imgui_click

Overwrite confirm

FILEDLG/OVERWRITE/OW_YES · .../OW_NO

imgui_click

8.19.33.1.6. Standalone vs live

Same convention as the other tutorials. daslang.exe runs the demo once and exits at exit_requested(); daslang-live keeps the window open and reloads on source edits.

See also

Full source: modules/dasImgui/examples/tutorial/file_dialog.das

Integration test: modules/dasImgui/tests/test_file_dialog.das — drives the dialog through imgui_playwright (open → cancel, save → type → confirm).

data_table — the sortable table the file list is built on. popup_modal — the modal container.