MIDGETS - A collection of CAPI widgets and utilities


 

Abstract

MIDGETS is a collection of re-usable CAPI widgets and utilities I wrote for various projects.

The code has been developed and used with LispWorks 4.4.6 and 5.0.2 for Windows, but it should work on Linux and OS X as well.

It comes with a BSD-style license so you can basically do with it whatever you want.

  
[Riddle: The dates above are not random...]

 

Contents

  1. Download and installation
  2. Color buttons
  3. Flat buttons
  4. Collecting display panes
  5. Time and date widgets and functions
  6. Saving dialog geometry
  7. Symbol index
  8. Acknowledgements

 

Download and installation

MIDGETS together with this documentation can be downloaded from http://weitz.de/files/midgets.tar.gz. The current version is 0.6.2. On Microsoft Windows, it depends on the LW-WIN library.

MIDGETS comes with a system definition for ASDF and with one for LispWorks' Common Defsystem.
 

Color buttons

Color buttons are rectangular areas on the screen that represent a certain color by displaying it and on being clicked calling CAPI:PROMPT-FOR-COLOR so that the user can change the color. They can be queried for the color they currently have, the color can be changed non-interactively, and they provide callbacks which are called when the color has changed. There are also color button panels which group several color buttons together.

Here's an example:

CL-USER 1 > (capi:contain 
             (make-instance 'midgets:color-button-panel
                            :items '(:background :foreground :border)
                            :title-function 'string-capitalize
                            :button-args '(:title-position :right)
                            :layout-class 'capi:column-layout
                            :layout-args '(:adjust :left :internal-border 10 :gap 10)
                            :callback (lambda (item color)
                                        (capi:display-message "You selected ~S as the new color for ~S."
                                                              color item))))
#<MIDGETS:COLOR-BUTTON-PANEL  21F2389B>


[Standard class]
color-button


This class is the user-visible implementation of a single color button. Technically, it's currently implemented as a simple pinboard layout wrapped around a drawn pinboard object, but that might change. You should treat it like a simple pane and only use the documented initargs, readers, and writers.

The initargs for this class are :COLOR, :CALLBACK, :ITEM, :WIDTH, :HEIGHT, :TITLE (and the other initargs for a titled object), :PROMPT-TEXT, :PROMPT-ARGS, and :VISIBLE-BORDER as well as the standard initargs :HELP-KEY, :NAME, and :PLIST. You should refrain from using other initargs like for example layout constraints unless you know exactly what you're doing.

color is the initial color of the button. callback, if not NIL, is a function of one argument which is called with the new color when the color has changed. prompt-text and prompt-args are arguments for the CAPI:PROMPT-FOR-COLOR function. width and height determine the fixed width and height of the button. item is an arbitrary Lisp object associated with the button which is only really useful for color button panels. The semantics of the other initargs are as usual in CAPI.


[Accessors]
color-button-callback color-button => callback
(setf (color-button-callback color-button) new-callback)
color-button-color color-button => color
(setf (color-button-color color-button) new-color)
color-button-prompt-args color-button => prompt-args
(setf (color-button-prompt-args color-button) new-prompt-args)
color-button-prompt-text color-button => prompt-text
(setf (color-button-prompt-text color-button) new-prompt-text)


These accessors can be used to get and set the values of the corresponding slots of the color button color-button. Note that changing the color will result in the graphical representation being updated and the callback being called (if there is one).


[Standard class]
color-button-panel


The class which implements color button panels, a number of color buttons layed out together with a group behaviour.

Technically, this is a simple layout, but you should for all practical purposes just treat it like a simple pane and only use the documented initargs, readers, and writers.

The initargs for this class are :ITEMS, :CALLBACK, :COLOR-FUNCTION, :TITLE-FUNCTION, :BUTTON-ARGS, :BUTTON-ARGS-FUNCTION, :LAYOUT-CLASS, :LAYOUT-ARGS, :TEST-FUNCTION, :HELP-KEYS, and :BUTTONS as well as the standard initargs :HELP-KEY, :NAME, and :PLIST.

items is a list of arbitrary Lisp objects representing the buttons. color-function should be a function of one argument which will be called for each item and should return its button's initial color. Likewise, title-function is supposed to provide a title for each item (i.e. button).

