KDAB / cxx-qt

Safe interop between Rust and Qt
https://kdab.github.io/cxx-qt/book/
1.07k stars 76 forks source link

Q_INVOKEABLE not added despite being marked #[qinvokeable] #1037

Closed SanderVocke closed 2 months ago

SanderVocke commented 2 months ago

See the following code. It is a bit long, but the essential part is only:

I am guessing this is not working because this type is not registered as a metatype. I am continuing to debug it.

But what I regard as a bug regardless of the root cause here, is that the generated code does not put Q_INVOKEABLE on the method in the generated class, even though I marked it as such in the bridge definition. I think that whatever the cause is, this should at least generate an error in the qinvokeable macro but not fail silently.

On a side note, cxx-qt-lib provides QMap_QString_QVariant and QVariant, but not the construction of the latter from the former or interpretation of the latter as the former. Maybe those conversions should be added.

use crate::logging::macros::*;
shoop_log_unit!("Frontend.TestPort");

pub mod constants {
}

#[cxx_qt::bridge(cxx_file_stem="qobj_test_port")]
pub mod ffi {
    unsafe extern "C++" {
        include!("cxx-qt-lib-shoop/qquickitem.h");
        type QQuickItem = crate::cxx_qt_lib_shoop::qquickitem::QQuickItem;
        type QObject = crate::cxx_qt_lib_shoop::qobject::QObject;

        include!("cxx-qt-lib/qmap.h");
        type QMap_QString_QVariant = cxx_qt_lib::QMap<cxx_qt_lib::QMapPair_QString_QVariant>;

        include!("cxx-qt-lib-shoop/qjsonobject.h");
        type QJsonObject = crate::cxx_qt_lib_shoop::qjsonobject::QJsonObject;
    }

    unsafe extern "RustQt" {
        #[qobject]
        #[base = "QQuickItem"]
        #[qproperty(QMap_QString_QVariant, connections_state)]
        type TestPort = super::TestPortRust;

        #[qinvokeable]
        fn determine_connections_state(self: Pin<&mut TestPort>) -> QMap_QString_QVariant;
    }

    unsafe extern "C++" {
        include!("cxx-qt-lib-shoop/qquickitem.h");

        #[rust_name="qquickitem_from_ref_test_port"]
        unsafe fn qquickitemFromRef(obj : &TestPort) -> &QQuickItem;
        #[rust_name="qquickitem_from_ptr_test_port"]
        unsafe fn qquickitemFromPtr(obj : *mut TestPort) -> *mut QQuickItem;

        include!("cxx-qt-shoop/make_unique.h");
        #[rust_name = "make_unique_test_port"]
        fn make_unique() -> UniquePtr<TestPort>;
    }

    impl cxx_qt::Constructor<(*mut QQuickItem,), NewArguments=()> for TestPort {}
    impl cxx_qt::Constructor<(), NewArguments=()> for TestPort {}
}

pub use ffi::make_unique_test_port as make_unique;
pub use ffi::TestPort;
use crate::cxx_qt_lib_shoop::qquickitem::{AsQQuickItem, IsQQuickItem};
use ffi::{QQuickItem, QMap_QString_QVariant, QJsonObject};
use std::pin::Pin;
use cxx::UniquePtr;

#[derive(Default)]
pub struct TestPortRust {
    connections_state : QMap_QString_QVariant,
}

impl cxx_qt::Constructor<(*mut QQuickItem,)> for TestPort {
    type BaseArguments = (*mut QQuickItem,); // Will be passed to the base class constructor
    type InitializeArguments = (); // Will be passed to the "initialize" function
    type NewArguments = (); // Will be passed to the "new" function

    fn route_arguments(args: (*mut QQuickItem,)) -> (
        Self::NewArguments,
        Self::BaseArguments,
        Self::InitializeArguments
    ) { ((), args, ()) }

    fn new(_args : ()) -> TestPortRust { TestPortRust::default() }
}

impl cxx_qt::Constructor<()> for TestPort {
    type BaseArguments = (); // Will be passed to the base class constructor
    type InitializeArguments = (); // Will be passed to the "initialize" function
    type NewArguments = (); // Will be passed to the "new" function

    fn route_arguments(_args: ()) -> (
        Self::NewArguments,
        Self::BaseArguments,
        Self::InitializeArguments
    ) { ((), (), ()) }

    fn new(_args : ()) -> TestPortRust { TestPortRust::default() }
}

impl AsQQuickItem for TestPort {
    unsafe fn as_qquickitem_ptr (self : Pin<&mut Self>) -> *mut QQuickItem {
        let ptr = self.get_unchecked_mut() as *mut Self;
        return ffi::qquickitem_from_ptr_test_port(ptr);
    }

    unsafe fn as_qquickitem_ref (self : &Self) -> &QQuickItem {
        return ffi::qquickitem_from_ref_test_port(self);
    }
}

impl IsQQuickItem for TestPort {}

impl TestPort {
    pub fn set_connections_state_from_json(self : Pin<&mut Self>, json : &str) -> Result<(), String> {
        let json_obj : UniquePtr<QJsonObject> = QJsonObject::from_json(json)?;
        let map : QMap_QString_QVariant = json_obj.as_ref()
            .unwrap().to_variant_map()?;
        self.set_connections_state(map);
        Ok(())
    }

    fn determine_connections_state(self: Pin<&mut TestPort>) -> QMap_QString_QVariant {
        self.connections_state().clone()
    }
}
ahayzen-kdab commented 2 months ago

I think you may have a typo it should be #[qinvokable] not #[qinvokeable], but we also should improve the errors so that unknown attributes error (can't remember if we have a bug already for this but it has been discussed between us at some point that that is what we should do).

SanderVocke commented 2 months ago

Whoops, of course! Thank you and closing.