mirror of
https://gitlab.com/eql/EQL5.git
synced 2025-12-09 20:01:10 -08:00
"quick": rename "Lisp.fun()" to "Lisp.call()"; allow optionally passing JS "this" (a QQuickItem) as first argument;
This commit is contained in:
parent
9e09c38766
commit
2aee9968c6
14 changed files with 117 additions and 44 deletions
|
|
@ -35,11 +35,14 @@ Try the following:
|
||||||
Since the logic of the buttons is defined in the QML file, changes to
|
Since the logic of the buttons is defined in the QML file, changes to
|
||||||
properties will be reflected immediately by the UI.
|
properties will be reflected immediately by the UI.
|
||||||
|
|
||||||
|
Please see also "../qml-lisp/qml/example.lisp" for documentation on calling
|
||||||
|
Lisp functions from QML.
|
||||||
|
|
||||||
|
|
||||||
TIP
|
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
|
functions, it is convenient to set both 'id:' and 'objectName:' to the
|
||||||
same name.
|
same name.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,10 @@ QObject* ini() {
|
||||||
qmlRegisterSingletonType<Lisp>("EQL5", 1, 0, "EQL5", lisp_provider); }
|
qmlRegisterSingletonType<Lisp>("EQL5", 1, 0, "EQL5", lisp_provider); }
|
||||||
return lisp; }
|
return lisp; }
|
||||||
|
|
||||||
QVariant Lisp::apply(const QString& function, const QVariantList& arguments) {
|
QVariant Lisp::apply(QObject* caller, const QString& function, const QVariantList& arguments) {
|
||||||
QVariant ret =
|
QVariant ret =
|
||||||
eql_fun("qml:qml-apply", QVariant::String,
|
eql_fun("qml:qml-apply", QVariant::String,
|
||||||
|
Q_ARG(QObject*, caller),
|
||||||
Q_ARG(QString, function),
|
Q_ARG(QString, function),
|
||||||
Q_ARG(QVariantList, arguments));
|
Q_ARG(QVariantList, arguments));
|
||||||
return ret; }
|
return ret; }
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ class Lisp : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Q_INVOKABLE QVariant apply(const QString&, const QVariantList& = QVariantList());
|
Q_INVOKABLE QVariant apply(QObject*, const QString&, const QVariantList& = QVariantList());
|
||||||
};
|
};
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,13 @@
|
||||||
(:nicknames :qml)
|
(:nicknames :qml)
|
||||||
(:export
|
(:export
|
||||||
#:*quick-view*
|
#:*quick-view*
|
||||||
|
#:*caller*
|
||||||
#:children
|
#:children
|
||||||
#:find-quick-item
|
#:find-quick-item
|
||||||
#:js
|
#:js
|
||||||
#:qml-get
|
#:qml-get
|
||||||
#:qml-set
|
#:qml-set
|
||||||
|
#:properties
|
||||||
#:root-item))
|
#:root-item))
|
||||||
|
|
||||||
(provide :qml-lisp)
|
(provide :qml-lisp)
|
||||||
|
|
@ -22,10 +24,11 @@
|
||||||
(in-package :qml-lisp)
|
(in-package :qml-lisp)
|
||||||
|
|
||||||
(defvar *qml-lisp* (qload-c++ "lib/qml_lisp"))
|
(defvar *qml-lisp* (qload-c++ "lib/qml_lisp"))
|
||||||
|
(defvar *caller* nil)
|
||||||
(defvar *quick-view* nil)
|
(defvar *quick-view* nil)
|
||||||
|
|
||||||
(defun string-to-symbol (name)
|
(defun string-to-symbol (name)
|
||||||
(let* ((upper (string-upcase name))
|
(let ((upper (string-upcase name))
|
||||||
(p (position #\: name)))
|
(p (position #\: name)))
|
||||||
(if p
|
(if p
|
||||||
(intern (subseq upper (1+ (position #\: name :from-end t)))
|
(intern (subseq upper (1+ (position #\: name :from-end t)))
|
||||||
|
|
@ -63,9 +66,10 @@
|
||||||
(princ "#<>") ; mark for passing to JS "eval()"
|
(princ "#<>") ; mark for passing to JS "eval()"
|
||||||
(print-js-readably object)))
|
(print-js-readably object)))
|
||||||
|
|
||||||
(defun qml-apply (function arguments)
|
(defun qml-apply (caller function arguments)
|
||||||
"Every 'Lisp.fun()' or 'Lisp.apply()' function call in QML will call this function."
|
"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 ((object (apply (string-to-symbol function)
|
(let* ((*caller* (if (qnull caller) nil (qt-object-? caller)))
|
||||||
|
(object (apply (string-to-symbol function)
|
||||||
arguments)))
|
arguments)))
|
||||||
(if (stringp object)
|
(if (stringp object)
|
||||||
object
|
object
|
||||||
|
|
@ -74,13 +78,14 @@
|
||||||
;;; utils
|
;;; utils
|
||||||
|
|
||||||
(defun root-item ()
|
(defun root-item ()
|
||||||
(|rootObject| *quick-view*))
|
(when *quick-view*
|
||||||
|
(|rootObject| *quick-view*)))
|
||||||
|
|
||||||
(defun find-quick-item (object-name)
|
(defun find-quick-item (object-name)
|
||||||
"Finds the first QQuickItem matching OBJECT-NAME."
|
"Finds the first QQuickItem matching OBJECT-NAME."
|
||||||
(if (string= (|objectName| (root-item)) object-name)
|
(if (string= (|objectName| (root-item)) object-name)
|
||||||
(root-item)
|
(root-item)
|
||||||
(qfind-child (root-item) object-name)))
|
(qt-object-? (qfind-child (root-item) object-name))))
|
||||||
|
|
||||||
(defun quick-item (item/name)
|
(defun quick-item (item/name)
|
||||||
(if (stringp item/name)
|
(if (stringp item/name)
|
||||||
|
|
@ -116,7 +121,7 @@
|
||||||
;;; JS
|
;;; JS
|
||||||
|
|
||||||
(defun js (item/name js-format-string &rest arguments)
|
(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)"
|
(qlet ((qml-exp "QQmlExpression(QQmlContext*,QObject*,QString)"
|
||||||
(|rootContext| *quick-view*)
|
(|rootContext| *quick-view*)
|
||||||
(quick-item item/name)
|
(quick-item item/name)
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ Rectangle {
|
||||||
width: board.width / 3
|
width: board.width / 3
|
||||||
height: board.height / 3
|
height: board.height / 3
|
||||||
|
|
||||||
onClicked: { Lisp.fun("tic-tac-clicked", index) }
|
onClicked: { Lisp.call("tic-tac-clicked", index) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -76,7 +76,7 @@ Rectangle {
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
running: messageDisplay.visible
|
running: messageDisplay.visible
|
||||||
onTriggered: { Lisp.fun("restart-game") }
|
onTriggered: { Lisp.call("restart-game") }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ Please see also the documentation in "qml/example.qml".
|
||||||
TIP
|
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
|
functions, it is convenient to set both 'id:' and 'objectName:' to the
|
||||||
same name.
|
same name.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,21 @@
|
||||||
|
|
||||||
(require :qml-lisp "qml-lisp")
|
(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 ()
|
(defun run ()
|
||||||
;; *quick-view* can be either a QQuickView or a QQuickWidget
|
;; *quick-view* can be either a QQuickView or a QQuickWidget
|
||||||
(setf qml:*quick-view* (qnew "QQuickView(QUrl)"
|
(setf qml:*quick-view* (qnew "QQuickView(QUrl)"
|
||||||
(|fromLocalFile.QUrl| "qml/example.qml")))
|
(|fromLocalFile.QUrl| "qml/example.qml")))
|
||||||
(|setResizeMode| qml:*quick-view* |QQuickView.SizeRootObjectToView|)
|
(|setResizeMode| qml:*quick-view* |QQuickView.SizeRootObjectToView|)
|
||||||
(|resize| qml:*quick-view* '(300 200))
|
(|resize| qml:*quick-view* '(350 350))
|
||||||
(|show| qml:*quick-view*))
|
(|show| qml:*quick-view*))
|
||||||
|
|
||||||
(run)
|
(run)
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,10 @@ QObject* ini() {
|
||||||
qmlRegisterSingletonType<Lisp>("EQL5", 1, 0, "EQL5", lisp_provider); }
|
qmlRegisterSingletonType<Lisp>("EQL5", 1, 0, "EQL5", lisp_provider); }
|
||||||
return lisp; }
|
return lisp; }
|
||||||
|
|
||||||
QVariant Lisp::apply(const QString& function, const QVariantList& arguments) {
|
QVariant Lisp::apply(QObject* caller, const QString& function, const QVariantList& arguments) {
|
||||||
QVariant ret =
|
QVariant ret =
|
||||||
eql_fun("qml:qml-apply", QVariant::String,
|
eql_fun("qml:qml-apply", QVariant::String,
|
||||||
|
Q_ARG(QObject*, caller),
|
||||||
Q_ARG(QString, function),
|
Q_ARG(QString, function),
|
||||||
Q_ARG(QVariantList, arguments));
|
Q_ARG(QVariantList, arguments));
|
||||||
return ret; }
|
return ret; }
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ class Lisp : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Q_INVOKABLE QVariant apply(const QString&, const QVariantList& = QVariantList());
|
Q_INVOKABLE QVariant apply(QObject*, const QString&, const QVariantList& = QVariantList());
|
||||||
};
|
};
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,13 @@
|
||||||
(:nicknames :qml)
|
(:nicknames :qml)
|
||||||
(:export
|
(:export
|
||||||
#:*quick-view*
|
#:*quick-view*
|
||||||
|
#:*caller*
|
||||||
#:children
|
#:children
|
||||||
#:find-quick-item
|
#:find-quick-item
|
||||||
#:js
|
#:js
|
||||||
#:qml-get
|
#:qml-get
|
||||||
#:qml-set
|
#:qml-set
|
||||||
|
#:properties
|
||||||
#:root-item))
|
#:root-item))
|
||||||
|
|
||||||
(provide :qml-lisp)
|
(provide :qml-lisp)
|
||||||
|
|
@ -22,10 +24,11 @@
|
||||||
(in-package :qml-lisp)
|
(in-package :qml-lisp)
|
||||||
|
|
||||||
(defvar *qml-lisp* (qload-c++ "lib/qml_lisp"))
|
(defvar *qml-lisp* (qload-c++ "lib/qml_lisp"))
|
||||||
|
(defvar *caller* nil)
|
||||||
(defvar *quick-view* nil)
|
(defvar *quick-view* nil)
|
||||||
|
|
||||||
(defun string-to-symbol (name)
|
(defun string-to-symbol (name)
|
||||||
(let* ((upper (string-upcase name))
|
(let ((upper (string-upcase name))
|
||||||
(p (position #\: name)))
|
(p (position #\: name)))
|
||||||
(if p
|
(if p
|
||||||
(intern (subseq upper (1+ (position #\: name :from-end t)))
|
(intern (subseq upper (1+ (position #\: name :from-end t)))
|
||||||
|
|
@ -63,9 +66,10 @@
|
||||||
(princ "#<>") ; mark for passing to JS "eval()"
|
(princ "#<>") ; mark for passing to JS "eval()"
|
||||||
(print-js-readably object)))
|
(print-js-readably object)))
|
||||||
|
|
||||||
(defun qml-apply (function arguments)
|
(defun qml-apply (caller function arguments)
|
||||||
"Every 'Lisp.fun()' or 'Lisp.apply()' function call in QML will call this function."
|
"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 ((object (apply (string-to-symbol function)
|
(let* ((*caller* (if (qnull caller) nil (qt-object-? caller)))
|
||||||
|
(object (apply (string-to-symbol function)
|
||||||
arguments)))
|
arguments)))
|
||||||
(if (stringp object)
|
(if (stringp object)
|
||||||
object
|
object
|
||||||
|
|
@ -74,13 +78,14 @@
|
||||||
;;; utils
|
;;; utils
|
||||||
|
|
||||||
(defun root-item ()
|
(defun root-item ()
|
||||||
(|rootObject| *quick-view*))
|
(when *quick-view*
|
||||||
|
(|rootObject| *quick-view*)))
|
||||||
|
|
||||||
(defun find-quick-item (object-name)
|
(defun find-quick-item (object-name)
|
||||||
"Finds the first QQuickItem matching OBJECT-NAME."
|
"Finds the first QQuickItem matching OBJECT-NAME."
|
||||||
(if (string= (|objectName| (root-item)) object-name)
|
(if (string= (|objectName| (root-item)) object-name)
|
||||||
(root-item)
|
(root-item)
|
||||||
(qfind-child (root-item) object-name)))
|
(qt-object-? (qfind-child (root-item) object-name))))
|
||||||
|
|
||||||
(defun quick-item (item/name)
|
(defun quick-item (item/name)
|
||||||
(if (stringp item/name)
|
(if (stringp item/name)
|
||||||
|
|
@ -116,7 +121,7 @@
|
||||||
;;; JS
|
;;; JS
|
||||||
|
|
||||||
(defun js (item/name js-format-string &rest arguments)
|
(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)"
|
(qlet ((qml-exp "QQmlExpression(QQmlContext*,QObject*,QString)"
|
||||||
(|rootContext| *quick-view*)
|
(|rootContext| *quick-view*)
|
||||||
(quick-item item/name)
|
(quick-item item/name)
|
||||||
|
|
|
||||||
|
|
@ -11,32 +11,48 @@ Item {
|
||||||
|
|
||||||
// Please note:
|
// 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);
|
// use JS arrays for lists (can be nested);
|
||||||
//
|
//
|
||||||
// * return values can be nested Lisp lists or vectors, which will be converted to
|
// * 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;
|
// 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
|
// (1) call CL function
|
||||||
console.log(Lisp.fun("format", false, "~R", 123))
|
console.log(Lisp.call("format", false, "~R", 123))
|
||||||
|
|
||||||
// (2) call EQL function
|
// (2) call EQL function
|
||||||
Lisp.fun("qmsg", "hello from QML")
|
Lisp.call("qmsg", "hello from QML")
|
||||||
|
|
||||||
// (3) pass list argument
|
// (3) pass list argument
|
||||||
console.log(Lisp.fun("x:join", ["11", "55"], ":"))
|
console.log(Lisp.call("x:join", ["11", "55"], ":"))
|
||||||
|
|
||||||
// (4) nested list arguments
|
// (4) nested list arguments
|
||||||
// N.B: don't get fooled by the printed representation of the return value:
|
// N.B: don't get fooled by the printed representation of the return value:
|
||||||
// it's a nested JS array internally
|
// 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 {
|
Text {
|
||||||
id: label
|
id: label
|
||||||
objectName: "label"
|
objectName: "label"
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
color: "blue"
|
color: "blue"
|
||||||
text: "Lisp enabled QML"
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
|
||||||
const char EQL::version[] = "17.1.8"; // Jan 2017
|
const char EQL::version[] = "17.1.9"; // Jan 2017
|
||||||
|
|
||||||
extern "C" void ini_EQL(cl_object);
|
extern "C" void ini_EQL(cl_object);
|
||||||
|
|
||||||
|
|
|
||||||
44
src/eql5.js
44
src/eql5.js
|
|
@ -1,4 +1,19 @@
|
||||||
// helper functions for convenient QML/EQL5 integration
|
//
|
||||||
|
// Helper functions for convenient QML/EQL5 integration.
|
||||||
|
//
|
||||||
|
// Both 'Lisp.call()' and 'Lisp.apply()' can be called passing optionally
|
||||||
|
// 'this' as first argument.
|
||||||
|
//
|
||||||
|
// The 'this' argument (a QQuickItem) can be accessed in Lisp via qml:*caller*.
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
//
|
||||||
|
// Lisp.call("cl:foo", x, y)
|
||||||
|
// Lisp.call(this, "cl:foo", x, y)
|
||||||
|
//
|
||||||
|
// Lisp.apply("cl:foo", [x, y])
|
||||||
|
// Lisp.apply(this, "cl:foo", [x, y])
|
||||||
|
//
|
||||||
|
|
||||||
function checkEval(arg) {
|
function checkEval(arg) {
|
||||||
// prepared in Lisp for JS evaluation
|
// prepared in Lisp for JS evaluation
|
||||||
|
|
@ -6,13 +21,26 @@ function checkEval(arg) {
|
||||||
return eval(arg.substr(3)); }
|
return eval(arg.substr(3)); }
|
||||||
return arg; }
|
return arg; }
|
||||||
|
|
||||||
function fun() {
|
function call() {
|
||||||
var name = arguments[0];
|
var arg1 = arguments[0];
|
||||||
var len = arguments.length - 1;
|
if(arguments.length > 1) {
|
||||||
|
var arg2 = arguments[1];
|
||||||
|
var len = arguments.length - 2;
|
||||||
var args = new Array(len);
|
var args = new Array(len);
|
||||||
for(var i = 0; i < len; i++) {
|
for(var i = 0; i < len; i++) {
|
||||||
args[i] = arguments[i + 1]; }
|
args[i] = arguments[i + 2]; }
|
||||||
return checkEval(EQL5.apply(name, args)); }
|
return apply(arg1, arg2, args); }
|
||||||
|
return apply(arg1); }
|
||||||
|
|
||||||
function apply(name, args) {
|
function apply(arg1, arg2, args) {
|
||||||
return checkEval(EQL5.apply(name, args)); }
|
var caller, name;
|
||||||
|
if(typeof(arg1) == "object") { // 'this'
|
||||||
|
caller = arg1;
|
||||||
|
name = arg2; }
|
||||||
|
else {
|
||||||
|
name = arg1;
|
||||||
|
if(args === undefined) {
|
||||||
|
args = arg2; }
|
||||||
|
else {
|
||||||
|
args.unshift(arg2); }}
|
||||||
|
return checkEval(EQL5.apply(caller, name, args)); }
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,12 @@ public:
|
||||||
|
|
||||||
static QByteArray vanillaQtSuperClassName(const QMetaObject* mo) {
|
static QByteArray vanillaQtSuperClassName(const QMetaObject* mo) {
|
||||||
QByteArray className(mo->className());
|
QByteArray className(mo->className());
|
||||||
while(!q_names.contains(className)) {
|
Q_FOREVER {
|
||||||
|
int p = className.indexOf('_');
|
||||||
|
if(p != -1) {
|
||||||
|
className.truncate(p); }
|
||||||
|
if(q_names.contains(className)) {
|
||||||
|
break; }
|
||||||
mo = mo->superClass();
|
mo = mo->superClass();
|
||||||
if(!mo) {
|
if(!mo) {
|
||||||
break; }
|
break; }
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue