You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

233 lines
8.2 KiB

local _, addon = ...
--[[ namespace:CreateFrame(_..._)
A wrapper for [`CreateFrame`](https://warcraft.wiki.gg/wiki/API_CreateFrame), mixed in with `namespace.eventMixin`.
--]]
function addon:CreateFrame(...)
return Mixin(CreateFrame(...), addon.eventMixin)
end
local KEY_DIRECTION_CVAR = 'ActionButtonUseKeyDown'
local function updateKeyDirection(self)
if C_CVar.GetCVarBool(KEY_DIRECTION_CVAR) then
self:RegisterForClicks('AnyDown')
else
self:RegisterForClicks('AnyUp')
end
end
local function onCVarUpdate(self, cvar)
if cvar == KEY_DIRECTION_CVAR then
addon:Defer(updateKeyDirection, self)
end
end
--[[ namespace:CreateButton(...)
A wrapper for `namespace:CreateFrame(...)`, but will handle key direction preferences of the client.
Use this specifically to create clickable buttons.
--]]
function addon:CreateButton(...)
local button = addon:CreateFrame(...)
button:RegisterEvent('CVAR_UPDATE', onCVarUpdate)
-- the CVar doesn't trigger during login, so we'll have to trigger the handlers ourselves
onCVarUpdate(button, KEY_DIRECTION_CVAR)
return button
end
do -- scrollbox
local function defaultSort(a, b)
-- convert to string first so we can sort mixed types
return tostring(a) > tostring(b)
end
local function initialize(scroll)
if scroll._provider then
return
end
-- TODO: assertions
local provider = CreateDataProvider()
provider:SetSortComparator(scroll._sort or defaultSort, true)
local view
if scroll.kind == 'list' then
view = CreateScrollBoxListLinearView(scroll._insetTop, scroll._insetBottom, scroll._insetLeft, scroll._insetRight, scroll._spacingHorizontal)
elseif scroll.kind == 'grid' then
local width = scroll:GetWidth() - scroll.bar:GetWidth() - (scroll._insetLeft or 0) - (scroll._insetRight or 0)
local stride = math.floor((width - (scroll._spacingHorizontal or 0)) / (scroll._elementWidth + (scroll._spacingHorizontal or 0)))
view = CreateScrollBoxListGridView(stride, scroll._insetTop, scroll._insetBottom, scroll._insetLeft, scroll._insetRight, scroll._spacingHorizontal, scroll._spacingVertical)
view:SetStrideExtent(scroll._elementWidth)
end
view:SetDataProvider(provider)
view:SetElementExtent(scroll._elementHeight)
view:SetElementInitializer(scroll._elementType, function(element, data)
if scroll._elementWidth and scroll.kind == 'grid' then
element:SetWidth(scroll._elementWidth)
end
if scroll._elementHeight then
element:SetHeight(scroll._elementHeight)
end
if not element._initialized then
element._initialized = true
if scroll._scripts then
for script, callback in next, scroll._scripts do
element:SetScript(script, callback)
if script == 'OnEnter' and not scroll._scripts.OnLeave then
element:SetScript('OnLeave', GameTooltip_Hide)
end
end
end
if scroll._onLoad then
local successful, err = pcall(scroll._onLoad, element)
if not successful then
error(err)
end
end
end
element.data = data
if scroll._onUpdate then
local successful, err = pcall(scroll._onUpdate, element, data)
if not successful then
error(err)
end
end
end)
ScrollUtil.InitScrollBoxListWithScrollBar(scroll, scroll.bar, view)
ScrollUtil.AddManagedScrollBarVisibilityBehavior(scroll, scroll.bar) -- auto-hide the scroll bar
scroll._provider = provider
end
local scrollMixin = {}
function scrollMixin:SetInsets(top, bottom, left, right)
self._insetTop = top
self._insetBottom = bottom
self._insetLeft = left
self._insetRight = right
end
function scrollMixin:SetElementType(kind)
self._elementType = kind
end
function scrollMixin:SetElementHeight(height)
self._elementHeight = height
end
function scrollMixin:SetElementWidth(width)
self._elementWidth = width
end
function scrollMixin:SetElementSize(width, height)
self:SetElementWidth(width)
self:SetElementHeight(height or width)
end
function scrollMixin:SetElementSpacing(horizontal, vertical)
self._spacingHorizontal = horizontal
self._spacingVertical = vertical or horizontal
end
function scrollMixin:SetElementSortingMethod(callback)
self._sort = callback
end
function scrollMixin:SetElementOnLoad(callback)
self._onLoad = callback
end
function scrollMixin:SetElementOnScript(script, callback)
self._scripts = self._scripts or {}
self._scripts[script] = callback
end
function scrollMixin:SetElementOnUpdate(callback)
self._onUpdate = callback
end
function scrollMixin:AddData(...)
initialize(self)
self._provider:Insert(...)
end
function scrollMixin:AddDataByKeys(data)
for key, value in next, data do
if value then -- must be truthy
self:AddData(key)
end
end
end
function scrollMixin:RemoveData(...)
self._provider:Remove(...)
end
function scrollMixin:ResetData()
self._provider:Flush()
end
local function createScrollWidget(parent, kind)
local box = CreateFrame('Frame', nil, parent, 'WowScrollBoxList')
box:SetPoint('TOPLEFT')
box:SetPoint('BOTTOMRIGHT', -8, 0) -- offset to not overlap scrollbar
box.kind = kind
local bar = CreateFrame('EventFrame', nil, parent, 'MinimalScrollBar')
bar:SetPoint('TOPLEFT', box, 'TOPRIGHT')
bar:SetPoint('BOTTOMLEFT', box, 'BOTTOMRIGHT')
box.bar = bar
return Mixin(box, scrollMixin)
end
--[[ namespace:CreateScrollList(_parent_)
Creates and returns a scroll box with scroll bar and a data provider in a list representation.
It gets automatically sized to fill the space of the parent.
It provides the following methods, and is initialized whenever data is provided, so do that last.
* `list:SetInsets([top], [bottom], [left], [right])` - sets scroll box insets (all optional)
* `list:SetElementType(kind)` - sets the element type or template (required)
* `list:SetElementHeight(height)` - sets the element height (required)
* `list:SetElementSpacing(spacing)` - sets the spacing between elements (optional)
* `list:SetElementSortingMethod(callback)` - sets the sort method for element data (optional)
* `list:SetElementOnLoad(callback)` - sets the OnLoad method for each element (optional)
* the callback signature is `(element)`
* `list:SetElementOnUpdate(callback)` - sets the callback for element data updates (optional)
* the callback signature is `(element, data)`
* `list:SetElementOnScript(script, callback)` - sets the script handler for an element (optional)
* `list:AddData(...)`
* `list:AddDataByKeys(table)`
* `list:RemoveData(...)`
* `list:ResetData()`
--]]
function addon:CreateScrollList(parent)
return createScrollWidget(parent, 'list')
end
--[[ namespace:CreateScrollGrid(_parent_)
Creates and returns a scroll box with scroll bar and a data provider in a grid representation.
It gets automatically sized to fill the space of the parent.
It provides the following methods, and is initialized whenever data is provided, so do that last.
* `grid:SetInsets([top], [bottom], [left], [right])` - sets scroll box insets (all optional)
* `grid:SetElementType(kind)` - sets the element type or template (required)
* `grid:SetElementHeight(height)` - sets the element height (required)
* `grid:SetElementWidth(width)` - sets the element width (required)
* `grid:SetElementSize(width[, height])` - sets the element width and height, shorthand for the two above, height falls back to width if not provided
* `grid:SetElementSpacing(horizontal[, vertical])` - sets the spacing between elements, vertical falls back to horizontal if not provided (optional)
* `grid:SetElementSortingMethod(callback)` - sets the sort method for element data (optional)
* `grid:SetElementOnLoad(callback)` - sets the OnLoad method for each element (optional)
* the callback signature is `(element)`
* `grid:SetElementOnUpdate(callback)` - sets the callback for element data updates (optional)
* the callback signature is `(element, data)`
* `grid:SetElementOnScript(script, callback)` - sets the script handler for an element (optional)
* `grid:AddData(...)`
* `grid:AddDataByKeys(table)`
* `grid:RemoveData(...)`
* `grid:ResetData()`
--]]
function addon:CreateScrollGrid(parent)
return createScrollWidget(parent, 'grid')
end
end