---@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 = "DiesalDropDownPullout" local Version = 1 ---@type backdropInfo local backdrop = { bgFile = "Interface\\ChatFrame\\ChatFrameBackground", edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", edgeSize = 8, tileSize = 8, tile = true, insets = { left = 2, right = 2, top = 2, bottom = 2 }, } local sliderBackdrop = { bgFile = "Interface\\Buttons\\UI-SliderBar-Background", edgeFile = "Interface\\Buttons\\UI-SliderBar-Border", tile = true, tileSize = 8, edgeSize = 8, insets = { left = 3, right = 3, top = 3, bottom = 3 }, } local Stylesheet = { ["frame-background"] = { type = "texture", layer = "BACKGROUND", color = Colors.UI_100, alpha = 0.95, }, ["frame-inline"] = { type = "outline", layer = "BORDER", color = "ffffff", alpha = 0.02, position = -1, }, ["frame-shadow"] = { type = "shadow", }, } local defaultWidth = 200 local defaultMaxItems = 20 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 -- HACK: This should be no part of the pullout, but there -- is no other 'clean' way to response to any item-OnEnter -- Used to close Submenus when an other item is entered local function OnEnter(item) local self = item.pullout for k, v in ipairs(self.settings.items) do if v.CloseMenu and v ~= item then v:CloseMenu() end end end ---@class Diesal.GUI.DropDownPullout.Methods local methods = { ---@param self Diesal.GUI.Object.DropDownPullout OnAcquire = function(self) self.frame:SetParent(DiesalGUI.UIParent) end, ---@param self Diesal.GUI.Object.DropDownPullout OnRelease = function(self) self:ClearAllPoints() self:Hide() end, ---@param self Diesal.GUI.Object.DropDownPullout ApplySettings = function(self) end, ---@param self Diesal.GUI.Object.DropDownPullout GetItemsHeight = function(self) local itemHeight = 0 for i, item in ipairs(self.settings.items) do itemHeight = itemHeight + item.text:GetStringHeight() end return itemHeight end, ---@param self Diesal.GUI.Object.DropDownPullout GetMaxHeight = function(self) local maxHeight = 0 for i, item in ipairs(self.settings.items) do if i <= self.maxItems then maxHeight = maxHeight + item:GetHeight(false) end end return maxHeight --return self:GetMaxItemHeight() * min(self.maxItems, #self.settings.items) end, ---@param self Diesal.GUI.Object.DropDownPullout ---@param item DiesalDropDownItem ---@param setPoints? boolean AddItem = function(self, item, setPoints) table.insert(self.settings.items, item) local maxWidth = 8 + (item.settings.indent * 6) + (16 * 2) if item.text then maxWidth = maxWidth + item.text:GetUnboundedStringWidth() end if item.check then maxWidth = maxWidth + item.check:GetWidth() + 4 end self.settings.maxItemWidth = max(self.settings.maxItemWidth, maxWidth) --self.frame:SetHeight(min(self:GetItemsHeight() + 28, self:GetMaxHeight() + 28)) if setPoints then item:SetPoint("LEFT", self.itemFrame, "LEFT", 20, 0) item:SetPoint("RIGHT", self.itemFrame, "RIGHT", 0, 0) end item:SetPullout(self) --item:SetScript("OnEnter", OnEnter) end, ---@param self Diesal.GUI.Object.DropDownPullout ---@param value number SetScroll = function(self, value) local status = self.scrollStatus local scrollFrame, itemFrame = self.scrollFrame, self.itemFrame local height, viewheight = scrollFrame:GetHeight(), itemFrame:GetHeight() --local minVal, maxVal = self.slider:GetMinMaxValues() local minVal, maxVal = height, viewheight local offset if height > viewheight then offset = 0 self.slider2:Hide() else offset = floor(maxVal * value) self.slider2:Show() end itemFrame:ClearAllPoints() itemFrame:SetPoint("TOPLEFT", scrollFrame, "TOPLEFT", 0, offset) itemFrame:SetPoint("TOPRIGHT", scrollFrame, "TOPRIGHT", self.slider2:IsShown() and -6 or 0, offset) status.offset = offset status.scrollvalue = (maxVal * value) end, ---@param self Diesal.GUI.Object.DropDownPullout ---@param value number MoveScroll = function(self, value) local status = self.scrollStatus local frame, child = self.scrollFrame, self.itemFrame local height, viewheight = frame:GetHeight(), child:GetHeight() --local minVal, maxVal = self.slider:GetMinMaxValues() local minVal, maxVal = height, self:GetItemsHeight() --child:SetHeight(self:GetItemsHeight()) if height > viewheight then self.slider2:Hide() else self.slider2:Show() local diff = height - viewheight local delta = 1 if value < 0 then delta = -1 end local scrollToVal = min(max(status.scrollvalue + delta * (maxVal / (diff / self.settings.itemHeight)), 0), maxVal) self.slider2:SetScrollPercentage(scrollToVal/maxVal) --self.slider:SetValue(scrollToVal) end end, ---@param self Diesal.GUI.Object.DropDownPullout FixScroll = function(self) local status = self.scrollStatus local frame, child = self.scrollFrame, self.itemFrame local height, viewheight = frame:GetHeight(), child:GetHeight() local offset = status.offset or 0 local minVal, maxVal = height, viewheight if viewheight < height then self.slider2:Hide() child:SetPoint("TOPRIGHT", frame, "TOPRIGHT", 0, offset) self.scrollFrame:SetVerticalScroll(0) else self.slider2:Show() local value = (offset / (viewheight - height) * maxVal) --self.slider:SetValue(value) self:SetScroll(value) if value < maxVal then child:ClearAllPoints() child:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, offset) child:SetPoint("TOPRIGHT", frame, "TOPRIGHT", 0, offset) status.offset = offset end end end, ---@param self Diesal.GUI.Object.DropDownPullout ---@param point FramePoint ---@param relFrame Frame ---@param relPoint FramePoint ---@param x number ---@param y number Open = function(self, point, relFrame, relPoint, x, y) local items = self.settings.items local frame = self.frame local itemFrame = self.itemFrame frame:SetPoint(point, relFrame, relPoint, x, y) local maxWidth = self.settings.maxItemWidth + self.slider2:GetWidth() if self.dropdown.settings.dropdownWidth then if self.dropdown.settings.dropdownWidth > maxWidth then frame:SetWidth(self.dropdown.settings.dropdownWidth) else frame:SetWidth(maxWidth) end else frame:SetWidth(max(maxWidth, self.dropdown.frame:GetWidth())) --frame:SetPoint("TOPRIGHT", relFrame, "BOTTOMRIGHT", x, y) end local height = 0 for i, item in ipairs(items) do item:SetPoint("TOPLEFT", i == 1 and itemFrame or items[i - 1].frame, i == 1 and "TOPLEFT" or "BOTTOMLEFT", 0, 0) item:Show() height = height + item.text:GetStringHeight() end frame:Show() frame:SetHeight(self:GetMaxHeight()) itemFrame:SetHeight(self:GetItemsHeight()) self.scrollFrame:SetScrollChild(itemFrame) fixstrata("TOOLTIP", self.scrollFrame, self.scrollFrame:GetChildren()) self:FireEvent("OnOpen") end, ---@param self Diesal.GUI.Object.DropDownPullout Close = function(self) self.frame:Hide() self:FireEvent("OnClose") end, ---@param self Diesal.GUI.Object.DropDownPullout Clear = function(self) local items = self.settings.items for i, item in ipairs(items) do items[i] = nil end for i,item in ipairs(self.itemFrame) do end end, ---@param self Diesal.GUI.Object.DropDownPullout IterateItems = function(self) return ipairs(self.settings.items) end, ---@param self Diesal.GUI.Object.DropDownPullout SetHideOnLeave = function(self, val) self.hideOnLeave = val end, ---@param self Diesal.GUI.Object.DropDownPullout GetRightBorderWidth = function(self) return 6 + (self.slider:IsShown() and 12 or 0) end, ---@param self Diesal.GUI.Object.DropDownPullout GetLeftBorderWidth = function(self) return 6 end, ---@param self Diesal.GUI.Object.DropDownPullout ClearFocus = function(self) self:Hide() end, ---@param self Diesal.GUI.Object.DropDownPullout ---@param height? number SetItemHeight = function(self, height) self.settings.itemHeight = height or self.defaults.itemHeight end, ---@param self Diesal.GUI.Object.DropDownPullout SetDropDown = function (self, dropdown) self.dropdown = dropdown end } ---@class Diesal.GUI.Object.DropDownPullout.Settings : Diesal.GUI.ObjectBase.Settings ---@field width number ---@field itemHeight number ---@field sliderWidth number ---@field hideOnLeave boolean ---@field items DiesalDropDownItem[] ---@field maxItemWidth number ---@class DiesalDropDownPullout : Diesal.GUI.Object.DropDownPullout local function Constructor() ---@class Diesal.GUI.Object.DropDownPullout : Diesal.GUI.ObjectBase, Diesal.GUI.DropDownPullout.Methods ---@field settings Diesal.GUI.Object.DropDownPullout.Settings ---@field items DiesalDropDownItem[] ---@field dropdown DiesalDropDown local self = DiesalGUI:Create(Type, true) self:SetMethods(methods) local frame = CreateFrame("Frame", nil, DiesalGUI.UIParent, "BackdropTemplate") self.frame = frame self:SetObj(self.frame) self.defaults = { width = 200, itemHeight = 12, sliderWidth = 10, hideOnLeave = true, items = {}, maxItemWidth = 0, } self.scrollStatus = { scrollvalue = 0, } self.hideOnLeave = false self.maxItems = 20 frame:SetBackdrop(backdrop) frame:SetBackdropColor(0, 0, 0, 1) frame:SetBackdropBorderColor(0.2, 0.2, 0.2, 1) frame:SetFrameStrata("FULLSCREEN_DIALOG") frame:SetClampedToScreen(true) frame:SetWidth(self.defaults.width) frame:SetHeight(34) frame:SetScript("OnHide", function(this) self.dropdown.open = nil self.dropdown.arrow:SetRotation(0) DiesalGUI:ClearFocus() end) self:CreateRegion("Frame", "content", frame) local scrollFrame = CreateFrame("ScrollFrame", nil, frame, "ScrollFrameTemplate") local itemFrame = CreateFrame("Frame", nil, scrollFrame) self.scrollFrame = scrollFrame self.itemFrame = itemFrame self:SetObj(self.scrollFrame) self:SetObj(self.itemFrame) ---@type MinimalScrollBar local slider2 = scrollFrame.ScrollBar self.slider2 = slider2 self.slider2:SetPoint("TOPLEFT", self.scrollFrame, "TOPRIGHT", -20, -2) self.slider2:SetPoint("BOTTOMLEFT", self.scrollFrame, "BOTTOMRIGHT", -20, 5) self.scrollFrame:SetVerticalScroll(0) self.slider2:SetVisibleExtentPercentage(0.25) self.slider2:SetThumbExtent(0.25) self.slider2:RegisterCallback("OnScroll", function(this, percent) self:SetScroll(percent) end, self.slider2) --local slider = CreateFrame("Slider", nil, scrollFrame, "BackdropTemplate") --slider:SetOrientation("VERTICAL") --slider:SetHitRectInsets(0, 0, -10, 0) --slider:SetBackdrop(sliderBackdrop) --slider:SetWidth(8) --slider:SetThumbTexture("Interface\\Buttons\\UI-SliderBar-Button-Vertical") --slider:SetFrameStrata("FULLSCREEN_DIALOG") --self.slider = slider --self:SetObj(self.slider) scrollFrame:SetPoint("TOPLEFT", frame, "TOPLEFT") scrollFrame:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT") scrollFrame:EnableMouseWheel(true) --[[ scrollFrame:SetScript("OnMouseWheel", function(this, value) self:MoveScroll(value) self:FireEvent("OnMouseWheel", value) end) ]] scrollFrame:SetToplevel(true) scrollFrame:SetFrameStrata("FULLSCREEN_DIALOG") itemFrame:SetPoint("TOPLEFT", scrollFrame, "TOPLEFT", 0, 0) itemFrame:SetPoint("TOPRIGHT", scrollFrame, "TOPRIGHT", -6, 0) --itemFrame:SetHeight(400) itemFrame:SetToplevel(true) itemFrame:SetFrameStrata("FULLSCREEN_DIALOG") scrollFrame.ItemFrame = itemFrame scrollFrame:SetScrollChild(scrollFrame.ItemFrame) --slider:SetPoint("TOPLEFT", scrollFrame, "TOPRIGHT", -16, 0) --slider:SetPoint("BOTTOMLEFT", scrollFrame, "BOTTOMRIGHT", -16, 0) --slider:SetScript("OnValueChanged", function(this, value) --self:SetScroll(value) --end) --slider:SetMinMaxValues(0, 1) --slider:SetValueStep(1) --slider:SetValue(0) scrollFrame:Show() itemFrame:Show() --slider:Hide() self:FixScroll() return self end DiesalGUI:RegisterObjectConstructor(Type, Constructor, Version)