# 3 Steps to Make a WINGs User Interface
WINGs library tutorial
Keywords: WINGs, tutorial, GUI, Window Maker, widget, Linux

LAST: Step 2 Processing Events | Contents | NEXT: Programming Details

#### Step 3: Adding widgets to a window

##### Adding buttons to a widget

As a window is a widget, we can create a button for it with the function
`WMButton * WMCreateButton (WMWidget *parent, WMButtonType type)`
To make the button visible, the function `WMMapSubwidgets` may be called on the window. A call to WMMapWidget on the button will do the same of course. Before mapping the button, its properties can be set with functions whose names speak for themselves

- `void WMSetButtonText (WMButton *bPtr, char *text)`
- `void WMSetButtonTextAlignment (WMButton *bPtr,WMAlignment alignment)`
- `void WMSetButtonAction (WMButton *bPtr, WMAction *action, void *clientData)`
- `void WMMoveWidget (WMWidget *wdiget, int pixelstoright, int pixelsdown)`
- `WMScreen * WMWidgetScreen (WMWidget *widget)`
- `WMSize WMGetViewSize (WMView *view)`
- `void WMMapSubwidgets (WMWidget *widget)`

As windows and buttons are both widgets, `WMResizeWidget()` is the same as for the window above, just as the function to set the background colour. Sizing and moving is in pixels, movements are in pixels from the parent widget's upper left corner. The default action on a button, which is a mouse click, will call the WMAction function, just as happens with the CloseAction on the window. WMAlignment can be WALeft, WACenter, WARight or WAJustified.

The different WMButtonTypes are

Touch buttons:
  `WBTMomentaryPush, WBTMomentaryChange,WBTMomentaryLight`
Alternate on/off buttons:
  `WBTPushOnPushOff, WBTOnOff, WBToggle`
Checkbox with label next to it:
  `WBTSwitch, WBTRadio`

Particular behaviour of a button can be obtained by using the function
`WMButton * WMCreateCustomButton (WMWidget *parent, int behaviourMask)`
By default a new widget will be placed in the window's top left corner. `WMMoveWidget` is used to place it correctly.

Buttons can be grouped together by using a WMBox widget. You would do this when you want to do something fancy with the buttons, eg. filling the width of a part of the window, and resizing with that part. Create the box before creating the button, WMMap the button, and next add the latter's view, extracted with WMWidgetView() to the box:

- `WMBox * WMCreateBox (WMWidget *parent)`
- `void WMSetBoxHorizontal (WMBox *box, Bool TrueorFalse)`
- `void WMAddBoxSubview (WMBox *box, WMView *buttonsview, Bool expand, Bool fill, int minSize, int maxSizeor0, int space)`
- `void WMSetBoxBorderWidth (WMBox *box, unsigned width)`
- `void WMSetViewExpandsToParent (WMView *boxview, int leftOffset, int topOffset, int rightOffset, int bottomOffset)`

In the `WMAddBoxSubview` function, setting expand to True will stretch the button to the height of the box. `space` sets the space after the button. To resize the widgetbox with the window, you can use the `WMSetViewExpandsToParent` function. Otherwise, you can calculate your own positions, and move the box to where it is supposed to be in a resized window. There are other ways to group buttons, or other widgets, eg. by a frame.

###### Resize events

To know the current window's size, and the size of any other widget, there is the function `WMSize WMGetViewSize (WMView *view)`. As before, the function `WMWidgetView` casts the widget into a view. `WMWidgetScreen` returns a pointer to the screen in which the widget was created.

We thus can get the window's size, and place widgets in their correct positions. What is left to do is a function which handles the event that the user resizes the window. The buttons, or the box containing them, should move to their correct positions in the window again in such an event, or resize with the window itself. There is an event `WMViewSizeDidChangeNotification` when the window is resized. For a WMWindow win, passing WMWidgetView(win) as the last argument to the function below will define what to do when this event occurs.
`void WMAddNotificationObserver (WMNotificationObserverAction *observerAction, void *observer,const char *name, void *object)`
The third argument should be the event's name (WMViewSizeDidChangeNotification), and the first argument is the name of the function which will be called. This function should look like `void observerAct(void *self, WMNotification *notification)`. It is all done in the sample code here.

