local _, S = ... local pairs, ipairs, string, type, time = pairs, ipairs, string, type, time S.Dropdown = {} local ENTRY_HEIGHT = 24 local MAX_HEIGHT = 400 -- Make a frame just below the dropdown menu that fills the screen -- When clicked it hides the dropdown menu -- Essentially adds GLOBAL_MOUSE_DOWN functionality to Classic local screen = CreateFrame("FRAME", nil, UIParent) screen:SetFrameStrata("DIALOG") screen:SetFrameLevel(1) screen:SetAllPoints() screen:SetScript("OnMouseDown", function(self) SortedDropdownMenu:Hide() self:Hide() end) screen:Hide() local dropdownMenu = CreateFrame("FRAME", "SortedDropdownMenu", UIParent) S.dropdownMenu = dropdownMenu table.insert(UISpecialFrames, "SortedDropdownMenu") dropdownMenu:SetFrameStrata("DIALOG") dropdownMenu:SetFrameLevel(10) dropdownMenu:SetClampedToScreen(true) dropdownMenu:SetScript("OnShow", function(self) screen:Show() end) dropdownMenu:SetScript("OnHide", function(self) screen:Hide() end) function dropdownMenu:UpdateScale() dropdownMenu:SetScale(S.Settings.Get("scale")) end S.Utils.RunOnEvent(dropdownMenu, "SettingChanged-scale", dropdownMenu.UpdateScale) dropdownMenu.scrollFrame = CreateFrame("SCROLLFRAME", nil, dropdownMenu) dropdownMenu.scrollFrame:SetPoint("TOPLEFT") dropdownMenu.scrollFrame:SetPoint("BOTTOM") dropdownMenu.content = CreateFrame("FRAME", nil, dropdownMenu.scrollFrame) dropdownMenu.scrollFrame:SetScrollChild(dropdownMenu.content) dropdownMenu.scrollBar = CreateFrame("Slider", nil, dropdownMenu.scrollFrame, "MinimalScrollBarTemplate") dropdownMenu.scrollBar.trackBG:Hide() dropdownMenu.scrollBar:SetPoint("TOPLEFT", dropdownMenu.scrollFrame, "TOPRIGHT", 1, -18) dropdownMenu.scrollBar:SetPoint("BOTTOM", 0, 16) dropdownMenu.scrollBar:SetMinMaxValues(1, 1) dropdownMenu.scrollBar:SetValueStep(1) dropdownMenu.scrollBar.scrollStep = 16 dropdownMenu.scrollBar:SetValue(0) local inset = 6 local outset = 4 dropdownMenu.bg = dropdownMenu:CreateTexture() dropdownMenu.bg:SetPoint("TOPLEFT", inset, -inset) dropdownMenu.bg:SetPoint("BOTTOMRIGHT", -inset, inset) dropdownMenu.bg:SetTexture("Interface\\Addons\\Sorted\\Textures\\Dropdown-Border") dropdownMenu.bg:SetTexCoord(0.49,0.5,0.49,0.5) --dropdownMenu.bg:SetColorTexture(0.04, 0.034, 0.035, 0.95) S.FrameTools.AddBorder(dropdownMenu, "border", "Interface\\Addons\\Sorted\\Textures\\Dropdown-Border", inset, outset) dropdownMenu.border:SetFrameLevel(dropdownMenu:GetFrameLevel() - 1) --[[for k,v in pairs(dropdownMenu.border.parts) do v:SetVertexColor(S.Utils.GetButtonTextColor()) end]] -- Original border, with textures rather than solid colour --[[dropdownMenu.bg:SetTexture("Interface\\Addons\\Sorted\\Textures\\Abstract", "REPEAT", "REPEAT") dropdownMenu.bg:SetHorizTile(true) dropdownMenu.bg:SetVertTile(true) dropdownMenu.bg:SetAllPoints() dropdownMenu:EnableMouse() dropdownMenu:SetScript("OnEnter", function(self) end) S.FrameTools.AddBorder(dropdownMenu, "border", "Interface\\Addons\\Sorted\\Textures\\Dropdown-Border", 10, 8) S.FrameTools.AddInnerShadow(dropdownMenu, 16) dropdownMenu.border:SetFrameLevel(dropdownMenu:GetFrameLevel() + 15) S.Utils.RunOnEvent(dropdownMenu, "SettingChanged-backdrop", function(self, event, setting, value) if setting == "backdrop" then self.bg:SetTexture(S.Utils.GetBackgroundPath(value)) elseif setting == "backdropColor" then self.bg:SetVertexColor(unpack(value)) end end)]] local function EntryOnEnter(self) self:GetHighlightTexture():Show() self.text:SetTextColor(self.textHighlightColor:GetRGB()) self.deleteButton:GetNormalTexture():SetTexCoord(0, 0.375, 0, 0.375) end local function EntryOnLeave(self) self:GetHighlightTexture():Hide() self.text:SetTextColor(self.textColor:GetRGB()) self.deleteButton:GetNormalTexture():SetTexCoord(0, 0.375, 0.375, 0.75) end local function EntryOnMouseWheel(self, delta) dropdownMenu.scrollBar:SetValue(dropdownMenu.scrollBar:GetValue() - delta * 50) end dropdownMenu.entries = {} local function CreateEntry() local index = #dropdownMenu.entries + 1 local self = CreateFrame("BUTTON", nil, dropdownMenu.content) self:SetPoint("TOPLEFT", 0, -index * ENTRY_HEIGHT + ENTRY_HEIGHT - 6) self:SetPoint("BOTTOMRIGHT", dropdownMenu.content, "TOPRIGHT", 0, -index * ENTRY_HEIGHT - 6) self:SetHighlightTexture("Interface\\Addons\\Sorted\\Textures\\Dropdown-Highlight") self:GetHighlightTexture():SetVertexColor(0.9, 0.7, 0.04) self:GetHighlightTexture():SetDrawLayer("ARTWORK") self:GetHighlightTexture():Hide() self:SetPushedTexture("Interface\\Addons\\Sorted\\Textures\\Dropdown-Highlight") self:GetPushedTexture():SetVertexColor(0.9, 0.7, 0.04, 0.5) self:GetPushedTexture():SetBlendMode("ADD") self.text = self:CreateFontString(nil, "OVERLAY", "SortedFont") self.text:SetPoint("LEFT", 8, 0) self.rightText = self:CreateFontString(nil, "OVERLAY", "SortedFont") self.rightText:SetPoint("RIGHT", -8, 0) self.deleteButton = CreateFrame("BUTTON", nil, self) self.deleteButton:SetSize(22, 22) self.deleteButton:SetPoint("RIGHT", -2, 0) self.deleteButton:SetNormalTexture("Interface\\Addons\\Sorted\\Textures\\Close-Button") self.deleteButton:GetNormalTexture():SetTexCoord(0, 0.375, 0.375, 0.75) self.deleteButton:SetHighlightTexture("Interface\\Addons\\Sorted\\Textures\\Close-Button-Highlight") self.deleteButton:SetPushedTexture("Interface\\Addons\\Sorted\\Textures\\Close-Button") self.deleteButton:GetPushedTexture():SetTexCoord(0.375, 0.75, 0, 0.375) self.deleteButton:SetScript("OnEnter", function(self) S.Tooltip.CreateLocalized(self, "ANCHOR_RIGHT", self.tooltip, S.GetData(self.data1).name) self:GetNormalTexture():SetTexCoord(0, 0.375, 0, 0.375) end) self.deleteButton:SetScript("OnLeave", function(self) S.Tooltip.Cancel() if not self:GetParent():IsMouseOver() then self:GetNormalTexture():SetTexCoord(0, 0.375, 0.375, 0.75) end end) self.checkboxTexture = self:CreateTexture(nil, "OVERLAY") self.checkboxTexture:SetTexture("Interface\\Addons\\Sorted\\Textures\\Checkbox-Tick") self.checkboxTexture:SetPoint("LEFT", 8, 0) self.checkboxTexture:SetSize(18, 18) self.radioButtonTexture = self:CreateTexture(nil, "OVERLAY") self.radioButtonTexture:SetTexture("Interface\\Addons\\Sorted\\Textures\\Radio-Button-Dot") self.radioButtonTexture:SetTexCoord(0.1, 0.9, 0.1, 0.9) self.radioButtonTexture:SetPoint("LEFT", 8, 0) self.radioButtonTexture:SetSize(18, 18) function self:SetChecked(checked) self.checked = checked if self.hasCheckbox then if checked then self.checkboxTexture:SetTexture("Interface\\Addons\\Sorted\\Textures\\Checkbox-Tick") else self.checkboxTexture:SetTexture("Interface\\Addons\\Sorted\\Textures\\Checkbox-Cross") end end if self.hasRadioButton then self.radioButtonTexture:SetShown(checked) end end self:SetScript("OnMouseDown", function(self) self.text:SetPoint("CENTER", 1, -1) end) self:SetScript("OnMouseUp", function(self) self.text:SetPoint("CENTER") end) self:SetScript("OnEnter", EntryOnEnter) self:SetScript("OnLeave", EntryOnLeave) self:SetScript("OnClick", function(self) if self.hasCheckbox then self:SetChecked(not self.checked) elseif self.hasRadioButton then if not self.checked or not self.disableTogglingOff then self:SetChecked(not self.checked) for k,v in pairs(dropdownMenu.entries) do if v.hasRadioButton and v ~= self then v:SetChecked(false) end end end else dropdownMenu:Hide() end self.OnClick(self) end) self:SetScript("OnMouseWheel", EntryOnMouseWheel) dropdownMenu.entries[index] = self return self end local currentEntryIndex = 1 function S.Dropdown.Reset() dropdownMenu.scrollPositionBeforeReset = dropdownMenu.scrollBar:GetValue() currentEntryIndex = 1 for i, v in ipairs(dropdownMenu.entries) do v:Hide() end end function S.Dropdown.AddEntry(text, OnClick, data1, data2, color) if not dropdownMenu.entries[currentEntryIndex] then CreateEntry() end local entry = dropdownMenu.entries[currentEntryIndex] entry:Show() entry:SetScript("OnEnter", EntryOnEnter) entry:SetScript("OnLeave", EntryOnLeave) entry.text:SetPoint("LEFT", 8, 0) entry.text:SetFont(S.Utils.GetFontPath(S.Settings.Get("font")), S.Settings.Get("fontSizePts")) entry.rightText:SetText("") entry.rightText:SetPoint("RIGHT", -8, 0) entry.deleteButton:Hide() entry.hasCheckbox = false entry.checkboxTexture:Hide() entry.hasRadioButton = false entry.checked = false entry.radioButtonTexture:Hide() entry.text:SetText(text) entry.data1 = data1 entry.data2 = data2 if not OnClick then entry:Disable() else entry:Enable() entry.OnClick = OnClick end if color then entry:GetHighlightTexture():SetVertexColor(color:GetRGB()) entry:GetPushedTexture():SetVertexColor(color:GetRGB()) entry.text:SetTextColor(color:GetRGB()) entry.textColor = color else entry:GetHighlightTexture():SetVertexColor(0.9, 0.7, 0.04) entry:GetPushedTexture():SetVertexColor(0.9, 0.7, 0.04) entry.text:SetTextColor(1, 1, 1) entry.textColor = CreateColor(1, 1, 1) end entry.textHighlightColor = CreateColor(entry.textColor.r + 0.4, entry.textColor.g + 0.4, entry.textColor.b + 0.4) currentEntryIndex = currentEntryIndex + 1 end function S.Dropdown.AddRightText(text) local entry = dropdownMenu.entries[currentEntryIndex - 1] entry.rightText:SetText(text) end function S.Dropdown.AddDeleteButton(OnClick, data1, data2, tooltip) local entry = dropdownMenu.entries[currentEntryIndex - 1] entry.rightText:SetPoint("RIGHT", -24, 0) entry.deleteButton:Show() entry.deleteButton.data1 = data1 entry.deleteButton.data2 = data2 entry.deleteButton:SetScript("OnClick", OnClick) entry.deleteButton.tooltip = tooltip end function S.Dropdown.AddCheckbox(checked) local entry = dropdownMenu.entries[currentEntryIndex - 1] entry.text:SetPoint("LEFT", 30, 0) entry.hasCheckbox = true entry.checkboxTexture:Show() entry:SetChecked(checked) end function S.Dropdown.AddRadioButton(checked, disableTogglingOff) local entry = dropdownMenu.entries[currentEntryIndex - 1] entry.text:SetPoint("LEFT", 30, 0) entry.hasRadioButton = true entry.checked = checked entry.disableTogglingOff = disableTogglingOff entry.radioButtonTexture:SetShown(checked) end function S.Dropdown.RestoreScrollPosition() dropdownMenu.scrollBar:SetValue(dropdownMenu.scrollPositionBeforeReset) end function S.Dropdown.SetFont(font) local entry = dropdownMenu.entries[currentEntryIndex - 1] entry.text:SetFont(S.Utils.GetFontPath(font), S.Settings.Get("fontSizePts")) end function S.Dropdown.OnEnter(func) local entry = dropdownMenu.entries[currentEntryIndex - 1] entry:HookScript("OnEnter", func) end function S.Dropdown.OnLeave(func) local entry = dropdownMenu.entries[currentEntryIndex - 1] entry:HookScript("OnLeave", func) end function S.Dropdown.Show(parent, anchor, anchorTo, offsetX, offsetY) dropdownMenu:ClearAllPoints() dropdownMenu:SetPoint(anchor, parent, anchorTo, offsetX, offsetY) dropdownMenu:SetFrameStrata("DIALOG") dropdownMenu:SetHeight((currentEntryIndex - 1) * ENTRY_HEIGHT + 12) dropdownMenu.content:SetHeight((currentEntryIndex - 1) * ENTRY_HEIGHT + 12) local scrollBarShown = false if dropdownMenu:GetHeight() > MAX_HEIGHT then dropdownMenu.scrollBar:Show() dropdownMenu.scrollBar:SetMinMaxValues(0, dropdownMenu:GetHeight() - MAX_HEIGHT) dropdownMenu.scrollBar:SetValue(0) dropdownMenu:SetHeight(MAX_HEIGHT) scrollBarShown = true else dropdownMenu.scrollBar:SetMinMaxValues(0, 0) dropdownMenu.scrollBar:Hide() end local width = 0 for i = 1,currentEntryIndex - 1 do local entry = dropdownMenu.entries[i] local entryWidth = entry.text:GetStringWidth() + entry.rightText:GetStringWidth() if entry.deleteButton:IsShown() then entryWidth = entryWidth + 24 end if entry.hasCheckbox or entry.hasRadioButton then entryWidth = entryWidth + 22 end if entryWidth > width then width = entryWidth end end width = width + 24 dropdownMenu.scrollFrame:SetWidth(width) dropdownMenu.content:SetWidth(width) if scrollBarShown then width = width + 24 end dropdownMenu:SetWidth(width) dropdownMenu:Show() --dropdownMenu.shown = true end function S.Dropdown.IsShown() return dropdownMenu:IsShown() --return dropdownMenu.shown end function S.Dropdown.Hide() dropdownMenu:Hide() end --[[dropdownMenu:SetScript("OnHide", function(self) -- Set result of IsShown() to false on the next frame -- This prevents GLOBAL_MOUSE_DOWN from hiding the dropdown menu on the same frame as something else toggles it, making it always toggle back to shown C_Timer.After(0.0001, function() self.shown = false end) end)]] S.primaryFrame:HookScript("OnHide", function(self) dropdownMenu:Hide() end) -- Hide when user clicks outside of the dropdown menu. (Now done with a fullscreen frame for compatibility with Classic) --[[if S.WoWVersion() >= 3 then dropdownMenu:RegisterEvent("GLOBAL_MOUSE_DOWN") dropdownMenu:SetScript("OnEvent", function(self, event) if S.Dropdown.IsShown() and event == "GLOBAL_MOUSE_DOWN" then if not self:IsMouseOver() then S.Dropdown.Hide() end end end) end]] S.Dropdown.Reset() dropdownMenu:Hide()