#+TITLE: 021 / #100daystooffload clim presentations, re/drawing rectangles by command
#+author: screwtape
M-x org-execute-buffer
Note I have McCLIM and dependencies inside =~/common-lisp/ already, slime and inferior-lisp set.
* Executing this buffer.
Well, M-x org-babel-execute-buffer
works for *me* but I have slime installed and the slime common-lisp repo
and all the other McCLIM dependencies in my ~/common-lisp/. good luck.
* Do what                                             :remind_me_of_the_babe:
** TODO Remember who tooted about org-roam
In the fediverse, someone told me they saved my gopher phost as an
org-roam note. I had that experience maybe we all have, where I hear
the name of an emacs mode that I've maybe heard before but I'm not
familiar with. org-roam sounded tramp-like: Though in fact it's
basically the third generation successor to the defunct org-mind-map
as a wikiwiki knowledge graph builder + several tie-ins such as
graphviz (derived from the org major mode).
* Cow launched
:PROPERTIES:
:session: clim
:END:
Remembering that we are going to use clim, so I'm duplicating this block from 015 here:
** Starting swank in eshell
#+name: eshell-swank
#+begin_src elisp :var my-slime-path="common-lisp/slime-v2.27/" :results none
    (eshell)
    (dolist (cmd `("cd"
		   ,(format "cd %s" my-slime-path)
		   "sbcl --load start-swank.lisp"))
      (insert cmd)
      (eshell-send-input))
    (previous-buffer)
#+end_src
swank is a wire protocol.
** Connecting slime to that swank
#+name: start-slime
#+begin_src elisp :results none
  (slime-connect "localhost" "4005")
#+end_src
** Doing some clim
*** Require McCLIM
#+name: require-clim
#+begin_src lisp :results none
  (Require :mcclim)
#+end_src

*** test application
#+name: define-test-frame
#+begin_src lisp
  (clim:define-application-frame myframe () ()
   (:pane :application))
#+end_src

#+RESULTS: define-test-frame
: DEFINE-MYFRAME-COMMAND

*** Make one of those
#+name: test-make-frame
#+begin_src lisp
  (defparameter *my-test-frame*
    (clim:make-application-frame 'myframe))
#+end_src

#+RESULTS: test-make-frame
: *MY-TEST-FRAME*

*** Run that top level
#+name: test-toplevel
#+begin_src lisp :results none
(clim:run-frame-top-level *my-test-frame*)
#+end_src

** Some class
*** A notion of tiles with a textual view
I wonder if I can have a tile class, tiles having slots ($\hat{x}\;\hat{y}$).
Then I can have a textual view of the tile which will be called like =(format t "~?" "tile-control" ())=.
**** Tile class
#+name: defclass-tile
#+begin_src lisp
  (defclass controlled.text ()
    ((control :initarg :of-control :accessor control-of)
     (text :initarg :of-text :accessor text-of)
     (controlled-slots :initarg :of-controlled-slots
		       :accessor controlled-slots-of))
    (:default-initargs :of-control "~a" :of-text "a"
		       :of-controlled-slots '(text-of)))
#+end_src

#+RESULTS: defclass-tile
: #<STANDARD-CLASS COMMON-LISP-USER::CONTROLLED.TEXT>

**** presentation-type thereof
#+name:presentation-controlled.text
#+begin_src lisp
  (clim:define-presentation-type controlled.text ())
#+end_src

#+RESULTS: presentation-controlled.text
: #<CLIM-INTERNALS::PRESENTATION-TYPE-PROXY {1004096673}>

***** Text view thereof
#+name:textual-view-controlled.text
#+begin_src lisp
  (clim:define-presentation-method clim:present
      (controlled.text (type controlled.text) stream
		       (view clim:textual-view) &key)
    (apply #'format stream (control-of controlled.text)
	   (loop for field in (controlled-slots-of controlled.text)
		 collecting (funcall field controlled.text))))
#+end_src

#+RESULTS: textual-view-controlled.text
: #<STANDARD-METHOD CLIM-INTERNALS::%PRESENT (CONTROLLED.TEXT T T T
:                                             CLIM:TEXTUAL-VIEW) {1004397673}>

***** Try it out
#+name: test-obj-present
#+begin_src lisp results output
(with-output-to-string (*standard-output*)
  (clim:present (make-instance 'controlled.text)))
#+end_src

#+RESULTS: test-obj-present
: a

Basically toplevel is somewhere else, hence needing to shadow it with org-babel's toplevel here.

**** collectiony
#+name: collected-formatted
#+begin_src lisp
  (let ((sequence (loop repeat 3 collect (make-instance 'controlled.text))))
    (with-output-to-string (*standard-output*)
      (clim:present sequence
		    `((sequence ,(clim:presentation-type-of (car sequence)))
		      :separator #\|))))
#+end_src

#+RESULTS: collected-formatted
: a|a|a

I'm happy with this, but I would like to know why the optional
type-specifier argument to the sequence is the presentation-type t
(which is probably #<object-of-class>) rather than the most specific
available presentation-type in the collection. I think somewhere it's
mentioned that it would like to be told this type information at
compile-time. Anyway, I have this option to say "this is an implicitly
non-empty sequence of the presentation-type of whatever its first
element is". So I have not been forced to hardcode the member type(s).

I guess I was forced to say that this was a sequence anyway. Probably
there's a good way such that a list is implicitly understood to be nil
or a sequence of the type member of the types present in the list.

Maybe I must say that that sort of thing is what I'm expecting at
compile time.

On the other hand, all we covered today is sequence types. I guess the next big topic is going to be collections of commands. So we might define a command that draws a square accepting two coordinate parameters, and then maybe cells of a grid could have PRESENTATION-TYPEs for both my CONTROLLED-TEXT and a SQUARE-GRID we could work on now.

***** SQUARE-COMMAND

****** In this case I think we need an application frame.
#+name: define-an-application-frame
#+begin_src lisp
  (defgeneric squares-of (obj))
  (defun paint-squares (frame stream)
    (declare (ignorable keys))
    (let ((squares (squares-of frame)))
      (when squares (apply #'clim:draw-rectangle* stream squares))))
  (clim:define-application-frame to.be.a.grid ()
    ((squares :initform '(120 30 145 135) :type list :accessor squares-of))
    (:panes (disp :application :width 512 :height 512 :scroll-bars nil
			       :display-function #'paint-squares)
	    (int :interactor))
    (:layouts (default (clim:horizontally () disp int))))

  (defparameter *frame* (clim:make-application-frame 'to.be.a.grid))
#+end_src

#+RESULTS: define-an-application-frame
: *FRAME*

****** Define a command that moves a square
#+name: add-a-command
#+begin_src lisp
  (define-to.be.a.grid-command (com-square :name t)
      ((x '(clim:integer 0 200)) (y '(clim:integer 0 200)) (w '(clim:integer 1 200)))
    (let ((new-square (list x y (+ x w) (+ y w))))
      (setf (squares-of clim:*application-frame*) new-square)))
#+end_src

#+RESULTS: add-a-command
: COM-SQUARE

****** We should be able to just run this right.
Running a frame does not have a return.
#+name: try-running
#+begin_src lisp :results none
  (clim:run-frame-top-level *frame*)
#+end_src

Try typing
#+BEGIN_QUOTE
s 1 2 200
#+END_QUOTE
for example. You could also press enter after s.
h c might be interesting too. q will complete to quit as well as the X.

* retrospective
I don't think my :session clim property worked.

* Talk together
- #phloggersgarage on libera.chat
- the gopher gopher://i-logout.cz/1/bongusta
- sdf commode chat https://sdf.org 
- The Lispy Gopher Climate w/screwtape at 000UTC every Wednesday!
- The mastodon https://mastodon.sdf.org/@screwtape
- @prahou@merveilles.town 's lemmy https://lemmy.sdf.org/c/unix_surrealism