[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [rust-qt-binding-generator] examples/todos: Add example Todos application from FOSDEM presentation
From:       Jos van den Oever <null () kde ! org>
Date:       2018-05-18 16:37:41
Message-ID: E1fJiNx-0006MV-6K () code ! kde ! org
[Download RAW message or body]

Git commit 574440ba588bc45dcf9488c18031c99ba03c2abb by Jos van den Oever.
Committed on 18/05/2018 at 16:37.
Pushed by vandenoever into branch 'master'.

Add example Todos application from FOSDEM presentation

https://fosdem.org/2018/schedule/event/rust_qt_binding_generator/

A  +73   -0    examples/todos/CMakeLists.txt
A  +13   -0    examples/todos/README.md
A  +64   -0    examples/todos/bindings.json
A  +10   -0    examples/todos/cmake/FindCargo.cmake
A  +10   -0    examples/todos/cmake/FindRust.cmake
A  +5    -0    examples/todos/cmake/FindRustQtBindingGenerator.cmake
A  +208  -0    examples/todos/main.qml     [License: UNKNOWN]  *
A  +5    -0    examples/todos/qml.qrc
A  +11   -0    examples/todos/rust/Cargo.toml
A  +127  -0    examples/todos/rust/src/implementation.rs
A  +319  -0    examples/todos/rust/src/interface.rs
A  +5    -0    examples/todos/rust/src/lib.rs
A  +320  -0    examples/todos/src/Bindings.cpp     [License: GENERATED FILE]  *
A  +62   -0    examples/todos/src/Bindings.h     [License: GENERATED FILE]  *
A  +18   -0    examples/todos/src/main.cpp     [License: UNKNOWN]  *

The files marked with a * at the end have a non valid license. Please read: \
https://community.kde.org/Policies/Licensing_Policy and use the headers which are \
listed at that page.


https://commits.kde.org/rust-qt-binding-generator/574440ba588bc45dcf9488c18031c99ba03c2abb


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 @@
+<RCC>
+    <qresource prefix="/">
+        <file>main.qml</file>
+    </qresource>
+</RCC>
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<TodosItem>,
+    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<T> {
+    data: T,
+    some: bool,
+}
+
+impl<T> From<Option<T>> for COption<T>
+where
+    T: Default,
+{
+    fn from(t: Option<T>) -> COption<T> {
+        if let Some(v) = t {
+            COption {
+                data: v,
+                some: true,
+            }
+        } else {
+            COption {
+                data: T::default(),
+                some: false,
+            }
+        }
+    }
+}
+
+
+pub enum QString {}
+
+fn set_string_from_utf16(s: &mut String, str: *const c_ushort, len: c_int) {
+    let utf16 = unsafe { slice::from_raw_parts(str, to_usize(len)) };
+    let characters = decode_utf16(utf16.iter().cloned())
+        .into_iter()
+        .map(|r| r.unwrap());
+    s.clear();
+    s.extend(characters);
+}
+
+
+
+#[repr(C)]
+pub enum SortOrder {
+    Ascending = 0,
+    Descending = 1,
+}
+
+#[repr(C)]
+pub struct QModelIndex {
+    row: c_int,
+    internal_id: usize,
+}
+
+
+fn to_usize(n: c_int) -> usize {
+    if n < 0 {
+        panic!("Cannot cast {} to usize", n);
+    }
+    n as usize
+}
+
+
+fn to_c_int(n: usize) -> c_int {
+    if n > c_int::max_value() as usize {
+        panic!("Cannot cast {} to c_int", n);
+    }
+    n as c_int
+}
+
+
+pub struct TodosQObject {}
+
+#[derive(Clone)]
+pub struct TodosEmitter {
+    qobject: Arc<Mutex<*const TodosQObject>>,
+    active_count_changed: fn(*const TodosQObject),
+    count_changed: fn(*const TodosQObject),
+    new_data_ready: fn(*const TodosQObject),
+}
+
+unsafe impl Send for TodosEmitter {}
+
+impl TodosEmitter {
+    fn clear(&self) {
+        *self.qobject.lock().unwrap() = null();
+    }
+    pub fn active_count_changed(&self) {
+        let ptr = *self.qobject.lock().unwrap();
+        if !ptr.is_null() {
+            (self.active_count_changed)(ptr);
+        }
+    }
+    pub fn count_changed(&self) {
+        let ptr = *self.qobject.lock().unwrap();
+        if !ptr.is_null() {
+            (self.count_changed)(ptr);
+        }
+    }
+    pub fn new_data_ready(&self) {
+        let ptr = *self.qobject.lock().unwrap();
+        if !ptr.is_null() {
+            (self.new_data_ready)(ptr);
+        }
+    }
+}
+
+pub struct TodosList {
+    qobject: *const TodosQObject,
+    data_changed: fn(*const TodosQObject, usize, usize),
+    begin_reset_model: fn(*const TodosQObject),
+    end_reset_model: fn(*const TodosQObject),
+    begin_insert_rows: fn(*const TodosQObject, usize, usize),
+    end_insert_rows: fn(*const TodosQObject),
+    begin_remove_rows: fn(*const TodosQObject, usize, usize),
+    end_remove_rows: fn(*const TodosQObject),
+}
+
+impl TodosList {
+    pub fn data_changed(&self, first: usize, last: usize) {
+        (self.data_changed)(self.qobject, first, last);
+    }
+    pub fn begin_reset_model(&self) {
+        (self.begin_reset_model)(self.qobject);
+    }
+    pub fn end_reset_model(&self) {
+        (self.end_reset_model)(self.qobject);
+    }
+    pub fn begin_insert_rows(&self, first: usize, last: usize) {
+        (self.begin_insert_rows)(self.qobject, first, last);
+    }
+    pub fn end_insert_rows(&self) {
+        (self.end_insert_rows)(self.qobject);
+    }
+    pub fn begin_remove_rows(&self, first: usize, last: usize) {
+        (self.begin_remove_rows)(self.qobject, first, last);
+    }
+    pub fn end_remove_rows(&self) {
+        (self.end_remove_rows)(self.qobject);
+    }
+}
+
+pub trait TodosTrait {
+    fn new(emit: TodosEmitter, model: TodosList) -> Self;
+    fn emit(&self) -> &TodosEmitter;
+    fn active_count(&self) -> u64;
+    fn count(&self) -> u64;
+    fn add(&mut self, description: String) -> ();
+    fn clear_completed(&mut self) -> ();
+    fn remove(&mut self, index: u64) -> bool;
+    fn set_all(&mut self, completed: bool) -> ();
+    fn row_count(&self) -> usize;
+    fn insert_rows(&mut self, _row: usize, _count: usize) -> bool { false }
+    fn remove_rows(&mut self, _row: usize, _count: usize) -> bool { false }
+    fn can_fetch_more(&self) -> bool {
+        false
+    }
+    fn fetch_more(&mut self) {}
+    fn sort(&mut self, u8, SortOrder) {}
+    fn completed(&self, item: usize) -> bool;
+    fn set_completed(&mut self, item: usize, bool) -> bool;
+    fn description(&self, item: usize) -> &str;
+    fn set_description(&mut self, item: usize, String) -> bool;
+}
+
+#[no_mangle]
+pub extern "C" fn todos_new(
+    todos: *mut TodosQObject,
+    active_count_changed: fn(*const TodosQObject),
+    count_changed: fn(*const TodosQObject),
+    todos_new_data_ready: fn(*const TodosQObject),
+    todos_data_changed: fn(*const TodosQObject, usize, usize),
+    todos_begin_reset_model: fn(*const TodosQObject),
+    todos_end_reset_model: fn(*const TodosQObject),
+    todos_begin_insert_rows: fn(*const TodosQObject, usize, usize),
+    todos_end_insert_rows: fn(*const TodosQObject),
+    todos_begin_remove_rows: fn(*const TodosQObject, usize, usize),
+    todos_end_remove_rows: fn(*const TodosQObject),
+) -> *mut Todos {
+    let todos_emit = TodosEmitter {
+        qobject: Arc::new(Mutex::new(todos)),
+        active_count_changed: active_count_changed,
+        count_changed: count_changed,
+        new_data_ready: todos_new_data_ready,
+    };
+    let model = TodosList {
+        qobject: todos,
+        data_changed: todos_data_changed,
+        begin_reset_model: todos_begin_reset_model,
+        end_reset_model: todos_end_reset_model,
+        begin_insert_rows: todos_begin_insert_rows,
+        end_insert_rows: todos_end_insert_rows,
+        begin_remove_rows: todos_begin_remove_rows,
+        end_remove_rows: todos_end_remove_rows,
+    };
+    let d_todos = Todos::new(todos_emit, model);
+    Box::into_raw(Box::new(d_todos))
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn todos_free(ptr: *mut Todos) {
+    Box::from_raw(ptr).emit().clear();
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn todos_active_count_get(ptr: *const Todos) -> u64 {
+    (&*ptr).active_count()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn todos_count_get(ptr: *const Todos) -> u64 {
+    (&*ptr).count()
+}
+
+#[no_mangle]
+pub extern "C" fn todos_add(ptr: *mut Todos, description_str: *const c_ushort, \
description_len: c_int) -> () { +    let mut description = String::new();
+    set_string_from_utf16(&mut description, description_str, description_len);
+    let o = unsafe { &mut *ptr };
+    let r = o.add(description);
+    r
+}
+
+#[no_mangle]
+pub extern "C" fn todos_clear_completed(ptr: *mut Todos) -> () {
+    let o = unsafe { &mut *ptr };
+    let r = o.clear_completed();
+    r
+}
+
+#[no_mangle]
+pub extern "C" fn todos_remove(ptr: *mut Todos, index: u64) -> bool {
+    let o = unsafe { &mut *ptr };
+    let r = o.remove(index);
+    r
+}
+
+#[no_mangle]
+pub extern "C" fn todos_set_all(ptr: *mut Todos, completed: bool) -> () {
+    let o = unsafe { &mut *ptr };
+    let r = o.set_all(completed);
+    r
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn todos_row_count(ptr: *const Todos) -> c_int {
+    to_c_int((&*ptr).row_count())
+}
+#[no_mangle]
+pub unsafe extern "C" fn todos_insert_rows(ptr: *mut Todos, row: c_int, count: \
c_int) -> bool { +    (&mut *ptr).insert_rows(to_usize(row), to_usize(count))
+}
+#[no_mangle]
+pub unsafe extern "C" fn todos_remove_rows(ptr: *mut Todos, row: c_int, count: \
c_int) -> bool { +    (&mut *ptr).remove_rows(to_usize(row), to_usize(count))
+}
+#[no_mangle]
+pub unsafe extern "C" fn todos_can_fetch_more(ptr: *const Todos) -> bool {
+    (&*ptr).can_fetch_more()
+}
+#[no_mangle]
+pub unsafe extern "C" fn todos_fetch_more(ptr: *mut Todos) {
+    (&mut *ptr).fetch_more()
+}
+#[no_mangle]
+pub unsafe extern "C" fn todos_sort(
+    ptr: *mut Todos,
+    column: u8,
+    order: SortOrder,
+) {
+    (&mut *ptr).sort(column, order)
+}
+
+#[no_mangle]
+pub extern "C" fn todos_data_completed(ptr: *const Todos, row: c_int) -> bool {
+    let o = unsafe { &*ptr };
+    o.completed(to_usize(row)).into()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn todos_set_data_completed(
+    ptr: *mut Todos, row: c_int,
+    v: bool,
+) -> bool {
+    (&mut *ptr).set_completed(to_usize(row), v)
+}
+
+#[no_mangle]
+pub extern "C" fn todos_data_description(
+    ptr: *const Todos, row: c_int,
+    d: *mut QString,
+    set: fn(*mut QString, *const c_char, len: c_int),
+) {
+    let o = unsafe { &*ptr };
+    let data = o.description(to_usize(row));
+    let s: *const c_char = data.as_ptr() as (*const c_char);
+    set(d, s, to_c_int(data.len()));
+}
+
+#[no_mangle]
+pub extern "C" fn todos_set_data_description(
+    ptr: *mut Todos, row: c_int,
+    s: *const c_ushort, len: c_int,
+) -> bool {
+    let o = unsafe { &mut *ptr };
+    let mut v = String::new();
+    set_string_from_utf16(&mut v, s, len);
+    o.set_description(to_usize(row), v)
+}
diff --git a/examples/todos/rust/src/lib.rs b/examples/todos/rust/src/lib.rs
new file mode 100644
index 0000000..d17196f
--- /dev/null
+++ b/examples/todos/rust/src/lib.rs
@@ -0,0 +1,5 @@
+
+extern crate libc;
+
+pub mod interface;
+mod implementation;
diff --git a/examples/todos/src/Bindings.cpp b/examples/todos/src/Bindings.cpp
new file mode 100644
index 0000000..75f8bae
--- /dev/null
+++ b/examples/todos/src/Bindings.cpp
@@ -0,0 +1,320 @@
+/* generated by rust_qt_binding_generator */
+#include "Bindings.h"
+
+namespace {
+
+    struct option_quintptr {
+    public:
+        quintptr value;
+        bool some;
+        operator QVariant() const {
+            if (some) {
+                return QVariant::fromValue(value);
+            }
+            return QVariant();
+        }
+    };
+    static_assert(std::is_pod<option_quintptr>::value, "option_quintptr must be a \
POD type."); +
+    typedef void (*qstring_set)(QString* val, const char* utf8, int nbytes);
+    void set_qstring(QString* val, const char* utf8, int nbytes) {
+        *val = QString::fromUtf8(utf8, nbytes);
+    }
+
+    struct qmodelindex_t {
+        int row;
+        quintptr id;
+    };
+    inline QVariant cleanNullQVariant(const QVariant& v) {
+        return (v.isNull()) ?QVariant() :v;
+    }
+    inline void todosActiveCountChanged(Todos* o)
+    {
+        emit o->activeCountChanged();
+    }
+    inline void todosCountChanged(Todos* o)
+    {
+        emit o->countChanged();
+    }
+}
+extern "C" {
+    bool todos_data_completed(const Todos::Private*, int);
+    bool todos_set_data_completed(Todos::Private*, int, bool);
+    void todos_data_description(const Todos::Private*, int, QString*, qstring_set);
+    bool todos_set_data_description(Todos::Private*, int, const ushort* s, int len);
+    void todos_sort(Todos::Private*, unsigned char column, Qt::SortOrder order = \
Qt::AscendingOrder); +
+    int todos_row_count(const Todos::Private*);
+    bool todos_insert_rows(Todos::Private*, int, int);
+    bool todos_remove_rows(Todos::Private*, int, int);
+    bool todos_can_fetch_more(const Todos::Private*);
+    void todos_fetch_more(Todos::Private*);
+}
+int Todos::columnCount(const QModelIndex &parent) const
+{
+    return (parent.isValid()) ? 0 : 2;
+}
+
+bool Todos::hasChildren(const QModelIndex &parent) const
+{
+    return rowCount(parent) > 0;
+}
+
+int Todos::rowCount(const QModelIndex &parent) const
+{
+    return (parent.isValid()) ? 0 : todos_row_count(m_d);
+}
+
+bool Todos::insertRows(int row, int count, const QModelIndex &)
+{
+    return todos_insert_rows(m_d, row, count);
+}
+
+bool Todos::removeRows(int row, int count, const QModelIndex &)
+{
+    return todos_remove_rows(m_d, row, count);
+}
+
+QModelIndex Todos::index(int row, int column, const QModelIndex &parent) const
+{
+    if (!parent.isValid() && row >= 0 && row < rowCount(parent) && column >= 0 && \
column < 2) { +        return createIndex(row, column, (quintptr)row);
+    }
+    return QModelIndex();
+}
+
+QModelIndex Todos::parent(const QModelIndex &) const
+{
+    return QModelIndex();
+}
+
+bool Todos::canFetchMore(const QModelIndex &parent) const
+{
+    return (parent.isValid()) ? 0 : todos_can_fetch_more(m_d);
+}
+
+void Todos::fetchMore(const QModelIndex &parent)
+{
+    if (!parent.isValid()) {
+        todos_fetch_more(m_d);
+    }
+}
+
+void Todos::sort(int column, Qt::SortOrder order)
+{
+    todos_sort(m_d, column, order);
+}
+Qt::ItemFlags Todos::flags(const QModelIndex &i) const
+{
+    auto flags = QAbstractItemModel::flags(i);
+    if (i.column() == 0) {
+        flags |= Qt::ItemIsEditable;
+    }
+    if (i.column() == 1) {
+        flags |= Qt::ItemIsEditable;
+    }
+    return flags;
+}
+
+bool Todos::completed(int row) const
+{
+    return todos_data_completed(m_d, row);
+}
+
+bool Todos::setCompleted(int row, bool value)
+{
+    bool set = false;
+    set = todos_set_data_completed(m_d, row, value);
+    if (set) {
+        QModelIndex index = createIndex(row, 0, row);
+        emit dataChanged(index, index);
+    }
+    return set;
+}
+
+QString Todos::description(int row) const
+{
+    QString s;
+    todos_data_description(m_d, row, &s, set_qstring);
+    return s;
+}
+
+bool Todos::setDescription(int row, const QString& value)
+{
+    bool set = false;
+    set = todos_set_data_description(m_d, row, value.utf16(), value.length());
+    if (set) {
+        QModelIndex index = createIndex(row, 0, row);
+        emit dataChanged(index, index);
+    }
+    return set;
+}
+
+QVariant Todos::data(const QModelIndex &index, int role) const
+{
+    Q_ASSERT(rowCount(index.parent()) > index.row());
+    switch (index.column()) {
+    case 0:
+        switch (role) {
+        case Qt::DisplayRole:
+        case Qt::UserRole + 0:
+            return QVariant::fromValue(completed(index.row()));
+        case Qt::UserRole + 1:
+            return QVariant::fromValue(description(index.row()));
+        }
+    case 1:
+        switch (role) {
+        case Qt::DisplayRole:
+        case Qt::UserRole + 1:
+            return QVariant::fromValue(description(index.row()));
+        }
+    }
+    return QVariant();
+}
+
+QHash<int, QByteArray> Todos::roleNames() const {
+    QHash<int, QByteArray> names = QAbstractItemModel::roleNames();
+    names.insert(Qt::UserRole + 0, "completed");
+    names.insert(Qt::UserRole + 1, "description");
+    return names;
+}
+QVariant Todos::headerData(int section, Qt::Orientation orientation, int role) const
+{
+    if (orientation != Qt::Horizontal) {
+        return QVariant();
+    }
+    return m_headerData.value(qMakePair(section, (Qt::ItemDataRole)role), role == \
Qt::DisplayRole ?QString::number(section + 1) :QVariant()); +}
+
+bool Todos::setHeaderData(int section, Qt::Orientation orientation, const QVariant \
&value, int role) +{
+    if (orientation != Qt::Horizontal) {
+        return false;
+    }
+    m_headerData.insert(qMakePair(section, (Qt::ItemDataRole)role), value);
+    return true;
+}
+
+bool Todos::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+    if (index.column() == 0) {
+        if (role == Qt::DisplayRole || role == Qt::UserRole + 0) {
+            if (value.canConvert(qMetaTypeId<bool>())) {
+                return setCompleted(index.row(), value.value<bool>());
+            }
+        }
+        if (role == Qt::UserRole + 1) {
+            if (value.canConvert(qMetaTypeId<QString>())) {
+                return setDescription(index.row(), value.value<QString>());
+            }
+        }
+    }
+    if (index.column() == 1) {
+        if (role == Qt::DisplayRole || role == Qt::UserRole + 1) {
+            if (value.canConvert(qMetaTypeId<QString>())) {
+                return setDescription(index.row(), value.value<QString>());
+            }
+        }
+    }
+    return false;
+}
+
+extern "C" {
+    Todos::Private* todos_new(Todos*, void (*)(Todos*), void (*)(Todos*),
+        void (*)(const Todos*),
+        void (*)(Todos*, quintptr, quintptr),
+        void (*)(Todos*),
+        void (*)(Todos*),
+        void (*)(Todos*, int, int),
+        void (*)(Todos*),
+        void (*)(Todos*, int, int),
+        void (*)(Todos*));
+    void todos_free(Todos::Private*);
+    quint64 todos_active_count_get(const Todos::Private*);
+    quint64 todos_count_get(const Todos::Private*);
+    void todos_add(Todos::Private*, const ushort*, int);
+    void todos_clear_completed(Todos::Private*);
+    bool todos_remove(Todos::Private*, quint64);
+    void todos_set_all(Todos::Private*, bool);
+};
+
+Todos::Todos(bool /*owned*/, QObject *parent):
+    QAbstractItemModel(parent),
+    m_d(0),
+    m_ownsPrivate(false)
+{
+    initHeaderData();
+}
+
+Todos::Todos(QObject *parent):
+    QAbstractItemModel(parent),
+    m_d(todos_new(this,
+        todosActiveCountChanged,
+        todosCountChanged,
+        [](const Todos* o) {
+            emit o->newDataReady(QModelIndex());
+        },
+        [](Todos* o, quintptr first, quintptr last) {
+            o->dataChanged(o->createIndex(first, 0, first),
+                       o->createIndex(last, 1, last));
+        },
+        [](Todos* o) {
+            o->beginResetModel();
+        },
+        [](Todos* o) {
+            o->endResetModel();
+        },
+        [](Todos* o, int first, int last) {
+            o->beginInsertRows(QModelIndex(), first, last);
+        },
+        [](Todos* o) {
+            o->endInsertRows();
+        },
+        [](Todos* o, int first, int last) {
+            o->beginRemoveRows(QModelIndex(), first, last);
+        },
+        [](Todos* o) {
+            o->endRemoveRows();
+        }
+)),
+    m_ownsPrivate(true)
+{
+    connect(this, &Todos::newDataReady, this, [this](const QModelIndex& i) {
+        this->fetchMore(i);
+    }, Qt::QueuedConnection);
+    initHeaderData();
+}
+
+Todos::~Todos() {
+    if (m_ownsPrivate) {
+        todos_free(m_d);
+    }
+}
+void Todos::initHeaderData() {
+    m_headerData.insert(qMakePair(0, Qt::DisplayRole), QVariant("completed"));
+    m_headerData.insert(qMakePair(1, Qt::DisplayRole), QVariant("description"));
+}
+quint64 Todos::activeCount() const
+{
+    return todos_active_count_get(m_d);
+}
+quint64 Todos::count() const
+{
+    return todos_count_get(m_d);
+}
+void Todos::add(const QString& description)
+{
+    return todos_add(m_d, description.utf16(), description.size());
+}
+void Todos::clearCompleted()
+{
+    return todos_clear_completed(m_d);
+}
+bool Todos::remove(quint64 index)
+{
+    return todos_remove(m_d, index);
+}
+void Todos::setAll(bool completed)
+{
+    return todos_set_all(m_d, completed);
+}
diff --git a/examples/todos/src/Bindings.h b/examples/todos/src/Bindings.h
new file mode 100644
index 0000000..6f2cab9
--- /dev/null
+++ b/examples/todos/src/Bindings.h
@@ -0,0 +1,62 @@
+/* generated by rust_qt_binding_generator */
+#ifndef BINDINGS_H
+#define BINDINGS_H
+
+#include <QObject>
+#include <QAbstractItemModel>
+
+class Todos;
+
+class Todos : public QAbstractItemModel
+{
+    Q_OBJECT
+public:
+    class Private;
+private:
+    Private * m_d;
+    bool m_ownsPrivate;
+    Q_PROPERTY(quint64 activeCount READ activeCount NOTIFY activeCountChanged FINAL)
+    Q_PROPERTY(quint64 count READ count NOTIFY countChanged FINAL)
+    explicit Todos(bool owned, QObject *parent);
+public:
+    explicit Todos(QObject *parent = nullptr);
+    ~Todos();
+    quint64 activeCount() const;
+    quint64 count() const;
+    Q_INVOKABLE void add(const QString& description);
+    Q_INVOKABLE void clearCompleted();
+    Q_INVOKABLE bool remove(quint64 index);
+    Q_INVOKABLE void setAll(bool completed);
+
+    int columnCount(const QModelIndex &parent = QModelIndex()) const override;
+    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const \
override; +    QModelIndex index(int row, int column, const QModelIndex &parent = \
QModelIndex()) const override; +    QModelIndex parent(const QModelIndex &index) \
const override; +    bool hasChildren(const QModelIndex &parent = QModelIndex()) \
const override; +    int rowCount(const QModelIndex &parent = QModelIndex()) const \
override; +    bool canFetchMore(const QModelIndex &parent) const override;
+    void fetchMore(const QModelIndex &parent) override;
+    Qt::ItemFlags flags(const QModelIndex &index) const override;
+    void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override;
+    QHash<int, QByteArray> roleNames() const override;
+    QVariant headerData(int section, Qt::Orientation orientation, int role = \
Qt::DisplayRole) const override; +    bool setHeaderData(int section, Qt::Orientation \
orientation, const QVariant &value, int role = Qt::EditRole) override; +    \
Q_INVOKABLE bool insertRows(int row, int count, const QModelIndex &parent = \
QModelIndex()) override; +    Q_INVOKABLE bool removeRows(int row, int count, const \
QModelIndex &parent = QModelIndex()) override; +    bool setData(const QModelIndex \
&index, const QVariant &value, int role = Qt::EditRole) override; +    Q_INVOKABLE \
bool completed(int row) const; +    Q_INVOKABLE bool setCompleted(int row, bool \
value); +    Q_INVOKABLE QString description(int row) const;
+    Q_INVOKABLE bool setDescription(int row, const QString& value);
+
+signals:
+    // new data is ready to be made available to the model with fetchMore()
+    void newDataReady(const QModelIndex &parent) const;
+private:
+    QHash<QPair<int,Qt::ItemDataRole>, QVariant> m_headerData;
+    void initHeaderData();
+signals:
+    void activeCountChanged();
+    void countChanged();
+};
+#endif // BINDINGS_H
diff --git a/examples/todos/src/main.cpp b/examples/todos/src/main.cpp
new file mode 100644
index 0000000..5b50148
--- /dev/null
+++ b/examples/todos/src/main.cpp
@@ -0,0 +1,18 @@
+#include "Bindings.h"
+
+#include <QtQml/qqml.h>
+#include <QApplication>
+#include <QQmlApplicationEngine>
+
+int main(int argc, char *argv[])
+{
+    QApplication app(argc, argv);
+    qmlRegisterType<Todos>("RustCode", 1, 0, "Todos");
+
+    QQmlApplicationEngine engine;
+    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
+    if (engine.rootObjects().isEmpty())
+        return -1;
+
+    return app.exec();
+}


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic