Annotated Ubit Examples

# The Ubit GUI Toolkit
Annotated Examples

###
Eric Lecolinet - INFRES / ENST

- Ubit Home Page
- Documentation Main Page

This directory contains simple examples of Ubit C++ source code. Fot the sake of clarity, header files are included in .cpp source files. It is important to read these examples in the order they are listed below:

## Example 1: Basic wigdets and callbacks

A basic example with a UFrame (= the main window of the application) that contains a ULabel and a UButton that activates member and non-member callback functions.

- Simple1.cpp : 1st version: verbose "Java-like" style

- Simple2.cpp : 2nd version: "Ubit style"
  changes:
  - in the header: the 'button' variable is a smart pointer
  - in the 'Demo' constructor: pseudo-declarative code and the + notation make source code more compact (and, we hope, easier to understand once accustomized to this notation)

- Simple3.cpp : 3rd version: plain object variables (a typical C++ feature)
  changes:
  - in the header: the 'button' variable is a plain object
  - in the 'main' function: we use plain objects instead of pointers
  - in the 'Demo' constructor: button' is a plain object

Important remarks

1. smart pointers should systematically be used instead of C++ pointers as in example 'Simple2' (except for temporary variables). Smart pointers manage memory in a safe way:

- objects pointed by smart pointers can't be deleted (and an error will be issued if you attempt do delete them)
- they are automatically deleted when no smart pointer points to them any longer (and if they are not part of the instance graph)

As a consequence, there is no need to take care of object deletion when smart pointers are used (as memory is freed automatically by the toolkit).

2. In contrast with Java, C++ variables can be 'plain objects' instead of pointers. Objects created in this way do not need to be freed explicitely by calling delete. However, you should be aware that:

- when an object is destroyed, its member variables are also destroyed
- when a function returns, its local variables are destroyed (hence, creating plain object local variables in other functions than main() may lead to unexpected results)

3. (advanced, can be read at a later time) you should be aware that:

- smart pointers don't work (= don't free memory) in the case of circular dependencies (which should not happen under normal circumstances)
- when an Ubit object is removed from the instance graph it is generally destroyed except if it is pointed by at least one smart pointer (see section "memory management" at the end of this page for details).

## Example 2 : Callbacks and graphical properties

In example BasicProps.cpp the first button exchanges the foreground and background colors of the label, the second button changes the sensitivity of the last button and the third button quits the application.

This example shows:

- how several buttons can share the same callback function (note: we could have used three different callback functions)
- how orientation, font and color properties can be specified statically by using predefined constants
- how foreground and background colors can be changed dynamically.

Remarks

1. As objects' child lists are evaluated in the left-to-right order, colors and other graphical properties must be inserted before strings and other visible objects.
Example:

      label.addlist( color + bgcolor + "Hello World!" )

2. We could of course have defined 'label', 'b1', 'b2', etc. as smart pointers rather than plain object variables. Besides, if separate callback functions were defined for each button, the 'b1', 'b2', 'b3' variables would be useless.

## Example 3 : Shared objects and composite text

In example BasicShared.cpp

the same foreground and background color objects are shared by three widgets. A Ubit string (UStr class) is also shared by these widgets. All widgets are automatically updated when the colors or the string is changed.

In addition, this example also shows:

- how composite text can be created (by combining strings, fonts, colors, images and pixmaps, etc.).
- how the number of instance variables can be minimized (there are only 2 member variables in the 'Demo' class and they could be suppressed by adding arguments to the 'xchangeColors()' function).

Remarks

1. `ustr("Hello")` is equivalent to: `*new UStr("Hello")`.

2. in expression: `UStr& str = ustr("Hello");` 'str' is a C++ reference.
One could also write:

      UStr* str = &ustr("Hello");        // C++ pointer
    or:
      uptr<UStr> str = &ustr("Hello");   // Ubit smart pointer
    or:
      uptr<UStr> str = ustr("Hello");    // Ubit smart pointer

3. All Ubit objects can be shared, not only strings or colors.

## Example 4: Layout and simple text

Example LayoutAndText.cpp shows how to create editable text and how to specify simple layout constraints.
The main frame contains 3 parts: a tool bar, a text area contained in a scroll pane and status bar. These 3 elements are flexible in the horizontal direction. The scroll pane is also flexible in the vertical dimension. These behaviors are specified by inserting uhflex(), utop(), uvflex(), ubottom() in the child list of the UFrame.

The tool bar contains a text field with an editable string. This text is appended to the central text area when pressing the Return key or clicking the "Ok" button.

See also the layout management section below.

## Example 5: List and exclusive items

- Lists.cpp

## Example 6: Drawing

- Drawing.cpp

## Example 7: Events, TimeOuts, Inputs

- Events.cpp

## Example 8: Tables

- Tables.cpp

## Example 9: More on Text : composition, callbacks, HTML-like layout

- Text1.cpp
- Text2.cpp

## More Examples: Dialogs, Menus, Shared Widgets, remote Displays

See the demos in directory: ubit/demos

## Complementary info

1. Recursive deletion

All destructors destroys the children and descendants of the object being deleted if:

- they do not have other (undeleted) parents, and
- they are not referenced by an uptr (Ubit smart pointer), and
- they have been created in the heap (ie. by using the 'new' primivite or a 'creator shortcut' such as ugroup())

Advices:

