diff --git a/templates/qt_quick/CMakeLists.txt b/templates/qt_quick/CMakeLists.txt
new file mode 100644
index 0000000..09571fd
--- /dev/null
+++ b/templates/qt_quick/CMakeLists.txt
@@ -0,0 +1,73 @@
+project (my_rust_qt_quick_project)
+
+cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR)
+cmake_policy(SET CMP0046 NEW)
+cmake_policy(SET CMP0063 NEW)
+LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
+
+string(TOUPPER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_UPPER)
+if(CMAKE_BUILD_TYPE_UPPER STREQUAL DEBUG)
+ set(RUST_TARGET_DIR target/debug/)
+ set(RUST_BUILD_FLAG)
+else()
+ set(RUST_TARGET_DIR target/release/)
+ set(RUST_BUILD_FLAG --release)
+endif()
+
+### find dependencies ###
+
+include(FeatureSummary)
+find_package(Cargo REQUIRED)
+find_package(Rust REQUIRED)
+set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
+find_package(Threads REQUIRED)
+
+set(QT_MIN_VERSION "5.6.0")
+find_package(Qt5 ${QT_MIN_VERSION} CONFIG
+ REQUIRED COMPONENTS Core Quick
+)
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTOUIC ON)
+set(CMAKE_AUTORCC ON)
+find_package(RustQtBindingGenerator REQUIRED)
+
+feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
+
+### build commands ###
+
+SET(RUST_DIR "${CMAKE_CURRENT_SOURCE_DIR}/rust")
+SET(RUST_LIB "${RUST_DIR}/${RUST_TARGET_DIR}/librust.a")
+
+# generate c++ and rust code from bindings.json
+add_custom_command(
+ OUTPUT "${RUST_DIR}/src/interface.rs"
+ "${CMAKE_CURRENT_SOURCE_DIR}/src/Bindings.h"
+ # if the cpp file is marked GENERATED, CMake will not check it for moc
+ # "${CMAKE_CURRENT_SOURCE_DIR}/src/Bindings.cpp"
+ COMMAND "${RustQtBindingGenerator_EXECUTABLE}"
+ "${CMAKE_CURRENT_SOURCE_DIR}/bindings.json"
+ DEPENDS bindings.json
+)
+
+# compile the rust code into a static library
+add_custom_command(
+ OUTPUT "${RUST_LIB}"
+ COMMAND ${Cargo_EXECUTABLE} build ${RUST_BUILD_FLAG}
+ DEPENDS rust/src/lib.rs
+ rust/src/implementation.rs
+ rust/src/interface.rs
+ WORKING_DIRECTORY "${RUST_DIR}"
+)
+add_custom_target(rust_target DEPENDS "${RUST_LIB}")
+
+list(APPEND Libs "${RUST_LIB}")
+list(APPEND Libs Qt5::Core Qt5::Quick Threads::Threads ${CMAKE_DL_LIBS})
+set(SRCS src/main.cpp src/Bindings.cpp "qml.qrc")
+add_executable(MyExe ${SRCS})
+add_dependencies(MyExe rust_target)
+target_link_libraries(MyExe ${Libs})
+set_target_properties(MyExe PROPERTIES
+ CXX_STANDARD 11
+ CXX_STANDARD_REQUIRED ON
+)
diff --git a/templates/qt_quick/MainForm.ui.qml b/templates/qt_quick/MainForm.ui.qml
new file mode 100644
index 0000000..9cbb5e0
--- /dev/null
+++ b/templates/qt_quick/MainForm.ui.qml
@@ -0,0 +1,34 @@
+import QtQuick 2.6
+import RustCode 1.0
+
+Rectangle {
+ property alias mouseArea: mouseArea
+ property alias textEdit: textEdit
+
+ Simple {
+ id: rust
+ }
+
+ width: 360
+ height: 360
+
+ MouseArea {
+ id: mouseArea
+ anchors.fill: parent
+ }
+
+ TextEdit {
+ id: textEdit
+ text: rust.message
+ verticalAlignment: Text.AlignVCenter
+ anchors.top: parent.top
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.topMargin: 20
+ Rectangle {
+ anchors.fill: parent
+ anchors.margins: -10
+ color: "transparent"
+ border.width: 1
+ }
+ }
+}
diff --git a/templates/qt_quick/README.md b/templates/qt_quick/README.md
new file mode 100644
index 0000000..b591995
--- /dev/null
+++ b/templates/qt_quick/README.md
@@ -0,0 +1,14 @@
+Qt Quick template project with Rust bindings
+
+This is a template project for writing a Qt Quick GUI on top of Rust code.
+
+bindings.json defines the interface between the Qt and Rust code.
+
+Build this code with
+
+```bash
+mkdir build
+cd build
+cmake ..
+make
+```
diff --git a/templates/qt_quick/bindings.json b/templates/qt_quick/bindings.json
new file mode 100644
index 0000000..6467a0f
--- /dev/null
+++ b/templates/qt_quick/bindings.json
@@ -0,0 +1,19 @@
+{
+ "cppFile": "src/Bindings.cpp",
+ "rust": {
+ "dir": "rust",
+ "interfaceModule": "interface",
+ "implementationModule": "implementation"
+ },
+ "objects": {
+ "Simple": {
+ "type": "Object",
+ "properties": {
+ "message": {
+ "type": "QString",
+ "write": true
+ }
+ }
+ }
+ }
+}
diff --git a/templates/qt_quick/cmake/FindCargo.cmake b/templates/qt_quick/cmake/FindCargo.cmake
new file mode 100644
index 0000000..6627d86
--- /dev/null
+++ b/templates/qt_quick/cmake/FindCargo.cmake
@@ -0,0 +1,10 @@
+include(FindPackageHandleStandardArgs)
+find_program(Cargo_EXECUTABLE cargo)
+execute_process(COMMAND "${Cargo_EXECUTABLE}" --version
+ OUTPUT_VARIABLE Cargo_VERSION_OUTPUT)
+STRING(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+"
+ Cargo_VERSION "${Cargo_VERSION_OUTPUT}")
+find_package_handle_standard_args(Cargo
+ REQUIRED_VARS Cargo_EXECUTABLE
+ VERSION_VAR Cargo_VERSION)
+mark_as_advanced(Cargo_EXECUTABLE)
diff --git a/templates/qt_quick/cmake/FindRust.cmake b/templates/qt_quick/cmake/FindRust.cmake
new file mode 100644
index 0000000..1b67045
--- /dev/null
+++ b/templates/qt_quick/cmake/FindRust.cmake
@@ -0,0 +1,10 @@
+include(FindPackageHandleStandardArgs)
+find_program(Rust_EXECUTABLE rustc)
+execute_process(COMMAND "${Rust_EXECUTABLE}" --version
+ OUTPUT_VARIABLE Rust_VERSION_OUTPUT)
+STRING(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+"
+ Rust_VERSION "${Rust_VERSION_OUTPUT}")
+find_package_handle_standard_args(Rust
+ REQUIRED_VARS Rust_EXECUTABLE
+ VERSION_VAR Rust_VERSION)
+mark_as_advanced(Rust_EXECUTABLE)
diff --git a/templates/qt_quick/cmake/FindRustQtBindingGenerator.cmake b/templates/qt_quick/cmake/FindRustQtBindingGenerator.cmake
new file mode 100644
index 0000000..4be2426
--- /dev/null
+++ b/templates/qt_quick/cmake/FindRustQtBindingGenerator.cmake
@@ -0,0 +1,5 @@
+include(FindPackageHandleStandardArgs)
+find_program(RustQtBindingGenerator_EXECUTABLE rust_qt_binding_generator)
+find_package_handle_standard_args(RustQtBindingGenerator
+ REQUIRED_VARS RustQtBindingGenerator_EXECUTABLE)
+mark_as_advanced(RustQtBindingGenerator_EXECUTABLE)
diff --git a/templates/qt_quick/main.qml b/templates/qt_quick/main.qml
new file mode 100644
index 0000000..6d8bd40
--- /dev/null
+++ b/templates/qt_quick/main.qml
@@ -0,0 +1,16 @@
+import QtQuick 2.6
+import QtQuick.Window 2.2
+
+Window {
+ visible: true
+ width: 640
+ height: 480
+ title: qsTr("Hello World")
+
+ MainForm {
+ anchors.fill: parent
+ mouseArea.onClicked: {
+ console.log(qsTr('Clicked on background. Text: "' + textEdit.text + '"'))
+ }
+ }
+}
diff --git a/templates/qt_quick/qml.qrc b/templates/qt_quick/qml.qrc
new file mode 100644
index 0000000..7684346
--- /dev/null
+++ b/templates/qt_quick/qml.qrc
@@ -0,0 +1,6 @@
+
+
+ main.qml
+ MainForm.ui.qml
+
+
diff --git a/templates/qt_quick/rust/Cargo.toml b/templates/qt_quick/rust/Cargo.toml
new file mode 100644
index 0000000..7013c67
--- /dev/null
+++ b/templates/qt_quick/rust/Cargo.toml
@@ -0,0 +1,11 @@
+
+[package]
+name = "rust"
+version = "1.0.0"
+
+[dependencies]
+libc = "*"
+
+[lib]
+name = "rust"
+crate-type = ["staticlib"]
diff --git a/templates/qt_quick/rust/src/implementation.rs b/templates/qt_quick/rust/src/implementation.rs
new file mode 100644
index 0000000..fb9c575
--- /dev/null
+++ b/templates/qt_quick/rust/src/implementation.rs
@@ -0,0 +1,26 @@
+use interface::*;
+
+pub struct Simple {
+ emit: SimpleEmitter,
+ message: String,
+}
+
+impl SimpleTrait for Simple {
+ fn new(emit: SimpleEmitter) -> Simple {
+ Simple {
+ emit: emit,
+ message: String::new(),
+ }
+ }
+ fn emit(&self) -> &SimpleEmitter {
+ &self.emit
+ }
+ fn message(&self) -> &str {
+ "Hello World!"
+ }
+ fn set_message(&mut self, value: String) {
+ self.message = value;
+ self.emit.message_changed();
+ }
+}
+
diff --git a/templates/qt_quick/rust/src/interface.rs b/templates/qt_quick/rust/src/interface.rs
new file mode 100644
index 0000000..9ddf376
--- /dev/null
+++ b/templates/qt_quick/rust/src/interface.rs
@@ -0,0 +1,110 @@
+/* generated by rust_qt_binding_generator */
+#![allow(unknown_lints)]
+#![allow(mutex_atomic, needless_pass_by_value)]
+use libc::{c_int, c_void, uint8_t, uint16_t};
+use std::slice;
+
+use std::sync::{Arc, Mutex};
+use std::ptr::null;
+
+use implementation::*;
+
+
+#[repr(C)]
+pub struct QString {
+ data: *const uint8_t,
+ len: c_int,
+}
+
+#[repr(C)]
+pub struct QStringIn {
+ data: *const uint16_t,
+ len: c_int,
+}
+
+impl QStringIn {
+ fn convert(&self) -> String {
+ let data = unsafe { slice::from_raw_parts(self.data, self.len as usize) };
+ String::from_utf16_lossy(data)
+ }
+}
+
+impl<'a> From<&'a str> for QString {
+ fn from(string: &'a str) -> QString {
+ QString {
+ len: string.len() as c_int,
+ data: string.as_ptr(),
+ }
+ }
+}
+
+impl<'a> From<&'a String> for QString {
+ fn from(string: &'a String) -> QString {
+ QString {
+ len: string.len() as c_int,
+ data: string.as_ptr(),
+ }
+ }
+}
+
+pub struct SimpleQObject {}
+
+#[derive(Clone)]
+pub struct SimpleEmitter {
+ qobject: Arc>,
+ message_changed: fn(*const SimpleQObject),
+}
+
+unsafe impl Send for SimpleEmitter {}
+
+impl SimpleEmitter {
+ fn clear(&self) {
+ *self.qobject.lock().unwrap() = null();
+ }
+ pub fn message_changed(&self) {
+ let ptr = *self.qobject.lock().unwrap();
+ if !ptr.is_null() {
+ (self.message_changed)(ptr);
+ }
+ }
+}
+
+pub trait SimpleTrait {
+ fn new(emit: SimpleEmitter) -> Self;
+ fn emit(&self) -> &SimpleEmitter;
+ fn message(&self) -> &str;
+ fn set_message(&mut self, value: String);
+}
+
+#[no_mangle]
+pub extern "C" fn simple_new(
+ simple: *mut SimpleQObject,
+ message_changed: fn(*const SimpleQObject),
+) -> *mut Simple {
+ let simple_emit = SimpleEmitter {
+ qobject: Arc::new(Mutex::new(simple)),
+ message_changed: message_changed,
+ };
+ let d_simple = Simple::new(simple_emit);
+ Box::into_raw(Box::new(d_simple))
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn simple_free(ptr: *mut Simple) {
+ Box::from_raw(ptr).emit().clear();
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn simple_message_get(
+ ptr: *const Simple,
+ p: *mut c_void,
+ set: fn(*mut c_void, QString),
+) {
+ let data = (&*ptr).message();
+ set(p, data.into());
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn simple_message_set(ptr: *mut Simple, v: QStringIn) {
+ (&mut *ptr).set_message(v.convert());
+}
diff --git a/templates/qt_quick/rust/src/lib.rs b/templates/qt_quick/rust/src/lib.rs
new file mode 100644
index 0000000..d17196f
--- /dev/null
+++ b/templates/qt_quick/rust/src/lib.rs
@@ -0,0 +1,5 @@
+
+extern crate libc;
+
+pub mod interface;
+mod implementation;
diff --git a/templates/qt_quick/src/Bindings.cpp b/templates/qt_quick/src/Bindings.cpp
new file mode 100644
index 0000000..7cf6e65
--- /dev/null
+++ b/templates/qt_quick/src/Bindings.cpp
@@ -0,0 +1,63 @@
+/* generated by rust_qt_binding_generator */
+#include "Bindings.h"
+
+namespace {
+
+ struct qstring_t {
+ private:
+ const void* data;
+ int len;
+ public:
+ qstring_t(const QString& v):
+ data(static_cast(v.utf16())),
+ len(v.size()) {
+ }
+ operator QString() const {
+ return QString::fromUtf8(static_cast(data), len);
+ }
+ };
+ typedef void (*qstring_set)(QString*, qstring_t*);
+ void set_qstring(QString* v, qstring_t* val) {
+ *v = *val;
+ }
+ inline void simpleMessageChanged(Simple* o)
+ {
+ emit o->messageChanged();
+ }
+}
+extern "C" {
+ Simple::Private* simple_new(Simple*, void (*)(Simple*));
+ void simple_free(Simple::Private*);
+ void simple_message_get(const Simple::Private*, QString*, qstring_set);
+ void simple_message_set(Simple::Private*, qstring_t);
+};
+
+Simple::Simple(bool /*owned*/, QObject *parent):
+ QObject(parent),
+ m_d(0),
+ m_ownsPrivate(false)
+{
+}
+
+Simple::Simple(QObject *parent):
+ QObject(parent),
+ m_d(simple_new(this,
+ simpleMessageChanged)),
+ m_ownsPrivate(true)
+{
+}
+
+Simple::~Simple() {
+ if (m_ownsPrivate) {
+ simple_free(m_d);
+ }
+}
+QString Simple::message() const
+{
+ QString v;
+ simple_message_get(m_d, &v, set_qstring);
+ return v;
+}
+void Simple::setMessage(const QString& v) {
+ simple_message_set(m_d, v);
+}
diff --git a/templates/qt_quick/src/Bindings.h b/templates/qt_quick/src/Bindings.h
new file mode 100644
index 0000000..6d2c6df
--- /dev/null
+++ b/templates/qt_quick/src/Bindings.h
@@ -0,0 +1,28 @@
+/* generated by rust_qt_binding_generator */
+#ifndef BINDINGS_H
+#define BINDINGS_H
+
+#include
+#include
+
+class Simple;
+
+class Simple : public QObject
+{
+ Q_OBJECT
+public:
+ class Private;
+private:
+ Private * m_d;
+ bool m_ownsPrivate;
+ Q_PROPERTY(QString message READ message WRITE setMessage NOTIFY messageChanged FINAL)
+ explicit Simple(bool owned, QObject *parent);
+public:
+ explicit Simple(QObject *parent = nullptr);
+ ~Simple();
+ QString message() const;
+ void setMessage(const QString& v);
+signals:
+ void messageChanged();
+};
+#endif // BINDINGS_H
diff --git a/templates/qt_quick/src/main.cpp b/templates/qt_quick/src/main.cpp
new file mode 100644
index 0000000..7a3d676
--- /dev/null
+++ b/templates/qt_quick/src/main.cpp
@@ -0,0 +1,18 @@
+#include "Bindings.h"
+
+#include
+#include
+#include
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+ qmlRegisterType("RustCode", 1, 0, "Simple");
+
+ QQmlApplicationEngine engine;
+ engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
+ if (engine.rootObjects().isEmpty())
+ return -1;
+
+ return app.exec();
+}