You are looking at historical revision 24712 of this page. It may differ significantly from its current revision.

bb

Description

A very simple GUI toolkit based on FLTK. This extension library has been tested with FLTK versions 1.1.4 and 1.1.6.

Author

felix winkelmann

Requirements

Download

bb.egg

Documentation

Base interface

bb:init
[procedure] (bb:init [SCHEME])

Initializes the toolkit. The optional argument SCHEME may be a string naming a particular graphical scheme (possible values "none" or "plastic") or #f (meaning the default). Calling this procedure a subsequent time has no effect.

bb:widget?
[procedure] (bb:widget? X)

Returns #t if X is a widget, or #f otherwise.

bb:make-widget
[procedure] (bb:make-widget TYPE [W H])
[procedure] (bb:make-widget TYPE X Y W H)

Creates and returns a widget of the type given by the symbol TYPE. Possible widget types are:

window
a normal top-level window
double-window
a double-buffered window
button
return-button
choice-button
menu-button
check-box
entry
a single-line text field
edit
a multiline text field
text-editor
a full text editor with scroll bars
int-entry
text field for editing integer numbers. Value is a string rather than number.
float-entry
text field for editing float numbers. Value is a string rather than number.
counter
a "spin-box" like widget
dial
a "clock"-type dial widget
clock
a proper clock
label
slider
adjuster
a widget for changing a value by dragging
roller
another adjuster-like widget
list
a vertical list of strings
radio-button
progress
tabs
a collection of tab widgets
tile
groups widgets with draggable boundaries
pack
packs widgets vertically or horizontally
group
a generic grouping widget
scroll
a group widget with scrollable contents
light-button
like a checkbox with a "light"
menu-bar
glwindow
a window that contains OpenGL graphics
live-image
an image that will be redrawn from a given pointer
tree
a tree control. Available only if bb was compiled with FLU support.
table
a simple table widget
html-view
a simple html viewer

A top-level window created with bb:make-widget will not automatically be shown until bb:show has been called.

bb:property
[procedure] (bb:property WIDGET PROPERTY)
[setter] (set! (bb:property WIDGET PROPERTY) VALUE)

Gets or sets the properties given in PROPERTY1 ... (which should be symbols). Values may also be lists, in that case the values are combined (this only applies to certain properties - see below).

>Some properties may be set for individual items of the tree and table widgets. They are specified in the form (list WIDGET ITEMID). ITEMID is either unique item id or one of the symbols:

In this case the VALUE will be applied either to root node, connector or all subsequent branches or leaves added to the tree.

width and align properties can be applied to the table widget's columns.

Allowed widget properties are:

x
y
width
height
Position and dimensions (integer). Positions are always relative to the container. Width property may be set for connector item of the tree widget.
text
The text of a label, button, text-fields or html-view. Also the title of a window. For the tree widget the value is the label of the subitem. For the table widget one can specify individual cells in the form (list TABLEWIDGET ROW COLUMN). The negative ROW means column header.
value
The value of a "range" widget (slider, roller, adjuster, counter or dial), in which case it should be a number. For check-box and radio-button widgets the value should be a boolean. For list widgets, the value is the index of the highlighted item, starting from 1. The value of the choice-button widget is the index of the selected item. For tree widgets, the value is the unique id of the currently selected item. The value of the html-view is a current file name.
box
The box type. A box type is one of the symbols
callback
The callback procedure that is invoked when the value of a widget changes. See the when property for more information. For tree widgets one can obtain additional information from properties callback-reason and callback-node.
image
An image that should be drawn into the widget. See bb:image for how to load images. You can also set the value image property to a string, which will load any image file with this name automatically. The value may also be a pointer object pointing to a data buffer for a live-image widget. Images may be set for a tree widget items. If subitem is the branch or connector, then VALUE can specify pair of the images: for closed and open state respectively.
type
The type of a widget. The possible type symbols depend on what kind of widget it applies to:

scroll:

(may be combined)

slider:

dial:

modal
Whether a window is resizable and/or modal.
direction
The direction of a widget, which should be one of the symbols horizontal or vertical.
color
The background color of a widget. This can either be a value returned by bb:rgb or one of the following symbols:

