Native dialogs
Access through app.dialogs (CryKit::Dialogs::Manager). Native dialogs support macOS 11+ and run synchronously on the application's UI context.
Methods
| Method | Returns | Purpose |
|---|---|---|
open_file(title, filters = [], directory = nil) | String? | Select one file; nil on cancel |
open_files(title, filters = [], directory = nil) | Array(String) | Select several files; empty on cancel |
open_directory(title, directory = nil) | String? | Select a directory; nil on cancel |
save_file(title, default_name = "", filters = [], directory = nil) | String? | Choose a save path; does not write a file |
info(title, message) | Nil | Show an information alert |
warning(title, message) | Nil | Show a warning alert |
error(title, message) | Nil | Show an error alert |
confirm(title, message) | Bool | Ask Yes/No; Escape selects No |
question(title, message, buttons, cancel:, default: nil) | Symbol | Return a custom button's action ID |
Arguments can be passed by name, as in app.dialogs.open_file(title: "Open"). confirm has no default Return action; Yes requires explicitly activating its button. Backend failures raise exceptions rather than returning cancellation.
if path = app.dialogs.open_file(title: "Open document")
content = File.read(path)
end
if app.dialogs.confirm(title: "Delete draft", message: "Delete this draft?")
delete_draft
endThese snippets assume application code running in a UI callback.
Calling context
| Call site | Supported? |
|---|---|
| Mounted view event, menu callback, or tray callback | Yes |
job.notify, stream handler, or channel handler | Yes, on the UI context |
| Window close or navigation approval callback | Yes |
| Background worker or before native startup | No |
The native backend raises CryKit::Dialogs::InvalidContextError outside the main UI thread or while the application is not running. Other platforms raise CryKit::Dialogs::UnsupportedError. Dialogs wait for the user's response; mounted event callbacks have no dialog response deadline.
Pass your application to views that need dialogs:
class FolderPicker
include CryKit::View
reactive path : String = "No directory selected"
def initialize(@app : CryKit::Application)
end
private def choose_directory : Nil
if path = @app.dialogs.open_directory(title: "Choose a directory")
reactive.path = path
end
end
template "folder_picker"
endSave folder_picker.ecr beside the Crystal file and mount with mount FolderPicker.new(application):
<button on_click=<%~ -> { choose_directory } %>>Choose directory</button>
<p><%= reactive.path %></p>Questions with custom buttons
action = app.dialogs.question(
title: "Unsaved changes",
message: "Save this document before closing?",
buttons: [
CryKit::Dialogs::Button.new(:save, "Save"),
CryKit::Dialogs::Button.new(:discard, "Don't Save"),
CryKit::Dialogs::Button.new(:cancel, "Cancel"),
],
default: :save,
cancel: :cancel)| Input | Rule |
|---|---|
buttons | At least one CryKit::Dialogs::Button; unique, nonempty Symbol IDs and nonblank labels |
cancel: | Required button ID; selected by Escape or native dismissal |
default: | Optional button ID selected by Return; must differ from cancel: |
| Title, message, labels | Valid UTF-8 without null bytes |
| Result | A supplied button ID, independent of its label |
Invalid requests raise ArgumentError. Omit default: when a decision should require an explicit choice. Button labels add no implicit keyboard shortcuts.
Injected backend contract
Implement question(request : CryKit::Dialogs::Question) : Symbol and return one of the request's IDs. Backends without question support inherit an UnsupportedError fallback. A response outside the supplied IDs raises.
Starting directory
All file pickers accept directory:. Use an absolute path for a specific initial location:
path = app.dialogs.save_file(
title: "Export report",
default_name: "report.csv",
filters: [{name: "CSV files", extensions: ["csv"]}],
directory: File.dirname(current_document_path))Omitted, nil, or empty directory: leaves the location to macOS, including its location restoration. CryKit does not check whether that directory exists or is accessible; macOS handles unavailable locations.
File filters
| Input | Behavior |
|---|---|
{name: "Images", extensions: ["png", "jpg"]} | A CryKit::Dialogs::FileFilter |
| Multiple groups | macOS allows the union of their extensions; group labels are not displayed |
| Empty groups or blank entries | Ignored; surrounding spaces are trimmed |
| No extensions | Unrestricted selection |
| Invalid extension | Raises before opening the panel |
Extensions omit the leading dot and cannot contain path separators, wildcards, delimiters, internal whitespace, or control characters. macOS uses allowedContentTypes, including dynamic types for custom extensions.
Usage guidance
- Treat cancellation as a normal result and leave application state unchanged.
- Validate selected file contents and handle read/write failures separately.
- Select paths on the UI context, then use background jobs for substantial import or export work.