button-args is a property list with additional initargs for the buttons that are going to be created. Or, if you want more fine-grained control, you can use button-args-function to provide different initargs per item.

help-keys, if provided, should be a list of help keys in one-to-one correspondence with the list items.

callback should be a function of two arguments which will be called with the item and the new color whenever the color of a button changes.

You can also simply provide a list buttons of color buttons. In this case, all the initargs mentioned above will be ignored.

test-function (the default is EQL) is the function used to test two items for equality.

layout-class and layout-args determine how the button panel will be layed out. This works like with CAPI's stock button panels.


[Reader]
color-button-panel-buttons color-button-panel => color-button-list


Returns a list of the color buttons which comprise the color button panel color-button-panel.


[Accessor]
color-button-panel-item-color color-button-panel item => color
(setf (color-button-panel-item-color color-button-panel item) new-color)


Gets or sets the color of the button from the color button panel color-button-panel that's associated with the item item. Will return NIL or do nothing if no such button can be found.


[Method]
color-button-panel-items color-button-panel => list-of-items


Returns the list of items associated with the buttons in the color button panel color-button-panel.


[Reader]
color-button-panel-test-function color-button-panel => test-function


Returns the test function of the color button panel color-button-panel.

 

Flat buttons

Flat buttons are kind of a simplified variant of push buttons. They also react to mouse clicks and have associated callbacks, but they only have a very simple "flat" visual appearance that doesn't change when they're pressed. Their main advantage is that they can be much smaller than push buttons. There are also flat button panels which group several flat buttons together.

Here's an example:

CL-USER 1 > (capi:contain 
             (make-instance 'midgets:flat-button-panel
                            :items (loop for i from 1 to 9 collect i)
                            :print-function (lambda (item)
                                              (format nil "~:(~R~)" item))
                            :button-args '(:foreground :dark-blue :background :yellow)
                            :layout-class 'capi:grid-layout
                            :layout-args '(:columns 3 :internal-border 10 :visible-max-width t)
                            :callback-type :data
                            :callback (lambda (item)
                                        (capi:display-message "You selected number ~D." item))))
#<MIDGETS:FLAT-BUTTON-PANEL  200C2E63>


[Standard class]
flat-button


This class is the user-visible implementation of a single flat button. Technically, it's currently implemented as a simple pinboard layout eventually wrapped around an item pinboard object, but that might change. You should treat it like a simple pane and only use the documented initargs, readers, and writers.

The initargs for this class are :CALLBACK, :CALLBACK-TYPE, :BACKGROUND, :FOREGROUND, :TEXT (and the other initargs for an item), :VISIBLE-BORDER, layout constraints like :VISIBLE-MIN-WIDTH as well as the standard initargs :HELP-KEY, :NAME, and :PLIST. You should refrain from using other initargs unless you know exactly what you're doing.

callback, if not NIL, is a function which is called when the button is clicked. callback-type determines how the callback is called and can have values as for the class CAPI:CALLBACKS. background and foreground are the color of the button and the color of the text on the button respectively. The semantics of the other initargs are as usual in CAPI.


[Reader]
flat-button-item flat-button => item


Returns the item associated with the flat button flat-button.


[Accessors]
flat-button-callback flat-button => callback
(setf (flat-button-callback flat-button) new-callback)
flat-button-callback-type callback-type => result
(setf (flat-button-callback-type flat-button) new-callback-type)


These accessors can be used to get and set the values of the corresponding slots of the flat button flat-button.


[Standard class]
flat-button-panel


The class which implements flat button panels, a number of flat buttons layed out together with a group behaviour.

Technically, this is a simple layout, but you should for all practical purposes just treat it like a simple pane and only use the documented initargs, readers, and writers.

The initargs for this class are :ITEMS, :CALLBACK, :CALLBACK-TYPE, :PRINT-FUNCTION, :BUTTON-ARGS, :BUTTON-ARGS-FUNCTION, :LAYOUT-CLASS, :LAYOUT-ARGS, :TEST-FUNCTION, and :BUTTONS, as well as the standard initargs :HELP-KEY, :NAME, and :PLIST.

items is a list of arbitrary Lisp objects representing the buttons. print-function should be a function of one argument which will be called for each item and should return its button's text.

