--[[
    Standard scrollable combo box ui widget for keyboard screens.
    Uses a custom control definition with the box border, selected item label, and a dropdown button.
    Implemented using a ZO_ScrollList. Anchoring is static and each scrollable combo box manages
    its own dropdown window.
]]--

local DEFAULT_HEIGHT = 250
local DEFAULT_FONT = "ZoFontGame"
local DEFAULT_TEXT_COLOR = ZO_ColorDef:New(GetInterfaceColor(INTERFACE_COLOR_TYPE_TEXT_COLORS, INTERFACE_TEXT_COLOR_NORMAL))
local DEFAULT_TEXT_HIGHLIGHT = ZO_ColorDef:New(GetInterfaceColor(INTERFACE_COLOR_TYPE_TEXT_COLORS, INTERFACE_TEXT_COLOR_CONTEXT_HIGHLIGHT))

local DEFAULT_ENTRY_ID = 1
local DEFAULT_LAST_ENTRY_ID = 2
ZO_COMBO_BOX_ENTRY_TEMPLATE_HEIGHT = 25
ZO_COMBO_BOX_ENTRY_TEMPLATE_LABEL_PADDING = 8
ZO_SCROLLABLE_COMBO_BOX_LIST_PADDING_Y = 5

ZO_ComboBox = ZO_ComboBox_Base:Subclass()

function ZO_ComboBox:Initialize(control)
    ZO_ComboBox_Base.Initialize(self, control)
    self.m_font = DEFAULT_FONT
    self.m_normalColor = DEFAULT_TEXT_COLOR
    self.m_highlightColor = DEFAULT_TEXT_HIGHLIGHT

    self.m_customEntryTemplateInfos = nil

    self.m_enableMultiSelect = false
    self.m_maxNumSelections = nil
    self.m_multiSelectItemData = {}

    self.m_containerWidth = control:GetWidth()

    self:SetHeight(DEFAULT_HEIGHT)

    self:SetDropdownObject(ZO_COMBO_BOX_DROPDOWN_KEYBOARD)

    local function OnEffectivelyHidden()
        self:HideDropdown()
    end
    control:SetHandler("OnEffectivelyHidden", OnEffectivelyHidden)
end

function ZO_ComboBox:AddCustomEntryTemplate(entryTemplate, entryHeight, setupFunction)
    if not self.m_customEntryTemplateInfos then
        self.m_customEntryTemplateInfos = {}
    end

    local customEntryInfo =
    {
        entryTemplate = entryTemplate,
        entryHeight = entryHeight,
        setupFunction = setupFunction,
    }

    self.m_customEntryTemplateInfos[entryTemplate] = customEntryInfo

    self.m_dropdownObject:AddCustomEntryTemplate(entryTemplate, entryHeight, setupFunction)
end

function ZO_ComboBox.SetItemEntryCustomTemplate(itemEntry, entryTemplate)
    itemEntry.customEntryTemplate = entryTemplate
end

function ZO_ComboBox:SetDropdownObject(dropdownObject)
    self.m_dropdownObject = dropdownObject

    if self.m_customEntryTemplateInfos then
        for entryTemplate, entryInfo in pairs(self.m_customEntryTemplateInfos) do
            self.m_dropdownObject:AddCustomEntryTemplate(entryInfo.entryTemplate, entryInfo.entryHeight, entryInfo.setupFunction)
        end
    end

    -- Adding these members for backwards compatibility, since a lot of old addons referenced them directly, 
    -- though we don't need them anymore
    self.m_dropdown = dropdownObject.control
    self.m_scroll = dropdownObject.scrollControl
end

function ZO_ComboBox:OnGlobalMouseUp(eventCode, button)
    if self:IsDropdownVisible() then
        if button == MOUSE_BUTTON_INDEX_LEFT and not self.m_dropdownObject:IsMouseOverControl() then
            self:HideDropdown()
        end
    else
        if self.m_container:IsHidden() then
            self:HideDropdown()
        else
            -- If shown in ShowDropdownInternal, the global mouseup will fire and immediately dismiss the combo box. We need to
            -- delay showing it until the first one fires.
            self:ShowDropdownOnMouseUp()
        end
    end
end

function ZO_ComboBox:HighlightLabel(labelControl, data)
    local color = self:GetItemHighlightColor(data)
    labelControl:SetColor(color:UnpackRGBA())
end

function ZO_ComboBox:UnhighlightLabel(labelControl, data)
    local color = self:GetItemNormalColor(data)
    labelControl:SetColor(color:UnpackRGBA())
end

function ZO_ComboBox:OnMouseEnterEntryBase(control)
    if self.onMouseEnterCallback then
        self:onMouseEnterCallback(control)
    end
end

function ZO_ComboBox:OnMouseExitEntryBase(control)
    if self.onMouseExitCallback then
        self:onMouseExitCallback(control)
    end
end

function ZO_ComboBox:SetSpacing(spacing)
    ZO_ComboBox_Base.SetSpacing(self, spacing)
end

function ZO_ComboBox:SetHeight(height)
    self.m_height = height or DEFAULT_HEIGHT
end

function ZO_ComboBox:IsDropdownVisible()
    return self.m_dropdownObject:IsOwnedByComboBox(self) and not self.m_dropdownObject:IsHidden()
end

function ZO_ComboBox:AddMenuItems()
    self.m_dropdownObject:Show(self, self.m_sortedItems, self.m_containerWidth, self.m_height, self:GetSpacing())
end

function ZO_ComboBox:ShowDropdownOnMouseUp()
    if self:IsEnabled() then
        self.m_dropdownObject:SetHidden(false)
        self:AddMenuItems()

        self:SetVisible(true)
    else
        --If we get here, that means the dropdown was disabled after the request to show it was made, so just cancel showing entirely
        self.m_container:UnregisterForEvent(EVENT_GLOBAL_MOUSE_UP)
    end
end

function ZO_ComboBox:SetSelected(index, ignoreCallback)
    local item = self.m_sortedItems[index]
    self:SelectItem(item, ignoreCallback)

    -- multi-select dropdowns will stay open to allow for selecting more entries
    if not self.m_enableMultiSelect then
        self:HideDropdown()
    end
end

function ZO_ComboBox:ShowDropdownInternal()
    -- Just set the global mouse up handler here... we want the combo box to exhibit the same behvaior
    -- as a context menu, which is dismissed when the user clicks outside the menu or on a menu item
    -- (but not in the menu otherwise)
    self.m_container:RegisterForEvent(EVENT_GLOBAL_MOUSE_UP, function(...) self:OnGlobalMouseUp(...) end)
end

function ZO_ComboBox:HideDropdownInternal()
    self.m_container:UnregisterForEvent(EVENT_GLOBAL_MOUSE_UP)
    if self.m_dropdownObject:IsOwnedByComboBox(self) then
        self.m_dropdownObject:SetHidden(true)
    end
    self:SetVisible(false)
    if self.onHideDropdownCallback then
        self.onHideDropdownCallback()
    end
end

function ZO_ComboBox:SetHideDropdownCallback(callback)
    self.onHideDropdownCallback = callback
end

function ZO_ComboBox:SetMouseOverCallbacks(onMouseEnterCallback, onMouseExitCallback)
    self.m_container:SetHandler("OnMouseEnter", onMouseEnterCallback)
    self.m_container:SetHandler("OnMouseExit", onMouseExitCallback)
end

function ZO_ComboBox:SetEntryMouseOverCallbacks(onMouseEnterCallback, onMouseExitCallback)
    self.onMouseEnterCallback = onMouseEnterCallback
    self.onMouseExitCallback = onMouseExitCallback
end

function ZO_ComboBox:AddItemToSelected(item)
    if not self.m_enableMultiSelect then
        return
    end

    table.insert(self.m_multiSelectItemData, item)
end

function ZO_ComboBox:RemoveItemFromSelected(item)
    if not self.m_enableMultiSelect then
        return
    end

    for i, itemData in ipairs(self.m_multiSelectItemData) do
        if itemData == item then
            table.remove(self.m_multiSelectItemData, i)
            return
        end
    end
end

function ZO_ComboBox:IsItemSelected(item)
    if not self.m_enableMultiSelect then
        return false
    end

    for i, itemData in ipairs(self.m_multiSelectItemData) do
        if itemData == item then
            return true
        end
    end

    return false
end

function ZO_ComboBox:GetNumSelectedEntries()
    if not self.m_enableMultiSelect then
        return 0
    end

    return #self.m_multiSelectItemData
end

function ZO_ComboBox:ClearAllSelections()
    self.m_multiSelectItemData = {}
    self:RefreshSelectedItemText()
    if self.m_dropdownObject:IsOwnedByComboBox(self) then
        self.m_dropdownObject:Refresh()
    end
end

function ZO_ComboBox:SetNoSelectionText(text)
    self.noSelectionText = text or GetString(SI_COMBO_BOX_DEFAULT_NO_SELECTION_TEXT)
    self:RefreshSelectedItemText()
end

function ZO_ComboBox:SetMultiSelectionTextFormatter(textFormatter)
    self.multiSelectionTextFormatter = textFormatter or SI_COMBO_BOX_DEFAULT_MULTISELECTION_TEXT_FORMATTER
    self:RefreshSelectedItemText()
end

function ZO_ComboBox:EnableMultiSelect(multiSelectionTextFormatter, noSelectionText)
    -- Order matters; we'll wait to set self.m_enableMultiSelect so that we don't needlessly refresh the text when setting it.
    self:SetMultiSelectionTextFormatter(multiSelectionTextFormatter)
    self:SetNoSelectionText(noSelectionText)
    self.m_enableMultiSelect = true
    self:ClearAllSelections()
end

function ZO_ComboBox:DisableMultiSelect()
    self.m_enableMultiSelect = false
    self:ClearItems()
end

-- a maxNumSelections of 0 or nil indicates no limit on selections
function ZO_ComboBox:SetMaxSelections(maxNumSelections)
    if not self.m_enableMultiSelect then
        return false
    end

    if maxNumSelections == 0 then
        maxNumSelections = nil
    end

    -- if the new limit is less than the current limit, clear all the selections
    if maxNumSelections and (self.m_maxNumSelections == nil or maxNumSelections < self.m_maxNumSelections) then
        self:ClearAllSelections()
    end

    self.m_maxNumSelections = maxNumSelections
end

function ZO_ComboBox:SetMaxSelectionsErrorText(errorText)
    self.m_overrideMaxSelectionsErrorText = errorText
end

function ZO_ComboBox:SetOnSelectionBlockedCallback(callback)
    self.onSelectionBlockedCallback = callback
end

function ZO_ComboBox:GetSelectionBlockedErrorText()
    if self.m_overrideMaxSelectionsErrorText then
        return self.m_overrideMaxSelectionsErrorText
    end

    return GetString(SI_COMBO_BOX_MAX_SELECTIONS_REACHED_ALERT)
end

function ZO_ComboBox:RefreshSelectedItemText()
    if not self.m_enableMultiSelect then
        return
    end

    local numSelectedEntries = self:GetNumSelectedEntries()
    if numSelectedEntries > 0 then
        self:SetSelectedItemText(zo_strformat(self.multiSelectionTextFormatter, numSelectedEntries))
    else
        self:SetSelectedItemText(self.noSelectionText)
    end
end

function ZO_ComboBox:SetEnabled(enabled)
    self.m_container:SetMouseEnabled(enabled)
    self.m_openDropdown:SetEnabled(enabled)
    self.m_selectedItemText:SetColor(self:GetSelectedTextColor(enabled))

    self:HideDropdown()
end

function ZO_ComboBox:IsEnabled()
    return self.m_openDropdown:GetState() ~= BSTATE_DISABLED
end

function ZO_ComboBox:ForwardDimensionConstraintsToSelectedItemText()
    -- Get the current dimension constraints applied to the combo box.
    local currentMinX, currentMinY, currentMaxX, currentMaxY = self.m_container:GetDimensionConstraints()

    -- Reset the current dimension constraints to be unconstrained.
    self.m_container:SetDimensionConstraints(0, 0, 0, 0)

    -- Redirect dimension constraint changes to the SelectedItemText label.
    local mt = getmetatable(self.m_container).__index
    self.m_container._SetDimensionConstraints = mt.SetDimensionConstraints
    mt.SetDimensionConstraints = function(control, minX, minY, maxX, maxY)
        control:GetNamedChild("SelectedItemText"):SetDimensionConstraints(minX, minY, maxX, maxY)
    end

    -- Reapply the dimension constraints to the SelectedItemText label.
    self.m_container:GetNamedChild("SelectedItemText"):SetDimensionConstraints(currentMinX, currentMinY, currentMaxX, currentMaxY)
end

-- Begin ZO_ComboBox_Base overrides

function ZO_ComboBox:GetSelectedItemData()
    if self.m_enableMultiSelect then
        return self.m_multiSelectItemData
    else
        return self.m_selectedItemData
    end
