-
-
Notifications
You must be signed in to change notification settings - Fork 317
Custom Dynamic Group
WeakAuras 2.12 introduces the ability to write custom functions for a dynamic group's Grow and Sort options, which will allow you to define what your group should look like very precisely.
WeakAuras 2.14 extended it to group and anchor regions by frames.
In a dynamic group, aura_env
has a slightly different meaning. There is no state information, so aura_env.state
is always nil. Additionally, there is no such thing as a cloned group, so aura_env.cloneId
is always nil. There is a new value which is provided to you, which can be accessed at aura_env.child_envs
. This is a table which contains all of the aura_env
tables of the group's children, in dataIndex order.
Both custom sort and custom grow will give you some special tables, which are called regionData (for the simple reason that they contain the region and the data). RegionData is structured as follows:
regionData = {
id = "string", -- name of the child,
cloneId = value, -- the cloneId. Usually string, but can be any value, including nil. If this child does not clone, then this value will be nil or empty string "".
dataIndex = 1, -- number which indicates the order the child appears in the options Pane, starting at 1.
data = {...}, -- table containing data with which to construct the child's aura
region = {...}, -- the region object which was built from the data for this child.
xOffset = 0, -- x offset from previous layout function execution. This may be nil on the first layout.
yOffset = 0, -- y offset from previous layout function execution. This may be nil on the first layout.
show = true, -- Boolean indicating if the region was allowed to be shown (note that this is not the same as a region being active)
}
Note: regionData objects are intended to be read only, but for performance reasons this is not enforced. Writing to the regionData object will result in undefined behavior.
Custom Sort is the more straightforward of the two new functions. You are given two regionData objects, and are expected to return true if the second must be sorted before the first:
function(a, b)
-- this is roughly equivalent to the "None" sort option
return a.dataIndex <= b.dataIndex
end
The underlying code for the builtin sort options uses a compositional system, which can be repurposed for custom code if you wish. In most cases this will lead to code that is much easier to write and maintain, particularly when the values which you are sorting on don’t necessarily exist.
The code of compositional sorting relies on a few concepts:
-
comparator
- A function which accepts two parameters, and returns
true
,false
, ornil
. A result oftrue
indicates that the arguments must be swapped, a result offalse
indicates that the arguments must not be swapped. A result ofnil
indicates an indeterminate result, either because the value are equal, or the values cannot be compared using this comparator. Note that the custom sort function itself is a kind of comparator, and WeakAuras will interpret anil
result in this case as being equivalent tofalse
. - A comparator can compare any two values, not just regionData objects. This is useful to simplify comparing values deep in the regionData objects, see the documentation on SortRegionData for more information.
- A function which accepts two parameters, and returns
-
path
- A list-like table consisting of strings, used as a locator for values in a regionData object. For example, to access
regionData.region.state.index
, the appropriate path is{"region", "state", "index"}
- A list-like table consisting of strings, used as a locator for values in a regionData object. For example, to access
... and is supported by a few API functions:
- WeakAuras.ComposeSorts(...)
- takes as arguments a variadic list of comparator functions, and returns a new comparator which composes them in the order they were passed in.
- This new comparator operates the following loop, which produces the effect that earlier comparators have higher "priority":
- Get first comparator from variadic list
- Run sub-comparator, and check result.
- If nil, repeat step 2 with next comparator from variadic list.
- If not nil, return result.
- WeakAuras.SortRegionData(path, valueComparator)
- takes as first argument a path to retrieve values with from the region data, and as second argument a comparator to sort said values with, and returns a comparator suitable for use as a custom sort function. This helper helps you abstracts away the tedium of accessing necessary information from the regionData, which is a common pain point with traditional sort functions.
- WeakAuras.InvertSort(comparator)
- takes as argument a single comparator, and returns a new comparator which has precisely the opposite behavior. Specifically, the new comparator is true exactly when the old one is false, false when it is true, and nil when it is nil.
- WeakAuras.SortNilFirst
- A builtin comparator which stably moves nil values first. Returns nil if and only if both parameters are not nil. For this reason, SortNilFirst is useful to place first in a sort composition, in order to guarantee that lower priority comparators receive well-formed data.
- WeakAuras.SortNilLast
- Inverted version of WeakAuras.SortNilFirst.
- WeakAuras.SortGreaterLast
- A builtin comparator which is effectively the "<" operator.
- WeakAuras.SortGreaterFirst
- Inverted Version of WeakAuras.SortGreaterLast
- WeakAuras.SortAscending(path)
- Alias for
WeakAuras.SortRegionData(path, WeakAuras.ComposeSorts(WeakAuras.SortNilFirst, WeakAuras.SortGreaterLast))
- Alias for
- WeakAuras.SortDescending(path)
- Inverted Version of WeakAuras.SortAscending(path).
Here are a few examples:
WeakAuras.SortDescending {"region", "state", "stacks"}
WeakAuras.ComposeSorts(
WeakAuras.SortAscending{"region", "state", "stacks"},
WeakAuras.SortDescending{"region", "state", "expirationTime"}
)
WeakAuras.SortRegionData(
{"region", "state", "stacks"},
WeakAuras.ComposeSorts(
WeakAuras.SortNilFirst,
function(a, b)
if a ~= 0 and b ~= 0 then
-- both belong in the "nonzero" section, return nil to let next comparator run
return nil
elseif a == 0 then
-- a belongs in the "zero" section and is already fist, so don't swap to avoid creating an unstable sort
return false
else
-- b == 0 and a ~= 0, a swap is needed
return true
end
end,
WeakAuras.SortGreaterFirst
)
)
Custom Grow is slightly more involved, but still straightforward. You are given 2 parameters. The first is an empty table which is expected to be filled with positioning data, and the second's is a table containing the regionData of all active children, in sorted order (note that positioning always occurs after sorting). You are not expected to return anything, and anything you do return from this function are ignored.
function(newPositions, activeRegions)
-- this function will produce a parabola shape
local mid = #activeRegions / 2
for i = 1, #activeRegions do
newPositions[i] = {
40 * (i - mid),
0.5 * (i - mid)^2
}
end
end
If a child is not given any position data, then it is hidden, and moved to position 0, 0. You may hide a child at a particular spot other than 0, 0 (useful if you use the animated expand/collapse option) by setting the third value in the position data to false
.
Since WeakAuras 2.14 Custom Grow function can also be used to group and anchor children per frame
function(newPositions, activeRegions)
-- make a list of regionData for each frame
local frames = {}
for _, regionData in ipairs(activeRegions) do
local unit = regionData.region.state and regionData.region.state.unit
if unit then
local frame = C_NamePlate.GetNamePlateForUnit(unit)
if frame then
frames[frame] = frames[frame] or {}
tinsert(frames[frame], regionData)
end
end
end
for frame, regionsData in pairs(frames) do
local totalWidth = #regionsData - 1
for _, regionData in ipairs(regionsData) do
totalWidth = totalWidth + (regionData.data.width or regionData.region.width)
end
local x, y = - totalWidth/2, - (#regionsData - 1)/2
newPositions[frame] = {}
for i, regionData in ipairs(regionsData) do
x = x + (regionData.data.width or regionData.region.width) / 2
newPositions[frame][regionData] = { x, y }
x = x + (regionData.data.width or regionData.region.width) / 2
end
end
end
With WeakAuras 5.3.6 as a measure to improve performance, dynamic groups are now required to declare which parts of state they depend on to function correctly. You will see this as a new input box above a custom grow/sort codebox, labeled "Run On...". Write into this box a comma-separated list of every state key your custom layout consumes.
For example, if you have a custom sort like this:
WeakAuras.SortAscending{"region", "state", "myKey"}
then you should add myKey
to the "Run On..." box in order to guarantee your sorting continues to function correctly. As an escape hatch, the special key changed
acts as a wildcard and causes WeakAuras to always trigger your custom sort & grow functions. We recommend you avoid this if possible, however, since it eliminates any opportunity for WeakAuras to optimize its performance.
The Group by Frame option use pre-built growers functions to group and anchor regions to frames.
Nameplates
to nameplates found from state.unit
.
Unit Frames
to unit frames found from state.unit
.
Custom Frames
give you control over which frame each region should be anchored to.
function(frames, activeRegions)
for _, regionData in ipairs(activeRegions) do
local unit = regionData.region.state and regionData.region.state.destUnit
if unit then
local frame = C_NamePlate.GetNamePlateForUnit(unit)
if frame then
frames[frame] = frames[frame] or {}
tinsert(frames[frame], regionData)
end
end
end
end
- Home
- API Documentation
- Getting Involved
- Setting up a Lua Dev Environment
- Deprecations
- Useful Snippets
- Aura Types
- Trigger Types
- Triggers and Untriggers
- Aura Activation
- Dynamic Information
- Text Replacements