[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