A simple Rust GUI with QML

2017-02-17

You may have heard of Rust by now. The new programming language that "pursuis the trifecta: safety, concurrency, and speed". You have to admit, even if you don't know what trifecta means, it sounds exciting.

I've been toying with Rust for a while and have given a presentation at QtCon comparing C++ and Rust. I've been meaning to turn that presentation into a blog post. This is not that blog post.

Here I show how you can use QML and Rust together to create graphical applications with elegant code. The example we're building is a very simple file browser. People that are familiar with Rust can ogle and admire the QML snippets. If you're a Qt and QML veteran, I'm sure you can read the Rust snippets here quite well. And if you're new to both QML and Rust, you can learn twice as much.

The example here is kept simple and poor in features intentionally. At the end, I'll give suggestions for simple improvements that you can make as an exercise. The code is available as a nice tarball and in a git repo.

Command-line Hello, world!

First we set up the project. We will need to have QML and Rust installed. If you do not have those yet, just continue reading this post and you'll be all the more motivated to go ahead and install them.

Once those two are installed, we can create a new project with Rust's package manager and build tool cargo.

[~/src]$ # Create a new project called sousa (it's a kind of dolphin ;-)
[~/src]$ cargo new --bin sousa
     Created binary (application) `sousa` project

[~/src]$ cd sousa

