From f1dd1d4eacac71e1e100057d0958f853acbfd61d Mon Sep 17 00:00:00 2001 From: Mikhail Zolotukhin Date: Thu, 6 Jan 2022 16:20:57 +0300 Subject: [PATCH 1/3] feat(qmetaobject): add "is" wrappers for special QJSValues --- qmetaobject/src/qtdeclarative.rs | 48 ++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/qmetaobject/src/qtdeclarative.rs b/qmetaobject/src/qtdeclarative.rs index 55605b40..41520cb4 100644 --- a/qmetaobject/src/qtdeclarative.rs +++ b/qmetaobject/src/qtdeclarative.rs @@ -926,6 +926,16 @@ cpp_class!( pub unsafe struct QJSValue as "QJSValue" ); +/// Wrapper for [`QJSValue::SpecialValue`][qt] +/// +/// [qt]: https://doc.qt.io/qt-5/qjsvalue.html#SpecialValue-enum +#[repr(u32)] +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum QJSValueSpecialValue { + NullValue = 0, + UndefinedValue = 1, +} + impl QJSValue { pub fn is_bool(&self) -> bool { cpp!(unsafe [self as "const QJSValue *"] -> bool as "bool" { @@ -933,6 +943,12 @@ impl QJSValue { }) } + pub fn is_null(&self) -> bool { + cpp!(unsafe [self as "const QJSValue *"] -> bool as "bool" { + return self->isNull(); + }) + } + pub fn is_number(&self) -> bool { cpp!(unsafe [self as "const QJSValue *"] -> bool as "bool" { return self->isNumber(); @@ -945,6 +961,12 @@ impl QJSValue { }) } + pub fn is_undefined(&self) -> bool { + cpp!(unsafe [self as "const QJSValue *"] -> bool as "bool" { + return self->isUndefined(); + }) + } + pub fn to_string(&self) -> QString { cpp!(unsafe [self as "const QJSValue *"] -> QString as "QString" { return self->toString(); @@ -1026,6 +1048,14 @@ impl From for QJSValue { } } +impl From for QJSValue { + fn from(a: QJSValueSpecialValue) -> QJSValue { + cpp!(unsafe [a as "QJSValue::SpecialValue"] -> QJSValue as "QJSValue" { + return QJSValue(a); + }) + } +} + impl QMetaType for QJSValue { fn register(_name: Option<&CStr>) -> i32 { cpp!(unsafe [] -> i32 as "int" { return qMetaTypeId(); }) @@ -1053,6 +1083,15 @@ mod qjsvalue_tests { assert!(!num_value.is_bool()); } + #[test] + fn test_is_null() { + let null_value = QJSValue::from(QJSValueSpecialValue::NullValue); + let num_value = QJSValue::from(42); + + assert!(null_value.is_null()); + assert!(!num_value.is_null()); + } + #[test] fn test_is_number() { let string_value = QJSValue::from(QString::from("Konqui")); @@ -1071,6 +1110,15 @@ mod qjsvalue_tests { assert!(!num_value.is_string()); } + #[test] + fn test_is_undefined() { + let undefined_value = QJSValue::from(QJSValueSpecialValue::UndefinedValue); + let num_value = QJSValue::from(42); + + assert!(undefined_value.is_undefined()); + assert!(!num_value.is_undefined()); + } + #[test] fn test_qvariantlist_from_iter() { let v = vec![1u32, 2u32, 3u32]; From 9c5ba849f05bbee368b3746bf16fbbef748132fc Mon Sep 17 00:00:00 2001 From: Mikhail Zolotukhin Date: Sat, 8 Jan 2022 22:23:50 +0300 Subject: [PATCH 2/3] feat(qmetaobject): add null and undefined functions to QJSValue --- qmetaobject/src/qtdeclarative.rs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/qmetaobject/src/qtdeclarative.rs b/qmetaobject/src/qtdeclarative.rs index 41520cb4..c63bca68 100644 --- a/qmetaobject/src/qtdeclarative.rs +++ b/qmetaobject/src/qtdeclarative.rs @@ -506,11 +506,7 @@ pub fn qml_register_type( /// /// [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, -) { +pub fn qml_register_module(uri: &CStr, version_major: u32, version_minor: u32) { let uri_ptr = uri.as_ptr(); cpp!(unsafe [ @@ -937,6 +933,18 @@ pub enum QJSValueSpecialValue { } impl QJSValue { + pub fn null() -> Self { + cpp!(unsafe [] -> QJSValue as "QJSValue" { + return QJSValue(QJSValue::SpecialValue::NullValue); + }) + } + + pub fn undefined() -> Self { + cpp!(unsafe [] -> QJSValue as "QJSValue" { + return QJSValue(QJSValue::SpecialValue::UndefinedValue); + }) + } + pub fn is_bool(&self) -> bool { cpp!(unsafe [self as "const QJSValue *"] -> bool as "bool" { return self->isBool(); @@ -1113,9 +1121,11 @@ mod qjsvalue_tests { #[test] fn test_is_undefined() { let undefined_value = QJSValue::from(QJSValueSpecialValue::UndefinedValue); + let default_value = QJSValue::default(); let num_value = QJSValue::from(42); assert!(undefined_value.is_undefined()); + assert!(default_value.is_undefined()); assert!(!num_value.is_undefined()); } From 7c1a37b6601d2450910279979df0474f878866da Mon Sep 17 00:00:00 2001 From: Mikhail Zolotukhin Date: Tue, 11 Jan 2022 03:29:29 +0300 Subject: [PATCH 3/3] feat(qmetaobject): add call & is_callable to QJSValue --- qmetaobject/src/qtdeclarative.rs | 173 +++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) diff --git a/qmetaobject/src/qtdeclarative.rs b/qmetaobject/src/qtdeclarative.rs index c63bca68..c6ced4d2 100644 --- a/qmetaobject/src/qtdeclarative.rs +++ b/qmetaobject/src/qtdeclarative.rs @@ -1,3 +1,6 @@ +use std::iter::FromIterator; +use std::ops::{Index, IndexMut}; + /* Copyright (C) 2018 Olivier Goffart Permission is hereby granted, free of charge, to any person obtaining a copy of this software and @@ -195,6 +198,15 @@ impl QmlEngine { }) } + /// Evaluates the expression `program` using [`QJSEngine::evaluate()`][qt-qjs] + /// + /// [qt-qjs]: https://doc.qt.io/qt-5/qjsengine.html#evaluate + pub fn evaluate(&self, program: QString) -> QJSValue { + cpp!(unsafe [self as "QmlEngineHolder *", program as "QString"] -> QJSValue as "QJSValue" { + return self->engine->evaluate(program); + }) + } + pub fn invoke_method(&mut self, name: QByteArray, args: &[QVariant]) -> QVariant { let args_size = args.len(); let args_ptr = args.as_ptr(); @@ -945,12 +957,24 @@ impl QJSValue { }) } + pub fn call(&self, args: QJSValueList) -> QJSValue { + cpp!(unsafe [self as "QJSValue *", args as "QJSValueList"] -> QJSValue as "QJSValue" { + return self->call(args); + }) + } + pub fn is_bool(&self) -> bool { cpp!(unsafe [self as "const QJSValue *"] -> bool as "bool" { return self->isBool(); }) } + pub fn is_callable(&self) -> bool { + cpp!(unsafe [self as "const QJSValue *"] -> bool as "bool" { + return self->isCallable(); + }) + } + pub fn is_null(&self) -> bool { cpp!(unsafe [self as "const QJSValue *"] -> bool as "bool" { return self->isNull(); @@ -1082,6 +1106,18 @@ mod qjsvalue_tests { assert_eq!(foo.to_variant().to_qbytearray(), "45".into()); } + #[test] + fn test_call() { + let engine = QmlEngine::new(); + let func = engine.evaluate(QString::from("(function(a, b){ return a + b; })")); + + let arg_list: QJSValueList = vec![11, 31].into_iter().collect(); + + let call_res = func.call(arg_list); + + assert_eq!(call_res.to_number(), 42 as f64); + } + #[test] fn test_is_bool() { let bool_value = QJSValue::from(true); @@ -1091,6 +1127,17 @@ mod qjsvalue_tests { assert!(!num_value.is_bool()); } + #[test] + fn test_is_callable() { + let engine = QmlEngine::new(); + + let eval_result = engine.evaluate(QString::from("(function(){})")); + let num_value = QJSValue::from(42); + + assert!(eval_result.is_callable()); + assert!(!num_value.is_callable()); + } + #[test] fn test_is_null() { let null_value = QJSValue::from(QJSValueSpecialValue::NullValue); @@ -1138,6 +1185,132 @@ mod qjsvalue_tests { } } +cpp_class!( + /// Wrapper around [`QJSValueList`][type] typedef. + /// + /// [type]: https://doc.qt.io/qt-5/qjsvalue.html#QJSValueList-typedef + pub unsafe struct QJSValueList as "QJSValueList" +); + +impl QJSValueList { + /// Wrapper around [`append(const T &)`][method] method. + /// + /// [method]: https://doc.qt.io/qt-5/qlist.html#append + pub fn push(&mut self, value: QJSValue) { + cpp!(unsafe [self as "QJSValueList*", value as "QJSValue"] { + self->append(std::move(value)); + }) + } + + /// Wrapper around [`insert(int, const QVariant &)`][method] method. + /// + /// [method]: https://doc.qt.io/qt-5/qlist.html#insert + pub fn insert(&mut self, index: usize, element: QJSValue) { + cpp!(unsafe [self as "QJSValueList*", index as "size_t", element as "QJSValue"] { + self->insert(index, std::move(element)); + }) + } + + /// Wrapper around [`takeAt(int)`][method] method. + /// + /// [method]: https://doc.qt.io/qt-5/qlist.html#takeAt + pub fn remove(&mut self, index: usize) -> QJSValue { + cpp!(unsafe [self as "QJSValueList*", index as "size_t"] -> QJSValue as "QJSValue" { + return self->takeAt(index); + }) + } + + /// Wrapper around [`size()`][method] method. + /// + /// [method]: https://doc.qt.io/qt-5/qlist.html#size + pub fn len(&self) -> usize { + cpp!(unsafe [self as "QJSValueList*"] -> usize as "size_t" { + return self->size(); + }) + } + + /// Wrapper around [`isEmpty()`][method] method. + /// + /// [method]: https://doc.qt.io/qt-5/qlist.html#isEmpty + pub fn is_empty(&self) -> bool { + cpp!(unsafe [self as "QJSValueList*"] -> bool as "bool" { + return self->isEmpty(); + }) + } +} + +impl Index for QJSValueList { + type Output = QJSValue; + + /// Wrapper around [`at(int)`][method] method. + /// + /// [method]: https://doc.qt.io/qt-5/qlist.html#at + fn index(&self, index: usize) -> &QJSValue { + assert!(index < self.len()); + unsafe { + &*cpp!([self as "QJSValueList*", index as "size_t"] -> *const QJSValue as "const QJSValue*" { + return &self->at(index); + }) + } + } +} +impl IndexMut for QJSValueList { + /// Wrapper around [`operator[](int)`][method] operator method. + /// + /// [method]: https://doc.qt.io/qt-5/qlist.html#operator-5b-5d + fn index_mut(&mut self, index: usize) -> &mut QJSValue { + assert!(index < self.len()); + unsafe { + &mut *cpp!([self as "QJSValueList*", index as "size_t"] -> *mut QJSValue as "QJSValue*" { + return &(*self)[index]; + }) + } + } +} + +/// Internal class used to iterate over a [`QJSValueList`][] +/// +/// [`QJSValueList`]: ./struct.QJSValueList.html +pub struct QJSValueListIterator<'a> { + list: &'a QJSValueList, + index: usize, + size: usize, +} + +impl<'a> Iterator for QJSValueListIterator<'a> { + type Item = &'a QJSValue; + fn next(&mut self) -> Option<&'a QJSValue> { + if self.index == self.size { + None + } else { + self.index += 1; + Some(&self.list[self.index - 1]) + } + } +} + +impl<'a> IntoIterator for &'a QJSValueList { + type Item = &'a QJSValue; + type IntoIter = QJSValueListIterator<'a>; + + fn into_iter(self) -> QJSValueListIterator<'a> { + QJSValueListIterator::<'a> { list: self, index: 0, size: self.len() } + } +} + +impl FromIterator for QJSValueList +where + T: Into, +{ + fn from_iter>(iter: I) -> QJSValueList { + let mut l = QJSValueList::default(); + for i in iter { + l.push(i.into()); + } + l + } +} + /// A QObject-like trait to inherit from QQmlExtensionPlugin. /// /// Refer to the Qt documentation of QQmlExtensionPlugin