The color attribute of a live-image widget designates the number of color channels (1-4). Also is applicable to the connector subitem of the tree widget.

image-width
The width of a live-image widget.
image-height
The height of a live-image widget.
focus
Whether this widget has the input focus. Calling bb:property for this property will always return 0 (but setting it will change the focus to the target widget).
spacing
The spacing inside group widgets (in pixels).
maximum
Maximum value for range widgets.
minimum
Minimum value for range widgets.
x-position
X-position for scroll widgets.
y-position
Y-position for scroll widgets.
text-color
Text color. Can also be applied to the tree subitems.
text-size
Text size. Can also be applied to the tree subitems.
text-font
Text font, which may be one of the following:

Can be specified for the tree branches and leaves.

label-color
Color of a label.
label-size
Size of a label.
label-font
Font of a label.
selection-color
The color of the selection in a text widget or the color of indicators in other widgets.
position
The position of the caret in an entry, edit or text-editor widget. Setting the position to -1 will move the caret to the end of the current text.
mark
The position of the selection mark in an entry, edit or text-editor widget. The text between the selection mark and the caret is the current selection.
selection
The currently selected text in an entry, edit or text-editor widget. When set, the value should be a pair containing start and end position of the selection in the buffer.
tooltip
A string that should be displayed, when the mouse hovers over a widget.
visible
Whether a widget is visible or not.
resizable-widget
The widget in a group, which should be exclusively resizable.
valid-context
A flag indicating whether the GL context for a glwindow is already initialized.
read-only
If true, an edit or entry widget can not be changed by the user.
align
The alignment of the widget label. May be combination of the following symbols:
when
An indicator when a widgets callback should be invoked. The default behaviour depends on the type of the widget. Possible settings are:

The default behaviour is to invoke the callback whenever the value of a widget changes, when a window is closed, when glwindow needs to be redrawn, or a button or list item has been clicked.

handler
The callback procedure that is invoked when the event occurs. The event is passed in a

first (and only) argument to the handler. A event is one of the symbols:

Additional information about event can be obtained with bb:event procedure. Returning #f from handler indicates that widget is not interested in handling this event. #t means that event was successfully handled. Any other value leads to invoking default handler of this widget.

The html-view widget's handler is invoked when user tries to follow the link (which URI is passed as an argument). Handler should return either the name of the temporary file or #f and set the text property.

callback-reason
The reason for callback. Available only for tree widget. Valid values are:
callback-node
The unique id of the node that caused callback. Available only for tree widget.
bb:event
[procedure] (bb:event PROPERTY)
[setter] (set! (bb:event PROPERTY) VALUE)

Gets or sets the event properties given in PROPERTY (which should be symbol). Only click? and clicks properties can be set.

Allowed event properties are:

Whether mouse or special keyboard button was pressed.

The number of clicks (N - 1 for N clicks)

Coordinates.

The length and text.

Which key was pressed. Possible values are:

for ordinary keys

for keypad keys

for special keys.

bb:message
[procedure] (bb:message MESSAGE)
[procedure] (bb:message TYPE MESSAGE [BUTTON1 [BUTTON2 [BUTTON3]]])

Shows a message box of type TYPE with the string MESSAGE. The optional BUTTON arguments should be strings the specify the text of any extra buttons. Message types may be:

message
information dialog with an "OK" button.
alert
alert box with an "OK" button.
ask
a "yes/no" request dialog.
choice
a request button with three choices.
bb:run
[procedure] (bb:run [WAIT])

Processes events. If WAIT is true or not specified, bb:run does not return until the last window closes. If WAIT is a number, then bb:run returns after that many seconds, or earlier, if no events are queued.

bb:add!
[procedure] (bb:add! WIDGET ITEM [CALLBACK [SHORTCUT]])
[procedure] (bb:add! LISTWIDGET TEXT [POSITION])
[procedure] (bb:add! TREEWIDGET TEXT [PARENT [POSITION [SUBWIDGET]]])
[procedure] (bb:add! TABLEWIDGET [CELLTEXT ...])
[procedure] (bb:add! TEXTEDITOR TEXT [REPLACE])

If WIDGET is a menu-bar, choice-button or menu-button, bb:add! adds a new menu with the text ITEM (a string), the keyboard-shortcut SHORTCUT (another string) and the callback CALLBACK (a procedure of no arguments).

