The Watch App we see in the video was created with the [druid] Crate in Rust.

In [druid], UI controls are known as Widgets. Our app contains two Widgets: A Label Widget that displays the counter, and a Button Widget that increments the counter.

Lemme explain the code in the [druid] app…

When the app is launched, we create a new window, passing it the ui_builder function that will return a list of Widgets and their layouts. (More about ui_builder later.)

Here we set the Application State to 0 . What’s this Application State?

Remember the counter value that’s incremented every time we tap the button? This counter value is stored in the [druid] Application State. Widgets will interact with the Application State: Our Button Widget updates the state and our Label Widget displays the state.

That’s why [druid] is known as a Data-Oriented Rust UI… [druid] Widgets are bound to data values in the Application State.

Recall that main_window contains a list of Widgets and their layouts. We’ll pass this to the AppLauncher to launch the app window. The app shall be launched with data , the Application State, set to 0. If the launch fails, we’ll stop with an error launch failed .

Now let’s look at ui_builder and how it creates the Widgets…

First we create a text string that contains the counter value. How shall we get the value of the text string? Through this Rust Closure…

What’s a Rust Closure? Think of it as a Rust Function without a name. The Closure above is equivalent to this verbose Rust Function…

So it’s simpler to use the Closure form. The Closure simply returns the value of the counter in data (the Application State) as the value of the text string. The into part converts the returned value from number to string.

Now that we have the counter value stored in our text string, let’s display it with a Label Widget.

Here we create a button labelled increment that calls this Rust Closure whenever the button is tapped…

This Closure is equivalent to the Rust Function…

data contains our Application State, which is our counter value. The Closure simply increments our counter value by 1, every time the button is tapped.

Hey Presto! That’s the magic behind our Data-Oriented UI… The button increments the counter in our Application State, the label displays the value of the counter in our Application State!

[druid] completes the magic act by wiring up the Widgets to the Application State… When our Application State changes (upon tapping the button), [druid] automagically refreshes our Label Widget to show the new counter value.

Let’s tell [druid] how to layout our Label and Button Widgets for display…

We’ll display the Label and Button Widgets in a single column col . The Label Widget goes on top, centered horizontally, with a padding of 5 pixels.

The Button Widget appears next in col . Also with a padding of 5 pixels.

Note that the second argument to add_child is the same for both the label and the button: 1.0 . This means that the label and button shall occupy equal vertical spacing, i.e. the label shall fill the top half of the screen, the button shall fill the bottom half. This is similar to the flexbox concept in CSS.

And lastly, we return the col to the caller. To recap: col contains the Label and Button Widgets, the Closures for displaying and incrementing the Application State counter, and the layout of the Widgets.

That’s the clever simplicity of [druid] apps. [druid] apps have a Declarative UI like SwiftUI and Flutter… Without the legacy baggage of iOS and Android. That’s why I think [druid] is perfect for building Watch Apps for PineTime!

Here’s the complete [druid] Watch App for PineTime…