button-args is a property list with additional initargs for the buttons that are going to be created. Or, if you want more fine-grained control, you can use button-args-function to provide different initargs per item.

help-keys, if provided, should be a list of help keys in one-to-one correspondence with the list items.

callback, if not NIL, is a function which is called when one of the buttons is clicked. callback-type determines how the callback is called and can have values as for the class CAPI:CALLBACKS.

You can also simply provide a list buttons of flat buttons. In this case, all the initargs mentioned above will be ignored.

test-function (the default is EQL) is the function used to test two items for equality.

layout-class and layout-args determine how the button panel will be layed out. This works like with CAPI's stock button panels.


[Reader]
flat-button-panel-buttons flat-button-panel => flat-button-list


Returns a list of the flat buttons which comprise the flat button panel flat-button-panel.


[Method]
flat-button-panel-items flat-button-panel => list-of-items


Returns the list of items associated with the buttons in the flat button panel flat-button-panel.


[Reader]
flat-button-panel-test-function flat-button-panel => test-function


Returns the test function of the flat button panel flat-button-panel.

 

Time and date widgets and functions

MIDGETS provides functions like PROMPT-FOR-DATE that you can use to ask users for dates, times, or both. And it also exposes the interfaces which were defined to implement this functions so that you can intergrate them into your own GUI design.


[Function]
prompt-for-date message &key time day month year callback ok-text cancel-text => new-time


Displays a date interface with the title message prompting the user for a date. The date entered by the user is returned as a universal time with seconds, minutes, and hours set to zero. The date interface is initialized with the values day, month, year, or with the universal time time which takes precedence over the other three values. The default is to use the current date.

ok-text is the text on the button which confirms the user input, cancel-text is the text on the button which cancels. The defaults are "OK" and "Cancel". If the users presses the cancel button, the function returns NIL.

If callback is not NIL (which is the default), it is supposed to be a function of one argument (the date interface) which is called whenever the user changes the date. Note that the callback might be called more than once for each change or even if nothing hasn't changed.


[Function]
prompt-for-time message &key time second minute hour callback ok-text cancel-text => new-time


Displays a time interface with the title message prompting the user for a time. The time entered by the user is returned as a universal time with day, month, and year set to 1900-01-01. The time interface is initialized with the values second, minute, hour, or with the universal time time which takes precedence over the other three values. The default is to use the current time.

ok-text is the text on the button which confirms the user input, cancel-text is the text on the button which cancels. The defaults are "OK" and "Cancel". If the users presses the cancel button, the function returns NIL.

If callback is not NIL (which is the default), it is supposed to be a function of one argument (the time interface) which is called whenever the user changes the date. Note that the callback might be called more than once for each change or even if nothing hasn't changed.


[Function]
prompt-for-date-and-time message &key time second minute hour day month year callback date-text time-text ok-text cancel-text => new-time


Displays a date interface and a time interface with the title message prompting the user for date and time. The date and time entered by the user are returned as a universal time. The interfaces are initialized with the values second, minute, hour, day, month, and year, or with the universal time time which takes precedence over the other six values. The default is to use the current date and time.

date-title is the title for the date interface, time-title is the title for the time interface. The defaults are "Date" and "Time". ok-text is the text on the button which confirms the user input, cancel-text is the text on the button which cancels. The defaults are "OK" and "Cancel". If the users presses the cancel button, the function returns NIL.

If callback is not NIL (which is the default), it is supposed to be a function of one argument (the date interface or the time interface) which is called whenever the user changes the date or the time. The callback must be able to distinguish between the date interface and the time interface. Note that the callback might be called more than once for each change or even if nothing hasn't changed.


[Standard class]
date-interface


This is a direct subclass of CAPI:INTERFACE. Instances of this class are used for the PROMPT-FOR-DATE function, but you can also use them independently. The relevant initargs are :DAY, :MONTH, :YEAR, and :CALLBACK, and they are used as in PROMPT-FOR-DATE.


[Accessors]
date-interface-callback date-interface => callback
(setf (date-interface-callback date-interface) callback)
date-interface-day date-interface => day
(setf (date-interface-day date-interface) day)
date-interface-month date-interface => month
(setf (date-interface-month date-interface) month)
date-interface-year date-interface => year
(setf (date-interface-year date-interface) year)