###### Adding the event handlers and widgets to the application

In the FirstWindow code, we insert the following lines to handle the resize notification:

       WMSetViewNotifySizeChanges(WMWidgetView(win), True);
      WMAddNotificationObserver(resizeHandler, NULL, WMViewSizeDidChangeNotification, WMWidgetView(win));

Before the `main` function we define the function (`resizeHandler`) which will handle the resize event for the two widgets, the text area and the box with buttons. There is a global variable ButtonsetSize, which contains the size of the box with buttons:

     WMSize ButtonsetSize;
     static void resizeHandler(void *self, WMNotification *notif){
     WMSize size = WMGetViewSize(WMWidgetView(win));
     WMMoveWidget(box, size.width-ButtonsetSize.width, size.height-ButtonsetSize.height);
     WMResizeWidget(text, size.width-MARGIN -10, size.height-80);
    }
    static void handleEvents(XEvent *event, void *data)
    {int i=0;
        WMWidget *widget = (WMWidget*)data;
        switch (event->type) {
        case ButtonPress:
          while (i<40)textbuf[i++]=' ';
          snprintf(textbuf,39,"Button down at (%i,%i) \n-",event->xbutton.x,event->xbutton.y);
          WMFreezeText(text);
          WMAppendTextStream(text,textbuf);
          WMThawText(text);
          break;
        }
    }

The buttons keep their size, but are moved to stay in the bottom right corner, the text area is resized along with the window, but stays 80 pixels away from the window bottom border. We also do something useful with the mouseclicks on the window itself. This is done in the function handleEvents. The event ButtonPress has the click time and position in a member `.xbutton`. When the button is pressed, we get that information, and print it to the text area.

The text is written to a text area. Function names for it are self-explanatory. We create this widget after creating the window. It will be WMmapped by the final `WMMapSubwidgets`. The code for setting it up is just:

     text = WMCreateText(win);
      WMResizeWidget(text, WINWIDTH-MARGIN, WINHEIGHT -80);
      WMMoveWidget(text, 10, 10)

The text is written to the area by passing character strings to the `WMAppendTextStream` function.

The important functions in creating the box and its widgets are:

     box=WMCreateBox(win);
     WMSetBoxBorderWidth(box, MARGIN);
     WMSetBoxHorizontal(box, True);
     Button =WMCreateButton(box,WBTMomentaryPush);
     WMSetButtonAction (Button, selectFiles, NULL);
     WMMapWidget(Button);
     ButtonsetSize = WMGetViewSize(WMWidgetView(QuitButton));
     WMAddBoxSubview(box, WMWidgetView(QuitButton), True,False, 60, 1000, MARGIN);
      /*-- make and add another button --*/
     WMResizeWidget(box, 4*MARGIN+2*ButtonsetSize.width,2*MARGIN+ButtonsetSize.height);
     ButtonsetSize =WMGetViewSize(WMWidgetView(box));
     resizeHandler(NULL,NULL)

The box is created before the button is, because the button will have to be created with the box (its parent widget) as its first argument. The box is the "outer" widget and will be mapped with `WMMapSubwidgets`, but we map the buttons separately. The function which will be called on the default button event is closeAll() for the quit button. The file button will call the `selectFiles()` function, in which we open a file selector widget. We have left it to WINGs to decide what size the button will be. We temporarily use ButtonsetSize to store this size. We add the button's view to the box. After adding all the buttons, We store the size of the resulting box, and use it in our `resizeHandler` function to keep the box in the corner. .Following the source code, pressing the file button will pop up the file selector dialog, and the name of the selected file is printed in the text area. Notice that, without the box widget, we would need two global Button pointers to change the position of both buttons in the `resizeHandler` function. We now need one Button pointer, which can remain local to `main`.

Here is the full code which we have now. As we have a text area, we can print text to it, as long as we are sure that the widget has not been destroyed. The scroll bar next to the area is obtained with one single function call.

###### Frames

The WMBox needs quite some configuration. It is not really intended for putting two simple buttons in a corner. We can use a frame to keep widgets together and move them with one single pointer. For buttons, we could also use the `WMGroupButtons` function to handle a group of buttons with just one pointer. To use a frame, use this code, instead of the WMBox functions: At global scope:

    WMFrame *controlframe;
    static void resizeHandler(void *self, WMNotification *notif){
         WMSize size = WMGetViewSize(WMWidgetView(win));
         WMMoveWidget(controlframe, size.width-ButtonsetSize.width, size.height-ButtonsetSize.height);
         WMResizeWidget(text, size.width-MARGIN -10, size.height-80);
    }

In `main`:

      controlframe=WMCreateFrame(win);
      Button =WMCreateButton(controlframe,WBTMomentaryPush);
      ButtonsetSize = WMGetViewSize(WMWidgetView(Button));
      WMMoveWidget(Button,MARGIN, MARGIN);
      /* (code to create a second button of the same size, with the same pointer)  */
      WMMoveWidget(Button,2*MARGIN+ButtonsetSize.width, MARGIN);
      ButtonsetSize.width = 3*MARGIN+2*ButtonsetSize.width;
      ButtonsetSize.height=2*MARGIN+ButtonsetSize.height;
      WMResizeWidget(controlframe,ButtonsetSize.width,ButtonsetSize.height);
      WMMapSubwidgets(controlframe);

As we created the buttons inside the frame, we WMMoveWidget them along coordinates with respect to the frame's upper left corner. We have left it to WINGs to set the buttons' size, so we get their size with `WMGetViewSize`. With the button sizes, we calculate what size the frame is to have, and how far we need to move the buttons from the upper left corner to get them to the right place. We WMMap both buttons inside the frame with `WMMapSubwidgets(controlframe);`. The frame is the outer widget, and will be WMMapped just before the window. Replacing the box with the frame, we get this source code. After compiling, we get the same application. The obvious frame properties are set with the frame functions

##### Next sections

The three steps up till now have shown the important points in programming with the WINGs library. The basics are all the same all the time. Create, specify WMAction, define the WMAction function somewhere, WMMap the widget. If this won't do, use PostNotification, and `AddNotificationObserver` to the widget which should react to it.

From the next sections, the library description gives a whole range of widgets which you can fit into the window this way, and related functions and data structures. There is sample code for those widgets whose code seems a bit more involved. The only thing which cannot be obtained by a simple function call is a menu with submenus, which is to work under any window manager, not just Window Maker. The third programming detail section explains, how to programme this kind of menu. The other two programming detail sections are not about the WINGs/wraster/windowmaker libraries themselves, except for a short section on the use of the WINGs functions to insert icons into widgets. Detail section 1 contains an example of how to get rid of the disappointing xterm window which you need to start your windowed application from, to read its messages on stderr or stdout. It shows the standard code to open a logging window on the screen when needed. For convenience, it uses xconsole for this, a standard application on unix systems with X11. The same section 1 shows how to get the correct information about monitor resolution when a virtual screen is used. Detail section 2 gives code samples which demonstrate how to mix Xlib code with the WINGs library. Xlib is the library which gives direct access to the X-server, and which is the underlying code to the WINGs library itself. It also contains a section which shows how to use OpenGL 3D graphics code in a WINGs frame. These extra sections do not explain all the details on the used libraries. However, by just following the code examples, you can reproduce their results, and change them to use them in your own programmes.

LAST: Step 2 Processing Events | Contents | NEXT: Programming Details