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.

180 lines
7.2 KiB

local _, T = ...
local XU, int, ScrollableBlockData, positionArchive, hoverWatcher = T.exUI, {}, {}, setmetatable({}, {__mode="k"}), nil
local MIN_SCROLL_ENTRIES, MAX_VISIBLE_ENTRIES, WHEEL_STEP = 20, 16, 8
local assert, getWidgetData, _, setWidgetData = XU:GetImpl()
DropDownList1:HookScript("OnHide", function() wipe(positionArchive) end)
local getFreeBlock do
local pool = {}
local function releaseBlock(block)
local d = getWidgetData(block, ScrollableBlockData)
if d then
block:Hide()
block:SetParent(nil)
block:ClearAllPoints()
pool[block], d.dataList, d.entryFormat, d.entrySelect = 1, nil
end
end
local function newBlock()
local block = CreateFrame("Frame")
block:Hide()
block:SetScript("OnHide", releaseBlock)
block:SetScript("OnMouseWheel", int.OnMouseWheel)
block:SetHitRectInsets(-4, -24, -8, -8)
local bb = block:CreateTexture(nil, "BACKGROUND")
bb:SetColorTexture(0.3, 0.3, 0.3)
bb:SetPoint("BOTTOMLEFT", -4, -0.5)
bb:SetPoint("BOTTOMRIGHT", 0, -0.5)
local clipRoot = CreateFrame("Frame", nil, block)
clipRoot:SetAllPoints()
clipRoot:SetClipsChildren(true)
local scrollOrigin = CreateFrame("Frame", nil, clipRoot)
scrollOrigin:SetPoint("TOPLEFT")
scrollOrigin:SetPoint("TOPRIGHT")
scrollOrigin:SetHeight(1)
local scrollBar = XU:Create("ScrollBar", nil, block)
scrollBar:SetPoint("TOPRIGHT", -1, 0)
scrollBar:SetPoint("BOTTOMRIGHT", -1, 0)
scrollBar:SetScript("OnValueChanged", int.OnScrollChanged)
scrollBar:SetWindowRange(MAX_VISIBLE_ENTRIES)
scrollBar:SetStepsPerPage(MAX_VISIBLE_ENTRIES-2)
local buttons = {}
for i=1,MAX_VISIBLE_ENTRIES+1 do
local b = CreateFrame("CheckButton", nil, clipRoot, nil, i)
b:SetSize(100, 16)
b:SetPoint("TOPLEFT", scrollOrigin, 0, 16-16*i)
b:SetPoint("TOPRIGHT", scrollOrigin, -16, 16-16*i)
b:SetScript("OnClick", int.OnEntryClick)
b:SetHighlightTexture([[Interface\QuestFrame\UI-QuestTitleHighlight]])
b:SetCheckedTexture([[Interface\Common\UI-DropDownRadioChecks]])
b:SetNormalTexture([[Interface\Common\UI-DropDownRadioChecks]])
b:SetNormalFontObject(GameFontHighlightSmallLeft)
b:SetDisabledFontObject(GameFontHighlightSmallLeft)
b:SetText("The Fifth Surprise")
b:GetFontString():ClearAllPoints()
b:GetFontString():SetPoint("LEFT", 22, 0)
local h, c, n = b:GetHighlightTexture(), b:GetCheckedTexture(), b:GetNormalTexture()
h:SetBlendMode("ADD")
h:SetAllPoints()
c:SetTexCoord(0, 0.5, 0.5, 1)
c:ClearAllPoints()
c:SetSize(16,16)
c:SetPoint("LEFT", 3, 0)
n:SetTexCoord(0.5, 1, 0.5, 1)
n:ClearAllPoints()
n:SetSize(16,16)
n:SetPoint("LEFT", 3, 0)
buttons[i] = b
end
local d = {root=block, clip=clipRoot, origin=scrollOrigin, scrollBar=scrollBar, buttons=buttons, bottomTex=bb}
setWidgetData(block, ScrollableBlockData, d)
return block
end
function getFreeBlock()
local r = next(pool) or newBlock()
pool[r] = nil
return r, getWidgetData(r, ScrollableBlockData)
end
end
function int:OnEntryClick()
local d = getWidgetData(self:GetParent():GetParent(), ScrollableBlockData)
local entrySelect, arg1 = d.entrySelect, d.dataList[self:GetID()]
PlaySound(SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON)
CloseDropDownMenus()
entrySelect(nil, arg1)
end
function int:OnMouseWheel(delta)
local d = getWidgetData(self, ScrollableBlockData)
d.scrollBar:Step(-delta*WHEEL_STEP, true)
int.SyncToBar(d, false)
end
function int:OnScrollChanged(_, internalEvent)
if internalEvent then
int.SyncToBar(getWidgetData(self:GetParent(), ScrollableBlockData), false)
end
end
function int.SyncToBar(d, fullSync)
local buttons, scrollBar, dataList, entryFormat = d.buttons, d.scrollBar, d.dataList, d.entryFormat
local position, isDone = scrollBar:GetValue(), scrollBar:IsValueAtRest()
positionArchive[dataList] = position
local oy, baseOffset = (position % 1)*16, math.floor(position)
d.origin:SetPoint("TOPLEFT", 0, oy)
d.origin:SetPoint("TOPRIGHT", 0, oy)
for i=1,#buttons do
local w, eid = buttons[i], i+baseOffset
local ek = dataList[eid]
w:SetShown(ek ~= nil)
if ek ~= nil then
if fullSync or w:GetID() ~= eid then
local text, selected = entryFormat(ek, dataList)
w:SetText(text)
w:SetChecked(selected)
w:SetID(eid)
w:GetFontString():GetLeft() -- BUG[8.1.5] Without this, it sometimes gets lost.
end
w:SetEnabled(isDone)
end
end
end
if UIDropDownMenu_StopCounting then
hoverWatcher = CreateFrame("Frame", nil, DropDownList1)
hoverWatcher:Hide()
hoverWatcher:SetScript("OnUpdate", function(self)
for l=(DropDownList1.isCounting or self.suspended) and UIDROPDOWNMENU_MENU_LEVEL or 0, 1, -1 do
local m = _G["DropDownList" .. l]
if m and m:IsMouseOver() then
self.suspended = true
return DropDownList1.isCounting and UIDropDownMenu_StopCounting(DropDownList1)
end
end
self.suspended = self.suspended and UIDropDownMenu_StartCounting(DropDownList1) and nil
end)
hoverWatcher:SetScript("OnHide", function(self) self.suspended = nil; self:Hide() end)
end
local function DisplayScrollableDropDownEntryList(level, dataList, entryFormatter, entrySelect, willContinue)
assert(type(level) == "number", 'ScrollableDropDownList: level argument must be a number')
assert(type(dataList) == "table", 'ScrollableDropDownList: dataList argument must be a table')
assert(type(entryFormatter) == "function", 'ScrollableDropDownList: entryFormatter argument must be a function')
assert(type(entrySelect) == "function", 'ScrollableDropDownList: entrySelect argument must be a function')
if #dataList < MIN_SCROLL_ENTRIES then
local info = {func=entrySelect, minWidth=level == 1 and UIDROPDOWNMENU_OPEN_MENU:GetWidth()-40 or nil}
for i=1,#dataList do
local k = dataList[i]
info.arg1, info.text, info.checked = k, entryFormatter(k, dataList)
UIDropDownMenu_AddButton(info, level)
end
return
end
local ddlName, maxV, arch = "DropDownList" .. level, #dataList-MAX_VISIBLE_ENTRIES, positionArchive[dataList]
local host, block, d = _G[ddlName], getFreeBlock()
block.parent, d.dataList, d.entryFormat, d.entrySelect = host, dataList, entryFormatter, entrySelect
block:SetParent(host)
local n1, nX = host.numButtons+1, MAX_VISIBLE_ENTRIES + host.numButtons
local minWidth, testFS = math.max(120, level == 1 and UIDROPDOWNMENU_OPEN_MENU:GetWidth()-40 or 0), d.buttons[1]:GetFontString()
for i=1,#dataList do
local text = entryFormatter(dataList[i], dataList)
testFS:SetText(text)
minWidth = math.max(minWidth, 60 + testFS:GetStringWidth())
end
local info = {notClickable=true, notCheckable=true, minWidth=minWidth}
for i=n1,nX do
UIDropDownMenu_AddButton(info, level)
end
local b1, bX = _G[ddlName .. "Button" .. n1], _G[ddlName .. "Button" .. nX]
block:SetPoint("TOPLEFT", b1, 0, n1 == 1 and 5 or 0)
d.clip:SetPoint("TOPLEFT", 0, n1 == 1 and -5 or 0)
block:SetPoint("BOTTOMRIGHT", bX, "BOTTOMRIGHT", 0, willContinue and 0 or -4)
d.scrollBar:SetMinMaxValues(0, maxV)
d.scrollBar:SetValue(arch and arch <= maxV and arch or 0)
int.SyncToBar(d, true)
d.bottomTex:SetHeight(PixelUtil.GetPixelToUIUnitFactor()/block:GetEffectiveScale()*1.001)
d.bottomTex:SetShown(not not willContinue)
block:Show()
block:SetFrameLevel(b1:GetFrameLevel()+2)
d.scrollBar:SetFrameLevel(block:GetFrameLevel()+3)
if hoverWatcher then hoverWatcher:Show() end
end
XU:RegisterFactory("ScrollableDropDownList", DisplayScrollableDropDownEntryList)