These accessors can be used to get and set the values of the corresponding slots of the date interface date-interface. Note that changing the day, month, or year will result in the graphical representation being updated and the callback being called (if there is one).


[Accessor]
date-interface-time date-interface => time
(setf (date-interface-time date-interface) time)


This accessor can be used to get and set the date represented by the date interface date-interface as a universal time. This is in a way equivalent to using DATE-INTERFACE-DAY, DATE-INTERFACE-MONTH, and DATE-INTERFACE-YEAR all at once. If used as a reader, the hours, minutes, and seconds of time are ignored. If used as a writer, the hours, minutes, and seconds returned are set to zero.


[Standard class]
time-interface


This is a direct subclass of CAPI:INTERFACE. Instances of this class are used for the PROMPT-FOR-TIME function, but you can also use them independently. The relevant initargs are :SECOND, :MINUTE, :HOUR, and :CALLBACK, and they are used as in PROMPT-FOR-TIME.


[Accessors]
time-interface-callback time-interface => callback
(setf (time-interface-callback time-interface) callback)
time-interface-second time-interface => second
(setf (time-interface-second time-interface) second)
time-interface-minute time-interface => minute
(setf (time-interface-minute time-interface) minute)
time-interface-hour time-interface => hour
(setf (time-interface-hour time-interface) hour)


These accessors can be used to get and set the values of the corresponding slots of the time interface time-interface. Note that changing the second, minute, or hour will result in the graphical representation being updated and the callback being called (if there is one).


[Accessor]
time-interface-time time-interface => time
(setf (time-interface-time time-interface) time)


This accessor can be used to get and set the time represented by the time interface time-interface as a universal time. This is in a way equivalent to using TIME-INTERFACE-SECOND, TIME-INTERFACE-MINUTE, and TIME-INTERFACE-HOUR all at once. If used as a reader, the day, month, and year of time are ignored. If used as a writer, the day, month, and year returned are 1900-01-01.


[Special variable]
*use-win32-locale-info*


This variable (which is only available on Windows) controls whether locale info provided by Windows should be used for weekday names, month names, and the beginning of the week. (See below.)


[Special variable]
*default-first-day-of-week*


Default value for the day the week starts with. 0 is Monday, 1 is Tuesday, and so on. The initial value is 6 (for Sunday). On Windows, locale-specific values will be used instead if *USE-WIN32-LOCALE-INFO* is true.


[Special variable]
*default-month-names*


Default values for the month names - a list of strings. The initial value is a list of the English month names. On Windows, locale-specific values will be used instead if *USE-WIN32-LOCALE-INFO* is true.


[Special variable]
*default-weekday-names*


Default values for the names of the weekdays - a list of strings starting with the name for Monday. The initial value is a list of the English weekday names. On Windows, locale-specific values will be used instead if *USE-WIN32-LOCALE-INFO* is true.


[Special variable]
*use-win32-color-info*


This variable (which is only available on Windows) controls whether theme-specific info provided by Windows should be used for text and background colors. (See below.)


[Special variable]
*inactive-caption-color*


Default value for the background color of inactive caption text. The initial value is :GREY40. On Windows, the theme-specific value will be used instead if *USE-WIN32-COLOR-INFO* is true.


[Special variable]
*inactive-caption-text-color*


Default value for the color of inactive caption text. The initial value is :GREY70. On Windows, the theme-specific value will be used instead if *USE-WIN32-COLOR-INFO* is true.


[Special variable]
*highlight-color*


Default value for the background color of highlighted text. The initial value is :MIDNIGHTBLUE. On Windows, the theme-specific value will be used instead if *USE-WIN32-COLOR-INFO* is true.


[Special variable]
*highlight-text-color*


Default value for the color of highlighted text. The initial value is :WHITE. On Windows, the theme-specific value will be used instead if *USE-WIN32-COLOR-INFO* is true.


[Special variable]
*window-color*


Default value for the background color of standard windows. The initial value is :WHITE. On Windows, the theme-specific value will be used instead if *USE-WIN32-COLOR-INFO* is true.


[Special variable]
*window-text-color*


Default value for the standard color of text. The initial value is :BLACK. On Windows, the theme-specific value will be used instead if *USE-WIN32-COLOR-INFO* is true.

 

Collecting display panes

