--- @type StdUi local StdUi = LibStub and LibStub('StdUi', true); if not StdUi then return end local module, version = 'Dropdown', 4; if not StdUi:UpgradeNeeded(module, version) then return end; local TableInsert = tinsert; -- reference to all other dropdowns to close them when new one opens local dropdowns = StdUiDropdowns or {}; StdUiDropdowns = dropdowns; local DropdownItemOnClick = function(self) self.dropdown:SetValue(self.value, self:GetText()); self.dropdown.optsFrame:Hide(); end local DropdownItemOnValueChanged = function(checkbox, isChecked) checkbox.dropdown:ToggleValue(checkbox.value, isChecked); end local DropdownMethods = { buttonCreate = function(parent) local dropdown = parent.dropdown; local optionButton; if dropdown.multi then optionButton = dropdown.stdUi:Checkbox(parent, '', parent:GetWidth(), 20); else optionButton = dropdown.stdUi:HighlightButton(parent, parent:GetWidth(), 20, ''); optionButton.text:SetJustifyH('LEFT'); end optionButton.dropdown = dropdown; optionButton:SetFrameLevel(parent:GetFrameLevel() + 2); if not dropdown.multi then optionButton:SetScript('OnClick', DropdownItemOnClick); else optionButton.OnValueChanged = DropdownItemOnValueChanged; end return optionButton; end, buttonUpdate = function(parent, itemFrame, data) itemFrame:SetWidth(parent:GetWidth()); itemFrame:SetText(data.text); if itemFrame.dropdown.multi then itemFrame:SetValue(data.value); else itemFrame.value = data.value; end end, ShowOptions = function(self) for i = 1, #dropdowns do dropdowns[i]:HideOptions(); end self.optsFrame:UpdateSize(self:GetWidth(), self.optsFrame:GetHeight()); self.optsFrame:Show(); self.optsFrame:Update(); self:RepaintOptions(); end, HideOptions = function(self) self.optsFrame:Hide(); end, ToggleOptions = function(self) if self.optsFrame:IsShown() then self:HideOptions(); else self:ShowOptions(); end end, SetPlaceholder = function(self, placeholderText) if self:GetText() == '' or self:GetText() == self.placeholder then self:SetText(placeholderText); end self.placeholder = placeholderText; end, RepaintOptions = function(self) local scrollChild = self.optsFrame.scrollChild; self.stdUi:ObjectList( scrollChild, scrollChild.items, self.buttonCreate, self.buttonUpdate, self.options ); self.optsFrame:UpdateItemsCount(#self.options); end, SetOptions = function(self, newOptions) self.options = newOptions; local optionsHeight = #newOptions * 20; local scrollChild = self.optsFrame.scrollChild; if not scrollChild.items then scrollChild.items = {}; end self.optsFrame:SetHeight(math.min(optionsHeight + 4, 200)); scrollChild:SetHeight(optionsHeight); self:RepaintOptions(); end, ToggleValue = function(self, value, state) assert(self.multi, 'Single dropdown cannot have more than one value!'); -- Treat is as associative array if self.assoc then self.value[value] = state; else if state then -- we are toggling it on if not tContains(self.value, value) then TableInsert(self.value, value); end else -- we are removing it from table if tContains(self.value, value) then tDeleteItem(self.value, value); end end end self:SetValue(self.value); end, SetValue = function(self, value, text) self.value = value; if text then self:SetText(text); else self:SetText(self:FindValueText(value)); end if self.multi then for _, checkbox in pairs(self.optsFrame.scrollChild.items) do local isChecked = false; if self.assoc then isChecked = self.value[checkbox.value]; else isChecked = tContains(self.value, checkbox.value); end checkbox:SetChecked(isChecked, true); end end if self.OnValueChanged then self.OnValueChanged(self, value, self:GetText()); end end, GetValue = function(self) return self.value; end, FindValueText = function(self, value) if type(value) ~= 'table' then for i = 1, #self.options do local opt = self.options[i]; if opt.value == value then return opt.text; end end return self.placeholder or ''; else local result = ''; for i = 1, #self.options do local opt = self.options[i]; if self.assoc then for key, checked in pairs(value) do if checked and key == opt.value then if result == '' then result = opt.text; else result = result .. ', ' .. opt.text; end end end else for x = 1, #value do if value[x] == opt.value then if result == '' then result = opt.text; else result = result .. ', ' .. opt.text; end end end end end if result ~= '' then return result else return self.placeholder or ''; end end end }; local DropdownEvents = { OnClick = function(self) self:ToggleOptions(); end }; --- Creates a single level dropdown menu --- local options = { --- {text = 'some text', value = 10}, --- {text = 'some text2', value = 11}, --- {text = 'some text3', value = 12}, --- } --- @return Dropdown function StdUi:Dropdown(parent, width, height, options, value, multi, assoc) --- @class Dropdown local dropdown = self:Button(parent, width, height, ''); dropdown.stdUi = self; dropdown.text:SetJustifyH('LEFT'); -- make it shorter because of arrow dropdown.text:ClearAllPoints(); self:GlueAcross(dropdown.text, dropdown, 2, -2, -16, 2); local dropTex = self:Texture(dropdown, 15, 15, [[Interface\Buttons\SquareButtonTextures]]); dropTex:SetTexCoord(0.45312500, 0.64062500, 0.20312500, 0.01562500); self:GlueRight(dropTex, dropdown, -2, 0, true); local optsFrame = self:FauxScrollFrame(dropdown, dropdown:GetWidth(), 200, 10, 20); optsFrame:Hide(); self:GlueBelow(optsFrame, dropdown, 0, 1, 'LEFT'); dropdown:SetFrameLevel(optsFrame:GetFrameLevel() + 1); dropdown.multi = multi; dropdown.assoc = assoc; dropdown.optsFrame = optsFrame; dropdown.dropTex = dropTex; dropdown.options = options; optsFrame.scrollChild.dropdown = dropdown; for k, v in pairs(DropdownMethods) do dropdown[k] = v; end if options then dropdown:SetOptions(options); end if value then dropdown:SetValue(value); elseif multi then dropdown.value = {}; end for k, v in pairs(DropdownEvents) do dropdown:SetScript(k, v); end TableInsert(dropdowns, dropdown); return dropdown; end StdUi:RegisterModule(module, version);