Whew, sorry for not finishing off the UI Objects. There's not too many left to document here before all the basic UI types should be covered, I think...
Anyway, on to UIListBox. UIListBox is a container similar to UIPane and anything listed under UIPane that makes sense for UIListBox probably does the same thing under UIListBox. So I'm going to stick to discussing the attributes that are unique to UIListBox. Refer to UIPane for the basic attributes (Like X, Y, Height, Width, etc).
There's a couple different ways list boxes are used.
A list box can be used for displaying multiple rows of objects. Things like the Feat List or the lines of text in the chat windows use it this way. This is accomplished by just adding multiple objects to the list box. Typically these objects are the same size, but that is not a requirement.
A list box can also be used for containing a single large text field and being able to scroll over that text field. This can be seen in places like the class and race descriptions on character creation. This approach is accomplished by having a single text field within the list box, making the height of the text field dynamic, and changing the text of the text field. The listbox will allow for scrolling over the large text field.
Another note, I may use the term 'control' a lot in this write up. 'Controls' in this context are the contents of the list box. These are different than 'children' of the list box, which would include every object contained by the listbox, like the scrollbar. I will try to call controls 'rows' to make it more clear, but if I slip up and call it a 'control,' I'm talking about a row. =)
The amount of padding that the listbox should put between the edges of the listbox and the contents of the listbox. This padding is applied equally to both sides. So if the padding is 5, and the ListBox is 100 pixels wide, then the ListBox will constrain the rows to a max width of 90.
The amoutn of padding that the list box puts between each row. This includes padding between the top row and the top of the list box and the bottom row and the bottom of the list box as well.
Setting this to 'true' will make it so that any time the text field in the listbox gets resized, the listbox will snap to the bottom to show the bottom of the text. This is used mostly for situations where text keeps getting added to a text field and you want the list box to keep scrolling to the bottom to display all the text. This is not how the chat window works, BTW.
This is how many pixels you want the listbox to scroll for each 'scroll click' on the part of the user. The parameter is a number, the default vaule is 1.
This is a somewhat complicated attribute. If you don't have any preference, it's usually best to just set it to true. The purpose of it is to speed up performance on list boxes where each row is the same height. So if your list box will definitely have each row with the same height, then set this to false to speed up scrolling with that list box.
This tells the list box if it should just clip the bottom row if it can't show the entire row. Defaults to true, parameters are 'true' or 'false.'
Setting this to true moves the scrollbar over to the right side of the list box. Setting it to false will move the scroll bar to the left side of the list box. The default behavior is 'true'.
This tells the game that you want the user to be able to click on the rows of the list box. This means that the list box is intended to contain a number of rows and you want the user to pick a row. It is false by default. If false, then clicking on a row will perform the 'LeftClick' callback if you click on a button row, or will set the text field as the focus if there is just a single large text field in the list box. If by true, clicking on the row will still execute the OnLeftClick callback for the button if there is one. It will also set that row as selected.
If this is set to true, the scrollbar will be hidden unless there are enough contents within the list box to require scrolling to see them all. Defaults to false.
If this is set to true, then the scrollbar will be hidden no matter what. This is for the rare case you may want text to scroll by but don't care to have the user being able to scroll back up. Defaults to false.
If the engine finds a UIFrame nested within a ListBox while loading the XML, it treats that Frame as a border for the full size of the list box. I'm not sure if this ever saw any actual testing though, as I believe we usually just added a separate UIFrame to the Scene to act as the border.
If the engine finds a UIScrollBar nested within a ListBox while loading the XML, that scrollbar will become the scrollbar for the ListBox and be attached to the left or right depending on the attributes.
A listbox can contain the following types of UIObjects as its rows:
Button, Label, Hotbarbutton, Text, Collapsable, Pane, Grid, Icon, TextTree
If the object found has an attribute called 'prototype=true', then that object becomes the ProtoType for the list box. The prototype defines all the attributes, styles, etc., that each row that is added dynamically to the listbox should have. Unfortunately, this is something better experimented with to understand rather than me trying to explain. :) But in brief, find a listbox that has a prototype object in the game, and see what effect changing the prototype's attributes have on the way the listbox rows look in the game.
Anyway, that's all I have to write about ListBox at the moment. I thought there would be more than this to write about it, so I imagine I'm missing some stuff. As always, just ask if I've neglected to cover some topic about UIListBox.
Wednesday, April 18, 2007
Whew, sorry for not finishing off the UI Objects. There's not too many left to document here before all the basic UI types should be covered, I think...
Friday, April 6, 2007
Just a note about a few more 1.06 things. But first, regarding 1.05 Final, it seriously, really, probably, maybe, should be coming out early next week... That's about all I know about it. =)
EnableAreaWater() - Turn water rendering on/off in an area.
SpeakOneLinerConversation() - Now takes a volume parameter (Only supports talk, whisper, and shout)
SetScale() - changed to accept x, y, z axis as parameters
Dedicated Server now correctly lists modules in directory format that are located in the MyDocs directories.
Dedicated Server now takes a -moduledir parameter to allow launching modules stored in directory format to be loaded via command line parameters.
Friday, March 30, 2007
Here's a list many of the scripting changes made in 1.06 so far. Most of them come from the 1.67 and 1.68 updates of NWN1.
ActionRest() - The bIgnoreNoRestFlag wasn't actually doing anything. It will now force rest even if the area is flagged NoRest or there are monsters nearby. If the character is still in combat, it will not force rest.
CreateItemOnObject() - Added new string parameter to provide the newly created object a new tag.
EffectHeal() now works on doors and placeables.
GetAbilityScore() now takes a 'nBaseAttribute' parameter to allow querying just the base attribute.
OnClick event for placeables that fires whether or not the player can actually path to them.
SpeakConversationOneLiner() now takes a parameter to determine if the spoken line should have whisper, talk, or shout volume.
GetTrapActive() - (Now works on doors and placeables too)
I also added a couple console commands to help with GUI debugging
Also added a console command:
This brings up an extra 'chat' window and will provide engine-level debugging info about mouse-overs, mouseinput, or callback execution. This feature isn't really finished as of this post, but I hope to spend some more time polishing it up for 1.06.
There are some new toolset features and bug fixes. The bug with seams showing up between tiles has been fixed as well as issues with Specular lighting not looking right. There is also now 5-level terrain undo for grass, water, and terrain adjustment and some improvements to some of the brushes.
There's some other things as well, but this is a pretty good sampling of the new things that will be in 1.06.
Wednesday, March 14, 2007
The 1.05 beta was posted today. One tweak that I intend to have made before 1.05 final is to remove the 'tabs = 5 spaces' change in the script editor. While some people do want some option like that, I think it needs to be implemented as something that can be turned on/off/adjusted, so for now, it's best to leave it out and re-implement it correctly in 1.06.
As far as 1.06 goes, I've spent a lot of time this week integrating a lot of the NWN1 1.67 changes, especially the script functions. I won't go into details now, but I will once I'm done. But in summary, most of the script functions that have been missing from NWN1's later patches should be in 1.06.
Monday, March 5, 2007
This callback will one of the most powerful new tools when it comes to creating custom actions in the game.
UIObject_Input_ActionTargetScript(), new in 1.06, will let you make 'GUI actions' similar to the 'actions' that are created when you click on a spell or feat button to cast it, or on a button in the DM Choser that then has you select a target to do the action to.
Once the user clicks on a valid target, a bunch of data will be sent to the server along with a request to execute a script associated with the click.
Anyway, enough summary, here's the details:
UIObject_Input_ActionTargetScript("sValidTargetTypes",nValidCursor,nInvalidCursor,nSpellTargetIndex,bUseHostile,"sScriptName", script parameter list...)
This parameter is a string that will list the types of objects that will be valid to click on. If you enter a "", that means all targets are valid for this action. Otherwise, you can enter a list, seperated by commas or any other deliminator you want to use. The valid target names are:
For example: "creature,placeable" would mean that valid targets would include clicking on placeables and clicking on creatures.
This is an integer constant to point to one of the cursors that the engine understands. You can only really use the cursors compiled into the game's executable. I will make a list of those constants at the end of this post. This is the cursor that you want to use when the cursor is over a valid target.
Just like the nValidCursor parameter, except this is the cursor you want displayed when the mouse is over an invalid target. For example, if my valid targets were creatures and placeables, but the mouse was over open ground, this is the cursor that would be shown.
This is a reference into the spelltarget.2da to indicate that you want to use one of the spell targeting projected textures like the ones used when targeting Area of Effect spells. If you do not want an AoE texture around the cursor, just enter -1 for this parameter.
This tells the engine if you want to use the Hostile Texture column from spelltarget.2da or not. If you entered -1 for nSpellTargetIndex, then this parameter doesn't mean anything. You must enter either "true" or "false", even if you entered -1 for nSpellTargetIndex.
The name of the script you want to follow. This follows the same restrictions that UIObject_Misc_ExecuteServerScript() usese, in that the script name must start with 'gui_', or else the server will reject the request.
This is an unlimited list of parameters just like ExecuteServerScript() supports. However, there are a few extra parameters that this function can support:
target.object - This will replace the parameter with the OBJECT_ID of the object that the user clicked on.
target.x - This will replace the parameter with the X value of the position that the user clicked on as a float. If the user clicked on a specific object, instead of open ground, then this X will be the position of that object as far as the client knows. Since the client could be a few milliseconds out of synch with the server, the server may consider this object at a slightly different position than the client does.
target.y - Same as X, except for the Y value of the position vector.
target.z - Same as X, except for the Z value of the position vector.
For example, if I wanted to create a DM Action button that deleted whatever placeable the DM targetted as well as all placeables within 15 yards of the target, I would create a UIButton with the following callback:
The number 51 would be for the CURSOR_KILL cursor, the number 53 would be for the CURSOR_NOKILL cursor, the number 17 would be for row 17 in spelltarget.2da whcih is a large circle with a 30 yard width (so 15 yard radius), and the "true" is because I want it to use the hostile texture out of that 2DA. I want to use the clicked on placeable as my starting point, so I go ahead and pass that along as a parameter as well. It will be passed into gui_eraseplaceables as either a string or an int, depending on what parameter I have that script expecting.
Anyway, the list of cursor constants is:
#define MOUSECURSOR_DEFAULT 1
#define MOUSECURSOR_DEFAULT_DOWN 2
#define MOUSECURSOR_WALK 3
#define MOUSECURSOR_WALK_DOWN 4
#define MOUSECURSOR_NOWALK 5
#define MOUSECURSOR_NOWALK_DOWN 6
#define MOUSECURSOR_ATTACK 7
#define MOUSECURSOR_ATTACK_DOWN 8
#define MOUSECURSOR_NOATTACK 9
#define MOUSECURSOR_NOATTACK_DOWN 10
#define MOUSECURSOR_TALK 11
#define MOUSECURSOR_TALK_DOWN 12
#define MOUSECURSOR_NOTALK 13
#define MOUSECURSOR_NOTALK_DOWN 14
#define MOUSECURSOR_FOLLOW 15
#define MOUSECURSOR_FOLLOW_DOWN 16
#define MOUSECURSOR_EXAMINE 17
#define MOUSECURSOR_EXAMINE_DOWN 18
#define MOUSECURSOR_NOEXAMINE 19
#define MOUSECURSOR_NOEXAMINE_DOWN 20
#define MOUSECURSOR_TRANSITION 21
#define MOUSECURSOR_TRANSITION_DOWN 22
#define MOUSECURSOR_DOOR 23
#define MOUSECURSOR_DOOR_DOWN 24
#define MOUSECURSOR_USE 25
#define MOUSECURSOR_USE_DOWN 26
#define MOUSECURSOR_NOUSE 27
#define MOUSECURSOR_NOUSE_DOWN 28
#define MOUSECURSOR_MAGIC 29
#define MOUSECURSOR_MAGIC_DOWN 30
#define MOUSECURSOR_NOMAGIC 31
#define MOUSECURSOR_NOMAGIC_DOWN 32
#define MOUSECURSOR_DISARM 33
#define MOUSECURSOR_DISARM_DOWN 34
#define MOUSECURSOR_NODISARM 35
#define MOUSECURSOR_NODISARM_DOWN 36
#define MOUSECURSOR_ACTION 37
#define MOUSECURSOR_ACTION_DOWN 38
#define MOUSECURSOR_NOACTION 39
#define MOUSECURSOR_NOACTION_DOWN 40
#define MOUSECURSOR_LOCK 41
#define MOUSECURSOR_LOCK_DOWN 42
#define MOUSECURSOR_NOLOCK 43
#define MOUSECURSOR_NOLOCK_DOWN 44
#define MOUSECURSOR_PUSHPIN 45
#define MOUSECURSOR_PUSHPIN_DOWN 46
#define MOUSECURSOR_CREATE 47
#define MOUSECURSOR_CREATE_DOWN 48
#define MOUSECURSOR_NOCREATE 49
#define MOUSECURSOR_NOCREATE_DOWN 50
#define MOUSECURSOR_KILL 51
#define MOUSECURSOR_KILL_DOWN 52
#define MOUSECURSOR_NOKILL 53
#define MOUSECURSOR_NOKILL_DOWN 54
#define MOUSECURSOR_HEAL 55
#define MOUSECURSOR_HEAL_DOWN 56
#define MOUSECURSOR_NOHEAL 57
#define MOUSECURSOR_NOHEAL_DOWN 58
#define MOUSECURSOR_RUNARROW 59
#define MOUSECURSOR_WALKARROW 75
#define MOUSECURSOR_PICKUP 91
#define MOUSECURSOR_PICKUP_DOWN 92
#define MOUSECURSOR_CHATBOX_SIZING 99
#define MOUSECURSOR_NODEFAULT 125
#define MOUSECURSOR_NODEFAULT_DOWN 126
#define MOUSECURSOR_MAP 128
#define MOUSECURSOR_MAP_DOWN 127
#define MOUSECURSOR_NOMAP 130
#define MOUSECURSOR_NOMAP_DOWN 129
#define MOUSECURSOR_WAIT_DOWN 131
#define MOUSECURSOR_WAIT 132
Friday, March 2, 2007
Thursday, March 1, 2007
Sorry for the delay in posting, but if you're into custom UI work, this information will have been worth the wait. :)
A new callback in 1.06 is:
UIObject_Misc_ExtractData( sourceobject, datatype, index, destinationvar )
This callback can be used to extract the data imbedded by the engine within UI Objects. First I'll go over the parameters, then I'll give some examples.
This parameter is used to define which UI Object we are extracting data from. It accepts the following options:
selected:listboxname - By replacing 'listboxname' with the name of the listbox you're interested in, you can tell this callback to use the selected row within the list box as the source of the data.
self: - By using 'self:', you are telling the callback to use the object that invoked the callback as the source from which to extract the data.
context:uiobject - By using context:uiobject:', you are telling the callback to use the UI Object that invoked the context menu. This obviously only means anything if the callback is being called from a context menu node. If called from anywhere else, the callback will do nothing.
objectname - If you enter any string besides the above 3 options, the callback will search for a UIObject within the same scene as the object executing this callback that has that name.
The datatype tells the callback which data type it should be extracting from the UIObject. Note that regardless of what the original data type is, the data type that gets stored in the GUI Variable at the end will always be a string.
The data type values are:
bool -'true' or 'false' are the strings that will get stored in the GUI variable
Again, it's important to remember that no matter what the originating data type is, the result will be stored as a string variable within the GUI variable specified.
This is the index of the data to extract. If the index is invalid for the UIObject, a default value will get stored in the GUI variable (Either "" or "0" or "false" depending on what the originating data type was).
This can be a local:# or global:#. The type of the stored data will always be a string.
Now for some real examples that will work.
The grid icons in the inventory grid have the following data imbedded in them:
OBJECT_ID(0) = The object currently displayed in that icon
OBJECT_ID(1) = The object that was dispalyed in that icon on the previous frame
int(0) = The stacksize for that object on the previous update frame
int(1) = Indicates if the item is identified or not
int(2) = Indicates if the item is usable or not
int(3) = Indicates if the item is ghosted or not
It would be possible for me to change the inventory screen to drop items if I double right click on them.
I would edit the 'InvPrototypeButton' inside of inventory.xml and add:
When I right double click that item, the first callback will extract the Object ID of the item and place it in local:3 as a string that looks like: "32144" or whatever the object ID is.
Then the 2nd callback would execute, asking the server to fire off the "gui_dropitem" script, and it would pass the 32144 value in as a parameter to the script (Either as a string "32144" or an integer 32144, depending on what the parameter type the script is expecting).
Now I will be adding an IntToObject() script funciton that will make it possible to take that int parameter and turn it into an object parameter. At that point, you could use the DropItem action and have the player drop that object.
Note that in this example, I would've had to remove the OnRightClick call to the context menu for the item button or it would end up blocking the double right click (Due to the context menu popping up and blocking the second click).
This example is a lot more complicated, I'll try to explain it clearly. :)
The spell buttons in the game usually have the following pieces of data:
int(0) = The spell ID that the button represents
int(1) = The multi-class index that can cast that spell
int(2) = Not used anymore, so nothing.
int(3) = The meta magic feat, if any
int(4) = Whether or not this is a spontanously cast spell
int(5) = Whether or not this is a domain spell
int(6) = Whether or not this is a cheat-command spell cast
The values for int(3), the meta magic feat, are:
1 = Empower
2 = Extend
4 = Maximize
8 = Quicken
16 = Silent
32 = Still
64 = Persistent
128 = Permanent
The warlock invocations are also meta-magic feats, as follows (Note, I'm not sure how many of these are actually in the game, I'm just going down the list of constants in the code):
256 = Draining Blast
512 = Eldritch Spear
1024 = Frightful Blast
2048 = Hideous Blow
4096 = Beshadowed Blast
8192 = Brimstone Blast
16384 = Eldritch Chain
32768 = Hellrime Blast
65536 = Bewitching Blast
13102 = Eldritch Cone
262144 = Noxious Blast
524288 = Vitrioloic Blast
1048576 = Eldritch Doom
2097152 = Utterdark Blast
Let's say I want to make my own custom Examine Spell command for the spells_known.xml listing of spells.
First off, let's add our own custom radial menu (Another new feature in 1.06).
In spells_known.xml, I would go to the SPELLPANE_PROTO pane and add a
root-customspell will be the name of a new root node that I would then add to the contextmenu.xml
For now, that root node will only have 1 sub node called: node-spellexamine
node-spellexamine will be the name of a new radial node that I would make with the following callbacks:
Now when I go into the game and bring up the Spells Known screen and right click on the spell icon for a spell in the list, I will get a context menu with 1 item called 'Examine'. Clicking on that will first call:
which will extract the spell ID from int(0) on the button I brought the context menu up on and place that ID in local:5
Then it will fire
and pass along the spell ID (As stored in local:5) as the parameter to the script: gui_spellexamine
From there I can do whatever I want with that data. :)
To recap, the new features mentioned in this article are:
Feel free to ask any time you would like me to provide a detailed list of what data is imbedded in a given UI Object. Some you'll probably be able to figure out on your own via trial and error, but I'll be happy to look into any specific UI Object if you need.
Friday, February 23, 2007
1.06 will have a number of new script functions that will give improved control over custom GUIs.
Two of the new script functions are:
This script function will let you set the position of a progress bar (Between 0.0 for empty and 1.0 for full).
The other new function is:
This script function will let you set the texture for a UIIcon, a UIFrame's fill texture, or a Button's Base Frame's fill texture.
Both script functions require the UIScene to be scriptloadable like all the other GUI related scripting functions.
Thursday, February 22, 2007
In 1.06, callbacks can take 2 new 'variable' parameters in addition to global:# and local:#.
The first new parameter type is
This will insert the row # that is currently selected in the listbox indicated by listboxname
For example, if I had a callback that looked like:
On the same UIScene I have a listbox called 'mylb' where I have selected the 3rd row.
When the OnLeftClick callback above gets executed, the 1 parameter that gets sent to that script will be an integer with the value of 2. (The first row in the LB would send a value of 0).
The second new parameter type also deals with ListBoxes:
This will insert the text contained by that row. If the listbox rows are buttons or text fields, then the textfieldname parameter above is not necessary. If instead they are complex UI Panes, then the textfieldname will be used to specify which object within the pane contains the text we want.
For example, if I had a callback that looked like:
In the same UIScene, I have a Listbox named 'mylb'. The rows of that lb are complex UIPanes that contain a text field in them called 'mytext'.
When that callback executes, the engine will look at 'mylb', find the currently selected row, then look for the object named 'mytext' within that row and insert the text within that object as a parameter to the script on the server.
Now obviously the real power of these new variable parameters will be when coupled with the ability for scripts to populate a gui listbox. That functionality isn't in yet, but is something else I intend to have in for 1.06.
There are other new 'variable' parameter syntaxes that will be coming along with 1.06 that I'll post about once I have actually implemented them. :)
Monday, February 19, 2007
This callback takes a single parameter. It's only purpose is to enable or disable a button based on whether or not the user has selected a row in a List Box.
The single parameter is:
sListBoxName = The name of the list box we want to check for selection.
If the user has a row selected in the listbox named in the parameter, then the UIButton will be set to Enabled.
If the user does not have a row selected in the listbox named in the parameter, then the UIButton will be disabled.
The only valid parameter to this callback is a local or global GUI variable index.
localguivars are expressed as local:#, where # is the index for the variable.
globalguivars are expressed as global:#, where # is the index for the variable.
This callback takes the engine-level data in a UIObject and stores it in a global or local gui variable. The engine-level data will vary from UIObject to UIObject. It is also impossible to specify which data item you want to set the GUI variable to, if the UIObject has multiple items of data.
This is a really old callback and is of limited use to custom GUIs at this time. My intent is to write a new callback that accomplishes what this callback does, but that takes more parameters in order to more accurately specify what data you are interested in from the UIObject.
UIObject_Tooltip_DisplayTooltipString takes up to 9 parameters, but only the first four parameters are required.
Message = The literal string to display in the tooltip
XLoc = The X origin for where the tooltip should appear. This can be a pixel location, or you can use the following two strings to make it relative: MOUSE_X will place the X origin of the tooltip near where the mouse is. OBJECT_X will place the tooltip next to the UIObject that is being moused over, if any. At first, it will try to place the tooltip to the right side of the UIObject. But if there is not enough screen space to fit the tooltip there, it will place it on the left side of the UIObject.
YLoc = The Y origin for where the tooltip should appear. This can be a pixel location, or you can use the following two strings to make it relative: MOUSE_Y will place the Y origin of the tooltip near where the mouse is. OBJECT_Y will place the tooltip next to the UIObject that is being moused over, if any. At first, it will try to place the tooltip on top of the UIObject. But if there is not enough screen space to fit the tooltip there, it will place it on the bottom of the UIObject.
Screen Tag = The Screen Tag for the XML file to be used for the tooltip
XAlignment = This parameter can be used to quickly position a tooltip relative to the full-screen space. The valid arguments are: ALIGN_NONE(default), ALIGN_CENTER, ALIGN_LEFT, ALIGN_RIGHT
YAlignment = This parameter can be used to quickly position a tooltip relative to the full-screen space. The valid arguments are: ALIGN_NONE(default), ALIGN_CENTER, ALIGN_TOP, ALIGN_BOTTOM
XPadding = This is used along with XAlign to quickly position the tooltip. It controls how far in the tooltip should be pushed from the left or right border of the screen space. For example, setting XAlignment to ALIGN_RIGHT and XPadding to 10 will result in the tooltip appearing on the right side of the screen, with a space of 10 pixels between the right edge of the tooltip scene and the edge of the screen.
YPadding = This is used along with YAlign to quickly position the tooltip. It controls how far in the tootip should be pushed from the top or bottom border of the screen space. For example, setting YAlignment to ALIGN_BOTTOM and YPadding to 10 will result in the tooltip appearing on the bottom of the screen with a space of 10 pixels between the bottom edge of the tooltip scene and the edge of the screen.
TextAlignment = How the text should be aligned in the tooltip. The options are: ALIGN_NONE(default), ALIGN_CENTER, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_TOP, ALIGN_BOTTOM
In 1.06, it will be possible to set up multiple GUI callbacks on a single UI event in XML. The syntax will be to use the event name and append a number to it. The engine will read in the callbacks until it fails to find the next sequential number.
For example, I could set up a UI Button to with the following callbacks:
When I click that button, it would open the Options screen, then set the local var to "true". On its next update, it would become disabled because of the OnUpdate check.
Note that the callback without a number will be handled before the 0'th callback.
So OnLeftClick will be handled before OnLeftClick0.
Also note that the callback without the number is not required but is mostly just supported for backwards compatibility.
For example, it would be valid to have the following callbacks:
That will work fine, executing 0, then 1, then 2.
It is also valid to have:
This will execute them in order.
It is not valid to start with any other number besides 0 however.
will result in no callbacks being loaded for that event for that UIObject.
Sometimes the engine assigns callbacks to UIObjects internally. The engine will only ever override the first callback, so if you are having a problem with the engine overriding your callback for some UI Object, you might need to insert a dummy callback first, before the callbacks you actually want to execute. This doesn't happen too often though, so I doubt this will be an issue for very many.
Note at this time, the only event that doesn't support multiple callbacks is OnTooltip, due to how the engine has to handle this event internally. I may or may not be able to fix OnTooltip by the time 1.06 comes out.
Monday, February 12, 2007
Taking a break from writing about the UI Elements for a moment, though there are still some of those that I will cover in future posts.
Most XML files are loaded as default UI Scene, which implies no special behaviors. There are, however, several XML files that will get special handling in code and most conform to specific rules in order to even be loaded. All of these scenes are distinguished by their identifying name in the INI file (Or the screen tag given to them when being loaded via script). The actual XML file name doesn't matter.
Note that when I list them below, if a name ends with a * mark, that means that only the text up to the * mark is required and that the name may be unique after that mark.
The following are special GUI screens that are loaded differently than most XML files:
SCREEN_FADE = The full screen 'fade' effect. It gets loaded like a normal XML file at first. After loaded, the first UIIcon object found (The one highest up in the file) becomes the engine's 'Fading' icon. The rest of the contents of the file will behave normally. In our default SCREEN_FADE, we include just the full screen icon. But there may be other things one would want to include on the fade, such as custom images, etc.
SCREEN_QUICKCHAT = The NWN1 style dialog box. First it is loaded like a normal XML file, then the following ui objects are searched for in order for the engine to use them:
npclistbox - Listbox of NPC spoken text
npctext - Text field for NPC spoken text (Contained by npclistbox generally)
replieslistbox - Listbox of Player Reply options
skipdialogbutton - Button for skipping through the NPC spoken nodes
speakername - Text field for containing the speaker's name
portrait - UIPortrait object
All but the speakername and portrait fields are necessary or the window will not be loaded.
SCREEN_CUTSCENE = The fill screen cutscene view with the black bars on the top and bottom. This window is loaded like any other, then the following objects are searched for by the engine:
topbar - UIFrame for the top black bar
bottombar - UIFrame for the bottom black bar
FULLSCREEN_IMAGE - UIIcon used for full screen images.
toplistbox - Listbox to contain the text shown on top
toplistboxtext - Textfield contained by toplistbox
bottomlistbox - Listbox to contain the text shown on the bottom. Spoken by NPCs
bottomlistboxtext - Text field contained by bottomlistbox
replieslistbox - Listbox to contain the replies available to a player.
skipdialogbutton - Fullscreen button for clicking through the dialog
Failure to locate any of the above objects will result in the screen not loading.
SCREEN_CONTEXTMENU = The rightclick menu system. Technically, this gets loaded like any other XML file. I'll make another post later about the syntax and format of the contextmenu.xml file.
SCREEN_MINIMAP = The in-game minimap GUI. I don't know a lot about how the Minimap works. If further documentation is requested for it, I can research it further later.
SCREEN_AREAMAP = The in-game area map that can be brought up. I don't know a lot about how the Area Map works. If further documentation is requested for it, I can research it further later.
SCREEN_MESSAGEBOX_SPLITSTACK* = Scene used for splitting stacks of items in inventory. The only gui element required is 'inputbox'.
SCREEN_MESSAGEBOX_SPLITSTACKSTORE* = Scene used for splitting stacks of items for stores. It's actually loaded identically to the SCREEN_MESSAGEBOX_SPLITSTACK and only requires the 'inputbox' object to exist. I'm not sure why it got made into a seperate entry.
SCREEN_MESSAGEBOX* = This is used for the generic message box popups. I'll probably have to write up more on them another time as they can be used effectively by scripts as well. The required elements for these are:
messagetext = Text field that contains the message.
okbutton = Button that will execute the OK callback
cancelbutton = Button that will execute the Cancel callback
messageboxlb = Listbox for containing the message text in case the text gets long.
MSGBOX_BACKGROUND = Frame used for the background, this one is optional.
SCREEN_STRINGINPUT_MESSAGEBOX* = Message boxes that prompt for user string input. They are identical to normal Message boxes, except that they also require a 'inputbox' text field.
SCREEN_MESSAGE* = Chat boxes, pretty much. Note that currently the engine only supports loading the hard coded ones in ingamegui.ini. I hope to change this to be a lot more flexible down the line. The required elements are:
messagelistbox - Listbox containing the scrollback text. If it is not found, the engine searches for messagelistbox2 instead. If neither is found, the screen is not loaded.
inputbox - Text field used for user input.
IMEREadingWindow - Used for IME support.
IMEReadingWindowBG - Used for IME support.
IMEComposeWindow - Used for IME support.
IMEComposeWindow - Used for IME support.
IMECandidateWindow - Used for IME support.
IMECandidateWindowBG - Used for IME support.
INPUT_CONTAINER - Used by the engine for easily hiding/unhiding the input related objects.
SCREEN_HOTBAR, SCREEN_HOTBAR_2, SCREEN_HOTBAR_V1, SCREEN_HOTBAR_V2 = All the hotbars built into the game. There are no required or special UI elements needed to load these.
If a Screen Tag doesn't match to any of the above screens, then a normal UI Scene gets created with all the default behaviors associated with it.
Friday, February 9, 2007
In 1.05 there is a new attribute that all UI Objects handle called hideoverride. If true, this attribute sets a UI Object hidden and keeps it that way until a script says otherwise via the SetGuiObjectHidden() script function.
This can be used to keep an item hidden that the GUI may try to make visible regardless what a script tries to do, such as a row in a list box.
As a result, objects that are set hidden via SetGuiObjectHidden() will STAY hidden no matter what, unless SetGuiObjectHidden() is later used to make it visible.
For example, if I add a new icon to a screen and set it hideoverride=true in the XML, it will remain hidden on that screen no matter what the GUI might try to do with it until I call SetGuiObjectHidden() with a false parameter to make it visible again, at which point the GUI once again has control over making that object hidden or unhidden.
Wednesday, February 7, 2007
UIText is the GUI object used for containing and rendering text. It is one of the more complicated UI Objects to work with.
UIText takes the following attributes:
This defines which font should be used via a string argument. The fonts are defined in fontfamily.xml. The 'name' attribute for the different UIFontFamily elements in fontfamily.xml is how they are identified here. If this attribute is not set, then the text field will use the 'Default' font family defined in fontfamily.xml.
Which control should have focus if the user hits tab while editing this UIText field. It only really makes sense to put UIText field names as the arguments to this attribute, as focus doesn't have any significant meaning for any other type of UIObject. Note that the nextcontrol must be in the same UIScene as this text field.
Which control should have focus if the user hits shift+tab while editing this UIText field. It only really makes sense to put UIText field names as the arguments to this attribute, as focus doesn't have any significant meaning for any other type of UIObject. Note that the prevcontrol must be in the same UIScene as this text field.
This attribute can be used to limit what text the user can enter into this text field when editing it. By default, any input character is valid. It takes strings as its arguments. The following strings are handled:
alpha - Only letters can be entered in this field.
numeric - Only numbers can be entered in this field.
alphanumeric - Only letters and numbers can be entered in this field.
signed_numeric - Only numbers in this field, except the '-' character, which may only be entered at the front of the string.
This number limits the max number that a user can enter when editing this text field. It only means anything if the text field's filter is set to numeric or signed_numeric.
If this attribute is false, then any space characters will not be allowed in this field, including tabs or newlines. It defaults to false.
This attribute only means anything if the filter type is set to alpha or alphanumeric. It allows graphical punctuation characters to be entered in the text field. If no filter type is specified, then this attribute doesn't do anything. It defaults to false, so must be set to true if the intent is to allow users to enter punctuation style characters into this text field.
Defaults to false. If set to true, then the characters in this string will be replaced with * marks when this text field gets rendered.
Defaults to false. If this attribute is set to true, then any string entered in this text field will be run through the built in swear-filter.
This is a modifier to the font that is being used. It takes a string or number as its argument. The following arguments are supported:
normal - Or style 1. This is defined by the UIFontNormal element in fontfamily.xml
bold - Or style 2. This is defined by the UIFontBold element in fontfamily.xml
italic - Or style 3. This is defined by the UIFontItalic element in fontfamily.xml
bolditalic - Or style 4. This is defined by the UIFontBoldItalic element in fontfamily.xml
This is the number of pixels to indent the first line of any text. It defaults to 0.
This is the number of pixels to indent all lines besides the first line in a multi-line text field. It defaults to 0.
This is how the text should be aligned in the text field. The following string arguments are handled: left, right, center. Defaults to left.
Defaults to false. If set true, then the text in this text field will hilight when moused over.
This is how the text should be aligned vertically within the text field. The following string arguments are handled: top, bottom, middle. Defaults to top.
This attribute is defunct and no longer does anything.
Defaults to false. If set to true, then the text field will extend its height downward as far as possible in order to contain the length of text within it. This is an old attribute and should be replaced by just setting the height of the text field to DYNAMIC.
This only means anything if the text field is a child element of a UIButton element. If left to the default of false, then this text field will become the normal text field that shows up in buttons. If set to true, it becomes Overlay Text. At this time, Overlay Text can only be manipulated by the engine.
Defaults to false. If set to true, then the user can edit the text within this text field by clicking it to place their cursor and typing like normal.
This attribute does nothing at this time.
The max number of lines this text field can contain. Setting this attribute to '1' will keep the engine from trying to break the text into multiple lines. Text that will not fit into the text field after the maximum number of lines has been reached will not get rendered. If not defined, the engine will try to wrap a string and fit it into the space available no matter what, which can sometimes result in no text at all appearing due to spacing errors.
This attribute toggles whether or not a user can hit enter to start a new line of text within a editable text field. It requires that allowspaces be also set to true or else it will seem to do nothing. It defaults to false. When false, the OnReturn callback will get called when the user hits enter, instead of a new line being inserted.
This attribute can be a bit confusing. It defaults to false and only means anything when used with multiline=true. What happens is that if this attribute is set to 'true', the text field will accept multiple lines of input and wrap them automatically, but if the user hits their return key, the OnReturn callback gets called instead of inserting a newline. An example of this behavior can be found in the chat input box.
Defaults to false. If true, all text rendered in this text field will be rendered in uppercase.
This limits the number of characters that can be typed into a text field by the user. Once the user has entered the max limit of characters, no additional characters can be input. If no maxlength is set, the user can keep typing indefinitely.
A color to blend with all of the characters within the text field. This can sometimes conflict with the use of the color tags in text fields.
This attribute defines the color that should be blended with the text when doing a mouse-over style highlight.
This attribute does nothing at this time.
This is just a clone of the hilite attribute mentioned above.
This is for hard coding the text that should show up in this text field. Overrides strref.
This is for coding which string ref should show up in the text field. It is overridden by text.
This callback gets executed when the user hits Enter while the cursor is in this text field. Note that if the text field is set with multiline=true and restrictedreturn=false, then this callback will not get executed.
This callback is executed when the cursor is taken out of the text field due to the user clicking elsewhere.
UI Frame is used for borders and backgrounds for other UI Objects. UIFrames are imbedded in buttons and a few of the container style UI Objects that will be discussed later.
The following attributes are handled by UIFrame:
The following is a list of the texture pieces that make up a frame. They take a texture name as an argument. The texture pieces are:
topleft, topright, top, left, right, bottom, bottomleft, bottomright, fill.
The next set of attributes are methods of mirroring one piece to other pieces. This mirroring happens at render time.
Stands for MirrorHorizontalTop. Defaults to false. If true, the Top Left Corner will be mirrored to the Top Right Corner. If combined with mvright, then the Top Left Corner will be mirrored to the Right Bottom Corner.
Stands for MirrorHorizontalBottom. Defaults to false. If true AND mvright is true, then the Top Left Corner will get mirrored to the Bottom Right Corner. If true AND mvright is false, then the Bottom Left Corner will get mirrored to the Bottom Right Corner.
Stands for MirrorVerticalLeft. Defaults to false. If true, the Top Left Corner will get mirrored to the Bottom Left Corner. If true AND mhbottom is true, the Top Left Corner will get mirrored to the Bottom Right Corner.
Stands for MirrorVerticalRight. Defaults to false. If true AND mhtop is true, the Top Left Corner will get mirrored to the Bottom Right Corner. If true AND mhbottom is false, the Top Right Corner will get mirrored to the Bottom Right Corner.
Stands for MirrorHorizontalSide. Defaults to false. If true, the Left Side will get mirrored to the Right Side.
Stands for MirrorVeritcalSide. Defaults to false. If true, the Top Side will very mirrored to the Bottom Side.
Stands for MirrorAllSides. Defaults to false. If true, the Left Side will get mirrored to the Top Side, Right Side, and Bottom Side.
The remaining attributes supported by UIFrame follow:
This tells the engine to fill in the center space of the frame with the fill texture using different algorithms. This attribute takes the following string arguments:
center - Center the 'fill' texture.
stretch - Stretch the 'fill' texture to take up the full center.
tile - Repeat the 'fill' texture to fill up the center.
This is the thickness of the border pieces in pixels. Note that it defaults to 0. If no border thickness is set, then all the textures besides 'fill' won't show up when the UIFrame gets rendered.
This takes color strings to define the color that should be blended with the frame textures.
Note that if a UIFrame doesn't have a height or width defined by its attributes, it automatically inherits the height and width of whatever object contains it.
Wednesday, January 31, 2007
UIButton is the generic, clickable object one expects to find on any PC game interface. It also acts as a 'restricted' container of sorts, as it can contain specific UI objects within it.
A button is made up of several child objects contained in a single pane. These pieces can be defined explicitly or left up to the engine to define.
The child objects that a UIButton can contain are:
Up to 1 UIText field. If a UIText object is not defined as a child object in the XML, the engine will create one automatically when creating the UIButton.
Up to 11 UIFrames. A UIFrame contained within a button must have a state attribute that only applies when specifically contained within a button. The following arguments are valid for the state attribute:
These states can mean different things depending on the context of the button, so I'll just make some notes about a few of them. 'header', 'hiheader', and 'downheader' are the states for a button that is to be the header of a UICollapsable object, which will be explained further in a later article. 'base' is useful for when using state frames that leave some of the 'base' button visible, such as hotbar button icons.
An unlimited number of UIIcons that will be added to the 'overlay' list for that button. Overlays are extra icons that can be made visible by the engine that stay with the button. At this time, there is no way to manipulate these overlays via script.
UIButton supports the following attributes:
This is a 'style' name from the stylesheet.xml file that this button should adopt for its default parameters. At this time there is not support for custom styles.
If this attribute is set to true, the OnLeftClick callback will get executed every frame as long as the left mouse button is held down on it. On the first update the OnLeftClick will execute on the mousedown. There will then be a 0.5 second pause, then after that the OnLeftClick callback will be executed every frame until the left mouse button is released.
This callback is applicable to radio buttons and toggle boxes. If the button is a toggle box, this callback is executed when the toggle box is activated. If the button is a radio button, this callback is executed when the button becomes the selected radio button.
This callback is applicable to radio buttons and toggle boxes. If the button is a toggle box, this callback is executed when the toggle box is de-activated. If the button is a radio button, this callback is executed if thi sbutton was the selected radio button, but now another radio button has been selected.
This is a group number for radio buttons. Group IDs are global for the entire XML file that contains the button. That is to say, there can only be one group of radio buttons with the group id of '1', or '2', etc. All buttons that share the same group id form a 'set' of radio buttons. Only one button in that set can be selected at a time, all the rest are non-selected. Selecting any one button in the set will unselect the previously selected radio button.
This gives the UI Button a unique within a set of radio buttons. There should only be one button with a specific groupmemberid in a set of buttons. Ideally these IDs should start with 1 and increment from there, but that is not completely necessary.
This attribute accepts only 'radio' or 'check' as arguments, defining the button as a radio button or checkbox style button.
This attribute accepts color strings. These colors will blend with any other colors found in the textures of the button. So setting this to grey, for example, will make a grey-scale style icon for the button.
This attribute accepts color strings. This will be the blending color for when the button is put into a disabled state.
This attribute accepts color strings. This color will change the color of any text contained in the button's text field if the button is put into a disabled state.
This is a STRREF that represents the string that should appear in the button.
This is hard coded text that should appear in the button. It overrides the strref attribute if it is present.
UIPane is the generic 'container' for other UI Objects. Panes can't be rendered, but they can contain 'child' objects which can be renderable objects.
Every scene has a 'rootpane', which is automatically defined by the engine when a new UIScene is loaded from a XML file.
Child objects within a pane will be positioned with their x,y origin relative to the origin of the UI Pane that contains the child. UIPanes can also simulate buttons with the tupple attribute, so many of the attributes for UIPane are similar to those found under UIButton.
The following attributes are supported by UI Pane:
Sound effect to be played when the Pane is clicked on. This only means anything if the Pane is a tupple style pane.
Similar to a button up sound effect, this only means anything if the Pane is a tupple style pane.
Sound effect for dragging a tupple style UI Pane object.
Sound effect when releasing a dragged tupple style UI Pane object.
Setting this attribute to true makes the entire UI Pane behave like a button rather than just a container. This makes it possible to design more complicated buttons than normal, default UIButton tag allows for such as having a defined layout or multiple icons within the same button, etc. Once a Pane becomes a tupple, it can be clicked on, gain focus, lose focus, become enabled, become disabled, etc., just like a normal button. Note that it is still possible to place buttons within a tupple style pane that should work fine. So you could have a listbox row that is a tupple style pane that also has a toggle button on every row, for example.
The callback to be executed when a tupple style UI Pane is left clicked on.
The callback to be executed when a tupple style UI Pane is double left clicked on.
The callback to be executed when a tupple style UI Pane is right clicked on.
The callback to be executed when a tupple style UI pane is double right clicked on.