- avoid using the delete primivite and prefer the UGroup::remove() or UGroup::removeAll() functions:

- Note that 'delete' does not update graphics and may produce unexpected results or generate errors because objects:
  - can be shared (= they can have several parents)
  - can be created in the static memory or in the stack
  - can be referenced by uptr's (Ubit smart pointers) deleting objects is illegal in the two later cases and will generate an error or an exception.

2. Ubit smart pointers : the uptr<> template

Ubit smart pointers make it possible to handle objects created in the heap (= in dynamic memory) in a safe way. They should always be used for pointing objects created by 'new' or 'creator shortcuts' (such as ubutton(), ugroup(), ustr(), etc).

Ubit smart pointers are somewhat similar to Java references. Objects pointed by uptrs are automatically destroyed when:

- they are not pointed by any uptr any longer, AND
- they do not have parents in the GUI graph, AND
- they have been allocated in the heap.

'delete' can not be used to destroy object pointed by 'uptr's (this would produce an error and the object would not be destroyed). To destroy an object : set all uptrs that point to it to null (and remove this object from the GUI graph).

Examples:

      uptr p1 = new UStr("111");   // p1 points to a new string
      uptr p2 = ustr("222");       // ustr(..) == *new UStr(...)
      *p1 = *p2;        // the CONTENT of string "222" is copied into
                        // string "111" (but p1 and p2 still point to
                        // different objects)
      p1 = p2;          // p1 now points to the same string as p2
    																				// the string that was pointed by p1 is deleted
    																				// as there is no other refernce to it
      p1->append("xyz") // the content of the string pointed by p1 is changed

      cerr << *p1;      // writes the content of the pointed string: 222xyz
      cerr << &p1;      // writes the address of the pointed string

      p1 = null;        // p1 points to null. the string object is deleted

3. Application Context

- there should be only one application context per program.

- many methods are inherited from UDisp, the Display Context

- applications can be configured through command line options or by a UConf object (see constructor and class UConf)

- Ubit applications won't work properly on X servers that do not support True Colors. They are always launched in True color mode if this feature is supported by the X server (even if this is not the default). See also UDisp::setTrueColors()/setPseudoColors()

- Timers and Input callbacks can be associated to the UAppli (see methods openTimer() and openInput()).Timers with a 0 or 1 mS delay can be used as a way to postpone callback actions (which will be executed when the event loop becomes idle).

- an application can open windows on an arbitrary number of remote Displays (see method openDisp()). Note that applications should be configured in an appropriate way in this case (through UConf or comline options). The --group option should typically be used.

- several Event flows can be associated to an application or a display (see inherited method UDisp::openFlow(). This will be done automatically if an UMS (Ubit Mouse Server) is running on the same display

- applications can send/request info to/from an UMS by opening a (socket) connection to it (see method openUMS())

4. Layout management

The toolkit provides several view renderers that performs the lay out of the children of the widgets they are associated to. In the current version, these renderer make it possible to create:

- horizontal and vertical boxes
- tables (that are similar to HTML tables)
- flows (that are similar to HTML pages)
- viewports, scroll panes and scroll bars

The box layout model works as follows:

       uhbox( uleft() + a + b + uhflex() + x + y + uright() + c + d )

uleft(), uhflex(), uright() (and uhcenter()) specify how the following objects are displayed. Objects after uhflex() are "flexible" . This means that their size is automatically adjusted to the size of the parent object. These specifications can be combined with their vertical counterparts: utop(), uvflex(), uvcenter(), ubottom(). This mechanism makes possible to specify a large variety of layout behaviors in a rather simple way.

The size of objects can be specified in the following way:

      uhbox( scale + uwidth(200) + uheight(300) + etc... )

this size is relative and depends on the value of the scale object (sizes are nominal if the scale == 0, larger if the scale is > 0, smaller if it is < 0). The size of the "flexible" children is automatically adjusted.

Tables can be used to specify more complex layouts:

      utable( utrow( a + b + c )
              + utrow( x + y + z )
              + utrow( utcell(2,1, w) + t)
            )

    example:

         **a**  *b* ***c***
         **x**  *y* ***z***
         *****w**** ***t***

all objects are then adjusted inside a virtual grid. Widget "w" occupies 2 columns (and 1 row).

The flow layout provides a simple way to create pages that contain arbitrary objects (textual elements can be combined with widgets, images, graphical symbols, etc.)

      uflowbox( UColor::blue + "abcd efg" + uima("whatever.gif")
                UColor::red + UFont::large + "xyz tuv" + any_widget )

The flow layout (as other layout styles) can be applied to any widget:

      ubutton( UFlowView::style + etc... )

In constrast with other layout styles, the Flow layout will automatically split strings into several subparts according to the size of the corresponding widget. For instance, if the previous "button" has a very small width, the two strings it contains will be splitted into subparts shown on separate lines (while other children will be clipped)

All layout style support text editing, even in a flow:

       ubutton( uedit() + UFlowView::style + etc... )

Widgets can also be located at arbitrary locations:

       uhbox( uleft() + a + uhflex() + x + SSS + uright() + d + WWW)

SSS and WWW will not follow the previous layout specifications if they contain an upos() object:

       SSS = ubutton( upos(30, 50) + etc... )

This upos object specify the relative location of SSS by reference to its parents (as SSS can possibily have several parents). The scale of the parents is taken into account when calculating the actual locations of the children.