246 lines
8.5 KiB
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
|