-
Notifications
You must be signed in to change notification settings - Fork 31
Nui tutorial
{{toc}}
The source code directory of nui contains a folder called tutorials. Have a look at the projects inside it, you will get a pretty good idea of the basic technics of nui.
Here we’re going to have a quick look to what you need to get started with nui, including a brief explanation of the tutorials.
Also, you can generate your own empty projects using the project generator of Yapuka. Yapuka is a nui application, located in nui source/tools/yapuka. It’s still work in progress but you can use it to generate an empty project already.
The main object of your program inherits from nuiApplication:
class Application : public nuiApplication
It implements the entry and exit callbacks of any nui app:
void OnInit();
void OnExit (int Code);
In the cpp, a macro creates the application instance for you:
NGL_APP_CREATE(Application);
In ::OnInit, choose the 3D renderer and create the window:
Most of the tutorials will show you how to handle the window parameters.
// never forget that :)
nuiInit(NULL);
nuiRenderer Renderer = eOpenGL;
nuiMainWindow::SetRenderer(Renderer);
[...]
mpMainWindow = new MainWindow(ContextInfo,Info, ShowFPS);
In ::OnExit, clean everything before the app exits:
// will properly delete the object tree attached to the window
delete mpMainWindow;
nuiUninit();
class MainWindow : public nuiMainWindow
The main action is to implement two callbacks:
// called when the window has been asked to close
void MainWindow::OnClose()
{
// don't close if a modal box is opened
if (GetNGLWindow()->IsInModalState())
return;
// properly quit the application. Will call the ::OnExit callback
App→Quit();
}
// called when the window is properly created and connected to the nui system.
// You can start to build your GUI here.
void MainWindow::OnCreation()
{
// create a vertical box for the GUI’s layout, or any other container
nuiVBox* pMainBox = new nuiVBox(0);
// attach your main container to the window
AddChild(pMainBox);
// and continue to create the widget tree
[…]
}
Here is a simple example:
void MainWindow::OnCreation()
{
// the main container: a vertical box with 2 cells
nuiVBox* pMainBox = new nuiVBox(2);
AddChild(pMainBox);
// a title in the first cell
nuiLabel* pTitle = new nuiLabel(_T("Hello World !"));
pMainBox->SetCell(0, pTitle);
// an horizontal box, with 2 cells, in the second cell
nuiHBox* pBox = new nuiHBox(2);
pMainBox->SetCell(1, pBox);
// a button in the first cell
nuiButton* pButton = new nuiButton(_T("go"));
// 'nuiCenter' position means the button will take the minimum size at the center of the cell,
// try 'nuiFill' and other values to see the difference
pButton->SetPosition(nuiCenter);
pBox->SetCell(0, pButton);
// an image in the second cell: 'rsrc://' means the resources path from the app binary
nuiImage* pImage = new nuiImage(_T("rsrc://decorations/image.png"));
// we choose to limit the size of the image. We could let the system handle that,
// the same way it does for the button.
pImage->SetUserSize(100,100);
pBox->SetCell(1, pImage);
}
First, in you class header .h, declare an event sink to process the events and an event receiver for the button’s action:
class MainWindow: public nuiMainWindow
{
[...]
private:
// event receiver
bool OnButtonClick(const nuiEvent& rEvent);
// event sink
nuiEventSink mEventSink;
};
Next, initialize the event receiver in the class constructor:
MainWindow:MainWindow([...])
: nuiMainWindow([...]), mEventSink(this)
Then, connect the button action to the given receiver:
void MainWindow::OnCreation()
{
[...]
nuiButton* pButton = new nuiButton(_T("go"));
[...]
mEventSink.Connect(pButton->Activated, &MainWindow::OnButtonClick, (void*)pLabel);
[...]
}
bool MainWindow::OnButtonClick(const nuiEvent& rEvent)
{
nuiLabel* pLabel = (nuiLabel*)rEvent.mpUser;
pLabel→SetText(_T(“Congrats for the best click ever…”));
return true;
}
When the button is clicked, the event Activated is sent by the nuiButton instance, and is immediately caught by the ::OnButtonClick receiver.
The nuiEvent communication system is synchronous.
The nuiLabel pointer is given as a “user parameter” to the event subscription. It’s completely optional and it has no type: it is cast in void*.
Instead of using this “user parameter”, you could use an instance attribute. It’s a developer choice, depending on what your receiver has to do.
Also, if you need more flexibility and security about an event parameters, you should take a look to the nuiSignal class below.
true means the event is caught by the receiver instance => other subscribers won’t receive this event
false means the event is released => next subscriber will receive this event
Sometimes, like in the previous example, you don’t care about returning true or false: your button is the only subscriber to this event. But when there are several subscribers to the same event, and when the action has to be exclusively processed by a specific instance, this is something you should pay attention to.
Most of the existing widgets have their own events. It’s quite impossible to program a nui application without using them.
Most of the existing events are related to the user inputs (a button click, a choice from a combo box, a selection from a list, …), but they don’t necessarly deals with a GUI issue.
In your own widgets (inheriting from a nui widget class), you can create any event you want for any purpose. It’s easy:
class MyWidget: public nuiButton
{
public:
[...]
nuiSimpleEventSource<0> MyEvent;
Let’s not worry about the templated shape of the event source right now, it’s not important.
Then, in the widget source code, you send the event when you want to warn the subscribers:
void MyWidget::MyMethod()
{
SomeStuff();
if (Condition())
MyEvent(); // send the event
}