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
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)
|