Skip to content

Commit 3991cfd

Browse files
committed
Add qml_register_uncreatable_type.
This allows for registering structs that don't implement Default, mirroring C++'s QQmlEngine::qmlRegisterUncreatableType.
1 parent 7f8c6d6 commit 3991cfd

File tree

2 files changed

+144
-4
lines changed

2 files changed

+144
-4
lines changed

qmetaobject/src/qtdeclarative.rs

Lines changed: 94 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -502,16 +502,106 @@ pub fn qml_register_type<T: QObject + Default + Sized>(
502502
})
503503
}
504504

505-
/// Wrapper around [`void qmlRegisterModule(const char *uri, int versionMajor, int versionMinor)`][qt] function.
505+
/// Register the given type as an uncreatable QML type
506506
///
507-
/// [qt]: https://doc.qt.io/qt-5/qqmlengine.html#qmlRegisterModule
508-
#[cfg(qt_5_9)]
509-
pub fn qml_register_module(
507+
/// Refer to the Qt documentation for QQmlEngine::qmlRegisterUncreatableType.
508+
pub fn qml_register_uncreatable_type<T: QObject + Sized>(
510509
uri: &CStr,
511510
version_major: u32,
512511
version_minor: u32,
512+
qml_name: &CStr,
513+
no_creation_reason: &CStr,
513514
) {
514515
let uri_ptr = uri.as_ptr();
516+
let qml_name_ptr = qml_name.as_ptr();
517+
let no_creation_reason_ptr = no_creation_reason.as_ptr();
518+
let meta_object = T::static_meta_object();
519+
520+
extern "C" fn extra_destruct(c: *mut c_void) {
521+
cpp!(unsafe [c as "QObject *"] {
522+
QQmlPrivate::qdeclarativeelement_destructor(c);
523+
})
524+
}
525+
526+
let size = T::cpp_size();
527+
528+
let type_id = <RefCell<T> as PropertyType>::register_type(Default::default());
529+
530+
cpp!(unsafe [
531+
qml_name_ptr as "char *",
532+
uri_ptr as "char *",
533+
version_major as "int",
534+
version_minor as "int",
535+
meta_object as "const QMetaObject *",
536+
size as "size_t",
537+
type_id as "int",
538+
no_creation_reason_ptr as "char *"
539+
] {
540+
// BEGIN: From QML_GETTYPENAMES
541+
// FIXME: list type?
542+
/*const int listLen = int(strlen("QQmlListProperty<"));
543+
QVarLengthArray<char,64> listName(listLen + nameLen + 2);
544+
memcpy(listName.data(), "QQmlListProperty<", size_t(listLen));
545+
memcpy(listName.data()+listLen, className, size_t(nameLen));
546+
listName[listLen+nameLen] = '>';
547+
listName[listLen+nameLen+1] = '\0';*/
548+
// END
549+
550+
int parserStatusCast = meta_object && qmeta_inherits(meta_object, &QQuickItem::staticMetaObject)
551+
? QQmlPrivate::StaticCastSelector<QQuickItem, QQmlParserStatus>::cast()
552+
: -1;
553+
554+
QQmlPrivate::RegisterType api = {
555+
/*version*/ 0,
556+
557+
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
558+
/*typeId*/ type_id,
559+
#else
560+
/*typeId*/ QMetaType(type_id),
561+
#endif
562+
/*listId*/ {}, // FIXME: list type?
563+
/*objectSize*/ int(size),
564+
/*create*/ nullptr,
565+
#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
566+
/* userdata */ nullptr,
567+
#endif
568+
/*noCreationReason*/ no_creation_reason_ptr,
569+
#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
570+
/* createValueType */ nullptr,
571+
#endif
572+
573+
/*uri*/ uri_ptr,
574+
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
575+
/*versionMajor*/ version_major,
576+
/*versionMinor*/ version_minor,
577+
#else
578+
/*version*/ QTypeRevision::fromVersion(version_major, version_minor),
579+
#endif
580+
/*elementName*/ qml_name_ptr,
581+
/*metaObject*/ meta_object,
582+
583+
/*attachedPropertiesFunction*/ nullptr,
584+
/*attachedPropertiesMetaObject*/ nullptr,
585+
586+
/*parserStatusCast*/ parserStatusCast,
587+
/*valueSourceCast*/ -1,
588+
/*valueInterceptorCast*/ -1,
589+
590+
/*extensionObjectCreate*/ nullptr,
591+
/*extensionMetaObject*/ nullptr,
592+
/*customParser*/ nullptr,
593+
/*revision*/ {} // FIXME: support revisions?
594+
};
595+
QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &api);
596+
})
597+
}
598+
599+
/// Wrapper around [`void qmlRegisterModule(const char *uri, int versionMajor, int versionMinor)`][qt] function.
600+
///
601+
/// [qt]: https://doc.qt.io/qt-5/qqmlengine.html#qmlRegisterModule
602+
#[cfg(qt_5_9)]
603+
pub fn qml_register_module(uri: &CStr, version_major: u32, version_minor: u32) {
604+
let uri_ptr = uri.as_ptr();
515605

516606
cpp!(unsafe [
517607
uri_ptr as "const char *",

qmetaobject/tests/tests.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,56 @@ fn register_type() {
211211
));
212212
}
213213

214+
#[derive(QObject)]
215+
struct RegisteredObjWithNoDefault {
216+
base: qt_base_class!(trait QObject),
217+
value: qt_property!(u32),
218+
square: qt_method!(
219+
fn square(&self, v: u32) -> u32 {
220+
self.value * self.internal_value * v
221+
}
222+
),
223+
internal_value: u32,
224+
}
225+
226+
impl RegisteredObjWithNoDefault {
227+
fn new(internal_value: u32) -> RegisteredObjWithNoDefault {
228+
RegisteredObjWithNoDefault {
229+
internal_value: internal_value,
230+
base: Default::default(),
231+
value: Default::default(),
232+
square: Default::default(),
233+
}
234+
}
235+
}
236+
237+
#[test]
238+
fn register_uncreatable_type() {
239+
qml_register_uncreatable_type::<RegisteredObjWithNoDefault>(
240+
CStr::from_bytes_with_nul(b"TestRegisterUncreatable\0").unwrap(),
241+
1,
242+
0,
243+
CStr::from_bytes_with_nul(b"RegisteredObjUncreatable\0").unwrap(),
244+
CStr::from_bytes_with_nul(b"Type has no default\0").unwrap(),
245+
);
246+
247+
let mut obj = RegisteredObjWithNoDefault::new(44);
248+
obj.value = 55;
249+
250+
assert!(do_test(
251+
obj,
252+
r"
253+
import TestRegister 1.0
254+
255+
Item {
256+
function doTest() {
257+
return _obj.square(66) === 44 * 55 * 66;
258+
}
259+
}
260+
"
261+
));
262+
}
263+
214264
#[test]
215265
#[cfg(qt_5_9)]
216266
fn register_module() {

0 commit comments

Comments
 (0)