Skip to content
jpastuszek edited this page Oct 21, 2010 · 4 revisions

Widget basics

nuiWidget

nuiWidget is the central class of the widget engine. It implements many services. The most important tasks of nuiWidgets are receiving user input , and drawing the widget to the screen. Most of the other services are there to support these two tasks as accurately and effortlessly as possible.

nuiContainer and nuiSimpleContainer

Containers can group widgets in order to create compositions. As they take care of a set of widgets they are also responsible for their layout. nuiContainer is mostly an abstract class for all containers. nuiSimpleContainer inherits from nuContainer and implements a simple layout manager that stack its children widget one above another. One very peculiar feature of NUI is that sibling widgets can overlap without creating any problem. When two widgets overlap, they are drawn in the order the were added to the parent container and they receive event in reverse order, i.e. the last widget drawn (the “top” widget) receives events first. This allows to create widget compositions very much like graphic software (i.e. Photoshop, PixelMator, etc.) handle their layer of compositing. All existing containers inherit from nuiSimpleContainer to take advantage of its widget collection mecanism.

nuiTopLevel

nuiTopLevel is a special kind of nuiContainer. It manages the whole widget tree. Its name comes from the fact that the root/top of the widget tree must be an nuiTopLevel. It handles many implementation details like user event propagation, life cycle of the nodes of the tree, caching and redrawing, etc.

nuiMainWindow

By itself nuiTopLevel is abstract in that it has no knowledge of the screen and actual user input. nuiMainWindow is an implementation of nuiTopLevel that also contains an nglWindow. It is the link between the user interactions and the widget tree: it knows where the window is, how big it is, it receives the mouse and keyboard events and passes them to the tree.

Events

Each widget can receive events from the user. The most common events are Mouse and Keyboard interaction.

Mouse

To receive mouse events on a widget you must create a subclass of this widget’s class and override the following methods:

// Bleh
bool MouseClicked(nuiSize X, nuiSize Y, nglMouseInfo::Flags Button);
bool MouseUnclicked(nuiSize X, nuiSize Y, nglMouseInfo::Flags Button);
bool MouseMoved(nuiSize X, nuiSize Y);

The X and Y parameters give you the mouse cursor coordinates in the local coordinate system of the widget when the event happened.
MouseClicked is called when the user presses a mouse button over a widget. The Button flag is a bit set giving information about the particular event: what button is pressed, if this was a double click, etc. Much similarly MouseUnclicked is called when the user depresses the mouse button.
MouseMoved is called when the mouse is moving over the widget.
Those three method have to return a boolean. Returning true means that you have handled the event and that you want its propagation along the widget tree to stop. Returning false tells the system that you were not interested in this particular event and that is can continue to look for a widget that can handle it in the widget tree.
Mouse event propagation works by walking up the widget tree in a reverse traversal operation which is the opposite of the drawing order: if you draw widget A, then B then C and a mouse event occurs, the event handling methods will be called in the verse order: widget C, then B (if C returned false), then A (if B returned false).

Keyboard

Keyboard events are only sent to a widget if it has the “Keyboard Focus”. Only one widget can hold the focus at any time in the tree.

bool TextInput(const nglString& rUnicodeText);

bool KeyDown(const nglKeyEvent& rEvent);
bool KeyUp(const nglKeyEvent& rEvent);

There are two kind of keyboard events, low level keyboard events are sent each time key is pressed or depressed, and high level text input are sent when the low level keyboard events eventually form a text entry. This is very important for internationalisation as many language need complex keyboard operations to compose text on screen. If you just want to be called when the user pressed the cursor keys or some other key, you can override the KeyDown and KeyUp methods. If you need to implement a text entry control then you will need to implement your own TextInput method.

Layout

Most GUI toolkit have only one view of widget layout: DIY (do it yourself!) :-). It means that you have to position widgets by hand and to update their positions whenever the global view is resized by the user. nuiWidget is different than those toolkit. While it provides a simple solution if you really prefer to do the layout in the form of nuiFixed. However there is a strong interface in between nuiWidget and nuContainer to permit automatic widget placement.

This piece of code in the constructor of an nuiMainWindow

MainWindow::MainWindow(
  const nglContextInfo& rContextInfo,
  const nglWindowInfo& rInfo,
  bool ShowFPS,
  const nglContext* pShared )
  : nuiMainWindow(rContextInfo, rInfo, pShared, nglPath(ePathCurrent))
{
#ifdef _DEBUG_
  SetDebugMode(true);
#endif
  
  nuiSplitter* pSplit = new nuiSplitter(nuiVertical);
  
  nuiTreeNode* pTree = new nuiTreeNode(_T("Root"));
  for (uint32 i = 0; i < 10; i++)
    pTree->AddChild(new nuiTreeNode(nglString().Add(_T("Item ")).Add(i)));

  pTree->Open(true);
  nuiTreeView* pTreeView = new nuiTreeView(pTree);
  pSplit->AddChild(pTreeView);
  
  nuiVBox* pBox = new nuiVBox();
  pBox->AddCell(new nuiImage(nglPath(_T("rsrc:/image.png"))));
  pBox->AddCell(new nuiLabel(_T("This is some text displayed as a label")));
  pSplit->AddChild(pBox);
  pBox->SetPosition(nuiCenter);
  pBox->SetCellPosition(0, nuiCenter);
  pBox->SetExpand(nuiExpandShrinkAndGrow);

  AddChild(pSplit);
}

Renders like this: