CL-WEBVIEW is a common lisp binding to webview/webview, a tiny tiny cross-platform webview library to build modern cross-platform GUIs.
(in-package :cl-webview)
;; in SLY/SLIME like multiple-thread situation
(with-webview (webview :title "Basic 1" :debug? t)
(webview-set-html webview "<h1>CL-WEBVIEW | Example</h1>"))
This will open a webview window with CL-WEBVIEW | Example
as h1
header.
;; Just copy the CLOG tutorial 01-tutorial.lisp
;; https://github.com/rabbibotton/clog/blob/main/tutorial/01-tutorial.lisp
(defpackage #:clog-webview-example
(:use :cl :clog :cl-webview)
(:export #:main))
(in-package :clog-webview-example)
(defun on-new-window (body)
"On-new-window handler. "
(setf (title (html-document body)) "CLOG and CL-WEBVIEW")
(let ((hello-element (create-child body "<h1> Hello World! (click me!) </h1>")))
(set-on-click hello-element
(lambda (obj)
(declare (ignore obj))
(setf (color hello-element) :green)))))
(defun main ()
"Start tutorial. "
(initialize #'on-new-window)
(with-webview (webview :debug t)
(webview-navigate webview (format nil "http://127.0.0.1:~A" *clog-port*)))
(uiop:quit))
;; And if you want, you can save the app like this:
;;
;; (sb-ext:save-lisp-and-die "clog-webview" :toplevel #'main :executable t :compression t)
;;
;; But here's a little tricky thing:
;; since libwebview is just linked to system's spec directory,
;; so you cannot just copy the final executable file and send to
;; normal user. see Installation section for more.
Clone, and load, that should be okay.
The binding is generated by c2ffi. You may need that to work.
The requirement for libwebview could be seen in webview/webview.
Behind the scene, in preload.lisp
, I wrote a small script to
execute the webview’s build.sh
and link the header file webview.h
under spec
folder and the library libwebview.dylib
under lib
folder.
The spec
folder will be added to cffi:*foreign-library-directories*
for library search. And the lib
folder will be automatically created.
If you found your machine failed to compile or just don’t need the
compile process, you could commit the eval-when
code (preload.lisp),
and compile yourself.
Also, if you want to pack the application and deliver it to normal
user. You may consider copy the dynamic lib with the executable,
and push the lib path to cffi:*foreign-library-directories*
.
I was using SBCL on macOS, and as you may know, in macOS, a GUI app
should have UI thread as the main thread, so the app should run
in main thread by run-in-main-thread
.
However, an error is that if an app crash and you choose to terminate it, it would hang up the main loop and won’t let next window to show. Another error is that i don’t know how to add multiple window support, since the main loop was all taken by the first window. Helps are needed.
Here is a simple script that I use for packing on macOS:
# inspired from:
# https://github.com/jimon/osx_app_in_plain_c/blob/master/appify
SOURCE=clog-webview-example.lisp
TARGET=HELLO
TOPLEVEL=clog-webview-example::main
DIR=${TARGET}.app/Contents
mkdir -p ${DIR}/{Resources/lib,MacOS}
sbcl --load $SOURCE \
--eval "(save-lisp-and-die \"${DIR}/MacOS/${TARGET}\" :toplevel '${TOPLEVEL} :executable t :compression t)"
it’s bit simple, but it is just workable :p. I’ll consider to add a better mechanics to build and release the application.
cannot open multiple window (=webview-run= hang up the main thread)- cannot close the window by
webview-terminate
andwebview-destroy
.this is a core library issue, see webview #588 for explanation.
Also, it is recommanded to not write multiple-window application, although you could for sure do this easily with cl-webview. The problem is that
webview_destroy
will stuck and thewebview_terminate
will quit all window. None of it could be executed properly, so I didn’t write a cleanning process thus even if you closed the webview window, it is still behind the scene. (that’s kinda horrible, like the living dead).No idea about how to solve it yet. But you could just ignore it, and write single window application.