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:
a trigger (button / menu item) calls
file_dialog_open(cfg)with a mode, a start directory and a list of extension filters;file_dialog_draw()is called every frame, at the top level ofupdate()(outside any window, so the registry path stays a cleanFILEDLG/...). It renders the modal when open and returns this frame’sFileDialogResult;on
confirmed, the chosen path(s) are read withfile_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 inimgui_table_builtin(the sortabledata_table),imgui_widgets_builtinandimgui_containers_builtin(the leaf widgets +popup_modal), anddaslib/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_namepre-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. (createis the “New” flavor — same name-entry path;newitself 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 |
|
snapshot / |
Parent / breadcrumb |
|
|
Path bar / Go |
|
|
New Folder |
|
|
Filter dropdown |
|
|
File table |
|
sort via header click |
File row i |
|
|
Filename field |
|
|
OK / Cancel |
|
|
Overwrite confirm |
|
|
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.