(MOS) Managed Windows API

2017 October 11

This document specifies the current API for managed windows in My own OS. The root document is My Own System Application Program Interface.


This package implements a window defined by a text specification. I use it for dialogs and preferences. The details of drawing these are encapsulated in a set of image item specifiers. You can also extend icons and fonts programmatically beyond the built-ins.

I originally defined the FormDoc widgit for displaying structured text in BibleTrans, then expanded the definition to do graphical data presentation in ShoImage, then moved the structured text part out of the kernel API, leaving only the graphical part. Hence these two widgits:

class FormDoc extends Widgit { // resource-based formatted document
  File ResFi, ActFi; // an open resource file, plus an alternate
  Char4 AcImPro; // an alternate process ID that owns this window
  int LineHi, HotHit; // scroll increment (line height); hilite element in HotSpots
  int imTall, imWide; // image height and width
  IntArray HotSpots;}~FormDoc

class ShoImage extends FormDoc { // user formatted image doc
  IntArray imImage, imIcons;
  int LineWi, ImSelec;}~ShoImage

The imImage array is a sequence of drawing instructions, typically one integer each. The high four bits specifies the operation, and the remaining bits are parameters. Vertical and horizontal coordinates are specified as 16 bits vertical, 12 bits (max 4095) horizontal. The image has some state associated with it, so you give it a current location, then do something there, possibly moving to a new current location. These operations are specified (others may be added later):

0,0,0 -- Done
2,v,h -- New location
0,v,h -- Fill rectangle (height>0,width>0)
3,v,h -- Line to, new location at end
1,n,f,c; text -- Set color + font, show text
4,name -- Named icon name
6,n,c; pixels -- Set color, show pixels
7,v,h; pixels -- Copy pixels
Following a "New location" operation, a sequence of "Line to" operations can be used to chain lines together into arbitrary polygons. The "Fill rectangle" operation keeps the current location as its top-left. Pixels (rectangles and icons) are drawn down and to the right of the current location, but text treats the current position as a baseline. Text (and lines) advance the location to the end (see also font data format below).

The two "Set color" operations have the specified color in the low byte using base-6 color specification (R+G*6+B*36) for 216 possible colors; each of the pixel bytes following the "Copy pixels" operation are in the same format. The text and "show pixels" operations use the specified color only for the specified pixels, leaving whatever color was previously there where the designated pixels are 0-bits. Both operations assume each integer represents one column of pixels up to 32 bits tall (least significant bit at the top), as described in more detail here and also below. Text is extracted, character by character, from the defined fonts, while the "show pixels" operation uses words following the defining code. You are limited to a maximum of 63 characters of text or 63 pixels as copied. The text must be followed by a null byte -- in an additional word, if necessary, but I always extend the text length +1 with a final space to prevent the whole word being 0. If the text length is 0, then there are no bytes of text after the control word (and no 0 terminator), but it still specifies the color for lines and rectangles to follow.

The "Named icon" uses a Char4 name filling the whole word, so only icons whose names begin with a capital letter (0x40-0x5F) can be used. The icons will be those defined in the system as extended by subclassing IconFontFetch, or else resources in the ResFi file and listed in the (now deprecated) imIcons array. The icons themselves are drawing operations, usually just "show pixels" operations for each layer of color, preceded by a height+width word.

There are a number of defined functions for building visual elements in this model. Each of these takes the array theImg being built (which must already be large enough), a current offset offs in that array, the parameters of the visual element being added, and returns the new offset. The here parameter is expected to be in the defined V*4096+H format:

int ImagText(String theTx, int nc, int offs, int here, IntArray theImg,
      int FontNo, int colo);
int ImagIcon(Char4 theIc, int here, int offs, IntArray theImg);
int ImagLineTo(int here, int thar, int colo, int offs, IntArray theImg);
int ImagFillRec(int here, int dims, int colo, int offs, IntArray theImg);
int ImgFrameRec(int here, int dims, int colo, int offs, IntArray theImg);
int ImgBevelFrame(int here, int dims, int offs, IntArray theImg);
int ImagPushBtn(String theTx, int wide, boolean hili, int offs, int here,
      IntArray theImg);