If you want to redirect all characters sent to a stream to a CAPI pane, the CAPI already provides collector panes which are quite handy. However, as they're based on editor panes, there are a couple of limitations. Collecting display panes (based on display panes) are a non-interactive alternative to collector panes which work quite similar but have a few important differences:

Here's an example:

CL-USER 1 > (defun fac (n)
              (if (zerop n) 1 (* n (fac (1- n))))) 
FAC

CL-USER 2 > (compile *)
FAC
NIL
NIL

CL-USER 3 > (capi:define-interface trace-interface ()
              ()
              (:panes
               (trace-pane
                midgets:collecting-display-pane
                :accessor trace-pane
                :font (gp:make-font-description :family "Lucida Console" :size 9)
                :force-output-p :newline
                :auto-scroll-p t)
               (clear-button
                capi:push-button
                :text "Clear pane"
                :callback-type :none
                :selection-callback (lambda ()
                                      (setf (capi:display-pane-text trace-pane) ""))))
              (:default-initargs
               :title "Trace Pane"
               :best-width 400
               :best-height 300))
TRACE-INTERFACE

CL-USER 4 > (defparameter *trace-interface* (capi:display (make-instance 'trace-interface)))
*TRACE-INTERFACE*

CL-USER 5 > (trace fac)
(FAC)

CL-USER 6 > (let ((*trace-output* (midgets:collecting-display-pane-stream (trace-pane *trace-interface*))))
              (fac 5))
120


[Standard class]
collecting-display-pane


A display pane which has a stream associated with it that writes to the pane, i.e. whenever you send output to the stream it ends up in the display pane. Collecting display panes inherit from CAPI:DISPLAY-PANE and are thus used like those and accept the same initargs although some defaults are different. (Use the Class Browser for details.) The only additional initargs they accept are :FORCE-OUTPUT-P and :AUTO-SCROLL-P. Follow the links for more information about these two.


[Accessor]
collecting-display-pane-auto-scroll-p collecting-display-pane => auto-scroll-p
(setf (collecting-display-pane-auto-scroll-p collecting-display-pane) new-auto-scroll-p)


With this accessor (or the :AUTO-SCROLL-P initarg to COLLECTING-DISPLAY-PANE) you can ask the pane's stream to automatically scroll down to the end of the pane whenever its buffer was flushed. Of course, this makes only sense if vertical scrolling is enabled for the pane (which is the default).


[Reader]
collecting-display-pane-lock collecting-display-pane => lock


Returns the lock which is internally used to synchronize primitive stream operations. You can use it yourself to wrap it around higher-order output functions like FORMAT.


[Reader]
collecting-display-pane-stream collecting-display-pane => stream


The stream (of type DISPLAY-PANE-STREAM) that's associated with collecting-display-pane, i.e. the stream the sink of which is the display pane. Note that you can write to this stream from any thread. The underlying implementation will make sure that the correct thread is used.


[Standard class]
display-pane-stream


The stream class used by collecting display panes, i.e. streams of this class are character output streams which write their output to a CAPI display pane. The streams are implemented on top of STREAM:BUFFERED-STREAM and hold a reference to the pane they write to. You should not instantiate objects of this class yourself unless you know what you're doing.


[Accessor]
display-pane-stream-force-output-p display-pane-stream => force-output-p
(setf (display-pane-stream-force-output-p display-pane-stream) new-force-output-p)


As display pane streams are buffered streams, their output will usually not be immediately shown but only after you've called FORCE-OUPUT or FINISH-OUPUT to flush the buffer.

With this accessor (or the :FORCE-OUTPUT-P initarg to COLLECTING-DISPLAY-PANE) you can ask the stream to automatically call FORCE-OUTPUT for you in certain situations - either after each character if the value is T or after each #\Newline if the value is :NEWLINE. If the value is NIL (which is the default) or any other value, FORCE-OUTPUT will not be called automatically.


[Reader]
display-pane-stream-lock display-pane-stream => lock


This is just a "trampoline" accessor from the stream to its associated pane to access at the lock they share.


[Reader]
display-pane-stream-pane display-pane-stream => collecting-display-pane


A reader to access the collecting display pane the stream writes to.

 

Saving dialog geometry

The CAPI offers a facility to store the geometry of an interface for future use - in the same application or even in another instance of the application after it has been shut down. Unfortunately, this isn't implemented for dialogs. The utility functions in this section can help to achieve similar effects for dialogs, though. For that, the MIDGETS library maintains an internal geometry cache which kind of "cooperates" with the external geometry storage of the CAPI.

However, you still have to do some of the work yourself. Something like the following:

  1. Define methods for CAPI:TOP-LEVEL-INTERFACE-SAVE-GEOMETRY-P and CAPI:TOP-LEVEL-INTERFACE-GEOMETRY-KEY as you would do for a non-dialog interface.
  2. Make sure that CACHE-INTERFACE-GEOMETRY is called whenever your dialog is destroyed. The easiest way is to make this function the destroy callback of your interface.
  3. Get the cached interface geometry and use it when you want to display your dialog. You could for example automate this with something like the following advice:
    (lw:defadvice (capi:display-dialog use-geometry-cache :around)
        (interface &rest args)
      (apply #'lw:call-next-advice interface
             ;; use the X and Y position from the last time the dialog was shown
             (append (lw:when-let (geometry (midgets:get-interface-geometry interface (getf args :screen)))
                       `(:position-relative-to nil :x ,(first geometry) :y ,(second geometry)))
                     args)))
    
  4. Optionally call CLEAR-INTERFACE-GEOMETRY-CACHE before you deliver an application or on application startup.


[Function]
clear-interface-geometry-cache => |


Clears the internal geometry cache. This function should for example be called before an image is delivered.


[Generic function]
get-interface-geometry interface &optional screen => geometry


Returns the cached or saved geometry for the CAPI interface interface and the screen screen. It first tries to find it in the internal cache of the MIDGETS library and, failing that, tries to look it up using LW:USER-PREFERENCE. The returned geometry is a list of four elements - the absolute x and y coordinates of the interface and its width and height.

This will only work for interfaces which are declared as interfaces that should be saved in the usual way.


[Generic function]
cache-interface-geometry interface => geometry


Saves the geometry for the CAPI interface interface in the internal cache of the MIDGETS library.

This will only work for interfaces which are declared as interfaces that should be saved in the usual way.

Note that only one geometry per interface is currently cached. There's no support for different screens or resolutions.


 

Symbol index

Here are all exported symbols of MIDGETS in alphabetical order linked to their corresponding entries:
  1. *default-first-day-of-week*
  2. *default-month-names*
  3. *default-weekday-names*
  4. *highlight-color*
  5. *highlight-text-color*
  6. *inactive-caption-color*
  7. *inactive-caption-text-color*
  8. *use-win32-color-info*
  9. *use-win32-locale-info*
  10. *window-color*
  11. *window-text-color*
  12. cache-interface-geometry
  13. clear-interface-geometry
  14. collecting-display-pane
  15. collecting-display-pane-auto-scroll-p
  16. collecting-display-pane-lock
  17. collecting-display-pane-stream
  18. color-button
  19. color-button-callback
  20. color-button-color
  21. color-button-panel
  22. color-button-panel-buttons
  23. color-button-panel-item-color
  24. color-button-panel-items
  25. color-button-panel-test-function
  26. color-button-prompt-args
  27. color-button-prompt-text
  28. date-interface
  29. date-interface-callback
  30. date-interface-day
  31. date-interface-month
  32. date-interface-time
  33. date-interface-year
  34. display-pane-stream
  35. display-pane-stream-force-output-p
  36. display-pane-stream-lock
  37. display-pane-stream-pane
  38. flat-button
  39. flat-button-callback
  40. flat-button-callback-type
  41. flat-button-item
  42. flat-button-panel
  43. flat-button-panel-buttons
  44. flat-button-panel-items
  45. flat-button-panel-test-function
  46. get-interface-geometry
  47. prompt-for-date
  48. prompt-for-date-and-time
  49. prompt-for-time
  50. time-interface
  51. time-interface-callback
  52. time-interface-hour
  53. time-interface-minute
  54. time-interface-second
  55. time-interface-time

 

Acknowledgements

This documentation was prepared with DOCUMENTATION-TEMPLATE.

$Header: /usr/local/cvsrep/midgets/doc/index.html,v 1.28 2013-08-23 08:04:14 edi Exp $

BACK TO MY HOMEPAGE