TradeSkillMaster/Core/UI/Elements/BaseDropdown.lua

246 lines
8.5 KiB
Lua

-- ------------------------------------------------------------------------------ --
-- 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