GUI Discussion — TC Lispers November Meeting December 9th, 2009
Patrick Stein

Cross-posted from TCLispers.org:

Lisp GUI Discussion on Vimeo.

Robert Goldman, Paul Krueger, and Patrick Stein gave short presentations on different approaches to Graphical User Interfaces with Common Lisp.

UI Paradigm from Scratch August 28th, 2009
Patrick Stein

I’m not fond of MVC and yet I know of nothing better. What would I want in an alternative?

Most UI toolkits claim to follow the model-view-controller paradigm if they make any such claim at all. In my experience though, they either mush together the controller and view, or the model and view, or all three. As such, there isn’t an opportunity to have the same information represented in multiple places within the interface. Instead, you have repeated information that is synchronized through a complex scaffolding of change listeners. Yuck.

Target platform

First, I should mention why I want such a thing. I am planning to participate in a study group centered around this MIT OpenCourseWare course in Numeric Photography. I’ve done plenty of digital imaging in my day. The assignments for this course though seem to center on novel user interfaces to digital imaging and artistic value of the end results. The original course was all done as Java applets. I considered using Armed Bear Common Lisp to try to write Lisp code that interacted with the user through a Java applet. My initial tests on this went poorly since the ABCL code wants to read and write from your .abclrc and such. I toyed with code signing and permission granting on it, but wasn’t too satisfied with how easily someone else would be able to use it. Plus, I wasn’t too keen on doing so much Java.

So, I want to start with as few dependencies as possible for standalone Lisp applications. If I could keep the dependencies down to a GUI library of my own crafted atop cl-opengl and cl-png, that would be great. I suspect that I will have to throw in zpb-ttf at some point to get text into the UI, too.

Toolkit Structure

I am going to use the example of a simple checkbox. At its heart, a checkbox is simply a boolean value. The meat or logic of the code doesn’t have to care whether the checkbox is visible or enabled or being clicked or has recently changed. The meat of the code only has to know if it is checked or not. It would be nice if I could get away with just having a boolean anywhere in my code. However, some things will need to be triggered when the boolean changes. As such, I am going to need some setter/getter artifice around the boolean. So, let’s make a structure with a single boolean slot.

(defclass checkbox ()
  ((value :initarg :value :type (member nil t)))
  (:default-initargs :value nil))

Let’s call this the Checkbox Base.

This is all that most of my application will need to know. Somehow there is a checkbox. It is either checked or not.

How is the checkbox state presented in the interface? It could be drawn on the screen as a little box that either contains a check or not. It could be drawn as a menu item that either has a check beside it or not. It could be reflected in the state of some LED somewhere. It could be presented in hundreds of web browsers across the company as either a green box or a red box. Let’s call each of these a Checkbox Representation.

Somehow, each of these Checkbox Representations has to be notified when the value changes. If I am willing to also include a dependency on Cells (which hasn’t been touched since May of 2005), then I can get this kind of notification for free.

(defclass checkbox ()
  ((value :initform (c-in nil) :accessor checkbox-value)))

(defclass checkbox-as-colored-box ()
  ((checkbox :initarg :checkbox :type checkbox :reader checkbox)
   (color :cell t :initform (c? (if (checkbox-value (checkbox self))
                                    :green
                                    :red)))))

(def-c-output color ((self checkbox-as-colored-box))
  (redraw-checkbox-as-colored-box new-value))

Any time the value of the checkbox changes, the color of the checkbox-as-colored-box will change and the trigger to redraw-checkbox-as-colored-box will be invoked with the new color value.

If I don’t use Cells, then I have to create a layer around the checkbox that tracks who needs to be notified when the value changes. Let’s call this a Checkbox Monitor. It is very tempting to fold this right into the checkbox. After all, when the checkbox changes, the monitor has to know. For a slightly better division of functionality, one might make monitored-checkbox a subclass of checkbox:

(defclass monitored-checkbox (checkbox)
  ((listeners :initform nil)))

In this way, UI components can register with the monitored-checkbox, but the main application never has to notice that the checkbox has the weird methods for adding listeners and such. With an appropriate :around or :after method, the checkbox code doesn’t even have to know if it is part of a monitored-checkbox. It would be nice if the code didn’t have to know this at instantiation time, but I’m not seeing an obvious way around that without Cells.

With Cells, each representation could monitor the Checkbox Base. Without the Cells-type approach, there can really only be one Monitor per Base. (Technically, there could be more, but they could only be through a direct inheritance chain… you could wrap the Monitor with a Monitor2 and that with a Monitor3, etc. But, you wouldn’t want to try to instantiate a Monitor123 or anything like that.) For now, let’s explore the non-Cells case.

The idea so far is that you can create a Checkbox Monitor which is a subclass of Checkbox Base. Then, you can create some Checkbox Representations and register them with the monitor. The meat of your code can think of it as a Checkbox Base.

How is it toggled? There are a variety of ways that could happen. Someone could hit a global hot-key that toggles the checkbox. Someone could select a menu item that toggles the checkbox. Someone could mouse click on a representation of that checkbox. My application could receive an event from a remote source like a secondary display, a database, or a filesystem that toggles the checkbox. Someone could hit the space bar or the enter key while a representation of that checkbox has keyboard focus.

I am not sure we need to create a class hierarchy around changing the checkbox. We can simply call the setter on the checkbox. The monitor will catch it and notify everyone who needs to know. Further, the checkbox itself will be set for anyone who needs to passively query (rather than needing to track its every change).

The big question then is can we enable or disable the checkbox? This would have to either be a property of the Checkbox Base (which doesn’t seem right to me) or a joint effort spanning all places that wish to toggle the checkbox (which also doesn’t seem right). Fortunately, I think we can use multiple-inheritance and more :around methods to take care of this.

