Browsing your mail with Rust and Qt

2018-09-16

Let’s write a mail viewer with Rust and Qt. This is another blog about Rust Qt Binding Generator, the project that lets you add a Qt GUI to your Rust code, or if you will, add Rust to your Qt program.

Rust Qt Binding Generator (Logo by Alessandro Longo)

Previous blogs about Rust Qt Binding Generator covered the initial announcement, building a simple clock, and making a todo list. Now, I’ll show a bit of how to make a more complex application: an email reader.

But first: Rust Qt Binding Generator takes an unusual approach to bindings. It is not a complete binding from the Qt API to Rust because I think that that is impossible: the C++ Qt API does not have all the safety guarantees that Rust has. So the binding API would be full of unsafe negating a big advantage of using Rust.

Instead, Rust Qt Binding Generator generates a binding for just your code to make your Rust code available to a Qt GUI.

In the first tutorial, we showed a clock. The model shared between Rust and Qt had three simple properties: hour, minute and second. The second blog was about a todo application. There, the model was a list of todo items shared between Rust and Qt.

Getting the code

This time, we move on to a more complex object: a tree.

A mail viewer written with Rust and Qt
A mail viewer written with Rust and Qt

We’re keeping with the theme of personal information management and are writing an email viewer. It can read mail from MailDir folders and IMAP servers. It is completely readonly and will not even change the state of your messages from unread to read. I feel comfortable using it on my own mails alongside my real mail programs.

The code is available in my personal KDE space. It requires Rust, Cargo, Qt, CMake, OpenSSL, and ninja (or make). You can retrieve and compile it with

mailparse, imap-proto and imap.

The shared data model

In an email application there are usually two prominent trees: one shows the email folders and the other shows the messages in the selected folder.

First we model the list of folders. Here is the JSON object from bindings.json that does this.

Bindings.cpp and Bindings.h) defines an implementation of QAbstractItemModel. This is the same base class as in the todo example. This time, it holds a tree instead of a list.

There is also Rust code generated. The file interface.rs is the binding to the Qt code. It defines a trait MailFoldersTrait that the developer needs to implement in a struct called MailFolders.

We’ll discuss some parts of the Rust implementation file.

The implementation should be backed by a structure. There are two structures: MailFolder which represents a node in the tree and MailFolders which contains all the nodes in a Vec and interfaces for communicating with other parts of the program.

channel.

When new data is available, the UI needs to update. This must be done by the UI thread. When the processing thread has new data it emits a signal to the UI thread. The UI thread then aquires accesses the data via a mutex that is shared between the two threads.

Communication between GUI and processing threads

QML for the folders

The Rust-implemented model is used from the QML. The connection between the TreeView and the model is made by the line model: mailmodel.folders. Each node is rendered according to the Text delegate. When the user selects a different folder the model is notified of this by handling the onCurrentIndexChanged event.

Post a comment