end

function ZO_ComboBox:ClearItems()
    ZO_ComboBox_Base.ClearItems(self)

    self:ClearAllSelections()
end

function ZO_ComboBox:SelectItem(item, ignoreCallback)
    if not self.m_enableMultiSelect then
        return ZO_ComboBox_Base.SelectItem(self, item, ignoreCallback)
    end

    if item.enabled == false then
        return false
    end

    local newSelectionStatus = not self:IsItemSelected(item)
    if newSelectionStatus then
        if self.m_maxNumSelections == nil or self:GetNumSelectedEntries() < self.m_maxNumSelections then
            self:AddItemToSelected(item)
        else
            if not self.onSelectionBlockedCallback or self.onSelectionBlockedCallback(item) ~= true then
                local alertText = self:GetSelectionBlockedErrorText()
                if ZO_REMOTE_SCENE_CHANGE_ORIGIN == SCENE_MANAGER_MESSAGE_ORIGIN_INTERNAL then
                    RequestAlert(UI_ALERT_CATEGORY_ALERT, SOUNDS.GENERAL_ALERT_ERROR, alertText)
                elseif ZO_REMOTE_SCENE_CHANGE_ORIGIN == SCENE_MANAGER_MESSAGE_ORIGIN_INGAME then
                    ZO_Alert(UI_ALERT_CATEGORY_ALERT, SOUNDS.GENERAL_ALERT_ERROR, alertText)
                end
                return false
            end
        end
    else
        self:RemoveItemFromSelected(item)
    end

    if not ignoreCallback then
        PlaySound(SOUNDS.COMBO_CLICK)

        if item.callback then
            item.callback(self, item.name, item)
        end
    end

    self:RefreshSelectedItemText()
    -- refresh the data that was just selected so the selection highlight properly shows/hides
    if self.m_dropdownObject:IsOwnedByComboBox(self) then
        self.m_dropdownObject:Refresh(item)
    end

    return true
end

-- End ZO_ComboBox_Base overrides

-------
-- Combo Box Dropdown
-------

ZO_ComboBoxDropdown_Keyboard = ZO_InitializingObject:Subclass()

function ZO_ComboBoxDropdown_Keyboard:Initialize(control)
    self.control = control

    self.scrollControl = control:GetNamedChild("Scroll")

    self.spacing = 0
    self.nextScrollTypeId = DEFAULT_LAST_ENTRY_ID + 1

    self.owner = nil

    self:SetupScrollList()
end

function ZO_ComboBoxDropdown_Keyboard:SetupEntryLabel(labelControl, data)
    labelControl:SetText(data.name)
    labelControl:SetFont(self.owner:GetDropdownFont())
    local color = self.owner:GetItemNormalColor(data)
    labelControl:SetColor(color:UnpackRGBA())
    labelControl:SetHorizontalAlignment(self.horizontalAlignment)
end

function ZO_ComboBoxDropdown_Keyboard:SetupEntryBase(control, data, list)
    control.m_owner = self.owner
    control.m_data = data
    control.m_dropdownObject = self

    if self.owner:IsItemSelected(data:GetDataSource()) then
        if not control.m_selectionHighlight then
            control.m_selectionHighlight = CreateControlFromVirtual("$(parent)Selection", control, "ZO_ComboBoxEntry_SelectedHighlight")
        end

        control.m_selectionHighlight:SetHidden(false)
    elseif control.m_selectionHighlight then
        control.m_selectionHighlight:SetHidden(true)
    end
end

function ZO_ComboBoxDropdown_Keyboard:SetupEntry(control, data, list)
    self:SetupEntryBase(control, data, list)

    control.m_label = control:GetNamedChild("Label")
    self:SetupEntryLabel(control.m_label, data)
end

function ZO_ComboBoxDropdown_Keyboard:SetupScrollList()
    local function SetupScrollableEntry(...)
        self:SetupEntry(...)
    end
    local entryHeightWithSpacing = ZO_COMBO_BOX_ENTRY_TEMPLATE_HEIGHT + self.spacing
    -- To support spacing like regular combo boxes, a separate template needs to be stored for the last entry.
    ZO_ScrollList_AddDataType(self.scrollControl, DEFAULT_ENTRY_ID, "ZO_ComboBoxEntry", entryHeightWithSpacing, SetupScrollableEntry)
    ZO_ScrollList_AddDataType(self.scrollControl, DEFAULT_LAST_ENTRY_ID, "ZO_ComboBoxEntry", ZO_COMBO_BOX_ENTRY_TEMPLATE_HEIGHT, SetupScrollableEntry)

    ZO_ScrollList_EnableHighlight(self.scrollControl, "ZO_TallListHighlight")
