From ESOUI Wiki
Work-in-progress docs can be founds at: /Docs
- When calling SetVertexColors on a Line, note that a Line's vertex points are named on the assumption that the line moves from left to right. If your line's anchors cause the line to move from top to bottom, then the "left" vertex points are the visible shape's top edge.
- Control:GetNamedChild doesn't work before a control has finished initializing; during the control's OnInitialized block, you must use GetControl(control, childName) instead.
- ESO does not handle sub-pixel positions or sizes consistently.
- If a formatting code has a start and an end (e.g. underlines, links), then there is a limit to how long the affected text can be. If the affected text is longer than 200 characters, then it will be truncated to about 253 characters and displayed improperly: the game may only display a handful of characters, or it may display a zero-length string entirely. If you are formatting strings of arbitrary length, you should split them at 200-character boundaries, format each chunk individually, and just put the formatted chunks back-to-back.
SCENE_MANAGERis an instance of
ZO_IngameSceneManager, defined here. You can modify the
SCENE_MANAGERinstance, but the class is file-local and therefore inaccessible.
HideTopLevelsmethod is what powers the Esc button, allowing you to hide all windows by pressing it.
- An EditBox cannot take focus if it is hidden.
- A Backdrop control should use a single texture for its edges. The texture is a horizontal strip of tiles; from left to right, the tile order is: left edge; right edge; top edge, but rotated to the same orientation as the left edge; bottom edge, but rotated to the same orientation as the right edge; upper left corner; upper right corner; lower left corner; lower right corner.
Notes that I should turn into an article at some point
Built-in format strings
- Used for raw unit names, i.e. LocalizeString(SI_UNIT_NAME, GetRawUnitName("player")).
It's a class which handles autocomplete for EditBoxes; the constructor accepts:
- An EditBox control
- A list of "include" flags; this defines the set of possible autocomplete items
- A list of "exclude" flags; this defines things that should never be an autocomplete item even if an "include" flag says it should be.
- An "onlineOnly" bool
- The maximum number of results to display
- Whether the autocomplete should open whenever the control is focused (AUTO_COMPLETION_AUTOMATIC_MODE) or only when your code tells it to (AUTO_COMPLETION_MANUAL_MODE). This argument can be omitted.
- An optional "allowArrows" bool
- An optional "dontCallHookedHandlers" bool
The "flags" are actually handlers defined here and registered via
ZO_AutoComplete.AddFlag. When your EditBox's input changes, a function loops over all of your "include" flags and calls them, passing these arguments:
- A table, the results argument, that is used to collect possible autocomplete matches
- An input argument, which is the text that has been entered into the textbox
- The onlineOnly flag specified when the ZO_AutoComplete instance was created
- The default "flag" functions use this for things like only allowing online friends' account names to be autocompleted
- An include argument, which indicates whether your flag is being used to include or exclude
If you look at the vanilla flag definitions, you can see how they use a function called
ZO_AutoComplete.IncludeOrExcludeResult to include or exclude results as appropriate. If you wish to add your own set of auto-complete options, then, you do so by defining your own flag. A word of warning, however: if a ZO_AutoComplete field uses
AUTO_COMPLETE_FLAG_ALL, then your custom flags will be included in that field! You should probably have your flags return immediately whenever they're not meant to be used (this may be complicated somewhat by the fact that your handler won't receive the ZO_AutoComplete instance or the attached EditBox as an argument).
If you look at the source code for ZO_AutoComplete, you'll notice that it doesn't create any controls or use any templates. Rather, it uses the following API functions:
These APIs are responsible for showing or hiding the list of autocomplete options as a drop-down menu; the ZO_AutoComplete class relies entirely on a hardcoded menu design. There are other Zenimax widgets that use XML-/Lua-defined controls as a menu instead. As these functions aren't documented yet, the precise operational definition of "a menu" remains unknown. Is it a widget that appears overtop everything else, without being tethered to a specific TopLevelControl? Is it a widget with specific behaviors, or tied to a specific event system? Something else?
- Xbox A button in text
These are the results of performance tests that I've run on basic operations. In many cases, the performance difference won't be significant — a single operation may complete in less than a millisecond no matter how you do it — but it's still worth knowing what code is the fastest, especially if you're working with relatively large data sets.
What's the fastest way to get the length of an array? If you're just starting out with add-on development, and learning from existing code, then you might be surprised by the answer!
Before we begin, though, let's define our terms. Lua doesn't have "arrays as first-class members," which is to say: there is no built-in array type. A single type, "table," provides the foundation for sets, maps, and arrays. For this discussion, then, an "array" is a table that meets the following definitions:
- It has integer keys
- We only care about the integer keys
- The lowest integer key is 1
- The integer keys are contiguous, i.e. there aren't any gaps in them.
- The "length" of the array is its highest integer key, given that again, there are no gaps in the middle
The four approaches we're going to test for getting the length are:
- table.getn i.e.
- the # operator i.e.
- using the # operator on a "sealed" array — that is, an array that we have tampered with by:
- caching its length in a key
- assigning a metatable...
__lenfunction returns that key's value (altering the behavior of
__newindexfunction prevents the adding of any new keys by just returning immediately
- directly accessing the cached length of our frozen array
If we run each method 10,000,000 times in a row, we come up with the following times:
|Method||Result for a 15-length array||Result for a 1500-length array|
|table.getn||0.460 seconds||0.512 seconds|
|# (normal)||0.099 seconds||0.213 seconds|
|# (sealed)||0.133 seconds||0.203 seconds|
|sealed length||0.138 seconds||0.128 seconds|
The performance variation isn't significant for one single attempt at getting an array's length. If you were to use the slowest method,
table.getn, to get the length of one array that has fifteen elements, then it would take just 0.000000046 seconds. Still,
# is built-in, faster to type, and faster to execute.
A few other stats:
- When testing a 1500-length array, we have to benchmark 12,500 length checks before times (other than table.getn) start consistently exceeding 0ms.
- When testing a 15-length array, we have to benchmark 25,000 length checks before times (other than table.getn) start consistently exceeding 0ms.
What's the fastest way to insert 15 elements into an empty array?
for i = 1, 15 do table.insert(dummy, i) end
It takes 0.164 seconds to execute this 50,000 times in a row.
myTable[#myTable + 1] = ...
for i = 1, 15 do dummy[#dummy + 1] = i end
It takes 0.143 seconds to execute this 50,000 times in a row.
Note, however, that there are situations where # may be slower: extremely long arrays and things that only recently became arrays (e.g. inserting with keys in reverse order) come to mind.
myTable[i] = ...
for i = 1, 15 do dummy[i] = i end
It takes 0.132 seconds to execute this 50,000 times in a row.