(defclass checkbox ()
  ((value :initform nil :type (member nil t) :accessor checkbox-value)))

(defclass monitor ()
  ((listeners :initform nil)))

(defclass enabler ()
  ((enabled :initform t :type (member nil t) :accessor control-enabled)))

(defclass enabled-checkbox-monitor (enabler monitor checkbox)
  ())

(defmethod (setf checkbox-value) :around (new-value checkbox)
  (when (control-enabled checkbox)
    (call-next-method)))

Is this any better than the status quo? I’m not sure. I will have to write more code to find out.

Alternatives to MVC? August 26th, 2009
Patrick Stein

The model-view-controller paradigm dominates the UI toolkit world. I should say, it dominates the talk about the UI toolkit world. Most UI toolkits have very poor separation between the model, the view, and the controller. For most UI toolkits, a text input field can only have one controller and one view. Further, all of the aspects of the controller, the view, and the model are accessed through the text input field object.

I tried to google around for “model view controller” alternatives and “model view controller” rethinking. The only alternative that I saw was DDP (data driven presentation). Data driven presentation seems like someone took the model-view-controller paradigm and collapsed the view state into the model. This makes for a somewhat nicer entity-relationship diagram because then the controller never has to speak directly to the view, it can just twiddle the data.

I don’t think data driven presentation is a real alternative to model-view-controller. It is a slight repackaging of something that’s never been packaged right before, to my knowledge.

I don’t really like the model-view-controller paradigm very much. I would love to hear about alternative approaches to representing the same control in multiple places in an application. Alas, in the absence of an alternative, I have been trying to think about how to do model-view-controller right.

Let’s try a checkbox

What is the model for a checkbox? At its simplest, a checkbox is either checked or not. It is a simple boolean value. The checkbox doesn’t exist in a vacuum though. It probably needs a list of listeners that want to be notified when the checkbox is toggled. So, the checkbox model is two things: a simple state and an initiator of state-change events.

Does the model have a label? I say, No. If the model had a label, then I cannot display it in two different places with two different labels. If it is going to have any sort of label at all in the model, it should merely be a label indicator. Let’s imagine that I am making an interface that is English on the left side of the dialog box and Spanish on the right side. If I am truly separating model from view, then I should be able to use the same checkbox model on both halves of the screen. I cannot keep the text label in the model unless one of my views knows how to translate. If one of my views is going to know how to translate, then both of them should. Me, I don’t think that either should.

What is the view for a checkbox? A checkbox view at its simplest is just an indicator showing whether the box is checked or not. It might be a rendering of the box on your screen either empty or containing a checked mark of some sort. It might be a rendering of the box on a remote screen. It might be the little Caps Lock light on your keyboard. It might be a green table cell on a web page.

Does the view have a label? I don’t know. It makes more sense here than in the model, but does it belong here? Suppose there is some event elsewhere that makes me want to change the label. Suppose my label was include all images. Then, suppose someone adds a sound file into the document, and I want to change the label to include all images and sounds. I have to track down all of the views and update them. I would still have to do this if the label were a separate entity apart from the checkbox. So, the question is, should it be a separate entity?

What is the controller for a checkbox? For a checkbox rendered to the screen, the controller has to recognize mouse clicks in the same area that the view is in. Either the controller has to moved around lock-step with the view so that it has the same geometry, or the controller has to know enough about the view to tell whether a mouse event hit it. Similarly, if the checkbox has focus, pressing the space bar or the enter key should probably toggle the checkbox. The view has to know if it has focus so that it can render itself to appear focused. The controller has to know if it has focus to know whether to care about the key press.

It is tempting to combine the controller into the view. It definitely simplifies the geometry and focus questions. Those become shared information. There are other possible controllers for the checkbox, too, though. Maybe the checkbox has a global hot-key. Maybe there is some menu item that uses the same checkbox model (though a good menu item will also act as a view of the checkbox model, so it really is a combination view and controller again).

Is enabled a property of the model or the view or the controller? I say, not the model. Of course, this is thorny. Suppose that my checkbox isn’t relevant at this point in my application. I should disable it. However, I have to update all of the views attached to this model instead of just updating the model. Feh. Of course, I also have the option of disabling just some of the views. For example, if my application supported multiple users watching, but only a single one editing, then I would need to disable this checkbox in every window except the one on the editor’s desktop.

Is focus a property of the model or the view or the controller? It shouldn’t be part of the model. If I am displaying the same model to two different desktops, there is no reason that the two users should have to synchronize which control they are using. Heck, what’s to say that the second user even has all of the controls that the first user has?

Where we are

The controller and view definitely need to know about the model. The controller and view both need to know about focus. The controller and view both need to know about enabled. The model needs to be able to tell all of its views that they are stale. For items that are going to be taking mouse input, the controller definitely has to know where the view is being rendered.

The fact that the controller and view both need to share the focus and enabled information (as well as caret position and selection range and such for other types of controls) indicates that there should be another entity in this paradigm that both the controller and view can refer to. There is both a model of the item and a model of how that item is to be presented—a model and a presentation model, if you will. If there are two users sharing the same text input field, then the GUI should have the choice of sharing the presentation model across users so that they both have the same caret position and selection range or allowing them each their own presentation model so that they each have their own caret position and selection range. It is further complicated by the fact that you may want to share the caret position and selection range without sharing the focus or enabled settings.

I am afraid that I haven’t reached much conclusion here. I still don’t have answers or alternatives. Any references or suggestions would be greatly appreciated.

l