end

function ZO_ComboBoxDropdown_Keyboard:AddCustomEntryTemplate(entryTemplate, entryHeight, setupFunction)
    if not self.customEntryTemplateInfos then
        self.customEntryTemplateInfos = {}
    end

    if self.customEntryTemplateInfos[entryTemplate] ~= nil then
        -- we have already added this template
        return
    end

    local customEntryInfo =
    {
        typeId = self.nextScrollTypeId,
        entryHeight = entryHeight,
    }

    self.customEntryTemplateInfos[entryTemplate] = customEntryInfo

    local entryHeightWithSpacing = entryHeight + self.spacing
    ZO_ScrollList_AddDataType(self.scrollControl, self.nextScrollTypeId, entryTemplate, entryHeightWithSpacing, setupFunction)
    ZO_ScrollList_AddDataType(self.scrollControl, self.nextScrollTypeId + 1, entryTemplate, entryHeight, setupFunction)

    self.nextScrollTypeId = self.nextScrollTypeId + 2
end

function ZO_ComboBoxDropdown_Keyboard:SetSpacing(spacing)
    self.spacing = spacing

    local newHeight = ZO_COMBO_BOX_ENTRY_TEMPLATE_HEIGHT + self.spacing
    ZO_ScrollList_UpdateDataTypeHeight(self.scrollControl, DEFAULT_ENTRY_ID, newHeight)

    if self.customEntryTemplateInfos then
        for entryTemplate, entryInfo in pairs(self.customEntryTemplateInfos) do
            ZO_ScrollList_UpdateDataTypeHeight(self.scrollControl, entryInfo.typeId, entryInfo.entryHeight + self.spacing)
        end
    end
end

function ZO_ComboBoxDropdown_Keyboard:Refresh(item)
    local entryData = nil
    if item then
        local dataList = ZO_ScrollList_GetDataList(self.scrollControl)
        for i, data in ipairs(dataList) do
            if data:GetDataSource() == item then
                entryData = data
                break
            end
        end
    end

    ZO_ScrollList_RefreshVisible(self.scrollControl, entryData)
end

function ZO_ComboBoxDropdown_Keyboard:CreateScrollableEntry(item, index, entryType)
    local entryData = ZO_EntryData:New(item)
    entryData.m_index = index
    entryData.m_owner = self.owner
    entryData.m_dropdownObject = self
    entryData:SetupAsScrollListDataEntry(entryType)
    return entryData
end

function ZO_ComboBoxDropdown_Keyboard:Show(comboBox, itemTable, minWidth, maxHeight, spacing)
    self.owner = comboBox

    local parentControl = comboBox:GetContainer()
    self.control:ClearAnchors()
    self.control:SetAnchor(TOPLEFT, parentControl, BOTTOMLEFT)

    ZO_ScrollList_Clear(self.scrollControl)

    self:SetSpacing(spacing)

    local numItems = #itemTable
    local dataList = ZO_ScrollList_GetDataList(self.scrollControl)

    local largestEntryWidth = 0
    local allItemsHeight = 0

    for i = 1, numItems do
        local item = itemTable[i]

        local isLastEntry = i == numItems
        local entryHeight = ZO_COMBO_BOX_ENTRY_TEMPLATE_HEIGHT
        local entryType = DEFAULT_ENTRY_ID
        if self.customEntryTemplateInfos and item.customEntryTemplate then
            local templateInfo = self.customEntryTemplateInfos[item.customEntryTemplate]
            if templateInfo then
                entryType = templateInfo.typeId
                entryHeight = templateInfo.entryHeight
            end
        end

        if isLastEntry then
            entryType = entryType + 1
        else
            entryHeight = entryHeight + self.spacing
        end

        allItemsHeight = allItemsHeight + entryHeight

        local entry = self:CreateScrollableEntry(item, i, entryType)
        table.insert(dataList, entry)

        local fontObject = self.owner:GetDropdownFontObject()
        local nameWidth = GetStringWidthScaled(fontObject, item.name, 1, SPACE_INTERFACE)
        if nameWidth > largestEntryWidth then
            largestEntryWidth = nameWidth
        end
    end

    -- using the exact width of the text can leave us with pixel rounding issues
    -- so just add 5 to make sure we don't truncate at certain screen sizes
    largestEntryWidth = largestEntryWidth + 5

    -- Allow the dropdown to automatically widen to fit the widest entry, but
    -- prevent it from getting any skinnier than the container's initial width
    local totalDropDownWidth = largestEntryWidth + (ZO_COMBO_BOX_ENTRY_TEMPLATE_LABEL_PADDING * 2) + ZO_SCROLL_BAR_WIDTH
    if totalDropDownWidth > minWidth then
        self.control:SetWidth(totalDropDownWidth)
    else
        self.control:SetWidth(minWidth)
    end

    -- Add padding one more time to account for potential pixel rounding issues that could cause the scroll bar to appear unnecessarily.
    allItemsHeight = allItemsHeight + (ZO_SCROLLABLE_COMBO_BOX_LIST_PADDING_Y * 2) + ZO_SCROLLABLE_COMBO_BOX_LIST_PADDING_Y

    local desiredHeight = maxHeight
    if allItemsHeight < desiredHeight then
        desiredHeight = allItemsHeight
    end

    self.control:SetHeight(desiredHeight)
    ZO_ScrollList_SetHeight(self.scrollControl, desiredHeight)

    ZO_ScrollList_Commit(self.scrollControl)
