Introduction to Druid

A simple explanation of what Druid is and why you want to use it (this could also be copied to the crates.io page)

Set up Druid

This tutorial assumes basic familliarity with Rust and a working setup with the basic tooling like Rustup and Cargo. This tutorial will use stable Rust (v1.39.0 at the time of writing) and the latest released version of Druid.

This tutorial will first walk you through setting up the dependencies for developing a Druid application, then it will show you how to set up a basic application, build it and run it.

Setting up Druid dependencies

In addition to including the druid library in your project

macOS

On macOS, druid requires cairo; if you use homebrew, brew install cairo should be sufficient. Removing this dependency is on the roadmap.

Linux

On Linux, druid requires gtk+3.

On Ubuntu this can be installed with

sudo apt-get install libgtk-3-dev

On Fedora

sudo dnf install gtk3-devel glib2-devel

See gtk-rs dependencies for more installation instructions.

Starting a project

Starting a project is as easy as creating an empty application with

cargo new my-application

and adding the druid dependency to your Cargo.toml

[dependencies]
druid = "0.4.0"

Get started with Druid

This chapter will walk you through setting up a simple druid application from start to finish.

Set up a Druid project

Setting up a project is a simple as creating a new Rust project;

> cargo new druid-example

And then adding druid as a dependency to Cargo.toml

[dependencies]
druid = "0.4.0"

To show a minimal window with a label replace main.rs with this;

use druid::{AppLauncher, WindowDesc, Widget};
use druid::widget::Label;
use druid::shell::Error;

fn build_ui() -> impl Widget<()> {
    Label::new("Hello world")
}

fn main() -> Result<(), Error> {
    AppLauncher::with_window(WindowDesc::new(build_ui)).launch(())?;
    Ok(())
}

In our main function we create an AppLauncher, pass it a WindowDesc that wraps build_ui function and launch it. Druid will use our build_ui function to build and rebuild our main window every time it needs to refresh. build_ui returns a tree of widgets. For now this tree consists of one simple label widget.

This is a very simple example application and it's missing some important pieces. We will add these in the coming few paragraphs.

Draw more widgets

The first thing we could do to make our example application more interesting is to draw more than one widget. Unfortunately WindowDesc::new expects a function that returns only one Widget. We also need a way to tell Druid how to lay-out our widgets. We solve both these problems by passing in a widget-tree with one single widget at the top. Widgets can have children and widgets higher up in the tree know how to lay-out their children. That way we describe a window as a widget-tree with layout containers as the branches and widgets as the leaves. Our build_ui function is then responsible for building this widget tree.

To see how this works we will divide our window in four. We'll have two rows and two columns with a single label in each of the quadrants. We can lay-out our labels using the Flex widget.


#![allow(unused_variables)]
fn main() {
fn build_ui() -> impl Widget<()> {
    Flex::row()
        .with_child(
            Flex::column()
                .with_child(Label::new("top left"), 1.0)
                .with_child(Label::new("bottom left"), 1.0), 
            1.0)
        .with_child(
            Flex::column()
                .with_child(Label::new("top right"), 1.0)
                .with_child(Label::new("bottom right"), 1.0),
            1.0)
}
}

This looks nice but the labels on the left are drawn right against the window edge, so we needs some padding. Lets say we also want to center the two bottom labels. Unlike many other UI frameworks, widgets in Druid don't have padding or alignment properties themselves. Widgets are kept as simple as possible.

Features like padding or alignment are implemented in separate widgets. To add padding you simply wrap the labels in a Padding widget. Centering widgets is done using the Align widget set to centered.


#![allow(unused_variables)]
fn main() {
fn build_ui() -> impl Widget<()> {
    Padding::new(
        10.0,
        Flex::row()
            .with_child(
                Flex::column()
                    .with_child(Label::new("top left"), 1.0)
                    .with_child(Align::centered(Label::new("bottom left")), 1.0), 
                1.0)
            .with_child(
                Flex::column()
                    .with_child(Label::new("top right"), 1.0)
                    .with_child(Align::centered(Label::new("bottom right")), 1.0),
                1.0))
}
}

Do not forget to import the new widgets;


#![allow(unused_variables)]
fn main() {
use druid::widget::{Label, Flex, Padding, Align};
}

Application state

We can display a window and draw and position widgets in it. Now it's time to find out how we can tie these widgets to the rest of our application. First lets see how we can display information from our application in the user interface. For this we need to define what our application's state looks like.

...

Handle user input

...

Putting it all together

...

Draw the user interface

This chapter will describe how to build up your user interface

Handling user input

This chapter will describe how to wire up your user interface to your Rust code

Handling state

This chapter will describe how to handle state in your application

Create custom widgets

This chapter will describe how to create a custom widget

More information

If you want more information about Druid this document contains links more tutorials, blogposts and youtube videos.

Related projects

These three projects provide the basis that Druid works on

  • Piet An abstraction for 2D graphics.
  • Kurbo A Rust library for manipulating curves
  • Skribo A Rust library for low-level text layout

Projects using Druid

Presentations

Some presentations about Druid, its background and related topics have been recorded

Blog posts

People have been blogging about Druid