The string encoding the menu-item can include subitems, using the syntax foo/bar/baz. As many levels as necessary are created.

The shortcut can be #f or a string describing the shortcut in one of two ways: [#+^]ASCII or [#+^]CHAR where a decimal value represents an ascii character (eg. 97 is the ascii for 'a'), and the optional prefixes enhance the value that follows. Multiple prefixes must appear in the above order.

#
Alt
{+
Shift
^
Control

If WIDGET is an edit, entry or text-edit widget, ITEM should be a string, which will be added to the end of the existing text. In case of a text-editor, the optional boolean argument REPLACE indicates whether the text should be inserted, or the current selection be replaced.

If WIDGET is a list, the ITEM should be a string, which will added to the list of existing lines. The string may be prefixed by a @... sequence to enable special formatting:

@.
Print rest of line, don't look for more '@' signs
@@
Print rest of line starting with '@'
@l
Use a large (24 point) font
@m
Use a medium large (18 point) font
@s
Use a small (11 point) font
@b
Use a bold font
@i
Use an italic font
@f or @t
Use a fixed-pitch font
@c
Center the line horizontally
@r
Right-justify the text
@B0, @B1, ... @B255
Fill the backgound with indexed color
@C0, @C1, ... @C255
Use indexed color to draw the text
@F0, @F1, ...
Use indexed font to draw the text
@S1, @S2, ...
Use point size n to draw the text
@u or @_
Underline the text.
@-
draw an engraved line through the middle.

If WIDGET is a widget of any other type, then ITEM should be a child widget, which will be added with WIDGET as its parent.

For tree widget TEXT can be either full path (items are separated with slash) or text label. If it is terminated with slash, the branch (rather than leaf) will be inserted. One can specify parent node id and position in it (default values are -1 for both). The SUBWIDGET is a widget that will be inserted as a node. The procedure returns either the unique id of the freshly inserted node or -1 if failed.

This procedure can be used to add either columns (if first CELLTEXT is symbol column) or cells to the table widget.

bb:image
[procedure] (bb:image X)
[procedure] (bb:image PTR W H D)

If X is a string, then bb:image will load an image file (if its format is supported by FLTK). If X is a pointer, then it is treated as a pointer to XPM data. The 4-argument form of bb:image creates an RGB image from the data pointed to by the foreign pointer PTR, with width W, height H and depth D, where D specifies the number of color channels (1-4).

bb:image-data
[procedure] (bb:image-data IMAGE)

Returns four values: list of pointers to IMAGE data (usually one element for all formats, except of pixmaps), width, height and depth of the IMAGE.

bb:remove!
[procedure] (bb:remove! WIDGET [INDEX])

Removes the entry at the position INDEX from the list WIDGET, or all items, if index is #t. If widget is an image pointer, the storage occupied by the image will be released. For tree WIDGET the node with id INDEX will be removed. Destroys the widget.

bb:set-menu-item-active!
[procedure] (bb:set-menu-item-active! WIDGET INDEX FLAG)

Activates or deactivates the menu item with the index INDEX in the menu-bar WIDGET, depending on the boolean FLAG. Counting menu-items starts with 0 and every sub-menu increases the count by one. Note that each sub-menu introduces an invisible extra menu-item that has to be counted in.

bb:redraw
[procedure] (bb:redraw WIDGET)

Redraws WIDGET.

bb:show
[procedure] (bb:show WINDOW [ARG ...])

Shows WINDOW. If WINDOW is already visible, it will be raised to the top. ARGs are the options to be parsed by FLTK. By default the name of the executable is passed. #f doesn't pass any arguments.

bb:select-file
[procedure] (bb:select-file MESSAGE PATTERN [FILENAME])

Opens a file-dialog and returns the selected filename (or #f if the file-selection has been canceled). PATTERN is a file-pattern that is used to match filenames that can be selected. The following syntax is used by pattern:

*
matches any sequence of 0 or more characters.
?
matches any single character
[set]
matches any character in the set. Set can contain any single characters, or a-z to represent a range. To match ] or - they must be the first characters. To match ^ or ! they must not be the first characters.
[^set]
Matches any character not in the set.
[!set]
Matches any character not in the set.
{X|Y|Z}
Matches any one of the subexpressions literally.
{X,Y,Z}
Matches any one of the subexpressions literally.
\x
Quotes the character x so it has no special meaning.
x
All other characters must be matched exactly.

FILENAME specifies the default filename, if given.

bb:select-color
[procedure] (bb:select-color [STRING])
[procedure] (bb:select-color COLOR [STRING])

Pops up a color-selection dialog. If COLOR is an exact integer, or a symbol naming one of the default colors, then the user can select a color index, which will then be returned. If COLOR is a three-element list or vector, then the user can select an RGB (or HSV) color. bb:select-color either returns a color value (an integer, encoding a color index or a packed RGB value), or #f if the selection dialog was closed or canceled.

bb:rgb
[procedure] (bb:rgb R [G B])

Transforms the red, green and blue components given in R, G and B into a color value. All components should be integers in the range 0 - 255. If G and B are not given, bb:rgb returns a list of the red, green and blue color components of the packed color value R.

bb:get-input
[procedure] (bb:get-input LABEL [DEFAULT])

Pops up a dialog the requests an input string. LABEL should be a string that will be shown in the dialog, DEFAULT is the default text.

bb:group
[procedure] (bb:group WIDGET THUNK)

Invokes the zero-argument procedure THUNK in a dynamic context in which all created widgets are added the group WIDGET (which should be a window, group, tabs, tile, pack or scroll).

Keybindings

The following is a list of keyboard and mouse shortcuts available in entry and edit widgets.

Mouse button 1
Moves the cursor to this point. Drag selects characters. Double click selects words. Triple click selects all text. Shift+click extends the selection. When you select text it is automatically copied to the clipboard.
Mouse button 2
Insert the clipboard at the point clicked. You can also select a region and replace it with the clipboard by selecting the region with mouse button 2.
Mouse button 3
Currently acts like button 1.
Backspace
Deletes one character to the left, or deletes the selected region.
Enter
May cause the callback, see the when property
^A or Home
Go to start of line.
^B or Left
Move left
^C
Copy the selection to the clipboard
^D or Delete
Deletes one character to the right or deletes the selected region.
^E or End
Go to the end of line.
^F or Right
Move right
^K
Delete to the end of line (next \n character) or deletes a single \n character. These deletions are all concatenated into the clipboard.
^N or Down
Move down (edit widget only, otherwise it moves to the next input field).
^P or Up
Move up (for edit widgets only, otherwise it moves to the previous input field).
^U
Delete everything.
^V or ^Y
Paste the clipboard
^X or ^W
Copy the region to the clipboard and delete it.
^Z or ^_
Undo. This is a single-level undo mechanism, but all adjacent deletions and insertions are concatenated into a single "undo". Often this will undo a lot more than you expected.
Shift+move
Move the cursor but also extend the selection.
RightCtrl or Compose
Start a compose-character sequence.

With compose-character sequences, the next one or two keys typed define the character to insert (see the table that follows.)

For instance, to type "á" type [compose][a]['] or [compose]['][a].

The character "nbsp" (non-breaking space) is typed by using [compose][space].

The single-character sequences may be followed by a space if necessary to remove ambiguity. For instance, if you really want to type "ª~" rather than "ã" you must type [compose][a][space][~].

The same key may be used to "quote" control characters into the text. If you need a ^Q character you can get one by typing [compose][Control+Q].

X may have a key on the keyboard defined as XK_Multi_key. If so this key may be used as well as the right-hand control key. You can set this up with the program xmodmap.

If your keyboard is set to support a foreign language you should also be able to type "dead key" prefix characters. On X you will actually be able to see what dead key you typed, and if you then move the cursor without completing the sequence the accent will remain inserted.

Character Composition Table
Keys Char Keys Char Keys Char Keys Char Keys Char Keys Char
sp nbsp * ° ` A À D - Ð ` a à d - ð
! ¡ + - ± ' A Á ~ N Ñ ' a á ~ n ñ
% ¢ 2 ² A ^ Â ` O Ò ^ a â ` o ò
# £ 3 ³ ~ A Ã ' O Ó ~ a ã ' o ó
$ ¤ ' ´ : A Ä ^ O Ô : a ä ^ o ô
y = ¥ u µ * A Å ~ O Õ * a å ~ o õ
| ¦ p A E Æ : O Ö a e æ : o ö
& § . · , C Ç x × , c ç - : ÷
: ¨ , ¸ E ` È O / Ø ` e è o / ø
c © 1 ¹ ' E É ` U Ù ' e é ` u ù
a ª o º ^ E Ê ' U Ú ^ e ê ' u ú
< < « > > » : E Ë ^ U Û : e ë ^ u û
~ ¬ 1 4 ¼ ` I Ì : U Ü ` i ì : u ü
- ­ 1 2 ½ ' I Í ' Y Ý ' i í ' y ý
r ® 3 4 ¾ ^ I Î T H Þ ^ i î t h þ
_ ¯ ? ¿ : I Ï s s ß : i ï : y ÿ

ASCII picture interface

bb:make-widgets
[procedure] (bb:make-widgets SPEC WIDTH HEIGHT [CHARMAP])

Creates the widgets defined in the graphical representation string SPEC in a window of the dimensions WIDTH and HEIGHT. The graphical representation string is an ASCII picture of the widget layout, with uppercase characters designating widget types:

A widgets dimensions are computed by drawing a contguous line along the upper and left border, starting from the origin of the widget:

 01234567890123456789
 ....................
 .....BBBBBBBBB......
 .....BBBBBBBBB......
 .....BBBBBBBBB......
 ....................

Here we would have a button at 5/1, with width 9 and height 3 (before adjusting the dimensions to the specified width and height of the complete layout).

bb:make-widgets returns an association list of the form (TAG . WIDGET) which maps widget-tags to created widgets. If the optional argument CHARMAP (an list of lists of the form (ALIASCHAR CHAR TAG)) is given, then any occurrence of ALIASCHAR in the picture is treated as CHAR. The TAG will be returned in the widget a-list. If CHARMAP is not given, then the tag defaults to the usual widget type character.

Specially delimited strings can be embedded in the widget pictures:

 "STRING"

Sets the text property of the widget.

 |STRING|

Sets the direction, box type, slider type or color of the widget. Valid values for STRING are:

Direction:

Slider type:

box type :

color:

 'STRING, ...'

Adds items to a list widget.

 #STRING#

STRING should be the name of an image file (as understood by bb:image).

 :STRING:

STRING should be the name of a global variable holding a callback, or an expression evaulating to a procedure or name.

SXML Interface

bb:render
[procedure] (bb:render SXML)

Creates the widgets defined by the SXML representation in SXML. Each element represents a widget where the tag specifies a widget type (as in bb:make-widget). Element attributes represent widget properties. Attribute-value strings are transformed according to the following mapping:

 x y width height spacing maximum minimum x-position y-position text-size

Numeric strings. x, y, width and height may also be specified as offsets given as strings prefixed with + or -, or percentages given as strings suffixed with %.

 color text-color selection-color

A string of the form #RRGGBB or a color name

 resizable visible focus modal read-only valid-context

Anything different from the string no means true.

 resizable-widget

an element id.

 when

A comma-separated list of indicator-names

Anything else is either treated as a numeric string or (if not a valid number) as a symbolic property value. Note that attribute values may also be of other types than strings. The content of an SXML element will be used as the text property value of the created widget, if given.

Elements may have any number of additional attributes. The attribute id can be used to identify elements.

A child widget will have the dimensions of it's parent, if no width and/or height attributes are given.

bb:element?
[procedure] (bb:element? X)

Returns #t if X is an element, or #f otherwise.

Element accessors
[procedure] (bb:element-widget ELEMENT)
[procedure] (bb:element-parent ELEMENT)
[procedure] (bb:element-children ELEMENT)
[procedure] (bb:element-id ELEMENT)
[procedure] (bb:element-tag ELEMENT)
[procedure] (bb:element-content ELEMENT)
[procedure] (bb:element-attributes ELEMENT)

Accessor procedures for element slots. Parent is an element or #f (if its the root element). Children is a list of child elements. Widget is the widget represented by this element. Id and tag are symbols. Attributes is a property list mapping attribute symbols to values. Content is a string.

bb:find-element
[procedure] (bb:find-element ID [ROOT])

Searches the element with the id ID, starting from parent element ROOT, or the value of (bb:root-element) if not given. If no element with this id can be found #f is returned.

bb:find-widget
[procedure] (bb:find-widget ID [ROOT])

Equivalent to (bb:element-widget (bb:find-element ID ROOT))

bb:root-element
[parameter] (bb:root-element [ELEMENT])

Parameter holding the current root element.

Examples

(use bb)

(bb:init)

(define w (bb:make-widget 'window 200 100))

(bb:group
 w
 (lambda ()
   (let ([lbl (bb:make-widget 'label 200 100)])
     (set! (bb:property lbl 'box) 'engraved-box)
     (set! (bb:property lbl 'text-size) 32)
     (set! (bb:property lbl 'text-font) 'times-bold-italic)
     (set! (bb:property lbl 'text) "Hello, World") ) ) )

(bb:show w)

;An alternative, simpler way of doing the above:
;
;(bb:render 
; '(window (@ (width 200) (height 100))
;	  (label (@ (box engraved-box) (text-size 32) (text-font times-bold-italic))
;		 "Hello, World")))

(bb:run)

A very simple shell:

(require-extension extras posix srfi-17 bb)

(bb:init)

(define width 300)
(define height 150)

(define w (bb:make-widget 'window width height))

(define str #<<EOF
LLLLLLLLLLLLLJJJJJJJ
LLLLLLLLLLLLLJJJJJJJ
LLLLLLLLLLLLLJJJJJJJ
EEEEEEEEEEEEEEEEEEEE
EOF
)

(define widgets
  (bb:group
   w
   (lambda ()
     (bb:make-widgets str width height) ) ) )

(define lst (cdr (assq #\L widgets)))
(define entry (cdr (assq #\E widgets)))

(set! (bb:property entry 'callback)
  (lambda ()
    (let ([cmd (bb:property entry 'text)])
      (bb:add! lst (string-append "@i@." cmd))
      (set! (bb:property entry 'text) "")
      (with-input-from-pipe 
       cmd
       (lambda ()
	 (let loop ()
	   (let ([ln (read-line)])
	     (unless (eof-object? ln)
	       (bb:add! lst (string-append "@." ln)) 
	       (loop) ) ) ) ) ) ) ) )

(bb:show w)
(bb:run)

A simple "notes" application:

(use utils srfi-17 bb matchable)

(bb:init)

(define (switch-note)
  (let ([item (bb:property item-list 'value)])
    (when (> item 0)
      (let ([note (list-ref all-notes (sub1 item))])
	(note-hidden?-set! note #f)
	(bb:show (note-window note)) ) ) ) )

(bb:render
 `(window (@ (width 200) (height 300) (resizable) (id w) (resizable-widget lst))
	  (menu-bar (@ (height 30) (id m)))
	  (list (@ (y 30) (width 200) (height 270) (id lst) (callback switch-note))) ) )

