diff --git a/examples/todos/CMakeLists.txt b/examples/todos/CMakeLists.txt
new file mode 100644
index 0000000..c2642b0
--- /dev/null
+++ b/examples/todos/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 Widgets
+)
+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}" #--overwrite-implementation
+ "${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 Qt5::Widgets Threads::Threads ${CMAKE_DL_LIBS})
+set(SRCS src/main.cpp src/Bindings.cpp "qml.qrc")
+add_executable(todos ${SRCS})
+add_dependencies(todos rust_target)
+target_link_libraries(todos ${Libs})
+set_target_properties(todos PROPERTIES
+ CXX_STANDARD 11
+ CXX_STANDARD_REQUIRED ON
+)
diff --git a/examples/todos/README.md b/examples/todos/README.md
new file mode 100644
index 0000000..cafcf59
--- /dev/null
+++ b/examples/todos/README.md
@@ -0,0 +1,13 @@
+Source code for https://fosdem.org/2018/schedule/event/rust_qt_binding_generator/
+
+Make sure `rust_qt_binding_generator` is in the `PATH`.
+
+```bash
+mkdir build
+cd build
+cmake -GNinja ..
+ninja
+export QT_QUICK_CONTROLS_MATERIAL_THEME=Dark
+export QT_QUICK_CONTROLS_STYLE=Material
+./todos
+```
diff --git a/examples/todos/bindings.json b/examples/todos/bindings.json
new file mode 100644
index 0000000..10ebc6d
--- /dev/null
+++ b/examples/todos/bindings.json
@@ -0,0 +1,64 @@
+{
+ "cppFile": "src/Bindings.cpp",
+ "rust": {
+ "dir": "rust",
+ "interfaceModule": "interface",
+ "implementationModule": "implementation"
+ },
+ "objects": {
+ "Todos": {
+ "type": "List",
+ "properties": {
+ "count": {
+ "type": "quint64"
+ },
+ "activeCount": {
+ "type": "quint64"
+ }
+ },
+ "itemProperties": {
+ "completed": {
+ "type": "bool",
+ "write": true,
+ "roles": [ [ "display" ] ]
+ },
+ "description": {
+ "type": "QString",
+ "write": true,
+ "roles": [ [], [ "display" ] ]
+ }
+ },
+ "functions": {
+ "add": {
+ "return": "void",
+ "mut": true,
+ "arguments": [{
+ "name": "description",
+ "type": "QString"
+ }]
+ },
+ "remove": {
+ "return": "bool",
+ "mut": true,
+ "arguments": [{
+ "name": "index",
+ "type": "quint64"
+ }]
+ },
+ "setAll": {
+ "return": "void",
+ "mut": true,
+ "arguments": [{
+ "name": "completed",
+ "type": "bool"
+ }]
+ },
+ "clearCompleted": {
+ "return": "void",
+ "mut": true,
+ "arguments": []
+ }
+ }
+ }
+ }
+}
diff --git a/examples/todos/cmake/FindCargo.cmake b/examples/todos/cmake/FindCargo.cmake
new file mode 100644
index 0000000..6627d86
--- /dev/null
+++ b/examples/todos/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/examples/todos/cmake/FindRust.cmake b/examples/todos/cmake/FindRust.cmake
new file mode 100644
index 0000000..1b67045
--- /dev/null
+++ b/examples/todos/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/examples/todos/cmake/FindRustQtBindingGenerator.cmake b/examples/todos/cmake/FindRustQtBindingGenerator.cmake
new file mode 100644
index 0000000..4be2426
--- /dev/null
+++ b/examples/todos/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/examples/todos/main.qml b/examples/todos/main.qml
new file mode 100644
index 0000000..749bc04
--- /dev/null
+++ b/examples/todos/main.qml
@@ -0,0 +1,208 @@
+import QtQuick 2.9
+import QtQuick.Controls 2.2
+import QtQuick.Layouts 1.3
+import RustCode 1.0;
+
+ApplicationWindow {
+ visible: true
+ width: 450
+ height: 580
+ header: ToolBar {
+ Label {
+ anchors.fill: parent
+ text: qsTr("todos")
+ font.pixelSize: 30
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
+ }
+
+ Component.onCompleted: {
+ input.forceActiveFocus()
+ }
+
+ Todos {
+ id: todoModel
+
+ Component.onCompleted: {
+ add("write bindings.json")
+ add("run rust_qt_binding_generator")
+ add("check bindings.h")
+ add("check bindings.cpp")
+ add("check interface.rs")
+ add("write implementation.rs")
+ add("write main.qml")
+ }
+ }
+
+ Component {
+ id: todoDelegate
+ RowLayout {
+ // the active tab determines if this item should be shown
+ // 0: all, 1: active, 2: completed
+ property bool show: filter.currentIndex === 0
+ || (filter.currentIndex === 1 && !completed)
+ || (filter.currentIndex === 2 && completed)
+ visible: show
+ width: parent.width
+ height: show ? implicitHeight : 0
+ CheckBox {
+ checked: completed
+ onToggled: todoModel.setCompleted(index, checked)
+ }
+ Item {
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ Label {
+ id: label
+ visible: !editInput.visible
+ text: description
+ anchors.fill: parent
+ verticalAlignment: Text.AlignVCenter
+ font.strikeout: completed
+ font.pixelSize: 20
+ }
+ MouseArea {
+ id: mouse
+ anchors.fill: parent
+ hoverEnabled: true
+ onDoubleClicked: {
+ editInput.text = label.text
+ editInput.visible = true
+ editInput.forceActiveFocus()
+ }
+ }
+ Button {
+ text: 'X'
+ visible: (mouse.containsMouse && !editInput.visible)
+ || closeMouse.containsMouse
+ anchors.right: parent.right
+ MouseArea {
+ id: closeMouse
+ anchors.fill: parent
+ hoverEnabled: true
+ onClicked: todoModel.remove(index)
+ }
+ }
+ TextField {
+ id: editInput
+ visible: false
+ anchors.fill: parent
+ text: description
+ font.pixelSize: label.font.pixelSize
+ onAccepted: {
+ todoModel.setDescription(index, text)
+ visible = false
+ }
+ onActiveFocusChanged: {
+ // hide when focus is lost
+ if (!activeFocus) {
+ visible = false
+ }
+ }
+ Keys.onPressed: {
+ // on escape, set value, hide (and lose focus)
+ if (event.key === Qt.Key_Escape) {
+ todoModel.setDescription(index, text)
+ visible = false
+ event.accepted = true
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Pane {
+ anchors.fill: parent
+ leftPadding: 0
+ Page {
+ anchors.fill: parent
+ header: RowLayout {
+ CheckBox {
+ tristate: true
+ // if there are no todos, do not show this checkbox
+ // but let it take up the same space
+ enabled: todoModel.count > 0
+ opacity: todoModel.count === 0 ? 0 : 1
+ checkState: {
+ if (todoModel.activeCount === 0) {
+ return Qt.Checked
+ } else if (todoModel.activeCount >= todoModel.count) {
+ return Qt.Unchecked
+ }
+ return Qt.PartiallyChecked
+ }
+ onCheckStateChanged: {
+ // if the change is triggered by a user action on this
+ // checkbox, check or uncheck all todos
+ // otherwise, do nothing
+ // (onToggle does not emit for tristate buttons)
+ if (activeFocus) {
+ var checked = checkState !== Qt.Unchecked
+ todoModel.setAll(checked)
+ }
+ }
+ }
+ TextField {
+ id: input
+ Layout.fillWidth: true
+ placeholderText: qsTr("What needs to be done?")
+ onAccepted: {
+ const todo = text.trim()
+ if (todo) {
+ todoModel.add(todo)
+ }
+ input.clear()
+ }
+ }
+ }
+ Flickable {
+ anchors.fill: parent
+ ListView {
+ anchors.fill: parent
+ model: todoModel
+ delegate: todoDelegate
+ }
+ }
+ }
+ }
+
+ footer: Pane {
+ padding: 0
+ ColumnLayout {
+ width: parent.width
+ TabBar {
+ id: filter
+ Layout.fillWidth: true
+ visible: todoModel.count > 0
+ TabButton {
+ text: qsTr("All")
+ checked: true
+ }
+ TabButton {
+ text: qsTr("Active")
+ }
+ TabButton {
+ text: qsTr("Completed")
+ }
+ }
+ RowLayout {
+ visible: todoModel.count > 0
+ width: parent.width
+ Label {
+ Layout.fillWidth: true
+ text: (todoModel.activeCount === 1)
+ ? qsTr("1 item left")
+ : todoModel.activeCount + qsTr(" items left")
+ }
+ Button {
+ enabled: todoModel.count > todoModel.activeCount
+ opacity: enabled
+ text: qsTr("Clear completed")
+ onClicked: todoModel.clearCompleted()
+ }
+ }
+ }
+ }
+}
diff --git a/examples/todos/qml.qrc b/examples/todos/qml.qrc
new file mode 100644
index 0000000..5f6483a
--- /dev/null
+++ b/examples/todos/qml.qrc
@@ -0,0 +1,5 @@
+
+
+ main.qml
+
+
diff --git a/examples/todos/rust/Cargo.toml b/examples/todos/rust/Cargo.toml
new file mode 100644
index 0000000..7013c67
--- /dev/null
+++ b/examples/todos/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/examples/todos/rust/src/implementation.rs b/examples/todos/rust/src/implementation.rs
new file mode 100644
index 0000000..0b93095
--- /dev/null
+++ b/examples/todos/rust/src/implementation.rs
@@ -0,0 +1,127 @@
+use interface::*;
+
+#[derive(Default, Clone)]
+struct TodosItem {
+ completed: bool,
+ description: String,
+}
+
+pub struct Todos {
+ emit: TodosEmitter,
+ model: TodosList,
+ list: Vec,
+ active_count: usize,
+}
+
+impl Todos {
+ fn update_active_count(&mut self) {
+ let ac = self.list.iter().filter(|i| !i.completed).count();
+ if self.active_count != ac {
+ self.active_count = ac;
+ self.emit.active_count_changed();
+ }
+ }
+}
+
+impl TodosTrait for Todos {
+ fn new(emit: TodosEmitter, model: TodosList) -> Todos {
+ Todos {
+ emit: emit,
+ model: model,
+ list: vec![TodosItem::default(); 0],
+ active_count: 0,
+ }
+ }
+ fn emit(&self) -> &TodosEmitter {
+ &self.emit
+ }
+ fn active_count(&self) -> u64 {
+ self.active_count as u64
+ }
+ fn count(&self) -> u64 {
+ self.list.len() as u64
+ }
+ fn row_count(&self) -> usize {
+ self.list.len()
+ }
+ fn completed(&self, item: usize) -> bool {
+ if item >= self.list.len() {
+ return false;
+ }
+ self.list[item].completed
+ }
+ fn set_completed(&mut self, item: usize, v: bool) -> bool {
+ if item >= self.list.len() {
+ return false;
+ }
+ self.list[item].completed = v;
+ self.update_active_count();
+ true
+ }
+ fn description(&self, item: usize) -> &str {
+ if item < self.list.len() {
+ &self.list[item].description
+ } else {
+ ""
+ }
+ }
+ fn set_description(&mut self, item: usize, v: String) -> bool {
+ if item >= self.list.len() {
+ return false;
+ }
+ self.list[item].description = v;
+ true
+ }
+ fn insert_rows(&mut self, row: usize, count: usize) -> bool {
+ if count == 0 || row > self.list.len() {
+ return false;
+ }
+ self.model.begin_insert_rows(row, row + count - 1);
+ for i in 0..count {
+ self.list.insert(row + i, TodosItem::default());
+ }
+ self.model.end_insert_rows();
+ self.active_count += count;
+ self.emit.active_count_changed();
+ self.emit.count_changed();
+ true
+ }
+ fn remove_rows(&mut self, row: usize, count: usize) -> bool {
+ if count == 0 || row + count > self.list.len() {
+ return false;
+ }
+ self.model.begin_remove_rows(row, row + count - 1);
+ self.list.drain(row..row + count);
+ self.model.end_remove_rows();
+ self.emit.count_changed();
+ self.update_active_count();
+ true
+ }
+ fn clear_completed(&mut self) -> () {
+ self.model.begin_reset_model();
+ self.list.retain(|i| !i.completed);
+ self.model.end_reset_model();
+ self.emit.count_changed();
+ }
+ fn add(&mut self, description: String) {
+ let end = self.list.len();
+ self.model.begin_insert_rows(end, end);
+ self.list.insert(end, TodosItem { completed: false, description });
+ self.model.end_insert_rows();
+ self.active_count += 1;
+ self.emit.active_count_changed();
+ self.emit.count_changed();
+ self.model.begin_reset_model();
+ self.model.end_reset_model();
+ }
+ fn remove(&mut self, index: u64) -> bool {
+ self.remove_rows(index as usize, 1)
+ }
+ fn set_all(&mut self, completed: bool) {
+ for i in &mut self.list {
+ i.completed = completed;
+ }
+ self.model.data_changed(0, self.list.len() - 1);
+ self.update_active_count();
+ }
+}
diff --git a/examples/todos/rust/src/interface.rs b/examples/todos/rust/src/interface.rs
new file mode 100644
index 0000000..a5135ad
--- /dev/null
+++ b/examples/todos/rust/src/interface.rs
@@ -0,0 +1,319 @@
+/* generated by rust_qt_binding_generator */
+#![allow(unknown_lints)]
+#![allow(mutex_atomic, needless_pass_by_value)]
+use libc::{c_char, c_ushort, c_int};
+use std::slice;
+use std::char::decode_utf16;
+
+use std::sync::{Arc, Mutex};
+use std::ptr::null;
+
+use implementation::*;
+
+
+#[repr(C)]
+pub struct COption {
+ data: T,
+ some: bool,
+}
+
+impl From