File API
Declared by <huxerui/file.h>.
Results and metadata
Section titled “Results and metadata”FileType:File,Directory,Other.FileInfo: type, byte size, optional modification time.IoErrorCode:NotFound,PermissionDenied,NotDirectory,IsDirectory,TooLarge,InvalidEncoding,Unsupported,Io,AlreadyExists,Timeout,NoSpace.IoError: code and message.IoResult<T>: construct from value/error;Succeeded, ref-qualifiedValue, andErroraccessors.
Construct from UTF-8 path/string view or parent plus child. Path queries: Path, Name, Stem, Extension, ParentPath, Parent, Child, and Resolve. Status queries: Exists, IsFile, IsDirectory, synchronous/async Stat.
I/O methods have synchronous and Async forms: read bytes/string; write and append bytes/string; list children; create one/all directories; delete or recursively delete; copy; and move. Copy/move accept overwrite = false. Async methods return Task.
Incremental file access
Section titled “Incremental file access”File::OpenRead() returns IoResult<InputStream>; OpenReadAsync() returns Task<IoResult<AsyncInputStream>>. Writers use OpenWrite(mode) or OpenWriteAsync(mode), with FileWriteMode::Truncate by default or Append. Opening a writer can create the file but not missing parent directories.
FileReference offers async read/write opening through its provider. Opening a writer replaces existing contents and requires writable ordinary-file access; it does not turn a read-only picker grant into a writable grant. Await CloseAsync() to finish output, especially for browser persistence and provider-backed staging. See Streams for EOF, operation ordering, and partial failures.
FileReference
Section titled “FileReference”Capability for a selected or activated file. Metadata: Name, optional Size, optional ContentType, CanWrite. Async operations: ReadBytesAsync, ReadStringAsync, ImportToAsync, and ReplaceWithAsync.
FilePicker
Section titled “FilePicker”FilePickerFilter contains name, extensions, and content types. SaveFileOptions contains suggested name and filter. Capability queries are CanOpenFiles and CanSaveFiles; operations are OpenFileAsync, OpenFilesAsync, and SaveFileAsync.
Application directories
Section titled “Application directories”UseApplication().Directories() returns AppDirectories: an optional executable directory and data, cache, and temporary directories. UseApplication().CurrentDirectory() queries the process working directory at the time of the call. These paths do not create entries or grant access. Capture the ApplicationHandle during composition; its directory accessors can also run outside composition. Obtain the picker separately with UseService<FilePicker>().
Directory values are captured at initialization and can outlive the Runtime; the returned reference requires the handle or service to stay alive. Copy a File when retaining one path. Missing directory support throws std::logic_error; failure to query the working directory throws std::runtime_error.
Complete picker example
Section titled “Complete picker example”#include <huxerui/huxerui.h>
using namespace huxerui;
Task<void> OpenText(std::shared_ptr<FilePicker> picker, State<std::string> content) { auto selected = co_await picker->OpenFileAsync({ .name = "Text", .extensions = {"txt"}, .content_types = {"text/plain"}, }); if (!selected) { co_return; } auto text = co_await selected->ReadStringAsync(); if (text.Succeeded()) { content = std::move(text).Value(); }}
[[huxerui::composable]]View OpenTextButton(State<std::string> content) { auto picker = UseService<FilePicker>(); auto tasks = UseTaskScope(); return Button("Open text file").OnClick([picker, tasks, content] { tasks.Launch(OpenText(picker, content)); });}Resolve services and task scope during composition before capturing them in an event.
An empty optional means no file was delivered: cancellation, denial, failure, and an unsupported host are not distinguished by this result. A returned FileReference preserves provider authority without inventing a local path.
Directories and access-bearing paths
Section titled “Directories and access-bearing paths”FilePicker::CanOpenDirectories(writable = false) checks the requested kind of directory access; OpenDirectoryAsync(writable = false) returns an optional FileReference. User cancellation is empty, not a directory with an empty path.
FileReference::Type() describes the captured entry kind. For a directory, ListChildrenAsync(), CreateDirectoryAsync(name), and CopyFileFromAsync(source, name, overwrite = false) operate through its retained provider grant. CopyDirectoryContentsToAsync(destination, overwrite = false) accepts a File or FileReference destination and returns IoResult<DirectoryCopySummary>. This is not a transaction: failure/cancellation can leave already copied output; success counts are meaningful only after successful completion.
AsFile() returns an optional local File when the provider has a known path. It neither imports data nor transfers the reference’s grant. Keep the FileReference alive while using a path that depends on its access/temporary-file ownership. Android document URIs and browser handles do not become local paths. File::ToUri() converts a local path to a URI; it does not manufacture provider authority.
Treat display names, captured MIME metadata, and sizes as potentially stale/untrusted. A writable directory grant does not promise every existing child can be overwritten.
External file drops
Section titled “External file drops”Declared by <huxerui/file_drop.h>. FileDropTarget::Accepts(FileDropOptions[, predicate]) receives ordinary files from the host drag system with Copy effect only.
#include <huxerui/huxerui.h>
#include <string>#include <vector>
using namespace huxerui;
View FileDropArea(State<std::string> status) { return Text("Drop text files here") .With(Padding(24.0F), FileDropTarget::Accepts({.extensions = {"txt"}})) .On<FileDropEvents::Dropped>([status](const std::vector<FileReference>& files, const FileDropEvent&) { status = "Received " + std::to_string(files.size()) + " files"; }) .On<FileDropEvents::Failed>([status](const IoError& error, const FileDropEvent&) { status = error.message; });}extensions and content_types are alternative filters; every file must pass before delivery. allows_multiple = false rejects a multiple-file batch in full. Metadata predicates must be quick and side-effect-free; unknown hover metadata is not proof of final acceptance.
Entered/Moved/Exited report hover; Exited also occurs at physical drop and does not imply cancellation. Deferred Dropped delivers a complete batch after validation; Failed reports preparation failure without partial delivery. Handlers run on the UI thread and do not run after unmount. Copy FileReference values if retaining them beyond a handler.
A drop does not read/import the content or grant write permission. Always provide an equivalent FilePicker button for touch, keyboard, accessibility, and unsupported hosts. Use DragSource/DropTarget for typed in-process transfers.
Path operations are not access grants
Section titled “Path operations are not access grants”File stores an absolute, lexically normalized path; Path() uses UTF-8 with forward slashes. Construction, Child, Resolve, Parent, and ToUri do not create/open an entry or grant permission. Child(name) accepts one nonempty segment, rejecting . and ..; Resolve(relative_path) accepts multiple segments and can escape the base with ... Neither is a symbolic-link containment check for untrusted input.
Exists, IsFile, and IsDirectory collapse query failures to false. Use Stat/StatAsync when an error category matters. Metadata is best-effort and can change before the next operation; an existence check is not protection from a race.
Read, write, and export contracts
Section titled “Read, write, and export contracts”ReadBytes/ReadString return IoResult<T>; an empty file is successful empty data. Text reads validate UTF-8, strip one leading UTF-8 BOM, and preserve newlines. Binary data, UTF-16, or a legacy encoding should not be fed to ReadString and treated as an empty document on error.
Write/append operations return bool, not IoResult. A write creates or truncates the target, requires an existing parent, and is not atomic. Failure/cancellation may leave partial output. Async inputs are owned by the Task; synchronous spans/views are borrowed only for the call. Prefer asynchronous operations from UI work, and use them for Web persistent paths where persistence requires the async path.
FilePicker::SaveFileAsync(source, options) exports an existing local file; it does not return a writable destination path. Prepare the source first, then show the picker. The source remains in place. A false result is not enough to claim the user explicitly canceled.
#include <huxerui/huxerui.h>
#include <memory>#include <string>
using namespace huxerui;
Task<void> ExportText(std::shared_ptr<FilePicker> picker, File prepared, std::string text, State<std::string> status) { if (!picker->CanSaveFiles()) { status = "Export is unavailable on this host"; co_return; } if (!co_await prepared.WriteStringAsync(text)) { status = "Could not prepare the export"; co_return; } const bool exported = co_await picker->SaveFileAsync(prepared, { .suggested_name = "notes.txt", .filter = {.name = "Text", .extensions = {"txt"}, .content_types = {"text/plain"}}, }); status = exported ? "Exported" : "No export completed";}The caller supplies an application-owned temporary File with an existing parent and launches this task through its TaskScope. Give concurrent exports different temporary paths and clean them up through their owning storage policy. Do not delete the prepared file before the picker finishes or assume a canceled write rolled back.
ImportToAsync copies a provider file into local storage; ReplaceWithAsync writes an existing writable provider reference from a local source. These are distinct from picking a new destination. Keep the FileReference alive for as long as its provider grant or temporary ownership is needed.