[~/src/sousa]$ # Add a dependency for the QML bindings version 0.0.9
[~/src/sousa]$ echo 'qml = "0.0.9"' >> Cargo.toml
[~/src/sousa]$ # Build, this will download and compile dependencies and the project.
[~/src/sousa]$ cargo build
    Updating registry `https://github.com/rust-lang/crates.io-index`
   Compiling libc v0.2.20
   Compiling qml v0.0.9
   Compiling lazy_static v0.2.2
   Compiling sousa v0.1.0 (file:///home/oever/src/sousa)
    Finished debug [unoptimized + debuginfo] target(s) in 25.39 secs

[~/src/sousa]$ # Run the program.
[~/src/sousa]$ cargo run
    Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
     Running `target/debug/sousa`
Hello, world!

The same without output:

cargo new --bin sousa
cd sousa
echo 'qml = "0.0.9"' >> Cargo.toml
cargo build
cargo run

The mix of Rust and QML lives! Of course the program is not using any QML yet. Let's fix that.

Hello, world! with QML

Now that we have a starting point we can start adding some QML. Let's change src/main.rs from a command-line Hello, world to a graphical Hello, world! application.

main.rs before

fn main() {
    println!("Hello, world!");
}

Some explanation for the people reading Rust code for the first time: things that look like functions but have a name that ends with ! are macros. Forget everything you know about C/C++ macros. Macros in Rust are elegant and powerful. We will see this below when we mock moc.

main.rs after

extern crate qml;

use qml::*;

fn main() {
    // Create a new QML Engine.
    let mut engine = QmlEngine::new();

    // Bind a message to the QML enviroment.
    let message = QVariant::from("Hello, world!");
    engine.set_property("message", &message);

    // Load some QML
    engine.load_data("
        import QtQuick 2.0
        import QtQuick.Controls 1.0

        ApplicationWindow {
            visible: true
            Text {
                anchors.fill: parent
                text: message
            }
        }
    ");
    engine.exec();
}

Modules in Rust are called "crates". This example uses QML bindings that currently have version number 0.0.9. So the API may change.

In the example above, the QML is placed literally in the code. Literal strings in Rust can span multiple lines.

Usually you do not need to specify the type of a variable, you can just type let (for immutable objects) or let mut for mutable ones. Like in C++, & is used to pass an object by reference. You have to use the & in the function definition, but also when calling the function (unless your variable is a reference already).

The QML code has an ApplicationWindow with a Text. The message, Hello, world! is passed to the QML environment as a QVariant. This is the first time in our program that information goes between Rust and QML.

Hello, world!

Hello, world!

Like above, the application can be run with cargo run.

Splitting the code

Let's make this code a bit more maintainable. The QML is moved to a separate file src/sousa.qml which we load from Rust.

import QtQuick 2.0
import QtQuick.Controls 1.0

ApplicationWindow {
    visible: true
    Text {
       anchors.fill: parent
       text: message
    }
}

You can see the adapted Rust code below. In debug mode, the file is read from the file system. In release mode, the file is embedded into the executable to make deployment easier.

extern crate qml;

use qml::*;

fn main() {
    // Create a new QML Engine.
    let mut engine = QmlEngine::new();

    // Bind a message to the QML enviroment.
    let text = QVariant::from("Hello, world!");
    engine.set_property("message", &text);

    // Load some QML
#[cfg(debug_assertions)]
    engine.load_file("src/sousa.qml");
#[cfg(not(debug_assertions))]
    engine.load_data(include_str!("sousa.qml"));
    engine.exec();
}

The statements #[cfg(debug_assertions)] and #[cfg(not(debug_assertions))] are conditional compilation for the next expression. So when you run cargo run, the QML file will be read from disk and with cargo run --release, the QML will be inside the executable. In debug mode it is convenient to avoid recompilation for changes to the QML code.

Listing the contents of a folder

Now that we've created an application that combines Rust and QML let's go a step further and list the contents of a directory instead of a simple message.

QML has a ListView that can display the contents of a ListModel. The ListModel can be filled by the Rust code. First we create a simple Rust structure that contains information about files.

Q_LISTMODEL_ITEM!{
    pub QDirModel<FileItem> {
        file_name: String,
        is_dir: bool,
    }
}

Q_LISTMODEL_ITEM! ends on a !, so it's a macro. Rust macros use pattern matching on the content of a macro. The matched values are used to generate code. The macro system is not unlike C++ templates, but with a more flexible sytax and simpler rules.

On the QML side, we'd like to show the file names. Directory names should be shown in italic.

ApplicationWindow {
    visible: true

    ListView {
        anchors.fill: parent
        model: dirModel
        delegate: Text {
            text: file_name
            font.italic: is_dir
        }
    }
}

The ListView shows data from a ListModel that we'll define later.

The delegate in the ListView is a kind of template. When an entry in the list is visible in the UI, the delegate is the UI component that shows that entry. The delegate that is shown here is very simple. It is just a Text that shows the file name.

Next, we need to connect the information on the file_system to the model. That is done in two steps.

Instead of binding a Hello, world! message to the QML environment, we create an instance of our QDirModel and bind it to the QML environment.

    // Create a model with files.
    let dir_str = ".";
    let current_dir = fs::canonicalize(dir_str)
        .expect(&format!("Could not canonicalize {}.", dir_str));
    let mut dir_model = QDirModel::new();
    list_dir(&current_dir, &mut dir_model).expect("Could not read directory.");
    engine.set_and_store_property("dirModel", &dir_model);

The model is initialized with the current directory. That directory is canonicalized. That means it is made absolute and symbolic links are resolved. This function may fail and Rust forces us to deal with that. If there is an error in fs::canonicalize(dir_str), the returned result is an error instead of a value. The function expect() takes the error and an additional message, prints it and stops the current thread or program in a controlled way. Rust is a safe programming language because of features like this where potential problems are prevented at compile-time.

The last missing piece is the function list_dir that reads entries in a directory and places them in the QDirModel.

fn list_dir(dir: &Path, model: &mut QDirModel) -> io::Result<()> {
    // get iterator over readable entries
    let entry_iter = fs::read_dir(dir)?.filter_map(|e| e.ok());

    model.clear();
    for entry in entry_iter {
        if let Ok(metadata) = entry.metadata() {
            if let Ok(file_name) = entry.file_name().into_string() {
                model.append_item(FileItem {
                    file_name: file_name,
                    is_dir: metadata.is_dir(),
                });
            }
        }
    }
    Ok(())
}

There is a lot happening in the first line of this function. An iterator is taken over the contents of a directory. If the reading of the directory fails, the function stops and returns an Err. This is coded by the ? in fs::read_dir(dir)?. When reading each entry, another error may occur. If that happens the iterator returns an Err. We choose here to skip over the erroneous reads; we filter them out with filter_map(|e| e.ok()).

Next, the entries are added to the model in a for loop. Again we see code that deals with possible errors. Reading the metadata for a file may give an error. We choose to skip entries with such errors. Only the entries for which Ok is returned are handled.

The UI should display the file name. Rust uses UTF-8 internally and the file name can be be nearly any sequence of bytes. If the entry is not a valid UTF-8 string, we ignore that entry here. Another option would be to keep the byte array (Vec<u8>) and use a lossy representation of the file name in the user interface that leaves out the parts that cannot be represented in UTF-8.

In other programming languages, it'd be easier to handle these cases sloppily. In Rust we have to be explicit. This explicit code is safer and more understandable for the next programmer reading it.

And here is the result of cargo run. A directory listing with two files and two folders.

a listing of files

a listing of files

A simple file browser

Listing only one fixed directory is no fun. We want to navigate to other directories by clicking on them. We'd like to have an object that can receive the name of a folder that it should enter and update the directory listing.

To achieve that we need a staple from the Qt stable: QObject. A QObject can send signals and receive signals. Signals are received in slots. When programming in C++, a special step is needed during compilation: the program moc generates code from the C++ headers.

Thanks to macroergonomics, Rust has more powerful macros and can skip this extra step. The syntax to define a QObject is simple in Rust and C++. This is our QDirLister:

pub struct DirLister {
    current_dir: PathBuf,
    model: QDirModel,
}
Q_OBJECT!{
    pub DirLister as QDirLister {
        signals:
        slots:
            fn change_directory(dir_name: String);
        properties:
    }
}

The macro Q_OBJECT takes the struct DirLister and wraps it in another struct QDirLister that has signals, slots and properties.

Our simple QDirLister defines only one slot, change_directory, that will receive signals from the QML code when a directory name is clicked. Here is the implementation:

impl QDirLister {
    fn change_directory(&mut self, dir_name: String) -> Option<&QVariant> {
        let new_dir = if dir_name == ".." {
            // go to parent if there is a parent
            self.current_dir.parent().unwrap_or(&self.current_dir).to_owned()
        } else {
            self.current_dir.join(dir_name)
        }; 
        if let Err(err) = list_dir(&new_dir, &mut self.model) {
            println!("Error listing {}: {}",
                     self.current_dir.to_string_lossy(),
                     err));
            return None;
        }
        // listing the directory succeeded so update the current_dir
        self.current_dir = new_dir;
        None
    }
}

If the directory is .., we move up one directory with parent(). Again we have to explicitly handle the case that there is no parent directory. We choose to stay on the same directory in that case.

If the directory is not .., we join() the directory name to the current_dir. We update the model with a new directory listing and print an error and stay on the current directory if that fails.

QDirLister has to be hooked up to the QML code. We add this snippet to the fn main() that we defined earlier.

    // Create a DirLister and pass it to QML
    let dir_lister = DirLister {
        model: dir_model,
        current_dir: current_dir.into(),
    };
    let q_dir_lister = QDirLister::new(dir_lister);
    engine.set_and_store_property("dirLister", q_dir_lister.get_qobj());

And this is how we use it from QML:

import QtQuick 2.0
import QtQuick.Controls 1.0

ApplicationWindow {
    visible: true

    ListView {
        anchors.fill: parent
        model: dirModel
        delegate: Text {
            text: file_name
            font.italic: is_dir

            MouseArea {
                anchors.fill: parent
                cursorShape: is_dir ? Qt.PointingHandCursor : Qt.ArrowCursor
                onClicked: {
                    if (is_dir) {
                        dirLister.change_directory(file_name);
                    }
                }
            }
        }
    }
}

To receive mouse input in QML, there needs to be a MouseArea. When it is clicked (onClicked), it calls a bit of Javascript that sends the file_name to the dirLister via the slot change_directory.

our file browser

our file browser

Conclusion

Hooking up QML and Rust is elegant. We've created a simple file browser with one QML file, sousa.qml, one Rust file, main.rs and one package/build file Cargo.toml.

There are many nice QML user interfaces out there that can be repurposed on top of Rust code. QML can be visually edited with QtCreator. QML can be used for mobile and desktop applications. It's very nice that this wonderful method of creating user interfaces can be used with Rust.

To the C++ programmers: I hope you enjoyed the Rust code and find some inspiration from it. Because Rust is a new language it can introduce innovative features that cannot be easily added to C++. Rust and C++ can be mixed in one codebase as is done in Firefox.

Rust has many more wonderful features than can be covered in this blog. You can read more in the Rust book.

Assignments

I promised some assignments. Here they are.

  1. Show an error dialog when a directory cannot be shown. (Hint: the code is already in the git repo and shows a QML feature that we did not use yet: signals.

  2. Show the file size in the file listing.

  3. Do not make directories clickable if the user has no permission to open them.

  4. Open simple files like pictures and text files when clicked by showing them in a separate pane.

Comments

Post a comment

Can't get this to build!

Can't get this to build on Windows 10 right now after installing Qt 5.8. Submitted an issue here:

https://gitlab.com/vandenoever/sousa/issues/1

Reply to this comment

A simple Rust GUI with QML

I've commented on that issue here:

https://github.com/White-Oak/qml-rust/issues/31

Success!

Reply to this comment

A simple Rust GUI with QML

Great write up!

Reply to this comment

Advanced Rust GUI with QML?

Thanks for the nice intro to this crate. I played around with the code and finally wanted to try to create a somehow more advanced project.

The idea is to provide a small GUI for an scientific image manipulation software written in Rust. I have coded functions to open and read the image into a buffer, which now I'm trying to display in QML. But here I'm stuck, since I don't know how to get the buffer content to be displayed in a QML Image {}. According to Qt docs I have to provide a QQuickImageProvider http://doc.qt.io/qt-5/qquickimageprovider.html.

Does somebody know if it is possible to achieve this directly in Rust using the qml crate somehow or do I need to add some C++ FFI code?

Reply to this comment

A simple Rust GUI with QML

It's appropriate time to make a few plans for the future and it's time to be happy. I've learn this post and if I may just I wish to recommend you some attention-grabbing things or tips. Maybe you can write next articles regarding this article. I want to read even more things about it! cheap jerseys

Reply to this comment

A simple Rust GUI with QML

Keep on working, great job!wholesale basketball jerseys from China

Reply to this comment

A simple Rust GUI with QML

If you are going for best contents like I do, only pay a visit this website every day because it offers quality contents, thankswholesale nba jerseys China

Reply to this comment

A simple Rust GUI with QML

Great article.cheap hockey jerseys

Reply to this comment

A simple Rust GUI with QML

Piece of writing writing is also a fun, if you be acquainted with then you can write if not it is complicated to write.wholesale nhl jerseys

Reply to this comment

A simple Rust GUI with QML

This piece of writing offers clear idea in favor of the new users of blogging, that genuinely how to do blogging.1534

Reply to this comment

A simple Rust GUI with QML

Yesterday, while I was at work, my sister stole my iPad and tested to see if it can survive a twenty five foot drop, just so she can be a youtube sensation. My apple ipad is now destroyed and she has 83 views. I know this is completely off topic but I had to share it with someone!wholesale baseball jerseys

Reply to this comment

A simple Rust GUI with QML

These are really great ideas in on the topic of blogging. You have touched some good factors here. Any way keep up wrinting.cheap nhl jerseys China

Reply to this comment

A simple Rust GUI with QML

Hello, all the time i used to check blog posts here early in the break of day, since i love to learn more and more.

cheap NHL jerseys

Reply to this comment

A simple Rust GUI with QML

I could not refrain from commenting. Perfectly written!cheap nhl jerseys

Reply to this comment

A simple Rust GUI with QML

Hey I am so happy I found your site, I really found you by accident, while I was researching on Google for something else, Nonetheless I am here now and would just like to say thanks a lot for a fantastic post and a all round thrilling blog (I also love the theme/design), I don’t have time to look over it all at the minute but I have saved it and also included your RSS feeds, so when I have time I will be back to read much more, Please do keep up the awesome work.discount football jerseys China

Reply to this comment

A simple Rust GUI with QML

Can I just say what a relief to uncover someone that actually understands what they are discussing on the web. You actually know how to bring a problem to light and make it important. A lot more people must read this and understand this side of your story. I was surprised that you are not more popular because you definitely possess the gift.cheap nba jerseys from China

Reply to this comment

A simple Rust GUI with QML

Its like you learn my thoughts! You appear to understand a lot about this, like you wrote the e-book in it or something. I think that you just can do with some % to pressure the message home a little bit, however instead of that, that is magnificent blog. A great read. I will certainly be back.authentic nfl jerseys

Reply to this comment

A simple Rust GUI with QML

I am sure this piece of writing has touched all the internet people, its really really pleasant article on building up new webpage.wholesale baseball jerseys

Reply to this comment

A simple Rust GUI with QML

Good way of telling, and fastidious piece of writing to obtain data concerning my presentation topic, which i am going to present in institution of higher education.wholesale jerseys free shipping from China

Reply to this comment

A simple Rust GUI with QML

Its like you read my mind! You appear to know a lot about this, like you wrote the book in it or something. I think that you could do with a few pics to drive the message home a bit, but other than that, this is magnificent blog. An excellent read. I will definitely be back.discount hockey jerseys

Reply to this comment

A simple Rust GUI with QML

Ebay same style wholesale sports jerseys china to offer. Online free shipping - cheap football jerseys wholesale

Reply to this comment

A simple Rust GUI with QML

Hello! I just wanted to ask if you ever have any problems with hackers? My last blog (wordpress) was hacked and I ended up losing a few months of hard work due to no back up. Do you have any solutions to protect against hackers?cheap baseball jerseys China

Reply to this comment

A simple Rust GUI with QML

I loved as much as you'll receive carried out right here. The sketch is attractive, your authored material stylish. nonetheless, you command get bought an shakiness over that you wish be delivering the following. unwell unquestionably come more formerly again as exactly the same nearly very often inside case you shield this hike.

cheap nfl jerseys

Reply to this comment

A simple Rust GUI with QML

Hey! I understand this is somewhat off-topic but I had to ask. Does operating a well-established blog such as yours require a lot of work? I'm completely new to running a blog but I do write in my journal on a daily basis. I'd like to start a blog so I can share my personal experience and views online. Please let me know if you have any ideas or tips for brand new aspiring blog owners. Thankyou!discount baseball jerseys

Reply to this comment

A simple Rust GUI with QML

This is the perfect site for everyone who would like to find out about this topic. You realize a whole lot its almost hard to argue with you (not that I actually will need to…HaHa). You certainly put a brand new spin on a subject which has been written about for years. Great stuff, just great!wholesale nhl jerseys

Reply to this comment

A simple Rust GUI with QML

Wonderful work! This is the kind of information that should be shared across the web. Disgrace on Google for not positioning this publish higher! Come on over and discuss with my site . Thanks =)p270

Reply to this comment

A simple Rust GUI with QML

Today, while I was at work, my sister stole my apple ipad and tested to see if it can survive a thirty foot drop, just so she can be a youtube sensation. My apple ipad is now broken and she has 83 views. I know this is completely off topic but I had to share it with someone!p46

Reply to this comment

A simple Rust GUI with QML

You are so awesome! I do not suppose I've read through a single thing like this before. So good to discover someone with a few original thoughts on this issue. Seriously.. thanks for starting this up. This website is one thing that is required on the web, someone with a little originality!cheap nhl jerseys

Reply to this comment

A simple Rust GUI with QML

continuously i used to read smaller articles or reviews that as well clear their motive, and that is also happening with this article which I am reading now.cheap baseball jerseys China

Reply to this comment

A simple Rust GUI with QML

Hello just wanted to give you a quick heads up. The words in your article seem to be running off the screen in Safari. I'm not sure if this is a format issue or something to do with web browser compatibility but I thought I'd post to let you know. The style and design look great though! Hope you get the issue fixed soon. Many thankswholesale jerseys from China

Reply to this comment

A simple Rust GUI with QML

This blog was... how do you say it? Relevant!! Finally I've found something that helped me. Thanks!authentic baseball jerseys China

Reply to this comment

A simple Rust GUI with QML

You really make it seem really easy along with your presentation but I in finding this topic to be really one thing that I feel I'd never understand.

It sort of feels too complex and very large for me.

I am looking ahead in your next publish, I'll attempt to get the hold of it!cheap nba jerseys free shipping

Reply to this comment

A simple Rust GUI with QML

Wow, incredible blog layout! How lengthy have you been running a blog for? you made blogging glance easy. The overall look of your website is excellent, let alone the content! cheap nfl jerseys

Reply to this comment

A simple Rust GUI with QML

Hello mates, its wonderful piece of writing on the topic of tutoringand entirely explained, keep it up all the time.wholesale jerseys

Reply to this comment

A simple Rust GUI with QML

I could not refrain from commenting. Perfectly written!post_47

Reply to this comment

A simple Rust GUI with QML

cheap football jerseys Amazon Shopper online retail,with link: authentic football jerseys

Reply to this comment

A simple Rust GUI with QML

I am nba jerseys for sale supplier online, take coupon code here: nba jerseys free shipping

Reply to this comment

A simple Rust GUI with QML

Hey all! Terrific comment! I enjoy the method that you described A simple Rust GUI with QML. It really is a superior crafted paper! Appreciate this kind of definitely.

Kind of possess capability! I will turn into nearly as good in the writing articles whilst, on the contrary it’s as opposed to personalized cup of tea . One don’t know in spite of whether I contend with offer your personal without web presence www.flickr.com. They to your world wide web site can make extraordinary user reviews a number of newspaper website writing treatments so that they can help to families avoid fraudulence and obtain greatest enterprises

Reply to this comment

A simple Rust GUI with QML

Incredible points. Solid arguments. Keep up the amazing spirit.discount basketball jerseys

Reply to this comment

A simple Rust GUI with QML

It's actually a great and useful piece of info. I am happy that you just shared this useful info with us. Please stay us up to date like this. Thank you for sharing.cheap nhl jerseys from China

Reply to this comment

A simple Rust GUI with QML

Greetings! Very useful advice in this particular post! It is the little changes that produce the largest changes. Thanks a lot for sharing!authentic nhl jerseys China

Reply to this comment

A simple Rust GUI with QML

Hey! Someone in my Facebook group shared this site with us so I came to give it a look. I'm definitely enjoying the information. I'm book-marking and will be tweeting this to my followers! Fantastic blog and terrific style and design.cheap nba jerseys from China

Reply to this comment

A simple Rust GUI with QML

I'm really enjoying the theme/design of your site. Do you ever run into any internet browser compatibility problems? A small number of my blog readers have complained about my blog not operating correctly in Explorer but looks great in Safari. Do you have any suggestions to help fix this problem?wholesale football jerseys free shipping

Reply to this comment

A simple Rust GUI with QML

Thank you for the good writeup. It in reality was a enjoyment account it. Glance complex to far delivered agreeable from you! By the way, how can we communicate?authentic nba jerseys

Reply to this comment

A simple Rust GUI with QML

Amazing issues here. I am very happy to look your post. Thanks a lot and I'm taking a look forward to touch you. Will you please drop me a e-mail?cheap nba jerseys from China

Reply to this comment

A simple Rust GUI with QML

If you are going for finest contents like myself, only go to see this website every day for the reason that it offers feature contents, thankswholesale jerseys from China

Reply to this comment

A simple Rust GUI with QML

I need to to thank you for this very good read!! I absolutely loved every bit of it. I have you book-marked to look at new stuff you post…authentic nfl jerseys China

Reply to this comment

A simple Rust GUI with QML

Hi! Do you know if they make any plugins to help with SEO? I'm trying to get my blog to rank for some targeted keywords but I'm not seeing very good gains. If you know of any please share. Cheers! wholesale mlb jerseys

Reply to this comment

A simple Rust GUI with QML

Everything is very open with a really clear clarification of the challenges.

It was definitely informative. Your website is very helpful. Many thanks for sharing!cheap baseball jerseys China

Reply to this comment

A simple Rust GUI with QML

wholesale jerseys youth Amazon Shopper online retail,with link: cheap la kings jerseys

Reply to this comment

A simple Rust GUI with QML

Excellent post. I was checking continuously this blog and I am impressed! Very helpful info specifically the last part :) I care for such info a lot. I was looking for this particular information for a very long time. Thank you and good luck.authentic nhl jerseys China

