-
Notifications
You must be signed in to change notification settings - Fork 2
Drawing UI XML vs LUA
Based on first party IMC code, it seems to be the norm to draw and modify UI frames and controls through XML to define the look and feel of a frame. However, drawing UI purely with LUA is also totally viable, and I even may recommend it for third party addons.
The problem really is the available dev tools. We addon developers currently have no good way to test xml changes without having to repackage an ipf, restart game, reload the addon, and repeat. On the other hand, Developer Console
addon allows us to reload LUA files, meaning that we can easily try making changes to the UI elements and test the changes in a very short amount of time. While developing an addon with its own frame FarmTracker, I ran into a really tough debugging experience just to get width and height correct. And at the end when I just needed to add an extra button and increase the height of the frame a bit, I gave up modifying the XML and just decided to make the change through code.
This page aims to document XML vs LUA differences.
<uiframe name="sequentialpickitem" x="0" y="0" width="380" height="135" create="open">
<frame layout_gravity="right bottom" margin="0 0 400 250"/>
<draw drawtitlebar="false" drawframe="false"/>
<option hideable="false" closebutton="false"/>
<input hittest="false"/>
<script OpenScp="SEQUENTIALPICKITEM_OPEN" CloseScp="SEQUENTIALPICKITEM_CLOSE"/>
<animation frameOpenAnim="sequentialpickitem_open" frameCloseAnim="sequentialpickitem_close"/>
<userconfig POPUP_DURATION="1.5" PUSHUP_ANIM_NAME="sequentialpickitem_pushup"/>
<layer layerlevel="120"/>
<controls>
</controls>
</uiframe>
<uiframe name="sequentialpickitem" x="0" y="0" width="0" height="0" create="open">
</uiframe>
local frame = ui.GetFrame('sequentialpickitem');
frame:MoveFrame(0, 0);
frame:Resize(380, 135);
-- create="open" and hud="true" XML attribute equivalent in LUA is unknown, probably doesn't exist. This needs to be set through XML.
frame:SetGravity(ui.RIGHT, ui.BOTTOM);
frame:SetMargin(0, 0, 400, 240);
frame:ShowTitleBar(0);
frame:ShowFrame(0); -- not sure
frame:EnableHide(0);
frame:EnableCloseButton(0);
frame:EnableHitTest(0);
frame:SetOpenScript(SEQUENTIALPICKITEM_OPEN);
frame:SetCloseScript(SEQUENTIALPICKITEM_CLOSE);
-- frame:SetAnimation(); cannot find usage, either the following might work
-- fadein:SetAnimation("openAnim", "sequentialpickitem_open"); OR fadein:SetAnimation("frameOpenAnim", "sequentialpickitem_open");
frame:SetUserConfig("POPUP_DURATION", 1.5);
frame:SetUserConfig("PUSHUP_ANIM_NAME", "sequentialpickitem_pushup");
frame:SetLayerLevel(120);
<uiframe ...>
...
<controls>
<groupbox name="pickitem" rect="0 0 380 135" layout_gravity="left top" container="true" draw="true" scrollbar="false" skin="normal_item_tootip_skin"/>
<picture name="pickitemslot" layout_gravity="left top" parent="pickitem" rect="0 0 380 135" margin="15 10 0 0" image="jour_new_item1"/>
</controls>
</uiframe>
local frame = ui.GetFrame('myframe');
-- you can also use frame:CreateControl() with same arguments, but CreateOrGetControl ensures control is created only once with the given unique name
local addontimer = frame:CreateOrGetControl("timer", "addontimer", 10, 10); -- args: type, name, width, height
local tipText = frame:CreateOrGetControl("richtext", "n_tip", 0, 0, 0, 0); -- args: type, name, ?
local btnReset = frame:CreateOrGetControl("button", "reset", 60, 30, ui.RIGHT, ui.BOTTOM, 0, 0, 15, 8); -- args: type, name, width, height, horiz align, verti align, margins
As good standard and convention, I recommend you always use the fully overloaded CreateOrGetControl()
variant as it helps you properly constrain the size of the UI elements as necessary.
Below is the documentation of all common attributes that are used.
Any attributes that are not documented below are either:
- Usage unknown
- Probably not important
Attribute | Description |
---|---|
name |
Unique identifier of the frame. Set to lowercase name of the addon. |
x |
Horizontal position of the frame |
y |
Vertical position of the frame |
width |
Width of the frame |
height |
Height of the frame |
layout_gravity |
Combination of Horizontal alignment + Vertical alignment. However, a frame's gravity is recommended to be ui.LEFT and ui.TOP
|
moveable |
Can drag to move the frame |
hittestframe |
HitTest is similar to "Enabled/Disabled" in various other UI frameworks. ie: if you have done iOS/Android UI or WPF/Winform development, a disabled control cannot be interacted with, clicked, modified, resized, etc. HitTest is a very similar concept. Click, drag, mouse over (hover), etc are all disabled, even drag to move becomes disabled. |
layerlevel |
Z position of the frame. Higher value means it will be displayed over other frames with lower value. |
drawframe |
Frame is not shown if false
|
frameskin |
Skin used for the frame. There will probably be an associated .tga file |
visible |
Frame is displayed. Use frame:ShowWindow(0 or 1) to programmatically set visibility. This seems to force frame visibility to 1 even when unwanted sometimes. I recommend always setting to false in XML, and programmatically set to true ON_INIT if you want frame to always be visible. |
alwaysVisible |
When set to true , addon stays visible during cutscenes. Should usually be set to false . |
autoopen |
frame that is closed during cutscene is re-opened after cutscene |
hideable |
Not sure. Supposed to prevent frame:ShowWindow(0) but it seems to be hiding frames fine. |
closebutton |
When set to true a close button is displayed on title bar. But this should not be used, explained right below: |
titlebar related stuff | Titlebar is a control?ish thing usually only used by NPC dialog. Most, if not all, frames, including frames that seem to have title, like Inventory, also does not use a frame's titlebar attributes. Should always be set to false. drawtitlebar and drawtitlebarframe are some of those examples. |
When you look at many first party and third party addon.xml files, you may be quite overwhelmed at all of the different attributes and properties that just seem to make no sense. And my advice is just set everything to false
, or just don't declare anything at all.
<uiframe name="myaddon" x="0" y="0" width="0" height="0">
<draw drawtitlebar="false"/>
<option visible="false" alwaysVisible="false" closebutton="false" hideable="false" moveable="true" autoopen="true"/>
<input hittestframe="true"/>
<layer layerlevel="10"/>
<controls>
</controls>
</uiframe>
The above is in my opinion, the standard XML of all addons that should not introduce any unwanted behavior. And I recommend only making modifications to the frame's properties through code in ON_INIT
rather than in the XML to emphasize that you want to deviate from the recommended baseline options.