
Building GUI Applications in Rust: A Guide to Using Druid
Introduction to Druid
Druid is a data-first Rust-native UI design toolkit. It is designed to be simple and efficient, allowing developers to build complex user interfaces with minimal boilerplate. Druid focuses on a declarative style of programming, making it easier to manage state and layout.
In this tutorial, we will cover:
- Setting up a Druid project
- Creating a basic window with widgets
- Managing state and handling events
Setting Up Your Environment
To get started with Druid, ensure you have Rust and Cargo installed. You can install Rust using rustup:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shOnce Rust is installed, create a new project:
cargo new druid_example
cd druid_exampleNext, add Druid as a dependency in your Cargo.toml file:
[dependencies]
druid = "0.7"Now, you can build your project and start coding your GUI application.
Creating a Basic Window
The first step in building a Druid application is to create a simple window. Here’s an example of how to set up a basic window:
use druid::{AppLauncher, Data, Lens, Widget, WidgetExt, WindowDesc, Label};
#[derive(Clone, Data, Lens)]
struct AppState {
text: String,
}
fn build_ui() -> impl Widget<AppState> {
Label::new(|data: &AppState, _env: &_| data.text.clone())
.padding(10.0)
}
fn main() {
let main_window = WindowDesc::new(build_ui())
.title("Druid Example")
.window_size((400.0, 400.0));
let initial_state = AppState {
text: "Hello, Druid!".to_string(),
};
AppLauncher::with_window(main_window)
.launch(initial_state)
.expect("Failed to launch application");
}Explanation
- AppState: This struct holds the application state. Here, it contains a single field
textfor demonstration purposes. - build_ui: This function creates the UI. In this case, it returns a
Labelwidget that displays the text fromAppState. - main: The entry point of the application where we define the main window and the initial state.
Adding Interactivity
To make the application interactive, we can add a button that changes the text displayed by the label. Here’s how to implement this feature:
use druid::{AppLauncher, Data, Lens, Widget, WidgetExt, WindowDesc, Label, Button, Flex};
#[derive(Clone, Data, Lens)]
struct AppState {
text: String,
}
fn build_ui() -> impl Widget<AppState> {
let label = Label::new(|data: &AppState, _env: &_| data.text.clone())
.padding(10.0);
let button = Button::new("Change Text").on_click(|_ctx, data: &mut AppState, _env| {
data.text = "Text Changed!".to_string();
});
Flex::column()
.with_child(label)
.with_spacer(20.0)
.with_child(button)
.padding(10.0)
}
fn main() {
let main_window = WindowDesc::new(build_ui())
.title("Druid Example with Interactivity")
.window_size((400.0, 400.0));
let initial_state = AppState {
text: "Hello, Druid!".to_string(),
};
AppLauncher::with_window(main_window)
.launch(initial_state)
.expect("Failed to launch application");
}Explanation
- Button: A button widget is created that, when clicked, changes the text in
AppState. - Flex: This layout widget arranges the label and button vertically, providing a simple and clean interface.
Managing State
Druid’s data management system allows you to easily manage and propagate state changes throughout your application. The Data trait ensures that any changes to the state are automatically reflected in the UI.
Example of State Management
You can extend the AppState struct to include more fields and functionality. Here’s an example that includes a counter:
#[derive(Clone, Data, Lens)]
struct AppState {
text: String,
counter: usize,
}
fn build_ui() -> impl Widget<AppState> {
let label = Label::new(|data: &AppState, _env: &_| format!("Count: {}", data.counter))
.padding(10.0);
let button = Button::new("Increment").on_click(|_ctx, data: &mut AppState, _env| {
data.counter += 1;
});
Flex::column()
.with_child(label)
.with_spacer(20.0)
.with_child(button)
.padding(10.0)
}
fn main() {
let main_window = WindowDesc::new(build_ui())
.title("Druid Counter Example")
.window_size((400.0, 400.0));
let initial_state = AppState {
text: "Hello, Druid!".to_string(),
counter: 0,
};
AppLauncher::with_window(main_window)
.launch(initial_state)
.expect("Failed to launch application");
}Explanation
- Counter: The
counterfield tracks the number of times the button has been clicked. - Label Update: The label dynamically updates to reflect the current value of the counter.
Conclusion
Druid provides an intuitive and powerful way to build GUI applications in Rust. With its data-first approach and straightforward widget system, developers can create responsive and interactive applications efficiently. This tutorial covered the basics of setting up a Druid project, creating a window, adding interactivity, and managing application state.
