Skip to content

Commit

Permalink
Add qml_register_uncreatable_type.
Browse files Browse the repository at this point in the history
This allows for registering structs that don't implement Default,
mirroring C++'s QQmlEngine::qmlRegisterUncreatableType.
  • Loading branch information
mhoff12358 committed Jan 17, 2022
1 parent 7f8c6d6 commit 3991cfd
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 4 deletions.
98 changes: 94 additions & 4 deletions qmetaobject/src/qtdeclarative.rs
Original file line number Diff line number Diff line change
Expand Up @@ -502,16 +502,106 @@ pub fn qml_register_type<T: QObject + Default + Sized>(
})
}

/// Wrapper around [`void qmlRegisterModule(const char *uri, int versionMajor, int versionMinor)`][qt] function.
/// Register the given type as an uncreatable QML type
///
/// [qt]: https://doc.qt.io/qt-5/qqmlengine.html#qmlRegisterModule
#[cfg(qt_5_9)]
pub fn qml_register_module(
/// Refer to the Qt documentation for QQmlEngine::qmlRegisterUncreatableType.
pub fn qml_register_uncreatable_type<T: QObject + Sized>(
uri: &CStr,
version_major: u32,
version_minor: u32,
qml_name: &CStr,
no_creation_reason: &CStr,
) {
let uri_ptr = uri.as_ptr();
let qml_name_ptr = qml_name.as_ptr();
let no_creation_reason_ptr = no_creation_reason.as_ptr();
let meta_object = T::static_meta_object();

extern "C" fn extra_destruct(c: *mut c_void) {
cpp!(unsafe [c as "QObject *"] {
QQmlPrivate::qdeclarativeelement_destructor(c);
})
}

let size = T::cpp_size();

let type_id = <RefCell<T> as PropertyType>::register_type(Default::default());

cpp!(unsafe [
qml_name_ptr as "char *",
uri_ptr as "char *",
version_major as "int",
version_minor as "int",
meta_object as "const QMetaObject *",
size as "size_t",
type_id as "int",
no_creation_reason_ptr as "char *"
] {
// BEGIN: From QML_GETTYPENAMES
// FIXME: list type?
/*const int listLen = int(strlen("QQmlListProperty<"));
QVarLengthArray<char,64> listName(listLen + nameLen + 2);
memcpy(listName.data(), "QQmlListProperty<", size_t(listLen));
memcpy(listName.data()+listLen, className, size_t(nameLen));
listName[listLen+nameLen] = '>';
listName[listLen+nameLen+1] = '\0';*/
// END

int parserStatusCast = meta_object && qmeta_inherits(meta_object, &QQuickItem::staticMetaObject)
? QQmlPrivate::StaticCastSelector<QQuickItem, QQmlParserStatus>::cast()
: -1;

QQmlPrivate::RegisterType api = {
/*version*/ 0,

#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
/*typeId*/ type_id,
#else
/*typeId*/ QMetaType(type_id),
#endif
/*listId*/ {}, // FIXME: list type?
/*objectSize*/ int(size),
/*create*/ nullptr,
#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
/* userdata */ nullptr,
#endif
/*noCreationReason*/ no_creation_reason_ptr,
#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
/* createValueType */ nullptr,
#endif

/*uri*/ uri_ptr,
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
/*versionMajor*/ version_major,
/*versionMinor*/ version_minor,
#else
/*version*/ QTypeRevision::fromVersion(version_major, version_minor),
#endif
/*elementName*/ qml_name_ptr,
/*metaObject*/ meta_object,

/*attachedPropertiesFunction*/ nullptr,
/*attachedPropertiesMetaObject*/ nullptr,

/*parserStatusCast*/ parserStatusCast,
/*valueSourceCast*/ -1,
/*valueInterceptorCast*/ -1,

/*extensionObjectCreate*/ nullptr,
/*extensionMetaObject*/ nullptr,
/*customParser*/ nullptr,
/*revision*/ {} // FIXME: support revisions?
};
QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &api);
})
}

/// Wrapper around [`void qmlRegisterModule(const char *uri, int versionMajor, int versionMinor)`][qt] function.
///
/// [qt]: https://doc.qt.io/qt-5/qqmlengine.html#qmlRegisterModule
#[cfg(qt_5_9)]
pub fn qml_register_module(uri: &CStr, version_major: u32, version_minor: u32) {
let uri_ptr = uri.as_ptr();

cpp!(unsafe [
uri_ptr as "const char *",
Expand Down
50 changes: 50 additions & 0 deletions qmetaobject/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,56 @@ fn register_type() {
));
}

#[derive(QObject)]
struct RegisteredObjWithNoDefault {
base: qt_base_class!(trait QObject),
value: qt_property!(u32),
square: qt_method!(
fn square(&self, v: u32) -> u32 {
self.value * self.internal_value * v
}
),
internal_value: u32,
}

impl RegisteredObjWithNoDefault {
fn new(internal_value: u32) -> RegisteredObjWithNoDefault {
RegisteredObjWithNoDefault {
internal_value: internal_value,
base: Default::default(),
value: Default::default(),
square: Default::default(),
}
}
}

#[test]
fn register_uncreatable_type() {
qml_register_uncreatable_type::<RegisteredObjWithNoDefault>(
CStr::from_bytes_with_nul(b"TestRegisterUncreatable\0").unwrap(),
1,
0,
CStr::from_bytes_with_nul(b"RegisteredObjUncreatable\0").unwrap(),
CStr::from_bytes_with_nul(b"Type has no default\0").unwrap(),
);

let mut obj = RegisteredObjWithNoDefault::new(44);
obj.value = 55;

assert!(do_test(
obj,
r"
import TestRegister 1.0
Item {
function doTest() {
return _obj.square(66) === 44 * 55 * 66;
}
}
"
));
}

#[test]
#[cfg(qt_5_9)]
fn register_module() {
Expand Down

0 comments on commit 3991cfd

Please sign in to comment.