Dialogs return information after the user clicks a button, selects a file, or closes the window.
Expr has two common result styles:
Use synchronous results for short scripts that can wait. Use asynchronous callbacks when the dialog should stay open while the rest of the application continues.
Message dialogs return a result `map`.
result = msg_ok_cancel() .title('Confirm') .message('Start probing?') .show(); if (result.ok) { print('start probing'); } else { print('cancelled'); }
Common result fields are:
| Field | Meaning |
|---|---|
| `action` | Text action, such as `ok`, `cancel`, or `none`. |
| `action_code` | Numeric host action code. |
| `ok` | `true` when OK or Yes was selected. |
| `cancel` | `true` when Cancel, No, or Close was selected. |
| `affirmative` | `true` for affirmative choices such as OK or Yes. |
| `save` | Save flag from the host dialog result. |
| `password` | Password text for `msg_password()`. Empty for other message dialogs. |
For most scripts, `result.ok` and `result.cancel` are the clearest fields to use.
File dialogs return an `array` of selected paths, or `none()` when the user cancels.
files = file_open() .title('Open program') .add_filter('Expr files', '*.expr;*.txt') .show(); if (files.is_none()) { print('cancelled'); } else { print('selected: ', files.get(0)); }
`file_save()` uses the same result style. The returned array normally contains one path.
files = file_save() .title('Save report') .file('report.txt') .add_filter('Text files', '*.txt') .show(); if (!files.is_none()) { report_file = files.get(0); print('save to: ', report_file); }
With `file_open().multi_select(true)`, the array can contain more than one path.
files = file_open() .title('Open images') .multi_select(true) .add_filter('Images', '*.png;*.jpg;*.webp') .show(); if (!files.is_none()) { for (i = 0; i < files.length(); i += 1) { print(files.get(i)); } }
When you use `on_result(callback)`, `show()` returns the dialog object immediately. The callback receives the result later.
function ConfirmResult(result) { if (result.ok) { print('confirmed'); } else { print('cancelled'); } } msg_ok_cancel() .title('Confirm') .message('Continue?') .on_result(ConfirmResult) .show();
For file dialogs, the callback receives the selected files array or `none()`.
function FilesSelected(files) { if (files.is_none()) { print('cancelled'); return; } print('first file: ', files.get(0)); } file_open() .title('Open file') .on_result(FilesSelected) .show();
Pass the callback name without parentheses. `FilesSelected` passes the callback. `FilesSelected()` calls it immediately.
For asynchronous dialogs, a class is usually the cleanest way to keep dialog state and result handling together.
class FileChoice() { dlg = none(); selected = none(); accepted = false; function Show() { dlg = file_open() .title('Open file') .add_filter('Expr files', '*.expr;*.txt') .on_result(this.OnResult); dlg.show(); } function OnResult(files) { accepted = !files.is_none(); selected = files; if (accepted) { print('selected: ', selected.get(0)); } else { print('cancelled'); } } } choice = FileChoice(); choice.Show();
This keeps the dialog object, selected files, and accepted flag together.
`msg_password()` returns the same message-dialog result map, with the `password` field filled when OK is selected.
result = msg_password() .title('Password') .message('Enter password') .password_label('Password:') .show(); if (result.ok) { password = result.password; print('password entered'); } else { print('cancelled'); }
Avoid printing or storing passwords unless that is really needed.
Custom `window()` dialogs use result codes in `on_request()` and `on_closed()`.
| Code | Meaning |
|---|---|
| `0` | Close |
| `1` | OK |
| `2` | Apply |
Use `on_request()` to validate before the window closes. Return `false` to keep the window open for OK or Close requests.
Use `on_closed()` to store final results after the window closes.
class ToolDialog() { wnd = none(); tool_input = none(); accepted = false; tool = ''; function Show() { tool_input = textinput() .position(90, 20) .size(150, 24) .text('T1'); wnd = window() .title('Tool') .size(280, 120) .buttons(false, true, true) .on_request(this.OnRequest) .on_closed(this.OnClosed); wnd.add(label().position(20, 20).size(60, 24).text('Tool')); wnd.add(tool_input); wnd.show(); } function OnRequest(code) { if (code == 1 && tool_input.text.length() == 0) { print('Tool is required'); return false; } return true; } function OnClosed(code) { accepted = code == 1; if (accepted) { tool = tool_input.text; print('tool: ', tool); } } } dlg = ToolDialog(); dlg.Show();
This is the same result pattern as built-in dialogs: keep the callback small, validate in request callbacks, and store final values in one object.
For synchronous dialogs, assign the return value directly:
result = msg_yes_no() .title('Question') .message('Continue?') .show(); continue_job = result.ok;
For asynchronous dialogs, store the result in an object field:
class ConfirmState() { done = false; ok = false; function OnResult(result) { done = true; ok = result.ok; } } confirm = ConfirmState(); msg_ok_cancel().message('Continue?').on_result(confirm.OnResult).show();
Avoid scattering dialog state across many root variables. One object is easier to inspect, clear, and reuse.
Using file dialog results without checking for cancel:
files = file_open().show(); print(files.get(0)); // wrong if user cancels
Better:
files = file_open().show(); if (!files.is_none()) { print(files.get(0)); }
Calling a callback instead of passing it:
dlg.on_result(OnResult()); // wrong dlg.on_result(OnResult); // correct
Expecting an asynchronous result immediately:
dlg = msg_ok_cancel().on_result(OnResult).show(); // result is not available here yet
For asynchronous dialogs, put result handling inside the callback.
Use synchronous dialogs for short scripts:
result = msg_ok_cancel().message('Continue?').show(); if (result.ok) { print('continue'); }
Use asynchronous dialogs with an object for longer workflows:
class DialogState() { accepted = false; function OnResult(result) { accepted = result.ok; } } state = DialogState(); msg_ok_cancel().message('Continue?').on_result(state.OnResult).show();