int AddHotRec(IntArray theHots, int hotx, int msg, int info, int tpl, int szr);

ImgBevelFrame builds a light/dark gray outline suitable for "3D-look" buttons as drawn by ImagPushBtn. Pushbuttons in particular need to be keyed to hotspots in the HotSpots (theHots) array; these can be added easily using the AddHotRec call. Each hotspot is defined by four numbers in the array, a msg (which should be a valid Char4 value, as it will be the event message sent when the hotspot is clicked), an additional info value (sent as EvInfo in the same message), a top/left tpl and size szr (two 16-bit numbers each). This function stores the four numbers in the array beginning at offset hotx, and returns the offset (hotx+4) to be used in the next call. You can also add your own drawing commands with these very-low-level commands to insert one or two arbitrary numbers into the array and return the new offset:

int ImagInt(int info, int offs, IntArray theImg);
int Imag2ints(int whom, int info, int offs, IntArray theImg);

These are low-level support operations. At a higher level, class ImDocWinBase provides a number of window management services, including drawing the items and processing events.

class ImDocWinBase { // base class for MoDialog & PrefsWin
  Window theWin; // FormWinStuff wants this first for PoorManConst'r
  int theDocID; // EzTxWindo wants these 3 early for ditto..
  String myWnTitle;
  int FiSeqNo; // = "DocX"/"Imag" if not BigTextEd
  FormDoc theWgt;
  boolean Logging;
  int theTint; // 1: red, 2: green, 3: yellow, 4: blue, 5: purple, 6: teal
  Process theOwnerProc;
  ImDocWinBase theLink; // in ImDocWinList

  void CheapConstor(int docno, String theTxt, int fino);
  void SetNewLnN(int here, int datum);
  boolean KeyPress(int theKey);
  void Mousing(int whom, int now, Point WnPt);
  void ShowMyWindow();
  void ActivateWin(int info);
  void MoveSize(int here, int size, boolean sho);
  void ForceRe4m();
  void SetWinKin(boolean sho, Char4 whom, int DocID, int noscro);
  void CloSaveWin();
  boolean DoDocEvent(Event theEv);}~ImDocWinBase

It is expected you will override most of these methods to provide desired functionality; the base class constructor will produce an error if you attempt to instantiate it other than as a subclass. This is already done in class ImageWinMgr, which can be used for modal dialogs and/or a single (non-modal) preference or dashboard window.

boolean MoDialog(String myItems, String Params, char delim, int Tall,
      int Wide, ImageItems CallBk);
boolean NewPrefsWin(String myItems, String Params, char delim, int Tall,
      int Wide, ImageItems CallBk, boolean noTi);
boolean IsPrefsWinEvent(Event theEv);

MoDialog contains its own event loop, and returns only when the dialog is dismissed by the arrival of one of the events "Done" or "Quit" or "CloW" or "OK  " or "Canc" (all of which return true, except "Canc"). NewPrefsWin takes the same parameters, but its window is non-modal (noTi is true if you want no titlebar on your window); it returns true if the window failed to open for some reason. From your own event loop you must offer all events to IsPrefsWinEvent, which will return true if the preferences window has handled it. You are expected to override the default operation of some events -- and in the case of a modal dialog, you must do so to recover any user interaction other than "OK" or "Cancel" -- using the ImageItems callback class:

class ImageItems {
  IntArray KeyCodeEvts; // holds KeyC items for MoDialog
  String theParams;
  boolean NeedRedraw; // set true, then call PrepEvent() to redraw
  ImDocWinBase myOwner; // set by ImageWinMgr.Initialize

  int EstSize(int topLft, int HiWi, int info) { // callback for User items
    return 0;}~EstSize //  size estimate >= actual image elts needed;
      // HiWord >= # hotspots needed, =0 if none
  int DrawMe(IntArray theImg, int offs, int topLft, int HiWi, int info,
      IntArray theHots, int hotx, boolean hili) { // callback for User items
    return offs;}~DrawMe // offs+size used (as from ImagFillRec etc)
      // if added to theHots, return Pack2ints(hotx,offs)
  boolean DoItmEvent(Event theEv) {return false;}~DoItmEvent // true if took
    // call FreshDraw from within DoItmEvent if redraw needed..
  void FreshDraw(String newPrams);}~ImageItems

At the very least you need to override DoItmEvent, which gives you first crack at the events processed within this window. You would use this to process button presses and also any data in data entry fields. You can also use it to completely reformat the window, if desired. In that case, you should call FreshDraw with a new parameter string from within your DoItmEvent handler. If that is not convenient, you can set theParams directly, then send a "FrDr" event to the containing window.

The image drawn in this window is defined by a permanent myItems string, and modified dynamically by Params, which can be adjusted from time to time to represent the current data or presentation. Both strings are presented at the time the window is opened, but Params is updated on each call to FreshDraw. The character delim presented when the window is opened is a separator within the body of Params, separating out up to 35 units of information. Tab characters in myItems followed by a non-zero digit or letter designate one of those 35 items of text which replace the tab-letter pair in myItems for purposes of display. Note that this might bring in multiple lines, possibly containing additional tab-letter codes for replacement, but that easily becomes too complicated to manage. The resulting parameterized text string as decoded consists of a sequence of lines, each line representing one display item. The general form of the lines is this:
where the items in brackets are part of only some of the specifications. The kind is nominally a Char4 label, but only the first letter is significant. In some cases, the remainder of the kind word is used to distinguish between separate buttons or controls during event processing. The color, if specified, is a number representing the one-byte color code as above, or else a triple "RnGnBn" where "n" after its respective letter is a digit representing that color component's value from 0-5. The font, if specified, is a number from 0-7, one of the eight fonts listed in Graphics Operations. Any specified text comprises the remainder of its line, possibly including commas (but not tabs, which cannot be displayed anyway). The "just" specification of text may be centered (V and/or H) or (R) right-justified; the default is left. These 17 display elements are defined:
Line,V,H,V,H,color -- start,end 1-pixel line
OutL,V,H,tall,wide,color -- 1-pixel rectangle frame
Text,V,H,tall,wide,color,font,just,text -- text display
Ntry,V,H,tall,wide,color,font,just,text -- text entry
Butn,V,H,tall,wide,font,msg4,num,hili,text -- hili: =1 if pressed
Grup,V,H,tall,wide -- depressed bevel frame (max nest: 8)
EndG -- items after Grup, before EndG: V,H relative
PopM,V,H,tall,wide,color,font,msg4,sele,text -- sele: sel'd item from text, 0=1st
Icon,V,H,name -- only predef'd for now
Xime,V,H,color -- 32x32 clock icon, real time in light grey
Move,V,H -- drag icon for dragging the win
User,V,H,tall,wide,info -- subclass ImageItems for access
Hots,V,H,tall,wide,msg4,num -- transparent button
KeyC,key,msg4,num -- KeyC=key input posts msg4+msUp
The text component of a popup menu is a comma-delimited list of items, and sele is a 0-base number designating which one is displayed in the unpopped state. You would normally use a parameter item to supply this number. The hili item of a button should be 0; MOS internally replaces it with a 1 while the button is being clicked, but that only works correctly if the kind word on that line is unique. The msg4 item should be a 4-letter event name that your application catches in your DoDocEvent handler. Checkboxes and radio buttons are be managed using a private array of 256 numbered bits, each selected by num. Your program can turn the bits on or off, or query their state using these functions:
CheckRadioBit(int whom, boolean set); // set bit whom to true or false
boolean RadioCheckBit(int whom); // return current setting of bit whom
The following built-in icons are available at this time, but you can extend this list by subclassing IconFontFetch:
"Smi!" -- a small "smiley" face 
"Bult" -- a small black dot useful as a bullet
"Maro" -- the small arrow in a popup menu
"ChkF" -- a checkbox in the off state
"ChkN" -- a checkbox in the on state
"RadF" -- a radio button in the off state
"RadN" -- a radio button in the on state
"MovI" -- the Move icon
"Clok" -- a clock icon with the current time when drawn
The Xime line makes sure the time is kept current by sending (and processing) periodic "Clok" events. The Move line displays the "MovI" icon, then if it is clicked, drags the whole window around to follow the mouse. This is useful for borderless (no title) windows that you want the user to be able to reposition.

