"quick": rename "Lisp.fun()" to "Lisp.call()"; allow optionally passing JS "this" (a QQuickItem) as first argument;

This commit is contained in:
polos 2017-01-26 20:37:44 +01:00
parent 9e09c38766
commit 2aee9968c6
14 changed files with 117 additions and 44 deletions

View file

@ -46,7 +46,7 @@ Please see also the documentation in "qml/example.qml".
TIP
===
In order to have uniform access to QML objects from both JS and Lisp
In order to have uniform access to QQuickItems from both QML and Lisp
functions, it is convenient to set both 'id:' and 'objectName:' to the
same name.

View file

@ -7,12 +7,21 @@
(require :qml-lisp "qml-lisp")
(use-package :qml)
;; for example (5) in "qml/example.qml"
(defun show-properties-dialog ()
(unless (find-package :properties)
(load (in-home "gui/properties")))
(funcall (find-symbol "SHOW" :properties) qml:*caller*))
(defun run ()
;; *quick-view* can be either a QQuickView or a QQuickWidget
(setf qml:*quick-view* (qnew "QQuickView(QUrl)"
(|fromLocalFile.QUrl| "qml/example.qml")))
(|setResizeMode| qml:*quick-view* |QQuickView.SizeRootObjectToView|)
(|resize| qml:*quick-view* '(300 200))
(|resize| qml:*quick-view* '(350 350))
(|show| qml:*quick-view*))
(run)

View file

@ -13,9 +13,10 @@ QObject* ini() {
qmlRegisterSingletonType<Lisp>("EQL5", 1, 0, "EQL5", lisp_provider); }
return lisp; }
QVariant Lisp::apply(const QString& function, const QVariantList& arguments) {
QVariant Lisp::apply(QObject* caller, const QString& function, const QVariantList& arguments) {
QVariant ret =
eql_fun("qml:qml-apply", QVariant::String,
Q_ARG(QObject*, caller),
Q_ARG(QString, function),
Q_ARG(QVariantList, arguments));
return ret; }

View file

@ -17,7 +17,7 @@ class Lisp : public QObject {
Q_OBJECT
public:
Q_INVOKABLE QVariant apply(const QString&, const QVariantList& = QVariantList());
Q_INVOKABLE QVariant apply(QObject*, const QString&, const QVariantList& = QVariantList());
};
QT_END_NAMESPACE

View file

@ -10,11 +10,13 @@
(:nicknames :qml)
(:export
#:*quick-view*
#:*caller*
#:children
#:find-quick-item
#:js
#:qml-get
#:qml-set
#:properties
#:root-item))
(provide :qml-lisp)
@ -22,11 +24,12 @@
(in-package :qml-lisp)
(defvar *qml-lisp* (qload-c++ "lib/qml_lisp"))
(defvar *caller* nil)
(defvar *quick-view* nil)
(defun string-to-symbol (name)
(let* ((upper (string-upcase name))
(p (position #\: name)))
(let ((upper (string-upcase name))
(p (position #\: name)))
(if p
(intern (subseq upper (1+ (position #\: name :from-end t)))
(subseq upper 0 p))
@ -63,10 +66,11 @@
(princ "#<>") ; mark for passing to JS "eval()"
(print-js-readably object)))
(defun qml-apply (function arguments)
"Every 'Lisp.fun()' or 'Lisp.apply()' function call in QML will call this function."
(let ((object (apply (string-to-symbol function)
arguments)))
(defun qml-apply (caller function arguments)
"Every 'Lisp.call()' or 'Lisp.apply()' function call in QML will call this function. The variable *CALLER* will be bound to the calling QQuickItem, if passed with 'this' as first argument to 'Lisp.call' / 'Lisp.apply()'."
(let* ((*caller* (if (qnull caller) nil (qt-object-? caller)))
(object (apply (string-to-symbol function)
arguments)))
(if (stringp object)
object
(print-to-js-string object))))
@ -74,13 +78,14 @@
;;; utils
(defun root-item ()
(|rootObject| *quick-view*))
(when *quick-view*
(|rootObject| *quick-view*)))
(defun find-quick-item (object-name)
"Finds the first QQuickItem matching OBJECT-NAME."
(if (string= (|objectName| (root-item)) object-name)
(root-item)
(qfind-child (root-item) object-name)))
(qt-object-? (qfind-child (root-item) object-name))))
(defun quick-item (item/name)
(if (stringp item/name)
@ -116,7 +121,7 @@
;;; JS
(defun js (item/name js-format-string &rest arguments)
"Evaluates a JS string, with 'this' bound to either ITEM, or first object matching NAME."
"Evaluates a JS string, with 'this' bound to either ITEM, or first object matching NAME. Arguments are passed through FORMAT."
(qlet ((qml-exp "QQmlExpression(QQmlContext*,QObject*,QString)"
(|rootContext| *quick-view*)
(quick-item item/name)

View file

@ -11,32 +11,48 @@ Item {
// Please note:
//
// * to call lisp functions, use either Lisp.fun() or Lisp.apply();
// * to call lisp functions, use either Lisp.call() or Lisp.apply();
// use JS arrays for lists (can be nested);
//
// * return values can be nested Lisp lists or vectors, which will be converted to
// nested JS arrays: they will be prepared in Lisp and passed to JS eval;
//
// * optionally pass 'this' (or any other item) as first argument to either
// Lisp.call() or Lisp.apply(); it can then be accessed in Lisp via qml:*caller*;
// (1) call CL function
console.log(Lisp.fun("format", false, "~R", 123))
console.log(Lisp.call("format", false, "~R", 123))
// (2) call EQL function
Lisp.fun("qmsg", "hello from QML")
Lisp.call("qmsg", "hello from QML")
// (3) pass list argument
console.log(Lisp.fun("x:join", ["11", "55"], ":"))
console.log(Lisp.call("x:join", ["11", "55"], ":"))
// (4) nested list arguments
// N.B: don't get fooled by the printed representation of the return value:
// it's a nested JS array internally
console.log(Lisp.fun("list", [[1, 2, 3], ["a", "b", "c"], 4, 5], 6, [[7, 8], 9]))
console.log(Lisp.call("list", [[1, 2, 3], ["a", "b", "c"], 4, 5], 6, [[7, 8], 9]))
// (5) pass 'this' as first argument (can be accessed in Lisp via qml:*caller*)
Lisp.call(this, "eql-user:show-properties-dialog")
}
Text {
id: label
objectName: "label"
anchors.centerIn: parent
color: "blue"
text: "Lisp enabled QML"
font.bold: true
font.pixelSize: 32
NumberAnimation on rotation {
from: 0; to: 360;
easing.type: Easing.InOutElastic;
duration: 3000;
loops: Animation.Infinite;
}
}
}