# Oberon/ETH Oberon/Tutorial/GadgetsProg

These tutorial pages were written by André Fischer (afi) with editorial assistance of Hannes Marais, were hosted at the ETHZ and remain under the ETH license. Related content is found in the system via Book.Tool. Extended content is also available on paper. Some tutorial pages are in the WayBack archive.

## Objective

Supply enough information on gadgets programming to enable you to extend Oberon System 3 with new exciting applications.
Estimated time: 90 minutes.

## Introduction

The Gadgets system builds on the basic Oberon System 3 release by adding special modules and conventions for programming user interfaces. In essence, it introduces a new type of object called a gadget. Gadgets are user interface elements that the user may combine at run-time to build user interfaces. They obey strict protocols allowing them to be embedded in many different applications. The central module is called Gadgets that provides the base classes for the system. The Gadgets module depends on further modules that provide clipping operations (Display3 and Printer3), a module to manage attributes (Attributes), and a module for special effects (Effects). On top of these modules a hierarchy of modules, each of which implements a new gadget type, exists. Many of these modules are provided standard with Oberon System 3.

## Programming a new gadget type

Learning how to program gadgets is best done by reading the source code of simple, but fully working examples.
- Skeleton.Mod is an example of how to program a visual gadget. It implements a small colored block that can be moved, resized, copied, printed and colored.
- Complex.Mod is an example of how to program a model gadget. It implements a model gadget for complex numbers.
- DocumentSkeleton.Mod is an example of how to program a document gadget. It implements a document that consists of a panel, only the color of which is stored.
Each of these three examples can be used as a basis for creating a new, custom and application oriented gadget type: a visual, a model and a document gadget.
When programming a new gadget, you will need the following:
1 - A new type for the new gadget, usually created by extending a existing "base" type. Here is a skeleton for such an extended type declaration:
The base type might be for example
    Gadgets.FrameDesc for a visual gadget
    Gadgets.ObjDesc for a model gadget
    Documents.DocumentDesc for a document gadget.
When extending an existing gadget the record type of that gadget is taken as base type. To ensure that the gadget is extensible, both the record and pointer types should be exported.
2 - A message handler.
3 - A New procedure.

### The New procedure

Creating a new instance of a gadget is like everything else in the Oberon system, done via a command. A module M contains a procedure P whose task is to dynamically allocate a new instance of a certain object type. This is called the object's New procedure. Executing the New procedure M.P (this is often referred to as generator string) causes a new instance of that object type to be created. The new object instance is initialized to a default state and is ready to accept messages (i.e. it is totally functional).
The following is a typical New procedure:

### The message handler

Handler is a standard Oberon message handler type for class Object and message base type ObjMsg (see Objects):
Handler = PROCEDURE (obj: Objects.Object; VAR M: Objects.ObjMsg);
In a realistic object-oriented environment, messages are rarely handled completely by the first recipient. Usually, they are passed through a complex network of objects. Thus a handler for a given gadget only handles messages which should be handled differently than in the base type. It passes all other messages on to the handler of the base type (e.g. Gadgets.framehandle for a visual gadget).
There are two important message classes in Gadgets:
- Messages derived from Display.FrameMsg: The frame messages in the Display module play a central role in interframe communication. These build a communications protocol allowing frames to communicate with each other without knowing about each other's internal working. The latter is crucial if foreign or yet unknown objects are to be integrated into the system and applications need to exchange objects with each other. The FrameMsg is defined as follows:
F plays a central role in the FrameMsg. It determines the destination, or target frame of a message. Often the destination frame of a message is unknown. This happens for example when model update messages are broadcast, in which case the F field is set to NIL.
- Messages not derived from Display.FrameMsg: These messages typically can be sent directly to the receiver object, by calling its handler (obj.handle(obj, msg)). E.g. Objects.AttrMsg
A typical message handler looks like the following:
Remarks:
- When a message is handled only partially or is not handled at all, then the handler of the base type should be called. - To ensure that the gadget can later be extended the FrameHandler should be exported. - Model gadgets should ignore messages of the Display.FrameMsg family.

## Messages derived from Display.FrameMsg
### Display.DisplayMsg

The DisplayMsg broadcasts a redraw request to a single or all frames. It is defined as follows:
When the destination (F) is NIL, all frames are implied. When id is set to Display.area, the area u, v, w, h inside the destination frame should be redrawn. These coordinates are relative to the upper-left corner of the destination gadget (thus v is normally negative).
A special display mask data structure (Display3.Mask) is used to indicate which areas of a gadget are visible. It is specified as a set of non-overlapping rectangles. Drawing primitives are issued through this mask, which has the effect of clipping them to only the visible areas of the gadget.
Handling the Display.DisplayMsg therefore might look as follows:
Remarks:
- Gadgets are usually rectangular, their size being described by F.W and F.H. x, y are the coordinates of the lower-left corner of the rectangle. - Normally the drawing routines of the Display3 module are used to draw a gadget.

### Display.PrintMsg

This is a request to a frame to print itself. It is defined as follows:
A whole tree of gadgets is implied when the destination is NIL. When the id is set to view, the frame has to print itself in the form it looks on the display. When the id is set to contents it should print its complete contents (for example a text that it may be displaying). By convention, the x, y coordinates indicate the absolute printer coordinates of the lower-left corner of the frame. The frame may assume that the printer driver has been initialized already.
Printing can also be done with clipping masks. All the primitives available for display masks (Display3), are also available for printing (Printer3). One major difference is that printing masks are stored using printer coordinates. Just like for display masks, a special routine is provided to calculate the print mask of a gadget (Gadgets.MakePrinterMask).

