From ca22ff02546f4cf0e2afe9cb2ab3ed794ecd6c02 Mon Sep 17 00:00:00 2001 From: polos Date: Fri, 24 Jul 2020 11:45:01 +0200 Subject: [PATCH] add convenience function '(eql:qml)' for auto generating vars according to QML item's 'objectName'; bump version number; --- doc/auto-doc.htm | 5 + doc/auto-doc.lisp | 4 +- .../sokoban/{ => lisp}/3rd-party/CONTRIBUTORS | 0 .../sokoban/{ => lisp}/3rd-party/COPYING | 0 .../sokoban/{ => lisp}/3rd-party/README.txt | 0 .../sokoban/{ => lisp}/3rd-party/levels.lisp | 0 .../{ => lisp}/3rd-party/my-levels.lisp | 0 .../sokoban/{ => lisp}/3rd-party/sokoban.lisp | 0 .../quick/sokoban/{ => lisp}/qml-lisp.lisp | 0 .../M-modules/quick/sokoban/lisp/ui-vars.lisp | 22 +++ examples/M-modules/quick/sokoban/sokoban.lisp | 23 ++-- lib/qml-ui-vars.lisp | 125 ++++++++++++++++++ src/eql.cpp | 2 +- src/eql_exe.qrc | 1 + src/lisp/ini.lisp | 5 + src/lisp/package.lisp | 1 + src/main.cpp | 3 +- 17 files changed, 176 insertions(+), 15 deletions(-) rename examples/M-modules/quick/sokoban/{ => lisp}/3rd-party/CONTRIBUTORS (100%) rename examples/M-modules/quick/sokoban/{ => lisp}/3rd-party/COPYING (100%) rename examples/M-modules/quick/sokoban/{ => lisp}/3rd-party/README.txt (100%) rename examples/M-modules/quick/sokoban/{ => lisp}/3rd-party/levels.lisp (100%) rename examples/M-modules/quick/sokoban/{ => lisp}/3rd-party/my-levels.lisp (100%) rename examples/M-modules/quick/sokoban/{ => lisp}/3rd-party/sokoban.lisp (100%) rename examples/M-modules/quick/sokoban/{ => lisp}/qml-lisp.lisp (100%) create mode 100644 examples/M-modules/quick/sokoban/lisp/ui-vars.lisp create mode 100644 lib/qml-ui-vars.lisp diff --git a/doc/auto-doc.htm b/doc/auto-doc.htm index 26454fe..1fed971 100644 --- a/doc/auto-doc.htm +++ b/doc/auto-doc.htm @@ -354,6 +354,11 @@ Converts a Unicode pathname to a simple ECL base string, using QString::to Convenience function: a simple message box, converting x to a string if necessary.
Returns its argument (just like print).


+QML () +

+Generates global variables for all QML items with objectName set. Requires the QML app to be running. Will show an error message if objectName is not unique. +
+

