-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Could you make the library interface more declarative? #39
Comments
I understand what you mean. You're saying that the If we want to achieve declarative UI, I believe we should use separate macros, just like in Relm4, instead of mixing them with the imperative API. |
I looked at the examples on Reml4, I like the view! macro, would it be difficult to write something like this? How long will it take? Unfortunately, I won't be able to write code for the library myself. |
Implementing macros in Lisp is always easier than in other languages. I might work on it after I finish dealing with my job hunting. Of course, you are also welcome to try implementing it yourself and submit a pull request. |
By any chance, are you looking for a declarative menu? |
I'm guess, what I can write view!-like macro myself, but I'm afraid, what I'll write a bad variant, and nobody more competent won't write better interface because my already is. |
Don't worry. You can try implementing your own version first, and I believe you will learn a lot of Lisp techniques in the process. After I finish my busy period, I will also implement one, and then we can discuss the strengths and weaknesses of both and learn from each other. |
I have written a simple declarative macro for defining UIs with cl-gtk4, i'm not really good with macros or anything and i haven't done exhaustive testing, but i've been using it successfully in my projects, so i thought I might share it here. The code is here: https://codeberg.org/seigakaku/gtk4-defui Here's the fibonacci example adapted to use it: (define-interface fibonacci
(container box (make-box :orientation +orientation-vertical+ :spacing 4)
(label label (make-label :str "0")
:properties (:hexpand t :vexpand t))
(nil box (make-box :orientation +orientation-horizontal+ :spacing 4)
:properties (:hexpand t :halign +align-center+)
(nil label (make-label :str "n: "))
(nil entry (make-entry)
:init (lambda (entry)
(setf (entry-buffer-text (entry-buffer entry)) (format nil "~A" n)))
:properties (:hexpand t :halign +align-fill+)
:connect (("changed" (lambda (entry)
(setf n (ignore-errors (parse-integer
(entry-buffer-text
(entry-buffer entry))))))))))
(nil button (make-button :label "Calculate")
:connect (("clicked" (lambda (button)
(bt:make-thread
(lambda ()
(when n
(run-in-main-event-loop ()
(setf (button-label button) "Calculating..."
(widget-sensitive-p button) nil))
(let ((result (fib n)))
(run-in-main-event-loop ()
(setf (label-text label) (format nil "~A" result)
(button-label button) "Calculate"
(widget-sensitive-p button) t)))))))))))
(n :type (or null fixnum) :initform 40))
(define-application (:name fibonacci :id "org.bohonghuang.gtk4-example.fibonacci")
(defun fib (n)
(if (<= n 2) 1 (+ (fib (- n 1)) (fib (- n 2)))))
(define-main-window (window (make-application-window :application *application*))
(setf (window-title window) "Fibonacci Calculator")
(let* ((fibonacci (make-instance 'fibonacci))
(container (fibonacci-container fibonacci)))
(setf (window-child window) container)
(unless (widget-visible-p window)
(window-present window))))) |
Thanks! |
Hello! |
There is a good example: https://docs.racket-lang.org/gui-easy/index.html |
This is a draft I completed earlier: (ql:quickload :symbol-munger)
(ql:quickload :trivial-arguments)
(in-package #:gtk4.example)
(defun expand-gui-definition (name fields &aux (package (symbol-package name)))
(alexandria:when-let* ((name-space-symbol (find-symbol (symbol-name '#:*ns*) package))
(gir-class (gir:nget (symbol-value name-space-symbol) (symbol-munger:lisp->studly-caps name)))
(constructor (find-symbol (format nil "~A-~A" '#:make name) package)))
(alexandria:with-gensyms (instance)
`(let ((,instance (,constructor . ,(loop :with keywords := (mapcar #'caar (nth-value 3 (alexandria:parse-ordinary-lambda-list
(trivial-arguments:arglist constructor))))
:for (key value) :on fields :by #'cddr
:when (member key keywords)
:nconc (list key value)))))
,@(loop :for (key value) :on fields :by #'cddr
:nconc (loop :for class := gir-class :then (gir:parent-of class)
:for symbol := (when class
(find-symbol
(format
nil "~A-~A"
(symbol-munger:camel-case->lisp-name
(gir:info-get-name (gir::info-of class))
:capitalize t)
key)
package))
:for setf-function := (ignore-errors (fdefinition `(setf ,symbol)))
:for function := (ignore-errors (fdefinition symbol))
:for value-form := (or (and (listp value) (expand-gui-definition (car value) (cdr value))) value)
:while class
:when setf-function
:return (list `(setf (,symbol ,instance) ,value-form))
:when (and function (= (length (trivial-arguments:arglist function)) 2))
:return (list `(,symbol ,instance ,value-form))))
,instance))))
(defmacro gui ((name &body body))
(expand-gui-definition name body))
(define-application (:name simple-counter
:id "org.bohonghuang.gtk4-example.simple-counter")
(define-main-window (window (make-application-window :application *application*))
(setf (window-title window) "Simple Counter"
(window-child window) (gui (box
:orientation +orientation-vertical+
:spacing 4
:append (label :str "0"
:hexpand-p t
:vexpand-p t)
:append (button :label "Add"))))
(unless (widget-visible-p window)
(window-present window)))) Perhaps you can continue to improve it. |
Hello!
I'm comparing application code on your library and on CL-CFFI-GTK4. The second library provides a more declarative interface. For example:
Your code, sorry for the directness, merges into one whole when you look at it. It is very difficult to distinguish one part from another. I wish the code was more like a tree:
So you can immediately see what is responsible for what.
Best wishes
Filipp
The text was updated successfully, but these errors were encountered: