-- ------------------------------------------------------------------------------ -- -- TradeSkillMaster -- -- https://tradeskillmaster.com -- -- All Rights Reserved - Detailed license information included with addon. -- -- ------------------------------------------------------------------------------ -- --- Base Dropdown UI Element Class. -- The base dropdown class is an abstract class which provides shared functionality between the @{SelectionDropdown} and -- @{MultiselectionDropdown} classes. It is a subclass of the @{Text} class. -- @classmod BaseDropdown local _, TSM = ... local NineSlice = TSM.Include("Util.NineSlice") local Color = TSM.Include("Util.Color") local Theme = TSM.Include("Util.Theme") local ScriptWrapper = TSM.Include("Util.ScriptWrapper") local BaseDropdown = TSM.Include("LibTSMClass").DefineClass("BaseDropdown", TSM.UI.Text, "ABSTRACT") local UIElements = TSM.Include("UI.UIElements") UIElements.Register(BaseDropdown) TSM.UI.BaseDropdown = BaseDropdown local private = {} local EXPANDER_SIZE = 18 local TEXT_PADDING = 8 local EXPANDER_PADDING = 8 -- ============================================================================ -- Meta Class Methods -- ============================================================================ function BaseDropdown.__init(self) local frame = UIElements.CreateFrame(self, "Button", nil, nil, nil) self.__super:__init(frame) self._nineSlice = NineSlice.New(frame) ScriptWrapper.Set(frame, "OnClick", private.FrameOnClick, self) frame.arrow = frame:CreateTexture(nil, "ARTWORK") self._widthText = UIElements.CreateFontString(self, frame) self._widthText:Hide() self._font = "BODY_BODY2" self._hintText = "" self._items = {} self._itemKeyLookup = {} self._disabled = false self._isOpen = false self._onSelectionChangedHandler = nil end function BaseDropdown.Release(self) self._hintText = "" wipe(self._items) wipe(self._itemKeyLookup) self._disabled = false self._isOpen = false self._onSelectionChangedHandler = nil self:_GetBaseFrame():Enable() self.__super:Release() self._font = "BODY_BODY2" end -- ============================================================================ -- Public Class Methods -- ============================================================================ --- Sets the hint text which is shown when there's no selection. -- @tparam BaseDropdown self The dropdown object -- @tparam string text The hint text string -- @treturn BaseDropdown The dropdown object function BaseDropdown.SetHintText(self, text) self._hintText = text return self end --- Add an item to be shown in the dropdown dialog list. -- @tparam BaseDropdown self The dropdown object -- @tparam string item The item to add to the list (localized string) -- @tparam[opt] string|number itemKey The internal representation of the item (if not specified will be the index) -- @treturn BaseDropdown The dropdown object function BaseDropdown.AddItem(self, item, itemKey) tinsert(self._items, item) self._itemKeyLookup[item] = itemKey or #self._items return self end --- Set the items to show in the dropdown dialog list. -- @tparam BaseDropdown self The dropdown object -- @tparam table items A list of items to be shown in the dropdown list -- @tparam[opt] table itemKeys A list of keys which go with the item at the corresponding index in the items list -- @treturn BaseDropdown The dropdown object function BaseDropdown.SetItems(self, items, itemKeys) wipe(self._items) wipe(self._itemKeyLookup) assert(not itemKeys or #itemKeys == #items) for i, item in ipairs(items) do self:AddItem(item, itemKeys and itemKeys[i]) end return self end --- Set whether or not the dropdown is disabled. -- @tparam BaseDropdown self The dropdown object -- @tparam boolean disabled Whether or not to disable the dropdown -- @treturn BaseDropdown The dropdown object function BaseDropdown.SetDisabled(self, disabled) self._disabled = disabled if disabled then self:_GetBaseFrame():Disable() else self:_GetBaseFrame():Enable() end return self end --- Registers a script handler. -- @tparam BaseDropdown self The dropdown object -- @tparam string script The script to register for (supported scripts: `OnSelectionChanged`) -- @tparam function handler The script handler which will be called with the dropdown object followed by any arguments -- to the script -- @treturn BaseDropdown The dropdown object function BaseDropdown.SetScript(self, script, handler) if script == "OnSelectionChanged" then self._onSelectionChangedHandler = handler else error("Invalid BaseDropdown script: "..tostring(script)) end return self end --- Sets whether or not the dropdown is open. -- @tparam BaseDropdown self The dropdown object -- @tparam boolean open Whether or not the dropdown is open -- @treturn BaseDropdown The dropdown object function BaseDropdown.SetOpen(self, open) assert(type(open) == "boolean") if open == self._isOpen then return self end self._isOpen = open if open then local width, height = self:_GetDialogSize() local dialogFrame = UIElements.New("Frame", "dropdown") :SetLayout("VERTICAL") :SetContext(self) :AddAnchor("TOPLEFT", self:_GetBaseFrame(), "BOTTOMLEFT", 0, -4) :SetPadding(0, 0, 4, 4) :SetBackgroundColor("ACTIVE_BG", true) :SetSize(max(width, self:_GetDimension("WIDTH")), height) :SetScript("OnHide", private.DialogOnHide) self:_AddDialogChildren(dialogFrame) dialogFrame:GetElement("list"):SetScript("OnSelectionChanged", private.ListOnSelectionChanged) self:GetBaseElement():ShowDialogFrame(dialogFrame) else self:GetBaseElement():HideDialog() end return self end function BaseDropdown.SetText(self) error("BaseDropdown does not support this method") end function BaseDropdown.SetTextColor(self, color) error("BaseDropdown does not support this method") end function BaseDropdown.Draw(self) self.__super:SetText(self:_GetCurrentSelectionString()) self.__super:Draw() local frame = self:_GetBaseFrame() TSM.UI.TexturePacks.SetTexture(frame.arrow, "iconPack.18x18/Chevron/Down") local frameHeight = frame:GetHeight() local paddingX = EXPANDER_PADDING local paddingY = (frameHeight - EXPANDER_SIZE) / 2 frame.text:ClearAllPoints() frame.text:SetPoint("TOPLEFT", TEXT_PADDING, 0) frame.text:SetPoint("BOTTOMRIGHT", -EXPANDER_SIZE, 0) frame.arrow:ClearAllPoints() frame.arrow:SetPoint("BOTTOMLEFT", frame.text, "BOTTOMRIGHT", -paddingX, paddingY) frame.arrow:SetPoint("TOPRIGHT", -paddingX, -paddingY) -- set textures and text color depending on the state self._nineSlice:SetStyle("rounded") local textColor = self:_GetTextColor() frame.text:SetTextColor(textColor:GetFractionalRGBA()) self._nineSlice:SetVertexColor(Theme.GetColor(self._disabled and "PRIMARY_BG_ALT" or "ACTIVE_BG"):GetFractionalRGBA()) frame.arrow:SetVertexColor(textColor:GetFractionalRGBA()) end -- ============================================================================ -- Private Class Methods -- ============================================================================ function BaseDropdown._GetTextColor(self) local color = Theme.GetColor(self._disabled and "PRIMARY_BG_ALT" or "ACTIVE_BG") -- the text color should have maximum contrast with the dropdown color, so set it to white/black based on the dropdown color if color:IsLight() then -- the dropdown is light, so set the text to black return Color.GetFullBlack():GetTint(self._disabled and "-DISABLED" or 0) else -- the dropdown is dark, so set the text to white return Color.GetFullWhite():GetTint(self._disabled and "+DISABLED" or 0) end end function BaseDropdown._GetDialogSize(self) local maxStringWidth = 100 -- no smaller than 100 self._widthText:Show() self._widthText:SetFont(Theme.GetFont(self._font):GetWowFont()) for _, item in ipairs(self._items) do self._widthText:SetText(item) maxStringWidth = max(maxStringWidth, self._widthText:GetUnboundedStringWidth()) end self._widthText:Hide() return maxStringWidth + Theme.GetColSpacing() * 2, 8 + max(16, min(8, #self._items) * 20) end -- ============================================================================ -- Local Script Handlers -- ============================================================================ function private.FrameOnClick(self) self:SetOpen(true) end function private.ListOnSelectionChanged(dropdownList, selection) local self = dropdownList:GetParentElement():GetContext() self:_OnListSelectionChanged(dropdownList, selection) self:Draw() end function private.DialogOnHide(frame) local self = frame:GetContext() self._isOpen = false end