QNEW-INSTANCE (class-name &rest arguments/properties)
QNEW diff --git a/doc/auto-doc.lisp b/doc/auto-doc.lisp index b6acd14..f9e6771 100644 --- a/doc/auto-doc.lisp +++ b/doc/auto-doc.lisp @@ -5,7 +5,7 @@ (defparameter *help* nil) (defun add-cpp-docu () - (with-open-file (s (eql:in-home "src/ecl_fun.cpp") :direction :input) + (with-open-file (s "../src/ecl_fun.cpp" :direction :input) (let (curr ex) (flet ((add-curr () (when curr @@ -45,7 +45,7 @@ (setf *help* nil) (add-cpp-docu) (add-lisp-docu) - (with-open-file (s (eql:in-home "doc/auto-doc.htm") :direction :output :if-exists :supersede) + (with-open-file (s "auto-doc.htm" :direction :output :if-exists :supersede) (format s "~%~ ~%~ ~%~ diff --git a/examples/M-modules/quick/sokoban/3rd-party/CONTRIBUTORS b/examples/M-modules/quick/sokoban/lisp/3rd-party/CONTRIBUTORS similarity index 100% rename from examples/M-modules/quick/sokoban/3rd-party/CONTRIBUTORS rename to examples/M-modules/quick/sokoban/lisp/3rd-party/CONTRIBUTORS diff --git a/examples/M-modules/quick/sokoban/3rd-party/COPYING b/examples/M-modules/quick/sokoban/lisp/3rd-party/COPYING similarity index 100% rename from examples/M-modules/quick/sokoban/3rd-party/COPYING rename to examples/M-modules/quick/sokoban/lisp/3rd-party/COPYING diff --git a/examples/M-modules/quick/sokoban/3rd-party/README.txt b/examples/M-modules/quick/sokoban/lisp/3rd-party/README.txt similarity index 100% rename from examples/M-modules/quick/sokoban/3rd-party/README.txt rename to examples/M-modules/quick/sokoban/lisp/3rd-party/README.txt diff --git a/examples/M-modules/quick/sokoban/3rd-party/levels.lisp b/examples/M-modules/quick/sokoban/lisp/3rd-party/levels.lisp similarity index 100% rename from examples/M-modules/quick/sokoban/3rd-party/levels.lisp rename to examples/M-modules/quick/sokoban/lisp/3rd-party/levels.lisp diff --git a/examples/M-modules/quick/sokoban/3rd-party/my-levels.lisp b/examples/M-modules/quick/sokoban/lisp/3rd-party/my-levels.lisp similarity index 100% rename from examples/M-modules/quick/sokoban/3rd-party/my-levels.lisp rename to examples/M-modules/quick/sokoban/lisp/3rd-party/my-levels.lisp diff --git a/examples/M-modules/quick/sokoban/3rd-party/sokoban.lisp b/examples/M-modules/quick/sokoban/lisp/3rd-party/sokoban.lisp similarity index 100% rename from examples/M-modules/quick/sokoban/3rd-party/sokoban.lisp rename to examples/M-modules/quick/sokoban/lisp/3rd-party/sokoban.lisp diff --git a/examples/M-modules/quick/sokoban/qml-lisp.lisp b/examples/M-modules/quick/sokoban/lisp/qml-lisp.lisp similarity index 100% rename from examples/M-modules/quick/sokoban/qml-lisp.lisp rename to examples/M-modules/quick/sokoban/lisp/qml-lisp.lisp diff --git a/examples/M-modules/quick/sokoban/lisp/ui-vars.lisp b/examples/M-modules/quick/sokoban/lisp/ui-vars.lisp new file mode 100644 index 0000000..c3a90b0 --- /dev/null +++ b/examples/M-modules/quick/sokoban/lisp/ui-vars.lisp @@ -0,0 +1,22 @@ +;;; THIS FILE IS GENERATED, see '(eql:qml)' + +(defpackage ui + (:use :cl :eql) + (:export + #:*board* + #:*level* + #:*rotate-player* + #:*wiggle-box* + #:*zoom-board-in* + #:*zoom-board-out*)) + +(provide :ui-vars) + +(in-package :ui) + +(defvar *board* "board") ; Rectangle "qml/sokoban.qml" +(defvar *rotate-player* "rotate_player") ; RotationAnimation "qml/items/player.qml" +(defvar *zoom-board-in* "zoom_board_in") ; ScaleAnimator "qml/sokoban.qml" +(defvar *zoom-board-out* "zoom_board_out") ; ScaleAnimator "qml/sokoban.qml" +(defvar *wiggle-box* "wiggle_box") ; SequentialAnimation "qml/items/box2.qml" +(defvar *level* "level") ; Slider "qml/sokoban.qml" diff --git a/examples/M-modules/quick/sokoban/sokoban.lisp b/examples/M-modules/quick/sokoban/sokoban.lisp index bc47948..aeff7b4 100644 --- a/examples/M-modules/quick/sokoban/sokoban.lisp +++ b/examples/M-modules/quick/sokoban/sokoban.lisp @@ -6,9 +6,10 @@ (qrequire :quick) -(require :sokoban "3rd-party/sokoban") -(require :levels "3rd-party/my-levels") -(require :qml-lisp "qml-lisp") +(require :sokoban "lisp/3rd-party/sokoban") +(require :levels "lisp/3rd-party/my-levels") +(require :qml-lisp "lisp/qml-lisp") +(require :ui-vars "lisp/ui-vars.lisp") (defpackage :qsoko (:use :common-lisp :eql :qml) @@ -48,13 +49,13 @@ (defvar *static-item* (qml-component "static.qml")) ; :wall :goal (defun board () - (qml:find-quick-item "board")) + (qml:find-quick-item ui:*board*)) (defun level () - (floor (q< |value| "level"))) + (floor (q< |value| ui:*level*))) (defun set-level (index) - (q> |value| "level" index)) + (q> |value| ui:*level* index)) (defun assoc* (item alist) (cdr (assoc item alist))) @@ -157,9 +158,9 @@ (+ (if (eql :next direction/index) 1 -1) (level))))))) (when (/= level (level)) - (queued (q> |running| "zoom_board_out" t) + (queued (q> |running| ui:*zoom-board-out* t) (set-level level) ; will call SET-MAZE from QML - (q> |running| "zoom_board_in" t)))) + (q> |running| ui:*zoom-board-in* t)))) (level)) (defun key-pressed (object event) @@ -283,8 +284,8 @@ t) (defun final-animation () - (queued (q> |running| "rotate_player" t) - (q>* |running| "wiggle_box" t))) + (queued (q> |running| ui:*rotate-player* t) + (q>* |running| ui:*wiggle-box* t))) (defun run () (x:do-with *quick-view* @@ -295,7 +296,7 @@ (qadd-event-filter nil |QEvent.KeyPress| 'key-pressed) (setf sokoban:*move-hook* 'move-item sokoban:*undo-hook* 'add-undo-step) - (q> |maximumValue| "level" (1- (length *my-mazes*))) + (q> |maximumValue| ui:*level* (1- (length *my-mazes*))) (set-maze)) (progn diff --git a/lib/qml-ui-vars.lisp b/lib/qml-ui-vars.lisp new file mode 100644 index 0000000..75afcc7 --- /dev/null +++ b/lib/qml-ui-vars.lisp @@ -0,0 +1,125 @@ +;;; generate global vars in a package named :ui for all QML items with +;;; 'objectName' set +;;; +;;; this expects all QML files to be under 'qml/' +;;; +;;; since 'objectName' must be unique, an error message box will be shown +;;; if more than one occurrence of the same name is found +;;; +;;; usage: +;;; - run app, e.g. 'eql5 run.lisp' (use -qtpl if called from the console) +;;; - call '(eql:qml)' +;;; - generated file is 'lisp/ui-vars.lisp' (replaced without warning) + +(defpackage qml-ui-variables + (:use :cl :eql)) + +(in-package :qml-ui-variables) + +(defparameter *warn-if-exists* nil) + +(defparameter *qml-items* (qfind-children qml:*quick-view*)) + +(defun class-name* (item) + (let ((name (|className| (|metaObject| item)))) + (subseq name + (let ((start 0)) + (dolist (q '("QDeclarative" "QQuick" "QQml" "Qml")) + (when (x:starts-with q name) + (setf start (length q)) + (return))) + start) + (position #\_ name)))) + +(defun sort* (list key &optional stable) + (funcall (if stable 'stable-sort 'sort) + list 'string< :key key)) + +(defun filter (list) + (remove-if (lambda (it) + (or (x:empty-string (|objectName| it)) + (find (class-name* it) + '("MnemonicLabel") ; exclude internal items + :test 'string=))) + list)) + +(defun grep (name) + (ext:run-program "grep" (list "-rn" + "--include" + "*.qml" + (format nil "objectName:[[:space:]]*~S" name) + "qml/"))) + +(defun one-space (string) + (x:join (remove-if 'x:empty-string (x:split string)))) + +(defun collect () + (setf *qml-items* + (sort* (sort* (filter *qml-items*) + '|objectName| t) + 'class-name*)) + (let ((max-name 0) + (max-class 0) + collected not-unique grepped) + (dolist (item *qml-items*) + (let* ((name (|objectName| item)) + (class* (class-name* item)) + (grep (ignore-errors (read-line (grep name))))) + (when (and grep + (not (find grep grepped :test 'string=))) + (push (list (substitute #\- #\_ name) + name + class* + (subseq grep 0 (position #\: grep))) + collected) + (setf max-name (max (length name) max-name) + max-class (max (length class*) max-class)) + (when (and (find name not-unique :test 'string=) + (not (find grep grepped :test 'string=))) + (qmsg (format nil "QML: not unique:
~A
" + (one-space (read-line (grep name)))))) + (pushnew grep grepped :test 'string=) + (pushnew name not-unique :test 'string=)))) + (values (setf collected (nreverse collected)) + max-name + max-class))) + +(defun write-ui-file (out &optional (ui-package "ui")) + (multiple-value-bind (items max-name max-class) + (collect) + (format out ";;; THIS FILE IS GENERATED, see '(eql:qml)'~ + ~%~ + ~%(defpackage ~(~A~)~% (:use :cl :eql)" + ui-package) + (format out "~% (:export~{~% #:*~A*~}))~%~%" + (sort (mapcar 'first items) 'string<)) + (format out "(provide :ui-vars)~%~%(in-package ~(:~A~))~%~%" ui-package) + (dolist (item items) + (let ((diff-name (make-string (- max-name (length (first item))))) + (diff-class (make-string (- max-class (length (third item)))))) + (format out "(defvar *~A*~A ~S)~A ; ~A~A ~S~%" + (first item) + diff-name + (second item) + diff-name + (third item) + diff-class + (fourth item)))) + (terpri))) + +(defun run () + (let ((ui "lisp/ui-vars.lisp")) + (when (and *warn-if-exists* + (probe-file ui) + (/= |QMessageBox.Yes| + (|question.QMessageBox| nil "Replace?" + (format nil "File ~S already exists.
Replace it?" ui) + (logior |QMessageBox.Yes| |QMessageBox.Cancel|)))) + (return-from run)) + (ensure-directories-exist ui) + (with-open-file (out ui :direction :output :if-exists :supersede) + (write-ui-file out) + (format t "~%UI file generated: ~S~%~%" ui)))) + +(run) + diff --git a/src/eql.cpp b/src/eql.cpp index d66a778..658179d 100644 --- a/src/eql.cpp +++ b/src/eql.cpp @@ -8,7 +8,7 @@ #include #include -const char EQL::version[] = "20.5.1"; // May 2020 +const char EQL::version[] = "20.7.1"; // July 2020 extern "C" void ini_EQL(cl_object); diff --git a/src/eql_exe.qrc b/src/eql_exe.qrc index e6088be..a47c277 100644 --- a/src/eql_exe.qrc +++ b/src/eql_exe.qrc @@ -22,6 +22,7 @@ ../lib/properties.lisp ../lib/properties.ui ../lib/qselect.lisp + ../lib/qml-ui-vars.lisp ../lib/quic.lisp ../lib/restart-dialog.lisp ../lib/thread-safe.lisp diff --git a/src/lisp/ini.lisp b/src/lisp/ini.lisp index 3913254..02bf730 100644 --- a/src/lisp/ini.lisp +++ b/src/lisp/ini.lisp @@ -633,6 +633,11 @@ (load (in-home "lib/quic"))) (funcall (intern "RUN" :quic) ui.h ui.lisp ui-package properties))) +(defun qml () + "args: () + Generates global variables for all QML items with objectName set. Requires the QML app to be running. Will show an error message if objectName is not unique." + (load (in-home "lib/qml-ui-vars"))) + (defun qrequire (module &optional quiet) (%qrequire module quiet)) diff --git a/src/lisp/package.lisp b/src/lisp/package.lisp index e84cab3..b42d9a4 100644 --- a/src/lisp/package.lisp +++ b/src/lisp/package.lisp @@ -58,6 +58,7 @@ #:qlocal8bit #:qlog #:qmessage-box + #:qml #:qmsg #:qnew #:qnew-instance diff --git a/src/main.cpp b/src/main.cpp index bbe70b8..1dc79e0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,7 +7,7 @@ void ini() { QString home(QDir::homePath() + "/.eql5/"); - if(!QFile::exists(home + "lib/gui.lisp")) { + if(!QFile::exists(home + "lib/qml-ui-vars.lisp")) { // latest added file QDir dir(QDir::homePath()); dir.mkdir(".eql5"); dir.setPath(home); @@ -35,6 +35,7 @@ void ini() { << "lib/properties.lisp" << "lib/properties.ui" << "lib/qselect.lisp" + << "lib/qml-ui-vars.lisp" << "lib/quic.lisp" << "lib/restart-dialog.lisp" << "lib/thread-safe.lisp"