SCI/Specifications/Graphics/Windows, Dialogs and Controls

From ScummVM :: Wiki
Jump to navigation Jump to search

Windows, Dialogs and Controls

Original document by Lars Skovlund

I am going to start by mentioning the menus. It has nothing to do with the material I deal with in this essay. They use different kernel calls, and such things as port management are handled internally by the kernel routines. The SCI program just sets up a menu structure using the kernel calls. Since they are irrelevant to the subject of this essay, I will not spend more time on them. The Rect structure is important (also to ports) since it is the basis for passing a screen position to the interpreter. It looks like this:

<syntax type="C"> typedef struct {

 short top, left, bottom, right;

} </syntax>

It will be seen from this that rectangle coordinates in SCI are not normally represented in the usual (x,y,width,height) fashion. So pay close attention to this structure! Also, it is not passed as a pointer, but rather as the four values in order. This is particularly true of SCI objects, where the property names nsTop etc. actually form a Rect structure which can be used directly by the interpreter.

Windows are created using the KNewWindow kernel function. Each window has six attributes which are passed from the script to the kernel function:

  • Bounding rectangle
  • Title
  • Type
  • Priority
  • Foreground color
  • Background color


Of these, the type and priority are the most interesting, because they decide the appearance of the window. The type is a bit field:

bit type
0 transparency
1 window does _not_ have a frame
2 the window has a title
3-6 unused
7 see below


Bit 0 specifies a transparent window. KNewWindow does not save the image behind the created window - it stays on the screen until the pic is redrawn, so windows with this style definitely can't be used as message boxes. It does have some special uses, though. If this bit is not set, KNewWindow draws a rectangle in the specified background color using the bounding rectangle coordinates (using the WM port). When this bit is set,

Bit 1 specifies a window without a frame. The frame is the black shading you can see in the corner of a message box.

Bit 2 tells KNewWindow to draw a grey title bar with a title printed in white. In the version I have used for this essay, it is not possible to change the title bar colors. Note that the bounding rectangle is always specified as if the window had no title bar. If this bit is set, ten pixels are reserved above the coordinates specified. Although this bit is set, the Title parameter may still be NULL. If this is the case, an empty title bar is drawn.

Bit 7 has a special meaning; it is used only in window type 0x81, and is not tested in any other way. When this style is chosen, KNewWindow does not draw anything at all. It is the caller's responsibility to draw a window frame on the WM port. CB1 uses this style for its ornate windows, and draws the frame manually.

The picture window which I mentioned in the last article is created using style 3 (that is, TRANS­PARENT | NOFRAME). The normal message box styles used in LSL3 are 0 and 4. I have not been able to investigate the priority property yet, so the following is based on supposi­tions. It is only used when drawing transparent windows. In this case, if priority is not -1 (which means not used), the window is drawn onto the priority map (with the specified priority value) as well as the screen.

There is a class called SysWindow which is just a simple wrapper around the following two kernel calls. Try breaking on SysWindow::open, then type c to inspect the current object. You can change all the parameters to KNewWindow (the Rect is split in its fields, to nsTop, nsLeft etc.)

To create a window structure, use KNewWindow (see Section 5.5.2.20); to remove it again, apply KDisposeWindow (see Section 5.5.2.23) on it. So how do we put stuff inside these windows? That question is a little complicated to answer, because it is really a shared effort between the interpreter and the object hierarchy, and this is one case where the interpreter actually interacts with the objects itself. I will start by explaining the classes involved.

All control types are descendants of a common class (I do not know its name, since it appears to have an invalid name property). Among other things, this common class contains a type number and a state. The type number is the only thing that distinguishes the control types from each other inside the interpreter - if a wrong type is set, the interpreter might try to change a non-existent property.

The type numbers are laid out as follows:

  1. Button control
  2. Text control
  3. Edit control
  4. Icon control
  5. not used
  6. Selector control (as in the Save and Restore boxes) The gauge ”controls” are not really controls. I don’t know how they work (yet). Each control also has a state value. These are laid out as follows:

bit 0 selectable. If this bit is set, the control can be selected using the Tab key. Except for the text and icon controls, all controls are selectable. bit 1 unknown. Always set, except for the text and icon controls bit 2 disabled. When this bit is set, a button is grayed out. No other control types are affected. bit 3 selected. When this bit is set, a frame is drawn around the control. Note that state 3 is by far the most common. With that explained, I’ll move on to the kernel functions. There are three functions associated with controls - KDrawControl (see Section 5.5.2.24), KHiliteControl (see Section 5.5.2.25) and KEditControl (see Section 5.5.2.26). Note that there is a KOnControl kernel call which is entirely unrelated to window management.

The dialogs are implemented using not one, but two classes - Dialog and Window. While the Win­dow class maintains the window (It is derived from SysWindow), the Dialog class is just a list of controls. It is derived from the List class, but has extended functionality to tell its members to redraw etc. There is a special function, located in script 255, which allows scripts to push information about the dialog on the stack instead of creating the Dialog object manually.

Note that the internal debugger uses the same window calls as the SCI script. That is why the screen messes up if you step through drawing code - the debugger has activated the Debug window port, and ”forgets” to switch back while stepping across instructions. Thus, all graphics commands are redirected to the debug window port. Not a pretty sight.