(define item-list (bb:find-widget 'lst))
(define menu-bar (bb:find-widget 'm))
(define notes-window (bb:find-widget 'w))
(define default-color 'yellow)
(define all-notes '())

(define-record note window edit title color hidden?)

(define (note-hider note)
  (lambda ()
    (note-hidden?-set! note #t)
    (set! (bb:property (note-window note) 'visible) #f) ) )

(define (add-note . title)
  (let* ([name (optional title (->string (gensym 'note)))]
	 [x (random 600)]
	 [y (random 400)]
	 [w (bb:make-widget 'window x y 200 150)] 
	 [e (bb:group w (lambda () (bb:make-widget 'edit 0 0 200 150)))] 
	 [note (make-note w e name default-color #f)] )
    (set! (bb:property w 'resizable) #t)
    (set! (bb:property w 'resizable-widget) e)
    (set! (bb:property e 'color) default-color)
    (set! (bb:property w 'callback) (note-hider note))
    (bb:show w)
    (bb:add! item-list (sprintf "@.~A" name))
    (set! all-notes (append all-notes (list note))) ) )

(define (choose-color)
  (and-let* ([col (bb:select-color default-color)])
    (set! default-color col) ) )

(define notesfile (make-pathname (get-environment-variable "HOME") ".bbnotes"))

(define (load-notes . file)
  (with-input-from-file (:optional file notesfile)
    (lambda ()
      (match (read)
	[(x y w h)
	 (set! (bb:property notes-window 'x) x)
	 (set! (bb:property notes-window 'y) y)
	 (set! (bb:property notes-window 'width) w)
	 (set! (bb:property notes-window 'height) h) ] )
      (let loop ()
	(match (read)
	  [(? eof-object?) #f]
	  [(title color pos text hidden?)
	   (let* ([w (apply bb:make-widget 'window pos)]
		  [e (bb:group w (lambda () (apply bb:make-widget 'edit 0 0 (cddr pos))))] 
		  [note (make-note w e title color hidden?)] )
	     (set! (bb:property e 'color) color)
	     (set! (bb:property w 'resizable) #t)
	     (set! (bb:property w 'resizable-widget) e)
	     (set! (bb:property w 'text) title)
	     (set! (bb:property w 'callback) (note-hider note))
	     (bb:add! e text)
	     (unless hidden? (bb:show w))
	     (set! all-notes (append all-notes (list note)))
	     (bb:add! item-list (string-append "@." title))
	     (loop) ) ] ) ) ) ) )

(define (save-notes . file)
  (with-output-to-file (:optional file notesfile)
    (lambda ()
      (write (map (cut bb:property notes-window <>) '(x y width height)))
      (newline)
      (for-each
       (lambda (note)
	 (let ([w (note-window note)]
	       [e (note-edit note)] )
	   (write 
	    (list 
	     (note-title note)
	     (note-color note)
	     (list (bb:property w 'x) (bb:property w 'y) (bb:property w 'width) (bb:property w 'height))
	     (bb:property e 'text) 
	     (note-hidden? note) ) )
	   (newline) ) )
       all-notes) ) ) )

(define (change-title)
  (let ([i (bb:property item-list 'value)])
    (when (> i 0)
      (let* ([note (list-ref all-notes (sub1 i))]
	     [title (bb:get-input "Enter new title" (note-title note))] )
	(set! (bb:property (note-window note) 'text) title)
	(note-title-set! note title) 
	(update-list) ) ) ) )

(define (update-list)
  (bb:remove! item-list #t)
  (for-each (lambda (note) (bb:add! item-list (string-append "@." (note-title note)))) all-notes) )

(define (change-color)
  (let ([i (bb:property item-list 'value)])
    (when (> i 0)
      (let* ([note (list-ref all-notes (sub1 i))]
	     [col (bb:select-color (note-color note))] )
	(set! (bb:property (note-edit note) 'color) col)
	(bb:redraw (note-edit note))
	(note-color-set! note col) ) ) ) )

(define (fini) 
  (save-notes)
  (exit) )

(set! (bb:property notes-window 'callback) fini) ; when ESC is pressed

(bb:add! menu-bar "File/Quit" fini "^q")
(bb:add! menu-bar "Notes/New" add-note "^n")
(bb:add! menu-bar "Notes/Set default color..." choose-color)
(bb:add! menu-bar "Change/Title..." change-title)
(bb:add! menu-bar "Change/Color..." change-color)

(bb:show notes-window)

(when (file-exists? notesfile) (load-notes))

(bb:run)

Event handlers usage:

(use srfi-17 bb)

(bb:init)

(define w (bb:make-widget 'window 200 100))
(define e (bb:make-widget 'edit 5 5 150 25))
(set! (bb:property e 'callback) (lambda () (display "edit was changed") (newline) ) )
(set! (bb:property e 'when) 'changed)
(set! (bb:property e 'handler)
  (lambda (e)
    (case e
      [(move)
       (printf "mouse was moved over edit at (~A, ~A)~%" (bb:event 'x) (bb:event 'y))
       #t]
      [(push)
       (printf "mouse was clicked on edit, clicks = 1 + ~A~%" (bb:event 'clicks))
       (set! (bb:event 'clicks) 0)
       (printf "clicks after setting: ~A~%" (bb:event 'clicks))
       #t]
      [(keydown)
       (printf "key was pressed: ~A~%" (bb:event 'key))
       -1]
      [else -1] ) ; pass other events to the base class
    ) )

(bb:show w)
(bb:run)

Changelog