---
layout: ../Site.layout.js
---
# A minimal GUI keyboard display in [McCLIM](https://mcclim.common-lisp.dev/main.html) common lisp interface manager

<img src="../nicclim-v2-accelerators.png"><img src="../keyboard-defaults.png">

```
(defparameter *approximate-keyboard*
 '(((1) (2) (3) (4) (5) (6) (7)  (8)   (9)   (0))
   ((q) (w) (e) (r) (t) (y) (u)  (i)   (o)   (p))
   ((a) (s) (d) (f) (g) (h) (j)  (k)   (l)  (|;|))
   ((z) (x) (c) (v) (b) (n) (m) (|,|) (|.|) (|/|))))
```

In at least some cases of laptop keyboard, this is pretty close to what you've got alphanumerically; I expect one's fingers hang around some of these keys. It's going to be a little ugly but I wanted to do a quick keystrokes (i.e. shortcuts/hotkeys) display for my [NicCLIM map editor](/lispgames/trying-nicclim), which in its upcoming version 2 now has lots of keyboard accelerators as I guess we'll see in another  moment.

I guess you're reasonably set up so the next two lines make sense. Otherwise: [my lisp intro](/fundamental/installing-lisp-etc/), [McCLIM's git README](https://codeberg.org/McCLIM/McCLIM).

```
(require :mcclim)
(in-package :clim-user)
```

## An `application-frame` with that keyboard.

I figure it should go in a slot of the frame.

```
(define-application-frame key-frame ()
 ((keyboard :initarg :keyboard))
 (:pane :application :display-function 'display-keyboard)
 (:default-initargs
  :keyboard  '(((1) (2) (3) (4) (5) (6) (7)  (8)   (9)   (0))
               ((q) (w) (e) (r) (t) (y) (u)  (i)   (o)   (p))
               ((a) (s) (d) (f) (g) (h) (j)  (k)   (l)  (|;|))
               ((z) (x) (c) (v) (b) (n) (m) (|,|) (|.|) (|/|)))))
```

## The `:display-function` `'display-keyboard`

```
(defun display-keyboard
   (frame pane)
 (with-slots
       (keyboard)
     frame
   (formatting-table
       (pane)
     (loop :for row :in keyboard :do
       (formatting-row
           (pane)
         (loop
           :for cell :in row :do
             (formatting-cell
                 (pane)
               (updating-output
                   (pane)
                 (surrounding-output-with-border
                     (pane)
                   (with-text-style
                       (pane
                        (make-text-style :sans-serif :roman :tiny))
                     (loop
                       :for symbol :in cell
                       :for hack := nil :then (fresh-line pane) :do
                         (present symbol 'symbol
                                  :stream pane))))))))))))
```

## Look at that

<img src="../keyboard-defaults.png">

```
(find-application-frame 'key-frame)
```

<img src="../keyboard-full-screenshot.png">

Could be worse for the defaults.

## Changing the key text

So we absolutely do not want to deface the intuitive keyboard layout list. What I *have* is a list of keyboard accelerator associated commands. Since each keyboard key is unique, I guess we can just use `loop`..`nsubst`.

My new [nicclim](https://lispy-gopher-show.itch.io/nicclim) shortcuts (listen, I program experimentally)

```
(defparameter *v2-nicclim-shortcuts*
 '(((b) (c-s-b execute list))
   ((q) (c-s-q funcall))
   ((w) (c-s-w apply))
   ((e) (c-s-e gets))
   ((r) (c-s-r setsgets))
   ((t) (c-s-t change player))
   ((a) (c-s-a extract player))
   ((s) (c-s-s doas))
   ((d) (c-s-d doas list))
   ((c) (c-s-c rect union))
   ((z) (c-s-z rect intersection))
   ((x) (c-s-x rect difference))
   ((f) (c-s-f cur1 rotatef))
   ((v) (c-s-v note location))))
```


# Open the same frame with these changes [`nsubst`](http://www.lispworks.com/reference/HyperSpec/Body/f_substc.htm)ituted

<img src="../nicclim-v2-accelerators.png">

```
(find-application-frame
'key-frame
:keyboard
(loop
  :with new-keyboard := (copy-list
                         *approximate-keyboard*)
  :for (old new) :in *v2-nicclim-shortcuts*
  :do
     (nsubst new old new-keyboard :test 'equal)
  :finally
     (return new-keyboard)))
```

<img src="../nicclim-v2-accelerators-full-screen.png">

# Conclusion

I can't say we did anything profoundly deep, but it worked like I thought it would- first, the realistically laid out list datastructure (my friend [notptr](https://social.cyberia9.org/notes/abyc9k5qmhae058d) says they normally write their game maps like this in the first place, so at least someone does it), then using a keyboard arguement with [common lisp's `loop`](https://www.lispworks.com/documentation/HyperSpec/Body/m_loop.htm) and `nsubst` to add [keyboard accelerator](https://www.lispworks.com/documentation/lw80/clim/clim-ch11-2.htm) descriptions.

The [common lisp interface manager spec](http://bauhh.dyndns.org:8000/clim-spec/index.html)'s [dynamic table resizing](https://www.lispworks.com/documentation/lwu41/climuser/GUID_281.HTM#HEADING281-0) did what I hoped it would do. I am not contending I should win any particular awards for graphic design. Maybe you will.

If you want to *take the next step* with McCLIM because this simple example only whet your voracious appetite, the next thing to do is to make the table cells [sensitive-inferiors](https://www.lispworks.com/documentation/lw80/clim/clim-ch6-3.htm#CLIM) (when choosing a command for example).

# Fin.

Let's [get on the mastodon](https://gamerplus.org/@screwlisp/115105693171271215), as per usual please.

Oh, if you didn't attend live/listen already, the sbcl-powered biggest-ever cosmic string simulation done by the Tufts Supercomputer for their astrophysics department (interview with Ken Olum) really answers this question: "who actually uses common lisp these days?"  https://communitymedia.video/w/9kysH4ZwVuP4J4erZozqFT