Back to Home

ESO Lua File v101044

libraries/zo_combobox/zo_combobox.lua

[◄ back to folders ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
--[[
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
    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)
end
    self.m_container:SetHandler("OnMouseEnter", onMouseEnterCallback)
    self.m_container:SetHandler("OnMouseExit", onMouseExitCallback)
end
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 = {}
    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)
end
function ZO_ComboBox:SetMultiSelectionTextFormatter(textFormatter)
    self.multiSelectionTextFormatter = textFormatter or SI_COMBO_BOX_DEFAULT_MULTISELECTION_TEXT_FORMATTER
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
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)
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
-- 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)
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
    PlaySound(SOUNDS.COMBO_CLICK)
    if item.callback and not ignoreCallback then
        item.callback(self, item.name, item)
    end
    -- 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
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)
    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: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
        PlaySound(SOUNDS.COMBO_CLICK)
    end
end
function ZO_ComboBoxDropdown_Keyboard.OnEntryMouseEnter(control)
    local dropdown = control.m_dropdownObject
    if dropdown then
    end
end
function ZO_ComboBoxDropdown_Keyboard.OnEntryMouseExit(control)
    local dropdown = control.m_dropdownObject
    if dropdown then
    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