end

function ZO_ComboBoxDropdown_Keyboard:IsOwnedByComboBox(comboBox)
    return self.owner == comboBox
end

function ZO_ComboBoxDropdown_Keyboard:IsHidden()
    local isHidden = self.control:IsHidden()
    return isHidden
end

function ZO_ComboBoxDropdown_Keyboard:SetHidden(isHidden)
    self.control:SetHidden(isHidden)

    ZO_ScrollList_Clear(self.scrollControl)
    ZO_ScrollList_Commit(self.scrollControl)
end

function ZO_ComboBoxDropdown_Keyboard:IsMouseOverControl()
    return MouseIsOver(self.control)
end

function ZO_ComboBoxDropdown_Keyboard:OnMouseEnterEntry(control)
    ZO_ScrollList_MouseEnter(self.scrollControl, control)

    if self.owner then
        self.owner:OnMouseEnterEntryBase(control)
        local data = control.m_data
        self.owner:HighlightLabel(control.m_label, data)
        if data.onEnter then
            data.onEnter(control)
        end
    end
end

function ZO_ComboBoxDropdown_Keyboard:OnMouseExitEntry(control)
    ZO_ScrollList_MouseExit(self.scrollControl, control)

    if self.owner then
        self.owner:OnMouseExitEntryBase(control)
        local data = control.m_data
        self.owner:UnhighlightLabel(control.m_label, data)
        if data.onExit then
            data.onExit(control)
        end
    end
end

function ZO_ComboBoxDropdown_Keyboard:OnEntrySelected(control)
    if self.owner then
        self.owner:SetSelected(control.m_data.m_index)
    end
end

function ZO_ComboBoxDropdown_Keyboard.OnClicked(control, button, upInside)
    if button == MOUSE_BUTTON_INDEX_LEFT and upInside then
        ZO_ComboBox_OpenDropdown(control)
        PlaySound(SOUNDS.COMBO_CLICK)
    end
end

function ZO_ComboBoxDropdown_Keyboard.OnEntryMouseEnter(control)
    local dropdown = control.m_dropdownObject
    if dropdown then
        dropdown:OnMouseEnterEntry(control)
    end
end

function ZO_ComboBoxDropdown_Keyboard.OnEntryMouseExit(control)
    local dropdown = control.m_dropdownObject
    if dropdown then
        dropdown:OnMouseExitEntry(control)
    end
end

function ZO_ComboBoxDropdown_Keyboard.OnEntryMouseUp(control, button, upInside)
    if button == MOUSE_BUTTON_INDEX_LEFT and upInside then
        local dropdown = control.m_dropdownObject
        if dropdown then
            dropdown:OnEntrySelected(control)
        end
    end
end

function ZO_ComboBoxDropdown_Keyboard.InitializeFromControl(control)
    local dropdownObject = ZO_ComboBoxDropdown_Keyboard:New(control)
    control.object = dropdownObject
end