For visual elements with no specified event message, you can overlay a "Hots" line. To add visual elements other than these, you can override the DrawMe and EstSize methods, which are given the relevant information for each "User" line. EstSize is called before drawing begins, to allocate the array size needed, then DrawMe is required to use up not more than the estimated size(s).

You can supply keyboard shortcuts using "KeyC" lines.

If you are filtering events, you need to make sure the following events make it to the event handler for whatever window is using this imaging model:

"Butn"/"ChkT"/"RadS"/"Mpop"/"Clok"/"MovI" -- Track clicks in their respective display elements
"mDrg"/"msUp" -- Needed to track mouse release
"KeyC" -- Only if you are using keystroke equivalents
"Imag" -- If a redraw is needed
"CloW" -- If you want the window to close itself
The AutoEvent function will make sure these windows get their events. The following public objects are defined in package ImDlogPkg:
final ImageWinMgr thePrefsWindow; // a Preferences window
final ImageWinMgr theDashWindow; // a separate dashboard
final ImageWinMgr theProgresBar; // a separate progress bar
You would open the Preferences window thus:
if (NewPrefsWin(ItemsTxt,PramTxt,'`',tall,wide,ItemsObj,false)) {/* failed */}
where ItemsObj is an instance of your subclass of ImageItems (or null if you don't need to react to any events). If you give it a negative height in tall, it will open the dashboard object window instead. Use the ProgressBar functions to open and manage the progress bar. If you are diverting "CloW" events, and you want any of these windows to self-close, you might check whether they are destined for such a window thus:
Window aWin = EventWindow(theEv);
if (aWin != null) if (aWin == thePrefsWindow.theWin) thePrefsWindow.CloseItDown();
and similarly for the others. See package VuResFiWn for another implementation of this model.

Adding Icons & Fonts

You can extend the range of named icons and fonts by extending this class:
class IconFontFetch { // to extend icons & fonts
  IconFontFetch NextUp;
  IntArray GetIconAddr(Char4 whom) {return null;} // o'ride to return tbl..
  IntArray GetFontAddr(Char4 whom) {return null;}
  void UnInstall() {/*uninstall this object*/}~UnInstall
  IconFontFetch() {/*install this object*/}~IconFontFetch}~IconFontFetch;
When you create an object of your extended class, the base class constructor installs it in a list of such objects that gets searched whenever your program asks for a named icon or font. Your access function gets first shot at all requests, so you can override the defaults if you wish; otherwise return null for any fonts or icons your code does not recognize. Java (and therefore also Turk/2) has no explicit destructors, so you must call UnInstall() before this object goes out of scope if allocated final within a function or global in a sublaunched process that quits.

Override GetIconAddr to return an integer array reference of the named icon, and GetFontAddr to return an integer array reference of the named font, or null in either case if your object does not recognize that name. An icon array begins with the icon height and width packed into a single integer (16 bits each, as V*0x10000+H), then a sequence of drawing commands (mostly just "show pixels" operations; embedded icons are not allowed), followed by a zero terminator.

A font specifier array is somewhat more complicated. It has a prolog, the index, then the pixels. The first number is by convention the font name. The second number is a composite of the line height, descent and ascent, packed one byte each as H*0x10000+D*0x100+A. The third and fourth numbers are the number of pixels separating each character, and the width of a space. A monospace font prolog has two more numbers, a zero followed by the character width (which should be the same as the space width), and the index is omitted; the pixels for "!" start in table position [6]. A proportional font index starts in position [4] for the space, which should be the same as for "!" because there are no non-white pixels in a space. The pixel information must be in ASCII order, and the final index position CHR(0x7F) pointing just past the last displayable character. Each character is encoded as a sequence of 1-word pixel columns, up to 32 pixels tall (the 32 bits in one integer), as many pixels wide as needed (but currently limited to a maximum of 63). You can think of each character as if rotated 90 degrees clockwise, thus:

with the pixels as bits in the table, as shown.


This package implements a window able to show and edit text files larger than available memory. It's not yet working properly.