Nui tutorial 1 basics
NUI Tutorial
NUI is a new Gui framework for NWN:EE. This is going to be a quick guide on how to get started.
Generally, one way of thinking about building a NUI is in 3 parts. #Construction, #Events and #Updates. Updates and Events can and do overlap in most applications.
Construction
This is creating of the window and components. Let's start with a simple 'hello world'
#include "nw_inc_nui"
// This is an inspector debug widget also included with NWN:EE, but not used in this example.
#include "nw_inc_nui_insp"
// Window IDs are text strings. They can be anything, but should be kept unique for each window "type"
const string NUI_TUTORIAL_HELLO_WORLD_WINDOW = "nui_tutorial_hello_world_window";
// This starts new window.
void Nui_Tutorial_Hello_World(object oPlayer)
{
// First we look for any previous windows, if found (ie, non-zero) we destory them so we can start fresh.
int nPreviousToken = NuiFindWindow(oPlayer, NUI_TUTORIAL_HELLO_WORLD_WINDOW);
if (nPreviousToken != 0)
{
NuiDestroy(oPlayer, nPreviousToken);
}
// More on layouts and elements and grouping latter, but this is "root" pane.
json jRoot = JsonArray();
// Make a new row, which is an array of elements.
json jRow = JsonArray();
// Our first element (also called Widgets). This is a basic text label.
json jLabel = NuiLabel(JsonString("Hello World (label)"), JsonInt(NUI_HALIGN_CENTER), JsonInt(NUI_VALIGN_MIDDLE));
// Add it to row.
jRow = JsonArrayInsert(jRow, jLabel);
// Transform row into a Nui Layout.
jRow = NuiRow(jRow);
// Add row to our root pane.
jRoot = JsonArrayInsert(jRoot, jRow);
// Transform entire root pane to a Nui Layout.
jRoot = NuiCol(jRoot);
// This is the main window with jRoot as the main pane. It includes titles and parameters (more on those later)
json nui = NuiWindow(jRoot, JsonString("Hello World (title)"), NuiBind("geometry"), NuiBind("resizable"), NuiBind("collapsed"), NuiBind("closable"), NuiBind("transparent"), NuiBind("border"));
// finally create it and it'll return us a non-zero token.
int nToken = NuiCreate(oPlayer, nui, NUI_TUTORIAL_HELLO_WORLD_WINDOW);
// This are binds, which are like varaiables to NUI elements that can be changed latter. Or in this case, changed now.
NuiSetBind(oPlayer, nToken, "geometry", NuiRect(-1.0f, -1.0f, 200.0f, 200.0f));
NuiSetBind(oPlayer, nToken, "collapsed", JsonBool(FALSE));
NuiSetBind(oPlayer, nToken, "resizable", JsonBool(TRUE));
NuiSetBind(oPlayer, nToken, "closable", JsonBool(TRUE));
NuiSetBind(oPlayer, nToken, "transparent", JsonBool(FALSE));
NuiSetBind(oPlayer, nToken, "border", JsonBool(TRUE));
}
Add the above code to a file called `nui_tutorial.nss`. Then you need to include it and call the `Nui_Tutorial_Hello_World(oPlayer)` to get the window appear. This could be called from a OnPlayerChat event, an item OnActivateItem, placeable switch OnUsed or other. It's left up to reader on how they wish to invoke their GUIs, but here is a quick player chat example for testing (You also need to configure the OnPlayerChat event)
void main()
{
object oPlayer = GetPCChatSpeaker();
string sMessage = GetPCChatMessage();
if (!GetIsPC(oPlayer))
{
return;
}
if (sMessage == "helloworld")
{
// They are a Player Character and typed "helloworld", give them the GUI.
Nui_Tutorial_Hello_World(oPlayer);
return;
}
}
Regardless of how it's invoked you will end up with :
Elements (Widgets)
Widgets are sometimes called Elements. They are visual and interactive elements, like Buttons, Text boxes, Check Boxes, etc. The building blocks of a GUI. See here for a full list: Widgets
Widgets are all of date type Json (with some special properties).
An example of a NuiButton would look like this
Besides the NuiButton being of type Json, notice that the parameter is also a nwn string wrapped in a JsonString call. That is fairly common for Nui* elements, but there are cases were it's mixed, so check the documentation.
If you replace the NuiLabel with NuiButton in first example, you should get this:
Values (Modifiers/Attributes)
Values (Modifiers/Attributes) change the attributes of a Widgets. It depends on both the Modifier and Widget on what expected results will be, but most are intuitive.
If you notice in above image, the text is a bit larger than the button so the end of the label isn't displayed. One way we could fix that is by using a NuiWidth modifier.
Let's make it longer (width wise) and a bit shorter (height wise).
jButton = NuiWidth(jButton, 175.0f);
jButton = NuiHeight(jButton, 32.0f);
That gives us a better looking button:
Layouts
Layouts control the positioning of Widget.
Typically Widgets are added to JsonArrays which then can be transformed to NuiRow or NuiCol or NuiGroup. All 3 layouts can be nested inside each other (for example, you can have Rows of Rows or Columns or Rows, etc)
Let's use our current example and add 7 buttons:
json jRow = JsonArray();
// Make 7 button widvets
json jButtonHello = NuiButton(JsonString("Hello"));
json jButtonYes = NuiButton(JsonString("Yes"));
json jButtonNo = NuiButton(JsonString("No"));
json jButtonStop = NuiButton(JsonString("Stop"));
json jButtonRest = NuiButton(JsonString("Rest"));
json jButtonBored = NuiButton(JsonString("Bored"));
json jButtonGoodBye = NuiButton(JsonString("GoodBye"));
// Add buttons to Row Array
jRow = JsonArrayInsert(jRow, jButtonHello);
jRow = JsonArrayInsert(jRow, jButtonYes);
jRow = JsonArrayInsert(jRow, jButtonNo);
jRow = JsonArrayInsert(jRow, jButtonStop);
jRow = JsonArrayInsert(jRow, jButtonRest);
jRow = JsonArrayInsert(jRow, jButtonBored);
jRow = JsonArrayInsert(jRow, jButtonGoodBye);
// Transform jRow into a Nui Layout.
jRow = NuiRow(jRow);
// Add row to our root pane.
jRoot = JsonArrayInsert(jRoot, jRow);
// Transform entire root pane to an element.
jRoot = NuiCol(jRoot);
Buttons are then added to a JsonArray called 'jRow' and then `NuiRow` is applied to that Array. Finally it's added to jRoot and jRoot then NuiCol is applied to jRoot.
It will show up like this, since all buttons are in the same Row (note: geometry width of NuiWindow was also modified to see buttons better.)
If we want the buttons laid out in a Column instead of row, we can simple change this one line from :
jRow = NuiRow(jRow);
To:
jRow = NuiCol(jRow);
Bad naming convention aside, it'll display like this:
Let's try something a bit more advanced. Let's have 3 rows, the first 2 rows have 2 buttons and last row having 3 buttons.
Code for it would look like this
json jButtonHello = NuiButton(JsonString("Hello"));
json jButtonGoodBye = NuiButton(JsonString("GoodBye"));
// Setup first row. Create jRow, add buttons, use NuiRow on jRow array then finally add it to jRoot.
json jRow = JsonArray();
jRow = JsonArrayInsert(jRow, jButtonHello);
jRow = JsonArrayInsert(jRow, jButtonGoodBye);
jRow = NuiRow(jRow);
jRoot = JsonArrayInsert(jRoot, jRow);
// More buttons
json jButtonYes = NuiButton(JsonString("Yes"));
json jButtonNo = NuiButton(JsonString("No"));
// This clears out the jRow, but that is intended since the prevous data is already in jRoot.
jRow = JsonArray();
jRow = JsonArrayInsert(jRow, jButtonYes);
jRow = JsonArrayInsert(jRow, jButtonNo);
jRow = NuiRow(jRow);
jRoot = JsonArrayInsert(jRoot, jRow);
json jButtonStop = NuiButton(JsonString("Stop"));
json jButtonRest = NuiButton(JsonString("Rest"));
json jButtonBored = NuiButton(JsonString("Bored"));
jRow = JsonArray();
jRow = JsonArrayInsert(jRow, jButtonStop);
jRow = JsonArrayInsert(jRow, jButtonRest);
jRow = JsonArrayInsert(jRow, jButtonBored);
jRow = NuiRow(jRow);
jRoot = JsonArrayInsert(jRoot, jRow);
// Transform entire root pane to an element.
jRoot = NuiCol(jRoot);
Which results in this window:
Quick Summary
The Process is same from here on out for all layouts.
- Create Widgets
- Add them to a JsonArray
- Use a Layout function on that JsonArray
- Add to bigger Array (and Apply Layout) or eventually, to a NuiWindow
Other Layout Factors
Besides using Layouts there are other factors that can affect the layout display.
- Widget themselves take up space and adding new ones will affect layouts.
- There is also a special widget just for this called NuiSpacer which is an empty placeholder.
- Widget values can also be changed with various Values (Modifiers/Attributes).
- For example, Applying a NuiWidth to an existing widget will change it's width and thus the layout.
Let's use some basic NuiSpacers to help better center our buttons:
jRow = JsonArrayInsert(jRow, NuiSpacer());
jRow = JsonArrayInsert(jRow, jButtonHello);
jRow = JsonArrayInsert(jRow, jButtonGoodBye);
jRow = JsonArrayInsert(jRow, NuiSpacer());
jRow = NuiRow(jRow);
jRoot = JsonArrayInsert(jRoot, jRow);
(Note: same was done for Yes/No buttons)
This will display as such:
A whole tutorial could be written about Layouts, but hopefully this gives you the basics to get started.
Events
Events are what enable scripts to react to NUI events. An example of that is a user pressing the button to "do something". That event will be sent and can be handled by the OnNuiEvent which will be responsible for implementing that "do something" functionality.
Please see OnNuiEvent on how to setup and event handler.
A simplified example of this for our tutorial would be:
OnModuleLoad Add this code:
Then create a new script: nui_tut_events and add this code:
#include "nui_tutorial"
void main() {
object oPlayer = NuiGetEventPlayer();
int nToken = NuiGetEventWindow();
string sEvent = NuiGetEventType();
string sElement = NuiGetEventElement();
int nIndex = NuiGetEventArrayIndex();
string sWindowId = NuiGetWindowId(oPlayer, nToken);
// Debug information for when first setting up.
SendMessageToPC(GetFirstPC(), "Receive an OnNuiEvent. Player: " + GetName(oPlayer) + " nToken: " + IntToString(nToken) + " sEvent: " + sEvent + " sElement: " + sElement + " nIndex: " + IntToString(nIndex) + " sWindowId: " + sWindowId);
}
Ids
Continuing the above example, the event handler should be setup - but nothing is happening. This is because our widgets (NuiButtons in this case) need to have an ID.
This done with NuiId
It can be done "inline" or separate.
Inline example:
or separately if preferred:
jButtonGoodBye = NuiId(jButtonGoodBye, "nui_tut_button_goodbye");
Both will have a string id now of either "nui_tut_button_hello" for our Hello Button or "nui_tut_button_goodbye" for our GoodBye button.
Now when we click on our Hello or GoodBye buttons we can see our `SendMessageToPC` debug message is trigger and also provides the ID as 'sElement' (which is captured from NuiGetEventElement)
Let's go ahead and finish up by adding IDs to all 7 buttons
json jButtonHello = NuiId(NuiButton(JsonString("Hello")), "nui_tut_button_hello");
json jButtonGoodBye = NuiId(NuiButton(JsonString("GoodBye")), "nui_tut_button_goodbye");
json jRow = JsonArray();
jRow = JsonArrayInsert(jRow, NuiSpacer());
jRow = JsonArrayInsert(jRow, jButtonHello);
jRow = JsonArrayInsert(jRow, jButtonGoodBye);
jRow = JsonArrayInsert(jRow, NuiSpacer());
jRow = NuiRow(jRow);
jRoot = JsonArrayInsert(jRoot, jRow);
json jButtonYes = NuiId(NuiButton(JsonString("Yes")), "nui_tut_button_yes");
json jButtonNo = NuiId(NuiButton(JsonString("No")), "nui_tut_button_no");
jRow = JsonArray();
jRow = JsonArrayInsert(jRow, NuiSpacer());
jRow = JsonArrayInsert(jRow, jButtonYes);
jRow = JsonArrayInsert(jRow, jButtonNo);
jRow = JsonArrayInsert(jRow, NuiSpacer());
jRow = NuiRow(jRow);
jRoot = JsonArrayInsert(jRoot, jRow);
json jButtonStop = NuiId(NuiButton(JsonString("Stop")), "nui_tut_button_stop");
json jButtonRest = NuiId(NuiButton(JsonString("Rest")), "nui_tut_button_rest");
json jButtonBored = NuiId(NuiButton(JsonString("Bored")), "nui_tut_button_bored");
jRow = JsonArray();
jRow = JsonArrayInsert(jRow, jButtonStop);
jRow = JsonArrayInsert(jRow, jButtonRest);
jRow = JsonArrayInsert(jRow, jButtonBored);
jRow = NuiRow(jRow);
jRoot = JsonArrayInsert(jRoot, jRow);
jRoot = NuiCol(jRoot);
Event Handling
Now that we're all setup, we want to do something useful when capturing these events.
For this example, we want to play an animation and voice chat when user clicks on each button.
{
object oPlayer = NuiGetEventPlayer();
int nToken = NuiGetEventWindow();
string sEvent = NuiGetEventType();
string sElement = NuiGetEventElement();
int nIndex = NuiGetEventArrayIndex();
string sWindowId = NuiGetWindowId(oPlayer, nToken);
// Debug information for when first setting up.
// SendMessageToPC(GetFirstPC(), "Receive an OnNuiEvent. Player: " + GetName(oPlayer) + " nToken: " + IntToString(nToken) + " sEvent: " + sEvent + " sElement: " + sElement + " nIndex: " + IntToString(nIndex) + " sWindowId: " + sWindowId);
// This is not our window, nothing to do.
if (sWindowId != NUI_TUTORIAL_HELLO_WORLD_WINDOW)
{
return;
}
// Not a mouseup event, nothing to do.
if (sEvent != "mouseup")
{
return;
}
if (sElement == "nui_tut_button_hello")
{
AssignCommand(oPlayer, ActionPlayAnimation(ANIMATION_FIREFORGET_GREETING));
PlayVoiceChat(VOICE_CHAT_HELLO, oPlayer);
return;
}
if (sElement == "nui_tut_button_goodbye")
{
AssignCommand(oPlayer, ActionPlayAnimation(ANIMATION_FIREFORGET_GREETING));
PlayVoiceChat(VOICE_CHAT_GOODBYE, oPlayer);
return;
}
if (sElement == "nui_tut_button_yes")
{
AssignCommand(oPlayer, ActionPlayAnimation(ANIMATION_FIREFORGET_SALUTE));
PlayVoiceChat(VOICE_CHAT_YES, oPlayer);
return;
}
if (sElement == "nui_tut_button_no")
{
AssignCommand(oPlayer, ActionPlayAnimation(ANIMATION_FIREFORGET_HEAD_TURN_LEFT));
AssignCommand(oPlayer, ActionPlayAnimation(ANIMATION_FIREFORGET_HEAD_TURN_RIGHT));
PlayVoiceChat(VOICE_CHAT_NO, oPlayer);
return;
}
if (sElement == "nui_tut_button_stop")
{
AssignCommand(oPlayer, ActionPlayAnimation(ANIMATION_LOOPING_PAUSE));
PlayVoiceChat(VOICE_CHAT_STOP, oPlayer);
return;
}
if (sElement == "nui_tut_button_rest")
{
AssignCommand(oPlayer, ActionPlayAnimation(ANIMATION_LOOPING_PAUSE_TIRED));
PlayVoiceChat(VOICE_CHAT_REST, oPlayer);
return;
}
if (sElement == "nui_tut_button_bored")
{
AssignCommand(oPlayer, ActionPlayAnimation(ANIMATION_FIREFORGET_PAUSE_BORED));
PlayVoiceChat(VOICE_CHAT_BORED, oPlayer);
return;
}
}
Which gives us this
Updates
One way to update a GUI would be to close, rebuild and reopen. This method can be a bit clunky and give a bad user experience.
A better way is to use Binds. You can think of this as variables for GUI components. When you update a Bind the GUI updates that specific Widget will display the new value. You can also read from Binds if you want to get information from the GUI.
Binding
Let's track and display how many times a user clicks a button. First let's create a text label for this and add it to our main NU (in the Construction script: nui_tutorial.nss)
jRoot = JsonArrayInsert(jRoot, lLabelCount);
If you notice here, instead of giving a JsonString to NuiLabel, instead we are binding it using NuiBind
NuiLabel(NuiBind("nui_tut_label_count")
Then after NUICreate we set that Bind to a value using NuiSetBind In this case we're taking it from LocalInt on Player
NuiSetBind(oPlayer, nToken, "nui_tut_label_count", JsonString("Count: " + IntToString(nCount)));
Lastly, you can also use NuiGetBind if you want to read that value.
Let's go back to the nui_tut_events.nss script and add this code to count number of clicks:
nCount++;
SetLocalInt(oPlayer, "nui_tut_button_clicks", nCount);
NuiSetBind(oPlayer, nToken, "nui_tut_label_count", JsonString("Count: " + IntToString(nCount)));
If you look down at the bottom, the label will update every time a player clicks on one of the buttons
Summary
Hopefully this has given you the basics of making your own NUIs.
Here is final tutorial code for reference
nui_tutorial.nss
#include "nw_inc_nui_insp"
const string NUI_TUTORIAL_HELLO_WORLD_WINDOW = "nui_tutorial_hello_world_window";
void Nui_Tutorial_Hello_World(object oPlayer)
{
int nPreviousToken = NuiFindWindow(oPlayer, NUI_TUTORIAL_HELLO_WORLD_WINDOW);
if (nPreviousToken != 0)
{
NuiDestroy(oPlayer, nPreviousToken);
}
json jRoot = JsonArray();
json jButtonHello = NuiId(NuiButton(JsonString("Hello")), "nui_tut_button_hello");
json jButtonGoodBye = NuiId(NuiButton(JsonString("GoodBye")), "nui_tut_button_goodbye");
json jRow = JsonArray();
jRow = JsonArrayInsert(jRow, NuiSpacer());
jRow = JsonArrayInsert(jRow, jButtonHello);
jRow = JsonArrayInsert(jRow, jButtonGoodBye);
jRow = JsonArrayInsert(jRow, NuiSpacer());
jRow = NuiRow(jRow);
jRoot = JsonArrayInsert(jRoot, jRow);
json jButtonYes = NuiId(NuiButton(JsonString("Yes")), "nui_tut_button_yes");
json jButtonNo = NuiId(NuiButton(JsonString("No")), "nui_tut_button_no");
jRow = JsonArray();
jRow = JsonArrayInsert(jRow, NuiSpacer());
jRow = JsonArrayInsert(jRow, jButtonYes);
jRow = JsonArrayInsert(jRow, jButtonNo);
jRow = JsonArrayInsert(jRow, NuiSpacer());
jRow = NuiRow(jRow);
jRoot = JsonArrayInsert(jRoot, jRow);
json jButtonStop = NuiId(NuiButton(JsonString("Stop")), "nui_tut_button_stop");
json jButtonRest = NuiId(NuiButton(JsonString("Rest")), "nui_tut_button_rest");
json jButtonBored = NuiId(NuiButton(JsonString("Bored")), "nui_tut_button_bored");
jRow = JsonArray();
jRow = JsonArrayInsert(jRow, jButtonStop);
jRow = JsonArrayInsert(jRow, jButtonRest);
jRow = JsonArrayInsert(jRow, jButtonBored);
jRow = NuiRow(jRow);
jRoot = JsonArrayInsert(jRoot, jRow);
json lLabelCount = NuiLabel(NuiBind("nui_tut_label_count"), JsonInt(NUI_HALIGN_CENTER), JsonInt(NUI_VALIGN_MIDDLE));
jRoot = JsonArrayInsert(jRoot, lLabelCount);
jRoot = NuiCol(jRoot);
json nui = NuiWindow(jRoot, JsonString("Hello World (title)"), NuiBind("geometry"), NuiBind("resizable"), NuiBind("collapsed"), NuiBind("closable"), NuiBind("transparent"), NuiBind("border"));
int nToken = NuiCreate(oPlayer, nui, NUI_TUTORIAL_HELLO_WORLD_WINDOW);
NuiSetBind(oPlayer, nToken, "geometry", NuiRect(-1.0f, -1.0f, 480.0f, 240.0f));
NuiSetBind(oPlayer, nToken, "collapsed", JsonBool(FALSE));
NuiSetBind(oPlayer, nToken, "resizable", JsonBool(TRUE));
NuiSetBind(oPlayer, nToken, "closable", JsonBool(TRUE));
NuiSetBind(oPlayer, nToken, "transparent", JsonBool(FALSE));
NuiSetBind(oPlayer, nToken, "border", JsonBool(TRUE));
int nCount = GetLocalInt(oPlayer, "nui_tut_button_clicks");
NuiSetBind(oPlayer, nToken, "nui_tut_label_count", JsonString("Count: " + IntToString(nCount)));
}
nui_tut_events.nss
#include "nw_inc_nui"
void main()
{
object oPlayer = NuiGetEventPlayer();
int nToken = NuiGetEventWindow();
string sEvent = NuiGetEventType();
string sElement = NuiGetEventElement();
int nIndex = NuiGetEventArrayIndex();
string sWindowId = NuiGetWindowId(oPlayer, nToken);
// Debug information for when first setting up.
// SendMessageToPC(GetFirstPC(), "Receive an OnNuiEvent. Player: " + GetName(oPlayer) + " nToken: " + IntToString(nToken) + " sEvent: " + sEvent + " sElement: " + sElement + " nIndex: " + IntToString(nIndex) + " sWindowId: " + sWindowId);
// This is not our window, nothing to do.
if (sWindowId != NUI_TUTORIAL_HELLO_WORLD_WINDOW)
{
return;
}
// Not a mouseup event, nothing to do.
if (sEvent != "mouseup")
{
return;
}
int nCount = GetLocalInt(oPlayer, "nui_tut_button_clicks");
nCount++;
SetLocalInt(oPlayer, "nui_tut_button_clicks", nCount);
NuiSetBind(oPlayer, nToken, "nui_tut_label_count", JsonString("Count: " + IntToString(nCount)));
if (sElement == "nui_tut_button_hello")
{
AssignCommand(oPlayer, ActionPlayAnimation(ANIMATION_FIREFORGET_GREETING));
PlayVoiceChat(VOICE_CHAT_HELLO, oPlayer);
return;
}
if (sElement == "nui_tut_button_goodbye")
{
AssignCommand(oPlayer, ActionPlayAnimation(ANIMATION_FIREFORGET_GREETING));
PlayVoiceChat(VOICE_CHAT_GOODBYE, oPlayer);
return;
}
if (sElement == "nui_tut_button_yes")
{
AssignCommand(oPlayer, ActionPlayAnimation(ANIMATION_FIREFORGET_SALUTE));
PlayVoiceChat(VOICE_CHAT_YES, oPlayer);
return;
}
if (sElement == "nui_tut_button_no")
{
AssignCommand(oPlayer, ActionPlayAnimation(ANIMATION_FIREFORGET_HEAD_TURN_LEFT));
AssignCommand(oPlayer, ActionPlayAnimation(ANIMATION_FIREFORGET_HEAD_TURN_RIGHT));
PlayVoiceChat(VOICE_CHAT_NO, oPlayer);
return;
}
if (sElement == "nui_tut_button_stop")
{
AssignCommand(oPlayer, ActionPlayAnimation(ANIMATION_LOOPING_PAUSE));
PlayVoiceChat(VOICE_CHAT_STOP, oPlayer);
return;
}
if (sElement == "nui_tut_button_rest")
{
AssignCommand(oPlayer, ActionPlayAnimation(ANIMATION_LOOPING_PAUSE_TIRED));
PlayVoiceChat(VOICE_CHAT_REST, oPlayer);
return;
}
if (sElement == "nui_tut_button_bored")
{
AssignCommand(oPlayer, ActionPlayAnimation(ANIMATION_FIREFORGET_PAUSE_BORED));
PlayVoiceChat(VOICE_CHAT_BORED, oPlayer);
return;
}
}
Debugging and Troubleshooting
Debug Mode
NWN:EE has a built in Debug Panel and has special debug mode for guis.
Press Ctrl+Shift+F12 and that brings up the Debug Panel. Check 'GUI' and then Under Casssowary check the 'Debug'
If that is enabled, when you hover over a GUI it gives you a nice debug display of your layout.
NUI Inspector
NUI Inspector also gives you some useful information.
In your EVENT_SCRIPT_MODULE_ON_NUI_EVENT script you first need to include the insp script:
then call
so it can process events.
Finally, you need to invoke the GUI (though playerchat, item powers, etc)
This tool provides a lot of information and also sends debug information to your combat console.
Json Dumping
Another way to get more information about your NUI is you can dump part or all of the Json using JsonDump, either by print to console or writing to log file.
// Or if you want to write to log
WriteTimestampedLogEntry(JsonDump(jRoot, 4));
This can be awkward to deal with, but it's an option if you are truly stuck. It will show you the underlying structure.
Common Issues
Wrong Type
Most Nui* functions take a type of json instead of nwn types, but some are mixed.
If you get this error:
nui_tutorial.nss(28): Error: NSC1002: Type mismatch in parameter 1 ("jLabel") in call to "NuiButton": Expected type "json", but got type "string"
It means you have the Data Type is wrong.
For example NuiText takes both Json and NWN types.
For the json types, you need to wrap them in a Json function: JsonString, JsonInt, JsonFloat, example:
Not Getting an Event on a Widget
If you have your events setup correctly, but still aren't able to get an Event from a widget, then make sure your widget has a NuiId. Elements without IDs won't be sent to the EventHandler.
Can not use at() with array
This happens when you try to add a JsonArray to NUI without wrapping it. JsonArray's are used to hold Nui Elements, but they need to be wrapped in a NuiRow, NuiCol or NuiGroup.
Example:
Wrong
json lLabel2 = NuiLabel(NuiBind("label1"), JsonInt(NUI_HALIGN_CENTER), JsonInt(NUI_VALIGN_MIDDLE));
jRow = JsonArray();
jRow = JsonArrayInsert(jRow, lLabel1);
jRow = JsonArrayInsert(jRow, lLabel2);
// Wrong: raw array, whoops!
jRoot = JsonArrayInsert(jRoot, jRow);
Right
json lLabel2 = NuiLabel(NuiBind("label1"), JsonInt(NUI_HALIGN_CENTER), JsonInt(NUI_VALIGN_MIDDLE));
jRow = JsonArray();
jRow = JsonArrayInsert(jRow, lLabel1);
jRow = JsonArrayInsert(jRow, lLabel2);
// Wrapping it as a NuiRow
jRow = NuiRow(jRow);
jRoot = JsonArrayInsert(jRoot, jRow);