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

8.18.1.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.18.1.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.18.1.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.18.1.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.18.1.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.