Add example Todos application from FOSDEM presentation
https://fosdem.org/2018/schedule/event/rust_qt_binding_generator/master
parent
f4ffeeda91
commit
574440ba58
|
@ -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
|
||||
)
|
|
@ -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
|
||||
```
|
|
@ -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": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>main.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
[package]
|
||||
name = "rust"
|
||||
version = "1.0.0"
|
||||
|
||||
[dependencies]
|
||||
libc = "*"
|
||||
|
||||
[lib]
|
||||
name = "rust"
|
||||
crate-type = ["staticlib"]
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
extern crate libc;
|
||||
|
||||
pub mod interface;
|
||||
mod implementation;
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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();
|
||||
}
|
Loading…
Reference in New Issue