As I mentioned on my show, while clim2 feels like there could be a more explanatory tutorial, attempts to create one pretty much converge to the superapp example. Anyway, in my one I focus on separating my class/es from the McCLIM clim2 implementation classes. I'll reproduce what I did in the post, though canonically it's at git@codeberg.org:j9000/lispusers -> tfw/high/stringlist.lisp Define this package: ``` (uiop:define-package :lispusers/tfw/high/stringlist (:mix :clim :clim-lisp :cl) (:export #:app-main)) (in-package :lispusers/tfw/high/stringlist) ``` Then I wanted to try the default redisplay - which redraws the entire application but only when a new command has been run ( :command-loop ) with both a string and a list data structure, so I deffed these similar classes: ``` (defclass textholder () ((my-string :initform "foo" :type 'string :accessor my-string))) (defclass textsholder () ((my-strings :initform '("foo") :type 'list :accessor my-strings))) ``` Now, I found if clim's #'define-application-frame gets told a superclass, it doesn't attempt to provide the clim classes, so there needs to be an ancestral default clim application frame. ``` (define-application-frame app () ()) ``` Hence I can combine my data-carrying class definition/s with the ancestral app clim default application. Since this definition defines the displayed app, all the app panes and layout stuff has to be together here. ``` (define-application-frame textap (app textsholder) () (:pointer-documentation t) (:panes (app :application :height 128 :width 300 :display-function 'display-app) (int :interactor :height 128 :width 300)) (:layouts (default (vertically () app int)))) ``` Now, the superapp example uses something like this function I removed: ``` #| (defun display-app (frame pane) (let ((strings (my-strings frame))) (format pane "~@{~a~%~}~%" string))) |# ``` Instead, since I wanted to change between my pure clos data classes ``` (defmethod display-app ((frame textholder) (pane stream)) (let ((string (my-string frame))) (format pane "~@{~a~%~}~%" string))) (defmethod display-app ((frame textsholder) (pane stream)) (let ((strings (my-strings frame))) (format pane "~{~a~%~}~%" strings))) ``` Now, the running and operated upon application is bound to the special scope *application-frame*, at least from the command's perspective. So quitting is ``` (define-textap-command (com-quit :name t) () (frame-exit "application-frame")) ``` But since *application-frame* isn't an arguement, I had to use typecase here: ``` (define-textap-command (com-addtext :name t) ((string 'string)) (typecase *application-frame* (textsholder (push string (my-strings *application-frame*))) (textholder (setf (my-string *application-frame*) string)))) ``` and a utility function for running one of this: ``` (defun app-main () (run-frame-top-level (make-application-frame 'textap))) ``` See the screenshot phosted next to this. Hope it helps someone, phosted responses and corrections welCOMed.