CLIM's standard set of presentation types will be useful in many cases, but most applications will need customized presentation types to represent the objects modeled in the application.
In defining a presentation type, you define all the user interface components of the entity:
In other words, in one place you provide all the information about an object necessary to display it to the user and to accept it as input from the user.
The set of presentation types forms a type lattice, an extension of the Common Lisp CLOS type lattice. When a new presentation type is defined as a subtype of another presentation type, it inherits all the attributes of the supertype except those explicitly overridden in the definition.
To define a new presentation type, you follow these steps:
Every presentation type is associated with a CLOS class. In the common case, the name of the presentation type is a class object or the name of a class, and that class is not a clos:built-in-class. In this case, the presentation type is associated with that CLOS class.
Otherwise, define-presentation-type defines a class with metaclass clim:presentation-type-class
and superclasses determined by the presentation type definition. This class is not named name, since that could interfere with built-in Common Lisp types such as and
, member, and integer. clos:class-name of this class returns a list (presentation-type name). clim:presentation-type-class
is a subclass of clos:standard-class.
Note: If the same name is defined with both clos:defclass (or defstruct) and define-presentation-type, the clos:defclass (or defstruct) must be done first.
Every CLOS class (except for built-in classes) is a presentation type, as is its name. If it has not been defined with define-presentation-type, it allows no parameters and no options. As in CLOS, inheriting from a built-in class does not work unless you specify the same inheritance that the built-in class already has; you may want to do this in order to add presentation-type parameters to a built-in class.
If you define a presentation type that does not have the same name as a CLOS class, you must define a presentation-typep presentation method for it. The function (as opposed to the presentation method) presentation-typep uses find-class if the presentation type is piggybacking on a CLOS type. Otherwise it depends on the user-defined presentation method.
If you define a presentation type that has parameters, you must define a presentation-subtypep for it. As noted previously, CLOS does not allow you to parameterize types, so you must provide a presentation-subtype
method even for presentation types based on CLOS classes.
Note that CLIM itself depends on these methods for its own presentation-based utilities.
If your presentation type has the same name as a class, does not have any parameters or options, does not have a history, and does not need a special description, you do not need to call define-presentation-type.
During method combination, presentation type inheritance is used both to inherit methods ("what parser should be used for this type?"), and to establish the semantics for the type ("what objects are sensitive in this context?"). Inheritance of methods is the same as in CLOS and thus depends only on the type name, not on the parameters and options.
Presentation type inheritance translates the parameters of the subtype into a new set of parameters for the supertype, and translates the options of the subtype into a new set of options for the supertype.
The following code shows how to define an accept for a structure (instance) with several fields. That accept is then used within another similar accept call.
A presentation type called ticket
is defined. The accept method has two recursive calls to accept, one to read the name of a candidate for president and another to read the name of the running mate. We provide two possible accept methods; in order to compare them, you will have to compile first one and then the other. The first reads the two names separated by a comma on the same line. The second reads the two names on separate lines, delimited by RETURN
. They both do completion within the field. That is, if you do (accept 'ticket :stream win)
with the first accept method, and type "Bu,Qu<RETURN>
", the screen appearance will be "Bush,Quayle"
and the return value will be (BUSH QUAYLE)
.
If you use the second accept method and type:
"Cl Go "
the window will contain:
"Clinton Gore"
and the return value will be (CLINTON GORE)
.
This example also demonstrates simple cross-field constraints by insisting that the two candidates be of the same party.
For key implementation details, read the comments in the code.
(in-package :clim-user) (define-presentation-type ticket ()) (setf (get 'bush 'party) 'republican) (setf (get 'quayle 'party) 'republican) (setf (get 'clinton 'party) 'democrat) (setf (get 'gore 'party) 'democrat) ;;; separated by comma version (define-presentation-method accept ((type ticket) stream view &key &allow-other-keys) (declare (ignore view)) (let ((president (accept '(member bush clinton) :stream stream :prompt nil ;; add comma as a completing delimiter :blip-characters '(#,)))) ;; Make sure that the names were separated by a comma (unless (eql (read-gesture :stream stream) #,) (simple-parse-error "Ticket members must be separated by commas")) (let ((veep (accept '(member quayle gore) :stream stream :prompt nil))) ;; Validate party affiliations (unless (eql (get president 'party) (get veep 'party)) (simple-parse-error "Ticket members must be of the same party")) (list president veep)))) ;;; Separated by Return version (define-presentation-method accept ((type ticket) stream view &key &allow-other-keys) (declare (ignore view)) (let ((president (accept '(member bush clinton) :stream stream :prompt nil ;; Remove Newline from activation characters :activation-characters `() ;; Add Newline as a delimiter, so that we get ;; completion and move-to-next-field behavior ;; when Return is typed. :blip-characters `(#\Return #\Newline)))) (unless (eql (read-gesture :stream stream) #\Newline) (simple-parse-error "Ticket members must be entered on separate lines")) (let ((veep (accept '(member quayle gore) :stream stream :prompt nil))) ;; Validate party affiliations (unless (eql (get president 'party) (get veep 'party)) (simple-parse-error "Ticket members must be of the same party")) (list president veep))))
CLIM 2.0 User Guide - 01 Dec 2021 19:38:57