Skip to content

Commit e1ca288

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 e1ca288

File tree

2 files changed

+143
-4
lines changed

2 files changed

+143
-4
lines changed

qmetaobject/src/qtdeclarative.rs

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

516605
cpp!(unsafe [
517606
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+
QString::from("Type has no default"),
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)