Reply to this comment

A simple Rust GUI with QML

I have been exploring for a little for any high-quality articles or blog posts in this sort of space . Exploring in Yahoo I ultimately stumbled upon this site. Reading this information So i am glad to exhibit that I have an incredibly excellent uncanny feeling I discovered just what I needed. I so much indisputably will make sure to do not forget this site and provides it a glance on a constant basis.Item_1

Reply to this comment

A simple Rust GUI with QML

tarot medium avis tarot tvi tarot gipsy tarot l etoile tarot gratuit tirage en croix mon avenir par le tarot tarot regle du jeu tarot divinatoire apprendre tarot amoureux et diable magie voyance tarot egyptien ton avenir selon le tarot divinatoir jouer tarot a 2 jeu tarot gratuit en ligne gratuit thai tarot blogspot tarot forum aufeminin comment jouer aux tarots tarot carte voyance gratuite tirage carte tarot zen tarot mensuel gratuit lire le tarot tarots gratuits en ligne marie claire jouer gratuitement tarot sans inscription tarot osho tirage gratuit jeux carte tarot marseille gratuit le mat tarot pratique point tarot tirage tarot lambert tarot gratuit janvier 2015 tarot en live karine fal tarot carte tarot gitane le grand livre du tarot cabalistique pdf flash tarots tirage tarot oui non gratuit en ligne interpreter le tarot persan comment apprendre a jouer au tarot tarot pierre yonas jouer au tarot tirage tarot amour gratuit oui ou non tarot cancer 2017 tirage tarot marseille en croix jouer tarots en ligne carte tarot signification le soleil tirage tarot avenir 2015 tarot divinatoire amour gratuit 2016 tarot reve d ange tarot d'amour tirage gratuit tarot belline interpretation signification tarot gratuit tarots avenir gratuit

