A cross-platform, native GUI input box for Rust. Yes, finally.
Picture this: you're writing a Rust CLI tool and you just want to pop up a little dialog that says "hey, what's your name?" and take whatever the user types. Simple, right?
So you look at the ecosystem.
rfd: Rusty File Dialogs! Cross-platform! Async! Beautiful! ...opens files. Not text. Files.native-dialog: Again, no input box, just message boxes and file pickers.tinyfiledialogs:input_boxlooks promising! ...but why does my input box turn into a password input whendefaultis empty (Some(""))? No multiline, no custom labels, no control over backends... Oh and it's a C binding.dialog: Finally, an input box! ...but not for Windows or macOS. It fully depends on tools likezenity,kdialogordialog.
You stare into the void. The void stares back. You write the dialog in HTML/JS because at least Electron works on all platforms.
Not anymore.
inputbox is a minimal, cross-platform Rust library that shows a native GUI input dialog and returns what the user typed. It uses whatever is available on the system. Should work™ most of the time.
[dependencies]
inputbox = "0.1.0"use inputbox::InputBox;
fn main() {
let result = InputBox::new()
.title("Greetings")
.prompt("What's your name?")
.show()
.unwrap();
match result {
Some(name) => println!("Hello, {name}!"),
None => println!("Fine, be that way."),
}
}use inputbox::{InputBox, InputMode};
let result = InputBox::new()
.title("Login")
.prompt("Enter your password:")
.mode(InputMode::Password)
.show()
.unwrap();use inputbox::InputBox;
let result = InputBox::new()
.title("Confirm")
.prompt("Are you sure?")
.ok_label("Yes, do it")
.cancel_label("No, abort")
.width(400)
.height(200)
.show()
.unwrap();use inputbox::InputBox;
InputBox::new()
.title("Async Input")
.prompt("Enter something:")
.show_async(|result| {
println!("Result: {:?}", result);
});Note: On iOS, you must use async methods. See the documentation for details.
- Multiple input modes — text, password, or multiline
- Highly customizable — title, prompt, button labels, dimensions, and more
- Works on most platforms — Windows, macOS, Linux, Android, iOS and OpenHarmony
- Pluggable backends — use a specific backend or let the library pick
- Synchronous and asynchronous — safe sync on most platforms, async required on iOS
| Backend | Platform | How it works | Extra setup |
|---|---|---|---|
PSScript |
Windows | PowerShell + WinForms, no extra install needed | None |
JXAScript |
macOS | osascript JXA, built into the OS |
None |
Android |
Android | AAR + JNI to show an Android AlertDialog | See Android Setup |
IOS |
iOS | UIKit alert | None |
OHOS |
OpenHarmony | NAPI + ArkTS dialog | See OHOS Setup |
Yad |
Linux | yad |
Install yad |
Zenity |
Linux | zenity — fallback on GNOME systems |
Install zenity |
You need to include AAR in your Android project to use the Android backend. The AAR is bundled with the crate, but you need to configure your Gradle build to find it.
Inside of your project's settings.gradle file, add the following code and Maven repository definition.
$PATH_TO_DEPENDENT_CRATE is the relative path to the Cargo manifest (Cargo.toml) of any crate in your workspace that depends on inputbox from the location of your settings.gradle file:
import groovy.json.JsonSlurper
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
// ...Other repositories...
maven {
url = findCrateBundledProject("inputbox-android")
metadataSources.artifact()
}
}
}
String findCrateBundledProject(packageName) {
def dependencyText = providers.exec {
commandLine("cargo", "metadata", "--format-version", "1", "--filter-platform", "aarch64-linux-android", "--manifest-path", "$PATH_TO_DEPENDENT_CRATE/Cargo.toml")
}.standardOutput.asText.get()
def dependencyJson = new JsonSlurper().parseText(dependencyText)
def manifestPath = file(dependencyJson.packages.find { it.name == packageName }.manifest_path)
return new File(manifestPath.parentFile, "maven").path
}Then, wherever you declare your dependencies, add the following:
implementation "moe.mivik:inputbox:latest.release"The jni crate must be initialized before the crate can interact with the JVM. Add the following code to your native library's initialization function:
// `jni` crate v0.22 or higher
#[export_name = "Java_com_orgname_android_rust_init"]
extern "system" fn java_init(
env: EnvUnowned,
_class: JClass,
) {
let result = env.with_env(|env| -> jni::errors::Result<()> {
inputbox::backend::Android::initialize(env)?;
// ...
});
// ...
}
// Earlier versions of `jni` or other JNI bindings
#[export_name = "Java_com_orgname_android_rust_init"]
extern "system" fn java_init(
env: JNIEnv,
_class: JClass,
) {
unsafe {
inputbox::backend::Android::initialize_raw(env.as_raw() as *mut _)?;
}
// ...
}Currently you need to implement the ArkTS dialog yourself and use registerInputboxCallback to connect it to inputbox. See the OHOS example for setup details.
MIT