### Oberon.InputMsg

This message sends mouse and keyboard input to frames. It is defined as follows:

#### Tracking the mouse

When the Oberon event loop senses a mouse movement or that a mouse button has been pressed, it sends a track message (id = Oberon.track) to the affected viewer. The gadget can do whatever it pleases, when it receives a track message. However if possible it should abide by the Oberon conventions.
Normally, gadgets have a control border in which the gadgets respond to mouse combinations for resize, move, delete and copy. These mouse combinations are handled by Gadgets.framehandle, so the mouse has to be tracked only inside the working area of the gadgets. Gadgets.InActiveArea checks whether or not the mouse is inside the working area.
Mouse clicks are normally recorded in a tracking loop. In this loop, the mouse driver is read directly and interclicks are recorded. The loop terminates when all three buttons are up again.
Thus mouse tracking may be programmed as follows:

#### Programming a caret

When a keyboard key is pressed, a consume message (id = Oberon.consume) is broadcast. However since the Oberon event loop does not know in which frame the caret is currently set, the recipient of the message is unknown (F = NIL). Only the frame containing the caret should consume the character.
A gadget implementing a caret typically has a BOOLEAN field indicating whether or not the caret is set. Thus the definition for MyGadgetDesc may look as follows:
The caret field is initialized to FALSE in the New procedure. The handling of the caret could then be implemented as follows:

### Oberon.ControlMsg

This message changes the state of a gadget. It is defined as follows:
When the destination (F) is NIL, all frames are implied. When id is set to Oberon.defocus, then the gadget should remove its caret. If id is set to Oberon.neutralize, then the gadget should remove all marks it contains (caret and selection). See Programming a caret for an example of how this message is used.

## Objects messages

The messages of the Objects module are common to all gadgets.

### Objects.AttrMsg

In Oberon System 3, object attribute management is done strictly by sending Objects.AttrMsg messages to objects.
Typically, for our case study example, you would handle these messages as follows:
Comments:
The object must only handle the attributes that have been added to the base type. The other attributes are processed by the base type handler.
(1) id=Objects.get, return the value of a named attribute. Each object should as a minimum handle the "Gen" attribute, i.e. return the New procedure string.
(2) id=Objects.set, change the value of a named attribute.
(3) id=Objects.enum, enumerate each attribute by calling M.Enum(extended attribute) repeatedly.

### Objects.FileMsg

The purpose of FileMsg messages is to load and store objects from and to a sequential file.
Typically, for our case study example, you would handle these messages as follows:
Comments:
The object must only handle the attributes that have been added to the base type. The other attributes are processed by the base type handler.
(1) id=Objects.load, the object is requested to store its data to the file specified by the rider M.R. (2) id=Objects.store, then the object is requested to load its data from the file specified by the rider M.R.
To keep loading and storing of objects portable among the different Oberon platforms, use the procedures of the Files module which read and write the different Oberon basic types (e.g. WriteInt, WriteString, ...).

### Objects.CopyMsg

Messages of type CopyMsg are used to create an exact copy of a given object.
We distinguish between shallow and deep copies. When a shallow copy has to be created, as many references to original components as possible are left unresolved, whereas in the case of a deep copy, all references are resolved by recursively creating copies of the components. Note that, in both cases, the copy message is at least passed through a part the entire data structure representing the original object.
Objects.CopyMsg:
Comments:
(1) The same copy message may arrive more than once. The time stamp field is thus used to detect if a copy of the object has already been made.

## Programming a new document type
### Load and store a document

Documents need not to handle messages of the type Objects.FileMsg. Loading and storing of documents is done by the two procedure variable fields Load and Store of its base type (Documents.Document). Thus the New procedure for a document looks as follows:
Where Load is defined as follows:
Remarks:
- All document files have a header consisting of tag, name, x, y, w and h. - The child gadget needs not to be a panel, any gadget can be used.
Where Store is defined as follows:

### Special attributes of a document

Compared to all other gadgets, documents have three additional read-only attributes (see Objects.AttrMsg):
- Menu: String attribute which specifies the contents of the menu bar. The syntax for this string is:
- Icon: String attribute which specifies the icon to be used, when the document is iconized with Desktops.MakeIcons *. The string gives the full name of a picture in the Icons.Lib.
- Adaptive: Boolean attribute which specifies whether a document should dynamically change its size, when opened as Oberon viewer.

### Displaying a document

Normally there is no need to explicitly handle the Display.DisplayMsg and Display.ModifyMsg messages. Documents.Handler is responsible for delegating these messages to the menu bar and the child gadgets. However, if e.g. the size of the document is limited to a minimal or maximal size, the Display.ModifyMsg message may be changed before calling Documents.Handler.

## Index

A
Adaptive
C
caret Complex.Mod
D
destination frame Display.DisplayMsg Display.FrameMsg Display.PrintMsg Display3.Mask Documents.DocumentDesc DocumentSkeleton.Mod
G
Gadgets.FrameDesc Gadgets.ObjDesc Gen attribute
H
handler
I
Icon
K
keyboard input
L
LoadDoc
M
Menu message mouse tracking
N
New procedure NewDoc
O
Oberon.ControlMsg Oberon.InputMsg Objects.AttrMsg Objects.CopyMsg Objects.FileMsg
S
Skeleton.Mod StoreDoc
T
target frame
Revised on July 23, 1996 Installed on 30 05 1997