Reply to this comment

A simple Rust GUI with QML

Write more, thats all I have to say. Literally, it seems as though you relied on the video to make your point. You clearly know what youre talking about, why throw away your intelligence on just posting videos to your weblog when you could be giving us something informative to read?wholesale hockey jerseys free shipping

Reply to this comment

A simple Rust GUI with QML

Incredible points. Outstanding arguments. Keep up the amazing work.discount football jerseys China

Reply to this comment

A simple Rust GUI with QML

tarot gratis amor 2015 tarot personal gratis 2015 tarot gitano trabajo tarot egipcio cartas pdf lectura gratis de cartas del tarot gitano on line tarotina tarot abril 2016 tarot mitico liz greene pdf tarot gratis josnell astrologico tirada tarot osho zen gratis tirada tarot gratis amor tarot vida tarot de marseille divitarot tarot para hoy tauro tirar cartas de tarot gratis+amor tarot gratis on line del amor tarot del amor gratis 2015 3 cartas tarot gratis del dinero alejandro jodorowsky plano creativo tarot consultas de tarot gratis por whatsapp libros para aprender tarot pdf aprender a leer el tarot egipcio tarot del dia gratis msn daily tarot horoscope jogar tarot wicca gratis tarot si no confiable sinopsis film tarot lengkap tarot los arcanos gratis 2016 cartas tarot trabajo gratis como leer las cartas del tarot tarot de vidas pasadas arcanos positivos tarot de marsella tarot virgo 2017 amor catalina tarot sol aguilar como hacer una tirada de tarot para el trabajo horoscopos tarot gratis diario semanal etc chat tarotistas gratis tarot telefono fijo barcelona lectura del tarot gratis para el 2015 tiradas del tarot gratis 2017 cartas tarot gratis tarot el oraculo tarot de hoy gratis para cancer significado el mago tarot amor tarot gratis tirada de 10 cartas horoscopos y tarot gratis terra tarot mensual julio 2017 telefono de tarot del amor tarot del trabajo gratis 2016 busco trabajo tarot casa tarot gratis del amor gitano

Reply to this comment

A simple Rust GUI with QML

This is a very good tip especially to those new to the blogosphere. Simple but very accurate info᾿Many thanks for sharing this one. A must read post!authentic nhl jerseys

Reply to this comment

A simple Rust GUI with QML

cheap jerseys website.Order cheap jerseys website from China via Jersey777, Free shipping. "Nhl youth jersey has come as described on the photo, fast delivery also.

Reply to this comment

A simple Rust GUI with QML

You actually make it seem so easy with your presentation but I find this topic to be really something that I think I would never understand. It seems too complicated and extremely broad for me. I'm looking forward for your next post, I'll try to get the hang of it! cheap jerseys

Reply to this comment