CONTROL(2)CONTROL(2)
NAME
Control, Controlset, activate, closecontrol, closecontrolset, controlcalled, controlwire, createbox, createboxbox, createbutton, createcolumn, createentry, createkeyboard, createlabel, createmenu, createradiobutton, createrow, createscribble, createslider, createstack, createtab, createtext, createtextbutton, ctlerror, ctlmalloc, ctlrealloc, ctlstrdup, ctlprint, deactivate, freectlfont, freectlimage, initcontrols, namectlfont, namectlimage, newcontrolset, resizecontrolset – interactive graphical controls
SYNOPSIS
#include <u.h> #include <libc.h> #include <draw.h> #include <thread.h> #include <keyboard.h> #include <mouse.h> #include <control.h>unhandled troff command .sp
typedef struct Control Control; typedef struct Controlset Controlset;
unhandled troff command .sp
struct Control { char *name; Rectangle rect; /* area on screen */ Rectangle size; /* min/max Dx, Dy (not a rect) */ Channel *event; /* chan(char*) to client */ Channel *data; /* chan(char*) to client */ ... };
unhandled troff command .sp
struct Controlset { ... Channel *ctl; Channel *data; ... int clicktotype; ... };
unhandled troff command .sp
void initcontrols(void)
unhandled troff command .sp
Controlset* newcontrolset(Image *i, Channel *kc, Channel *mc, Channel *rc)
unhandled troff command .sp
void closecontrolset(Controlset *cs)
unhandled troff command .sp
int namectlfont(Font *font, char *name)
unhandled troff command .sp
int freectlfont(char *name)
unhandled troff command .sp
int namectlimage(Image *image, char *name)
unhandled troff command .sp
int freectlimage(char *name)
unhandled troff command .sp
Control* createbox(Controlset *cs, char *name)
unhandled troff command .sp
Control* createboxbox(Controlset *cs, char *name)
unhandled troff command .sp
Control* createbutton(Controlset *cs, char *name)
unhandled troff command .sp
Control* createcolumn(Controlset*, char*)
unhandled troff command .sp
Control* createentry(Controlset *cs, char *name)
unhandled troff command .sp
Control* createkeyboard(Controlset *cs, char *name)
unhandled troff command .sp
Control* createlabel(Controlset *cs, char *name)
unhandled troff command .sp
Control* createmenu(Controlset *cs, char *name)
unhandled troff command .sp
Control* createradiobutton(Controlset *cs, char *name)
unhandled troff command .sp
Control* createrow(Controlset*, char*)
unhandled troff command .sp
Control* createscribble(Controlset *cs, char *name)
unhandled troff command .sp
Control* createslider(Controlset *cs, char *name)
unhandled troff command .sp
Control* createstack(Controlset*, char*)
unhandled troff command .sp
Control* createtab(Controlset*, char *)
unhandled troff command .sp
Control* createtext(Controlset *cs, char *name)
unhandled troff command .sp
Control* createtextbutton(Controlset *cs, char *name)
unhandled troff command .sp
void closecontrol(Control *c)
unhandled troff command .sp
int ctlprint(Control*, char*, ...);
unhandled troff command .sp
void ctlerror(char *fmt, ...)
unhandled troff command .sp
Control* controlcalled(char *name)
unhandled troff command .sp
void controlwire(Control *c, char *cname, Channel *ch)
unhandled troff command .sp
void activate(Control *c)
unhandled troff command .sp
void deactivate(Control *c)
unhandled troff command .sp
void resizecontrolset(Controlset *cs)
unhandled troff command .sp
void* ctlmalloc(uint n)
unhandled troff command .sp
void* ctlrealloc(void *p, uint n)
unhandled troff command .sp
char* ctlstrdup(char *s)
unhandled troff command .sp
int ctldeletequits;
DESCRIPTION
This library provides a set of interactive
controls for graphical displays: buttons, sliders, text entry boxes, and so on.
It also provides aggregator
The library provides a simple mechanism for automatic layout:
the minimum and maximum sizes of each simple control can be specified.
Message format
All messages are represented as
Messages sent to a
The sender (and the colon following it) may be omitted.
For example, the initial field of a text entry control called
to its
To make it easy to write messages, the function
The
chanprint(e->event, "value %q", "Don't touch!");
It is wise to use
The destination of a message can be a named control, or a set of controls identified by name or type. The command
(note the quotation) sends the ‘show’ command to the entry named
\f2 Note that we are still experimenting with destination names. One proposal is that a destination of the form "‘name1 name2 ⋯ type1 type2 ⋯’ selects all controls of the named types in the control hierarchies (of columns, rows and stacks) whose names precede the types.
Messages sent by a control on its
The
Initialization and Control sets
After
Each control is represented by a
The function
The public elements of a
Commands for controls are sent through the
The function
If all windows are organized in a hierachy of
Fonts and images
Fonts and images must be given names so they may be referenced
in messages.
The functions
The function
Creation
Each type of control has an associated creation function:
The function
Configuration
After a control is created, it must be configured using the control-specific
commands documented below.
Commands are sent to the
Messages sent to the
The recipient of a message ignores the initial
Activation
When they are created, controls are disabled: they do not respond to user input. Not all controls need to be responsive; for example, labels are static and a text display might show a log of messages but not be useful to edit. But buttons, entry boxes, and other text displays should be active.
To enable a control, call the
Controls can be either
The function
Controls
The following sections document the individual controls in alphabetical order.
The layout of each section is a brief description of the control’s behavior,
followed by the messages it sends on
All controls accept the following messages:
Set the bounding rectangle for the control on the display.
The syntax generated by the
Set the minimum and maximum size for automatic layout in
Disable drawing of the control and ignore mouse and keyboard events
until the control is once again revealed.
Grouping
This is the opposite of
Display the
Many messages are common between multiple
Specify the alignment of (some part of) the
Inset the
Paint the border of the control with the named color, default black.
The
Set the format of ‘value’ messages sent on the
Many controls set a background image or color for display.
The
These commands set the font and color for displaying text.
The defaults are the default
Set the value of the
Box
A box is a trivial control that does nothing more than pass keyboard, mouse,
and focus messages back on its
boxname: key 0xnn
where
boxname: mouse [x y] but msec
where
boxname: focus n
where
The box displays within its rectangle an image, under mask, with specified alignment. The control messages it accepts are:
Controls the placement of the image in the rectangle (unimplemented).
Boxbox
A
adds the named control to the box of controls. The display order is determined by the order of adding. The first named control is top left, the second goes below it, etc. It is possible to add one control to multiple grouping controls but the layout of the result will be quite unpredictable.
This command is passed on to the member controls.
Background color displayed between member controls.
This command is passed on to the member controls.
Set the separation between member controls to
The member controls are layed out within the given rectangle according to
the minimum and maximum sizes given. If the rectangle is not large enough
for the minimum a fatal error is currently generated.
If the controls at their maximum size are not big enough to fit, they are top-left justified
at their maximum size in the space given.
Otherwise, controls will get their minimum size and be enlarged proportional
to the extra size given by the maximum until they fit given rectangle.
The members are separated by borders of the width established by
Remove the named control from the box.
This command is passed on to the member controls. Show also (re)displays background and borders.
Button
A button is a simple control that toggles its state when mouse button 1 is pressed on its rectangle. Each state change triggers an event message:
buttonname: value n
where
The button displays an image (which may of course be a simple color) and illuminates in the standard way when it is ‘on’. The control messages it accepts are:
Controls the placement of the image in the rectangle (unimplemented).
Set the button to ‘on’ (if
Column
A column is a grouping control which lays out its members vertically, from top to bottom. Currently, columns ignore mouse and keyboard events, but there are plans to allow dragging the borders (when they have non-zero width) between constituent members.
adds the named control to the column of controls. The vertical order is determined by the order of adding. The first named control goes at the top. It is possible to add one control to multiple grouping controls but the layout of the result will be quite unpredictable.
Set the border between members to the width given.
Background color displayed between member controls.
Set the separation between member controls to
These three commands are passed on to the member controls. Show also (re)displays the borders between members.
The member controls are layed out within the given rectangle according to
the minimum and maximum sizes given. If the rectangle is not large enough
for the minimum a fatal error is currently generated. However, see the example
at the end of this man page.
If the controls at their maximum size are not big enough to fit, they are centered
at their maximum size in the space given.
Otherwise, controls will get their minimum size and be enlarged proportional
to the extra size given by the maximum until they fit given rectangle.
The members are separated by borders of the width established by
Remove the named control from the column.
Without arguments, this command computes the minimum and
maximum size of a column by adding the minimum and maximum
heights to set
Entry
The entry control manages a single line of editable text. When the user hits a carriage return anywhere in the text, the control generates the event message,
entryname: value s
with
The cursor can be moved by clicking button 1; at the moment, there is no way to select characters, only a typing position. Some control characters have special actions: control-H (backspace) deletes the character before the cursor; control-U clears the line; and control-V pastes the snarf buffer at the typing position. Most important, carriage return sends the text to the event channel.
To enter passwords and other secret text without displaying the
contents, set the font to one in which all characters are the same.
The easiest way to do this is to make a font containing only one character,
at position 0 (NUL), since that position is used to render all
characters not otherwise defined in the font (see
The control messages the entry control accepts are:
Controls the placement of the text in the rectangle.
After receiving this message, the entry will send its value to its
When it receives focus, the entry box displays a typing cursor. When it does not have focus, the cursor is not displayed.
Set the string displayed in the entry box.
Keyboard
The keyboard control implements a simulated keyboard useful on palmtop devices. Keystrokes, generated by mouse button 1 on the simulated keys, are sent as event messages:
keyboardname: value 0xnn
where
There are two special keys,
The image, mask, light rules are used to indicate that a key is pressed, but to aid clumsy fingers the keystroke is not generated until the key is released, so it is possible to slide the pointer to a different key to correct for bad aim.
The control messages the keyboard accepts are:
Sets the font for the keys.
If only one font is named, it is used for all keys.
If two are named, the second is used for key caps with special names such
as Shift and Enter.
(Good choices on the Bitsy are
Label
A label is like a textbutton
Controls the placement of the image in the rectangle.
The value is a string that can be modified only by sending this message to the
Menu
A menu is a pop-up window containing a set of textual selections. When a selection is made, it removes itself from the screen and reports the selection by value:
menuname: value n
If no selection is made, no message is reported.
Because it creates a window, programs using a menu must have their
The control messages accepted by a menu are:
Add a line of
Controls the left-right placement of the text in its rectangle.
Only the origin of the rectangle is significant; menus calculate the appropriate size.
Set the color in which to highlight selected lines; default yellow.
Set the color in which to draw the text in selected lines; default black.
Display the menu. Not usually needed unless the menu is changed while visible; use
With no arguments, toggle the menu’s visibility; otherwise make it visible (1) or invisible (0). When the selection is made, the menu will remove its window automatically.
Radiobutton
The radiobutton assembles a group of buttons or textbuttons into a single control with a numeric value. Its value is –1 if none of the constituent buttons is pressed; otherwise it is the index, starting at zero, of the button that is pressed. Only one button may be pressed; the radiobutton manipulates its buttons to guarantee this. State changes trigger an event message:
radiobuttonname: value n
Buttons are added to the radio button using the
The control messages the radiobutton accepts are:
Add the control with the specified
Row
A row groups a number of member controls left to right in a rectangle.
Rows behave exactly like columns with the roles of
The control messages it accepts are:
Scribble
The scribble control provides a region in which strokes drawn with mouse button
1 are interpreted as characters in the manner of
The control messages it accepts are:
Controls the placement of the image in the rectangle (unimplemented).
Used to display the indicia.
The color in which to draw the strokes; default black.
Stack
A stack groups a number of member controls in the same shared rectangle. Only one of these controls will be visible (revealed), the others are hidden.
The control messages it accepts are:
Without argument,
Without argument,
Slider
A slider controls an integer value by dragging the mouse with a button. Configured appropriately, it can serve as a scroll bar with the standard Plan 9 behavior. When the value changes, an event message is sent:
slidername: value n
The slider is a good candidate for connecting to another control
by setting its format and rewiring its
The geometry of the slider is defined by three numbers:
The control messages the slider accepts are:
If
The
Set the color in which to draw the indicator; default black.
Set the maximum value of the range covered by the slider.
The string
Set the visible area shown by the indicator.
Tab
A tab control combines radiobottuns with a stack of windows giving the appearance of tabbed controls. Currently, the tabs are positioned at the top of the stack. The radiobutton consists of textbuttons, the stack can be composed of any type of control.
Control messages are
Adds a button to the radiobutton, and an associated control to the stack. Buttons and controls are numbered in the order of addition. There is no remove operation.
When a format string is defined, the tab control reports which tab
is selected using the format string (which must print a
Color between member controls.
Spacing between buttons in the radiobutton and between the row of buttons and the stack below it.
Value must be an integer indicating which tab to bring to the top.
Text
A text control presents a set of lines of text. The text cannot be edited with the keyboard, but can be changed by control messages. (A more interactive text control will be created eventually.) The mouse can be used to select lines of text. The only event message reports a state change in the selection of a line:
textname: select n s
states that line
The control messages the text control accepts are:
With one argument, append the string
Controls the placement of each line of text left-to-right in its rectangle. Vertically, lines are tightly packed with separation set by the font’s interline spacing.
Delete all text.
Delete line
Replace line
If
Set the selection state of line
Set the color in which to highlight selected lines; default yellow.
The string
Scroll the text so the top visible line is number
Delete all the text in the control and then add the single line
Textbutton
A textbutton is a textual variant of a plain button. Each state change triggers an event message:
textbuttonname: value n
where
Like a regular button, the value of a textbutton is an integer; the
Controls the placement of the text in the rectangle.
Set the color in which to display text when the textbutton is pressed.
Set the text displayed in the button.
Set the button to ‘on’ (if
Helper functions
The function
The functions
Finally, for debugging, if the global variable
ctlerror("delete");
Caveat
This library is very new and is still missing a number of important features. The details are all subject to change. Another level of library that handles geometry and has sensible default appearances for the controls would be useful.
One unusual design goal of this library was to make the controls themselves easy to implement. The reader is encouraged to create new controls by adapting the source to existing ones.
EXAMPLES
This example creates two entry boxes,
#include <u.h> #include <libc.h> #include <thread.h> #include <draw.h> #include <mouse.h> #include <keyboard.h> #include <control.h>unhandled troff command .sp
Controlset *cs;
unhandled troff command .sp
int ctldeletequits = 1;
unhandled troff command .sp
void resizecontrolset(Controlset*) { int i; Rectangle r, r1, r2;
unhandled troff command .sp
if(getwindow(display, Refnone) < 0) sysfatal("resize failed: %r"); r = insetrect(screen->r, 10); r1 = r; r2 = r; r1.max.y = r1.min.y+1+font->height+1; r2.min.y = r1.max.y+10; r2.max.y = r2.min.y+1+font->height+1; chanprint(cs->ctl, "top rect %R\ntop show", r1); chanprint(cs->ctl, "bot rect %R\nbot show", r2); }
unhandled troff command .sp
void threadmain(int argc, char *argv[]) { char *s, *args[3]; Channel *c; Control *top, *bot; int n;
unhandled troff command .sp
initdraw(0, 0, "example"); initcontrols(); cs = newcontrolset(screen, nil, nil, nil); cs->clicktotype = 1;
unhandled troff command .sp
top = createentry(cs, "top"); chanprint(cs->ctl, "top image paleyellow"); chanprint(cs->ctl, "top border 1"); bot = createentry(cs, "bot"); chanprint(cs->ctl, "bot image paleyellow"); chanprint(cs->ctl, "bot border 1");
unhandled troff command .sp
c = chancreate(sizeof(char*), 0); controlwire(top, "event", c); controlwire(bot, "event", c);
unhandled troff command .sp
activate(top); activate(bot); resizecontrolset(cs);
unhandled troff command .sp
for(;;){ s = recvp(c); n = tokenize(s, args, nelem(args)); if(n==3 && strcmp(args[1], "value")==0){ if(strcmp(args[0], "top:") == 0) chanprint(cs->ctl, "bot value %q", args[2]); else chanprint(cs->ctl, "top value %q", args[2]); } } threadexitsall(nil); }
A richer variant couples a text entry box to a slider.
Since the value of a slider is its numerical setting, as a decimal number,
all that needs changing is the setup of
bot = createslider(cs, "bot"); chanprint(cs->ctl, "bot border 1"); chanprint(cs->ctl, "bot image paleyellow"); chanprint(cs->ctl, "bot indicatorcolor red"); chanprint(cs->ctl, "bot max 100"); chanprint(cs->ctl, "bot clamp low 1"); chanprint(cs->ctl, "bot orient horizontal");
The rest is the same. Of course, the value of the entry box is only meaningful to the slider if it is also a decimal number.
Finally, we can avoid processing events altogether by cross-coupling
the controls. Replace the rest of
chanprint(cs->ctl, "bot format %q", "%q: top value %q"); chanprint(cs->ctl, "top format %q", "%q: bot value %q");unhandled troff command .sp
controlwire(top, "event", cs->ctl); controlwire(bot, "event", cs->ctl);
unhandled troff command .sp
activate(top); activate(bot); resizecontrolset(cs);
unhandled troff command .sp
for(;;) yield(); threadexitsall(nil);
SOURCE
SEE
BUGS
The library is strict about matters of formatting, argument count in messages,
etc., and calls