---@type Diesal.GUI local DiesalGUI = LibStub("DiesalGUI-2.0") ---@type Diesal.Tools local DiesalTools = LibStub("DiesalTools-2.0") ---@type Diesal.Style local DiesalStyle = LibStub("DiesalStyle-2.0") -- ~~| Diesal Upvalues |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ local Colors = DiesalStyle.Colors local HSL, ShadeColor, TintColor = DiesalTools.HSL, DiesalTools.ShadeColor, DiesalTools.TintColor -- ~~| Lua Upvalues |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ local tsort = table.sort local sub, format, lower, upper, gsub = string.sub, string.format, string.lower, string.upper, string.gsub -- ~~| WoW Upvalues |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- ~~| Dropdown |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ local Type = "DiesalDropDown" local Version = 2 -- ~~| Dropdown Stylesheets |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ local Stylesheet = { ["frame-background"] = { type = "texture", layer = "BACKGROUND", gradient = { "VERTICAL", Colors.UI_400_GR[1], Colors.UI_400_GR[2] }, }, ["frame-outline"] = { type = "outline", layer = "BORDER", color = "000000", }, ["frame-inline"] = { type = "outline", layer = "BORDER", gradient = { "VERTICAL", "FFFFFF", "FFFFFF" }, alpha = { 0.07, 0.02 }, position = -1, }, ["frame-hover"] = { type = "texture", layer = "HIGHLIGHT", color = "ffffff", alpha = 0.05, }, ["text-color"] = { type = "Font", color = Colors.UI_TEXT, }, ["dropdown-background"] = { type = "texture", layer = "BACKGROUND", color = Colors.UI_100, alpha = 0.95, }, ["dropdown-inline"] = { type = "outline", layer = "BORDER", color = "ffffff", alpha = 0.02, position = -1, }, ["dropdown-shadow"] = { type = "shadow", }, } -- ~~| Dropdown Locals |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ local function compare(a, b) return a.value < b.value end local function sortList(list, orderedKeys) if orderedKeys then return orderedKeys end local sortedList = {} local orderedKeys = {} for key, value in pairs(list) do sortedList[#sortedList + 1] = { key = key, value = value } end tsort(sortedList, compare) for i, value in ipairs(sortedList) do orderedKeys[#orderedKeys + 1] = value.key end return orderedKeys end local function OnPulloutOpen(this) local self = this.userdata.obj local value = self.value if not self.multiselect then for i, item in this:IterateItems() do --item:SetValue(item.userdata.value == value) end end self.open = true self:FireEvent("OnOpened") end local function OnPulloutClose(this) local self = this.userdata.obj self.open = nil self:FireEvent("OnClosed") end local function fixlevels(parent, ...) local i = 1 local child = select(i, ...) while child do child:SetFrameLevel(parent:GetFrameLevel() + 1) fixlevels(child, child:GetChildren()) i = i + 1 child = select(i, ...) end end local function fixstrata(strata, parent, ...) local i = 1 local child = select(i, ...) parent:SetFrameStrata(strata) while child do fixstrata(strata, child, child:GetChildren()) i = i + 1 child = select(i, ...) end end -- ~~| Dropdown Methods |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ---@class Diesal.GUI.DropDown.Methods local methods = { ---@param self Diesal.GUI.Object.DropDown OnAcquire = function(self) --local pullout = DiesalGUI:Create("DiesalDropDownPullout") --self.pullout = pullout --self.pullout.userdata.obj = self --self.pullout:SetEventListener("OnClose", OnPulloutClose) --self.pullout:SetEventListener("OnOpen", OnPulloutOpen) --self.pullout.frame:SetFrameLevel(self.frame:GetFrameLevel() + 1) --fixlevels(self.pullout.frame, self.pullout.frame:GetChildren()) self:ApplySettings() self:SetStylesheet(Stylesheet) -- self:SetStylesheet(wireFrameSheet) self:Show() self:SetFrameStrata("TOOLTIP") end, ---@param self Diesal.GUI.Object.DropDown OnRelease = function(self) if self.pullout then self.pullout:Release() end end, ---@param self Diesal.GUI.Object.DropDown ApplySettings = function(self) local settings = self.settings local content = self.content local frame = self.frame local children = self.children local dropdown = self.dropdown frame:SetWidth(settings.width) frame:SetHeight(settings.height) content:SetPoint("TOPLEFT", dropdown, settings.dropdownPadLeft, -settings.dropdownPadTop) content:SetPoint("BOTTOMRIGHT", dropdown, -settings.dropdownPadRight, settings.dropdownPadBottom) local menuHeight = 0 for i = 1, #children do menuHeight = menuHeight + children[i].frame:GetHeight() end dropdown:SetHeight(settings.dropdownPadTop + menuHeight + settings.dropdownPadBottom) end, ---@param self Diesal.GUI.Object.DropDown ---@param list Diesal.GUI.DropDownItem.Settings[] SetList = function(self, list) self:ReleaseChildren() self:SetText("") local settings = self.settings settings.list = list local foldColor = "353535" local expandColor = "5a5a5a" self.dropdown:SetFrameStrata("TOOLTIP") self.pullout.settings.items = {} self.pullout:Clear() for position, item in ipairs(settings.list) do local dropdownItem = DiesalGUI:Create("DiesalDropDownItem") DiesalGUI:OnMouse(dropdownItem.frame, "LeftButton") dropdownItem:SetParentObject(self, true) dropdownItem:SetSettings({ key = item.key, value = item.value, position = position, clickable = item.clickable, indent = item.indent or 0, icon2 = item.icon2, }, true) dropdownItem.text:SetPoint("TOPLEFT", 12 + (item.indent * 6), -2) if item.indent and item.indent > 0 then local leftOffset = -6 + (-6 * item.indent) dropdownItem:UpdateStyle("frame-lineV", { type = "texture", layer = "BORDER", color = foldColor, position = { leftOffset, nil, -8, nil }, height = 1, width = 6, }) dropdownItem:UpdateStyle("frame-lineH", { type = "texture", layer = "BORDER", color = foldColor, position = { leftOffset, nil, 0, 0 }, width = 1, }) if item.last then dropdownItem:UpdateStyle("frame-lineH", { type = "texture", position = { leftOffset, nil, 0, -7 }, }) end end self.pullout:AddItem(dropdownItem, true) end self:ApplySettings() end, ---@param self Diesal.GUI.Object.DropDown SetValue = function(self, key) local selectionTable = {} local selectedKey, dropdownText, selectedValue local selectedCount = 0 if key ~= "CLEAR" then if self.settings.multiSelect then for i = 1, #self.children do if self.children[i].settings.key == key then selectedKey = key self.children[i]:SetSelected(true) end if self.children[i].settings.selected then selectedCount = selectedCount + 1 if dropdownText then dropdownText = format("%s, %s", dropdownText, self.children[i].settings.value) else dropdownText = self.children[i].settings.value end selectionTable[#selectionTable + 1] = self.children[i].settings.key end end else for i = 1, #self.children do local child = self.children[i] child:SetSelected(false) if child.settings.icon2 then child.icon2:Show() else child.icon2:Hide() end if child.settings.key == key then child:SetSelected(true) dropdownText = child.settings.value selectionTable = { key } selectedKey = key selectedValue = self.value end end end else self:ClearSelection() end if self.settings.multiSelect then dropdownText = string.format("+%d", selectedCount) end if selectedKey then self:SetText(dropdownText) self:FireEvent("OnValueChanged", selectedKey, selectedValue, selectionTable) else self:SetText("") self:FireEvent("OnValueChanged", selectedKey, selectedValue, selectionTable) end end, ---@param self Diesal.GUI.Object.DropDown ClearSelection = function(self) for i = 1, #self.children do self.children[i]:SetSelected(false) end self:SetText("") end, ---@param self Diesal.GUI.Object.DropDown SetValueTable = function(self, keyTable) if not self.settings.multiSelect or type(keyTable) ~= "table" then return end local dropdownItems = self.children local selectionTable = {} local selectedKey local dropdownText local selectedCount = 0 for i = 1, #dropdownItems do local dropdownItem = dropdownItems[i] dropdownItem:SetSelected(false) for _, key in ipairs(keyTable) do if dropdownItem.settings.key == key then dropdownItem:SetSelected(true) selectedCount = selectedCount + 1 if not self.settings.multiSelect then dropdownText = dropdownText and format("%s, %s", dropdownText, dropdownItem.settings.value) or dropdownItem.settings.value end selectionTable[#selectionTable + 1] = dropdownItem.settings.key end end end self:FireEvent("OnValueChanged", nil, nil, selectionTable) if self.settings.multiSelect then dropdownText = string.format("+%d", selectedCount) end self:SetText(dropdownText) end, ---@param self Diesal.GUI.Object.DropDown SetMultiSelect = function(self, state) self.settings.multiSelect = state end, ---@param self Diesal.GUI.Object.DropDown SetText = function(self, text) self.text:SetText(text) for i = 1, #self.children do local child = self.children[i] if child.settings.value == text then self.value = child.settings.key end end end, ---@param self Diesal.GUI.Object.DropDown SetFocus = function(self) DiesalGUI:SetFocus(self) end, ---@param self Diesal.GUI.Object.DropDown ClearFocus = function(self) self.dropdownShown = false self.dropdown:Hide() end, ---@param self Diesal.GUI.Object.DropDown EnableMouse = function(self, state) self.frame:EnableMouse(state) end, ---@param self Diesal.GUI.Object.DropDown RegisterForClicks = function(self, ...) self.frame:RegisterForClicks(...) end, ---@param self Diesal.GUI.Object.DropDown SetJustifyV = function(self, justify) self.text:SetJustifyV(justify) end, ---@param self Diesal.GUI.Object.DropDown SetJustifyH = function(self, justify) self.text:SetJustifyH(justify) end, ---@param self Diesal.GUI.Object.DropDown ShowDropDown = function(self) self.dropdown:SetFrameStrata("TOOLTIP") self.dropdown:SetParent(self.frame) self.dropdown:Show() self.dropdownShown = true end, ---@param self Diesal.GUI.Object.DropDown HideDropDown = function(self) self.dropdown:Hide() self.dropdownShown = false end, ---@param self Diesal.GUI.Object.DropDown ToggleDropDown = function(self) if self.dropdownShown then self:HideDropDown() else self:ShowDropDown() end end, ---@param self Diesal.GUI.Object.DropDown ---@param strata FrameStrata ---@param parent Frame ---@param children Frame[] FixStrata = function(self, strata, parent, children) parent:SetFrameStrata(strata) parent:Raise() for i, child in ipairs(children) do self:FixStrata(strata, child, { child:GetChildren() --[[@as Frame]], }) end end, ---@param self Diesal.GUI.Object.DropDown ---@param button mouseButton ---@param down boolean OnClick = function(self, button, down) if button == "LeftButton" then if self.open then self.open = false self.pullout:Close() self.arrow:SetRotation(0) DiesalGUI:ClearFocus() else self.open = true self.pullout:Open("TOPLEFT", self.frame, "BOTTOMLEFT", 0, 0) DiesalGUI:SetFocus(self.pullout) self.arrow:SetRotation(math.pi) end end end, } ---@class Diesal.GUI.Object.DropDown.Settings : Diesal.GUI.ObjectBase.Settings ---@field dropdownPadLeft number ---@field dropdownPadRight number ---@field dropdownPadTop number ---@field dropdownPadBottom number ---@field itemHeight number ---@field width number ---@field height number ---@field list? Diesal.GUI.DropDownItem.Settings[] ---@field multiSelect? boolean ---@field dropdownWidth? number ---@class DiesalDropDown : Diesal.GUI.Object.DropDown -- ~~| Dropdown Constructor |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ local function Constructor() ---@class Diesal.GUI.Object.DropDown : Diesal.GUI.ObjectBase, Diesal.GUI.DropDown.Methods ---@field arrow Texture ---@field settings Diesal.GUI.Object.DropDown.Settings ---@field children Diesal.GUI.Object.DropDown.Item[] ---@field text FontString ---@field dropdown Frame ---@field content Frame ---@field value string ---@field pullout DiesalDropDownPullout local self = DiesalGUI:Create(Type, true) self:SetMethods(methods) self.dropdownShown = false self.open = false local frame = CreateFrame("Button", nil, DiesalGUI.UIParent) frame:SetFrameStrata("TOOLTIP") frame:SetClampedToScreen(true) self.frame = frame self.isContainer = true self.defaults = { dropdownPadLeft = 4, dropdownPadRight = 4, dropdownPadTop = 4, dropdownPadBottom = 4, itemHeight = 16, width = 100, height = 16, } -- ~~ Registered Events ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- OnValueChanged(event,selectedKey,selectedValue,selectionTable) -- OnValueSelected(event,selectedKey,selectedValue,selectionTable) -- OnEnter, OnLeave -- ~~ Construct ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ frame:RegisterForClicks("AnyDown") frame:SetScript("OnClick", function(this, button, down) self:OnClick(button, down) self:FireEvent("OnClick", button, down) end) frame:SetScript("OnEnter", function(this, ...) self:FireEvent("OnEnter", ...) end) frame:SetScript("OnLeave", function(this, ...) self:FireEvent("OnLeave", ...) end) local arrow = self:CreateRegion("Texture", "arrow", frame) arrow:SetVertexColor(1, 1, 1, 0.5) arrow:SetDrawLayer("BORDER") arrow:SetAtlas("uitools-icon-chevron-down") arrow:SetPoint("TOPRIGHT", -7, -3) arrow:SetSize(10, 10) local text = self:CreateRegion("FontString", "text", frame) text:ClearAllPoints() text:SetPoint("TOPLEFT", 4, -1) text:SetPoint("BOTTOMRIGHT", -14, 1) text:SetJustifyV("MIDDLE") text:SetJustifyH("LEFT") local dropdown = self:CreateRegion("Frame", "dropdown", frame) dropdown:SetFrameStrata("TOOLTIP") dropdown:SetFrameLevel(dropdown:GetFrameLevel() + 2) dropdown:SetPoint("TOPRIGHT", frame, "BOTTOMRIGHT", 0, -2) dropdown:SetPoint("TOPLEFT", frame, "BOTTOMLEFT", 0, -2) dropdown:SetScript("OnShow", function(this) self:SetFocus() end) dropdown:SetScript("OnHide", function(this) self.dropdownShown = false if self.pullout then self.pullout:Close() end self.arrow:SetRotation(0) DiesalGUI:ClearFocus() end) dropdown:Hide() self.dropdown = dropdown self:CreateRegion("Frame", "content", dropdown) -- ~~ Methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --self:SetMethods(methods) --[[ for method, func in pairs(methods) do self[method] = func end ]] -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ self.pullout = DiesalGUI:Create("DiesalDropDownPullout") self.pullout.userdata.obj = self self.pullout:SetEventListener("OnClose", OnPulloutClose) self.pullout:SetEventListener("OnOpen", OnPulloutOpen) self.pullout.frame:SetFrameLevel(self.frame:GetFrameLevel() + 1) fixlevels(self.pullout.frame, self.pullout.frame:GetChildren()) self.pullout:SetDropDown(self) self:SetObj(self.frame) return self end DiesalGUI:RegisterObjectConstructor(Type, Constructor, Version)