diff --git a/esoui/ingame/lfg/keyboard/zo_activityfindertemplate_keyboard.lua b/esoui/ingame/lfg/keyboard/zo_activityfindertemplate_keyboard.lua
new file mode 100644
index 0000000..2111c3e
--- /dev/null
+++ b/esoui/ingame/lfg/keyboard/zo_activityfindertemplate_keyboard.lua
@@ -0,0 +1,434 @@
+local GROUP_SIZE_ICON_FORMAT = zo_iconFormat("EsoUI/Art/LFG/LFG_icon_groupSize.dds", 32, 32)
+
+------------------
+--Initialization--
+------------------
+
+ZO_ActivityFinderTemplate_Keyboard = ZO_ActivityFinderTemplate_Shared:Subclass()
+
+function ZO_ActivityFinderTemplate_Keyboard:New(...)
+ return ZO_ActivityFinderTemplate_Shared.New(self, ...)
+end
+
+function ZO_ActivityFinderTemplate_Keyboard:Initialize(dataManager, categoryData)
+ local control = CreateControlFromVirtual(dataManager:GetName() .. "_Keyboard", GuiRoot, "ZO_ActivityFinderTemplateTopLevel_Keyboard")
+ ZO_ActivityFinderTemplate_Shared.Initialize(self, control, dataManager, categoryData)
+end
+
+function ZO_ActivityFinderTemplate_Keyboard:InitializeControls()
+ self.listSection = self.control:GetNamedChild("ListSection")
+ self.lfmPromptSection = self.control:GetNamedChild("LFMPromptSection")
+ self.lfmPromptBodyLabel = self.lfmPromptSection:GetNamedChild("Body")
+
+ self.filterControl = self.control:GetNamedChild("Filter")
+ self.notLeaderLabel = self.control:GetNamedChild("NotLeader")
+ self.joinQueueButton = self.control:GetNamedChild("QueueButton")
+ self.lockReasonLabel = self.control:GetNamedChild("LockReason")
+
+ ZO_ActivityFinderTemplate_Shared.InitializeControls(self, "ZO_ActivityFinderTemplateRewardTemplate_Keyboard")
+
+ self:InitializeNavigationList()
+end
+
+function ZO_ActivityFinderTemplate_Keyboard:InitializeFilters()
+ local filterComboBox = ZO_ComboBox_ObjectFromContainer(self.filterControl)
+ filterComboBox:SetSortsItems(false)
+ filterComboBox:SetFont("ZoFontWinT1")
+ filterComboBox:SetSpacing(4)
+ self.filterComboBox = filterComboBox
+ self:RefreshFilters()
+end
+
+function ZO_ActivityFinderTemplate_Keyboard:InitializeNavigationList()
+ self.navigationTree = ZO_Tree:New(self.listSection:GetNamedChild("ScrollChild"), 60, -10, 600)
+
+ local openTexture = "EsoUI/Art/Buttons/tree_open_up.dds"
+ local closedTexture = "EsoUI/Art/Buttons/tree_closed_up.dds"
+ local overOpenTexture = "EsoUI/Art/Buttons/tree_open_over.dds"
+ local overClosedTexture = "EsoUI/Art/Buttons/tree_closed_over.dds"
+
+ local function TreeHeaderSetup(node, control, name, open)
+ control.text:SetModifyTextType(MODIFY_TEXT_TYPE_UPPERCASE)
+ control.text:SetText(name)
+
+ control.icon:SetTexture(open and openTexture or closedTexture)
+ control.iconHighlight:SetTexture(open and overOpenTexture or overClosedTexture)
+
+ local ENABLED = true
+ local DISABLE_SCALING = true
+ ZO_IconHeader_Setup(control, open, ENABLED, DISABLE_SCALING)
+ end
+
+ local function TreeEntrySetup(node, control, data, open)
+ control.text:SetText(data.nameKeyboard)
+ local isLocked = data.isLocked
+ control.enabled = not isLocked and not IsCurrentlySearchingForGroup()
+ control.check:SetHidden(isLocked)
+ control.lockIcon:SetHidden(not isLocked)
+ control.text:SetEnabled(control.enabled)
+
+ if not isLocked then
+ ZO_CheckButton_SetCheckState(control.check, data.isSelected)
+ end
+ end
+
+ self.navigationTree:AddTemplate("ZO_ActivityFinderTemplateNavigationHeader_Keyboard", TreeHeaderSetup, nil, nil, nil, 0)
+
+ local function TreeEntryEquality(left, right)
+ return left.name == right.name
+ end
+
+ self.navigationTree:AddTemplate("ZO_ActivityFinderTemplateNavigationEntry_Keyboard", TreeEntrySetup, nil, TreeEntryEquality)
+ self.navigationTree:SetOpenAnimation("ZO_TreeOpenAnimation")
+
+ self.lfgActivityIndexToTreeNode = {}
+end
+
+function ZO_ActivityFinderTemplate_Keyboard:InitializeFragment()
+ local function OnStateChange(oldState, newState)
+ if newState == SCENE_SHOWN then
+ local shouldShowLFMPrompt, lfmPromptActivityName = self:GetLFMPromptInfo()
+ if shouldShowLFMPrompt then
+ self.lfmPromptBodyLabel:SetText(zo_strformat(GetString(SI_LFG_FIND_REPLACEMENT_TEXT), lfmPromptActivityName))
+ self.lfmPromptSection:SetHidden(false)
+ self:HidePrimaryControls()
+ end
+
+ self:RefreshView()
+ end
+ end
+ self.fragment = ZO_FadeSceneFragment:New(self.control)
+ self.fragment:RegisterCallback("StateChange", OnStateChange)
+ self.categoryData.categoryFragment = self.fragment
+
+ GROUP_MENU_KEYBOARD:AddCategory(self.categoryData)
+end
+
+function ZO_ActivityFinderTemplate_Keyboard:RegisterEvents()
+ ZO_ActivityFinderTemplate_Shared.RegisterEvents(self)
+ ZO_ACTIVITY_FINDER_ROOT_MANAGER:RegisterCallback("OnSelectionsChanged", function(...) self:RefreshJoinQueueButton(...) end)
+end
+
+-----------
+--Updates--
+-----------
+
+function ZO_ActivityFinderTemplate_Keyboard:RefreshView()
+ if not self.fragment:IsShowing() then
+ return
+ end
+
+ local shouldShowLFMPrompt = self:GetLFMPromptInfo()
+ if not shouldShowLFMPrompt then
+ self:ResetLFMPrompt()
+ end
+
+ if not IsUnitSoloOrGroupLeader("player") then
+ self:HidePrimaryControls()
+ self.notLeaderLabel:SetHidden(false)
+ return
+ else
+ self:ResetNotLeaderWarning()
+ end
+
+ local filterData = self.filterComboBox:GetSelectedItemData().data
+ if filterData.singular then
+ local activityType
+ local lockReasonText
+ if filterData.random then
+ activityType = filterData.activityType
+ ZO_ACTIVITY_FINDER_ROOT_MANAGER:SetActivityTypeSelected(activityType, true)
+ lockReasonText = ZO_ACTIVITY_FINDER_ROOT_MANAGER:GetLockReasonForActivityType(activityType)
+ else
+ activityType = filterData.location.activityType
+ ZO_ACTIVITY_FINDER_ROOT_MANAGER:SetLocationSelected(filterData.location, true)
+ if filterData.location.isLocked then
+ lockReasonText = filterData.location.lockReasonText
+ end
+ end
+
+ local levelLockText = self:GetLevelLockTextByActivity(activityType)
+ if levelLockText then
+ lockReasonText = levelLockText
+ end
+
+ if lockReasonText then
+ self.lockReasonLabel:SetText(zo_iconTextFormat("EsoUI/Art/Miscellaneous/locked_disabled.dds", 16, 16, lockReasonText))
+ else
+ self.lockReasonLabel:SetText("")
+ end
+ else
+ ZO_ClearTable(self.lfgActivityIndexToTreeNode)
+ self.navigationTree:Reset()
+
+ for activityIndex, activityType in ipairs(filterData.activityTypes) do
+ local isLocked = self:GetLevelLockInfoByActivity(activityType)
+ if not isLocked then
+ local locationData = ZO_ACTIVITY_FINDER_ROOT_MANAGER:GetLocationsData(activityType)
+ local headerText = GetString("SI_LFGACTIVITY", activityType)
+ local HEADER_OPEN = true
+ local headerNode = self.navigationTree:AddNode("ZO_ActivityFinderTemplateNavigationHeader_Keyboard", headerText, nil, SOUNDS.JOURNAL_PROGRESS_CATEGORY_SELECTED, HEADER_OPEN)
+
+ for locationIndex, location in ipairs(locationData) do
+ local lfgNode = self.navigationTree:AddNode("ZO_ActivityFinderTemplateNavigationEntry_Keyboard", location, headerNode, SOUNDS.JOURNAL_PROGRESS_SUB_CATEGORY_SELECTED)
+ if not self.lfgActivityIndexToTreeNode[activityType] then
+ self.lfgActivityIndexToTreeNode[activityType] = {}
+ end
+ self.lfgActivityIndexToTreeNode[activityType][location.lfgIndex] = lfgNode
+ end
+ end
+ end
+
+ self.navigationTree:Commit()
+ end
+
+ self:RefreshJoinQueueButton()
+end
+
+function ZO_ActivityFinderTemplate_Keyboard:RefreshFilters()
+ local function OnFilterChanged(...)
+ self:OnFilterChanged(...)
+ end
+
+ local previousSelection = self.filterComboBox:GetSelectedItemData()
+ local reselectedEntry = nil
+ self.filterComboBox:ClearItems()
+
+ local modes = self.dataManager:GetFilterModeData()
+ local activityTypes = modes:GetActivityTypes()
+ --Add potential randoms
+ for _, activityType in ipairs(activityTypes) do
+ local randomInfo = modes:GetRandomInfo(activityType)
+ if randomInfo and DoesLFGActivityHasAllOption(activityType) then
+ local isLocked = self:GetLevelLockInfoByActivity(activityType)
+ if not isLocked then
+ local activityName = zo_strformat(SI_ACTIVITY_FINDER_RANDOM_TITLE_FORMAT, GetString("SI_LFGACTIVITY", activityType))
+ local minGroupSize, maxGroupSize = ZO_ACTIVITY_FINDER_ROOT_MANAGER:GetGroupSizeRangeForActivityType(activityType)
+ level = true
+ rank = true
+ local entry = ZO_ComboBox:CreateItemEntry(activityName, OnFilterChanged)
+ entry.data =
+ {
+ singular = true,
+ random = true,
+ activityType = activityType,
+ activityName = activityName,
+ description = randomInfo.description,
+ background = randomInfo.keyboardBackground,
+ minGroupSize = minGroupSize,
+ maxGroupSize = maxGroupSize,
+ }
+
+ if previousSelection and previousSelection.name == activityName then
+ reselectedEntry = entry
+ end
+ self.filterComboBox:AddItem(entry, ZO_COMBOBOX_SUPRESS_UPDATE)
+ end
+ end
+ end
+
+ --Add specifics
+ if modes:AreSpecificsInSubmenu() then
+ local entry = ZO_ComboBox:CreateItemEntry(modes:GetSpecificFilterName(), OnFilterChanged)
+ entry.data =
+ {
+ singular = false,
+ activityTypes = activityTypes,
+ }
+
+ if previousSelection and previousSelection.name == entry.name then
+ reselectedEntry = entry
+ end
+
+ self.filterComboBox:AddItem(entry, ZO_COMBOBOX_SUPRESS_UPDATE)
+ else
+ for _, activityType in ipairs(activityTypes) do
+ local locationData = ZO_ACTIVITY_FINDER_ROOT_MANAGER:GetLocationsData(activityType)
+ for _, location in ipairs(locationData) do
+ local entry = ZO_ComboBox:CreateItemEntry(location.nameKeyboard, OnFilterChanged)
+ entry.data =
+ {
+ singular = true,
+ random = false,
+ activityName = location.nameKeyboard,
+ description = location.description,
+ background = location.descriptionTextureLargeKeyboard,
+ location = location,
+ minGroupSize = location.minGroupSize,
+ maxGroupSize = location.maxGroupSize,
+ }
+
+ if previousSelection and previousSelection.name == entry.name then
+ reselectedEntry = entry
+ end
+
+ self.filterComboBox:AddItem(entry, ZO_COMBOBOX_SUPRESS_UPDATE)
+ end
+ end
+ end
+
+ if reselectedEntry then
+ local IGNORE_CALLBACK = false
+ self.filterComboBox:SelectItem(reselectedEntry, IGNORE_CALLBACK)
+ else
+ self.filterComboBox:SelectFirstItem()
+ end
+end
+
+function ZO_ActivityFinderTemplate_Keyboard:RefreshJoinQueueButton()
+ self.joinQueueButton:SetEnabled(ZO_ACTIVITY_FINDER_ROOT_MANAGER:IsAnyLocationSelected() and not ZO_ACTIVITY_FINDER_ROOT_MANAGER:GetIsCurrentlyInQueue())
+end
+
+----------
+--Events--
+----------
+
+function ZO_ActivityFinderTemplate_Keyboard:OnFilterChanged(comboBox, entryText, entry)
+ ZO_ACTIVITY_FINDER_ROOT_MANAGER:ClearSelections()
+
+ local data = entry.data
+ self.singularSection:SetHidden(not data.singular)
+ self.listSection:SetHidden(data.singular)
+
+ if data.singular then
+ self.titleLabel:SetText(data.activityName)
+ self.descriptionLabel:SetText(data.description)
+ self.backgroundTexture:SetTexture(data.background)
+ self.SetGroupSizeRangeText(self.groupSizeRangeLabel, data, GROUP_SIZE_ICON_FORMAT)
+ self:RefreshRewards(data.random, data.activityType)
+ end
+
+ self:RefreshView()
+end
+
+function ZO_ActivityFinderTemplate_Keyboard:OnGroupingToolsStatusUpdate()
+ self:RefreshView()
+end
+
+function ZO_ActivityFinderTemplate_Keyboard:OnCooldownsUpdate()
+ local filterData = self.filterComboBox:GetSelectedItemData().data
+ if filterData.singular then
+ self:RefreshRewards(filterData.random, filterData.activityType)
+ end
+end
+
+function ZO_ActivityFinderTemplate_Keyboard:ShowPrimaryControls()
+ self.filterControl:SetHidden(false)
+ self.joinQueueButton:SetHidden(false)
+ local filterData = self.filterComboBox:GetSelectedItemData().data
+ if filterData.singular then
+ self.singularSection:SetHidden(false)
+ self.lockReasonLabel:SetHidden(false)
+ else
+ self.listSection:SetHidden(false)
+ end
+end
+
+function ZO_ActivityFinderTemplate_Keyboard:HidePrimaryControls()
+ self.singularSection:SetHidden(true)
+ self.listSection:SetHidden(true)
+ self.filterControl:SetHidden(true)
+ self.joinQueueButton:SetHidden(true)
+ self.lockReasonLabel:SetHidden(true)
+end
+
+function ZO_ActivityFinderTemplate_Keyboard:ResetLFMPrompt()
+ if not self.lfmPromptSection:IsHidden() then
+ self.lfmPromptSection:SetHidden(true)
+ self:ShowPrimaryControls()
+ end
+end
+
+function ZO_ActivityFinderTemplate_Keyboard:ResetNotLeaderWarning()
+ if not self.notLeaderLabel:IsHidden() then
+ self.notLeaderLabel:SetHidden(true)
+ self:ShowPrimaryControls()
+ end
+end
+
+function ZO_ActivityFinderTemplate_Keyboard:OnHandleLFMPromptResponse()
+ if self.fragment:IsShowing() then
+ self:ResetLFMPrompt()
+ end
+end
+
+-------------
+--Accessors--
+-------------
+
+function ZO_ActivityFinderTemplate_Keyboard:GetFragment()
+ return self.fragment
+end
+
+----------
+--Layout--
+----------
+
+function ZO_ActivityFinderTemplate_Keyboard.ShowActivityTooltip(control)
+ local data = control.node.data
+ local tooltip = ZO_ActivityFinderTemplateTooltip_Keyboard
+ InitializeTooltip(tooltip, control, TOPRIGHT, -70, 0, TOPLEFT)
+
+ local groupSizeLabel = tooltip:GetNamedChild("ContentsGroupSizeLabel")
+ ZO_ActivityFinderTemplate_Shared.SetGroupSizeRangeText(groupSizeLabel, data, GROUP_SIZE_ICON_FORMAT)
+
+ local nameLabel = tooltip:GetNamedChild("ContentsNameLabel")
+ nameLabel:SetText(data.nameKeyboard)
+
+ local artTexture = tooltip:GetNamedChild("ArtTexture")
+ artTexture:SetTexture(data.descriptionTextureSmallKeyboard)
+
+ local lockedInfoLabel = tooltip:GetNamedChild("ContentsLockedInfoLabel")
+ if data.isLocked then
+ lockedInfoLabel:SetHidden(false)
+ lockedInfoLabel:SetColor(ZO_ERROR_COLOR:UnpackRGBA())
+ lockedInfoLabel:SetText(zo_iconTextFormat("EsoUI/Art/Miscellaneous/locked_disabled.dds", 16, 16, data.lockReasonText))
+ else
+ lockedInfoLabel:SetHidden(true)
+ end
+end
+
+function ZO_ActivityFinderTemplate_Keyboard.HideActivityTooltip()
+ ClearTooltip(ZO_ActivityFinderTemplateTooltip_Keyboard)
+end
+
+----------------
+--Global Hooks--
+----------------
+
+function ZO_ActivityFinderTemplateNavigationEntryKeyboard_OnClicked(control, button)
+ if control.enabled then
+ ZO_CheckButton_OnClicked(control.check)
+ ZO_ACTIVITY_FINDER_ROOT_MANAGER:ToggleLocationSelected(control.node.data)
+ end
+end
+
+function ZO_ActivityFinderTemplateNavigationEntryKeyboard_OnMouseEnter(control)
+ if control.enabled then
+ ZO_SelectableLabel_OnMouseEnter(control.text)
+ control.check:SetShowingHighlight(true)
+ end
+ ZO_ActivityFinderTemplate_Keyboard.ShowActivityTooltip(control)
+end
+
+function ZO_ActivityFinderTemplateNavigationEntryKeyboard_OnMouseExit(control)
+ if control.enabled then
+ ZO_SelectableLabel_OnMouseExit(control.text, false)
+ control.check:SetShowingHighlight(false)
+ end
+ ZO_ActivityFinderTemplate_Keyboard.HideActivityTooltip()
+end
+
+function ZO_ActivityFinderTemplateQueueButtonKeyboard_OnClicked(control)
+ if not IsCurrentlySearchingForGroup() then
+ ZO_ACTIVITY_FINDER_ROOT_MANAGER:StartSearch()
+ end
+end
+
+function ZO_ActivityFinderTemplateNavigationEntryKeyboard_OnInitialized(control)
+ control.lockIcon = control:GetNamedChild("LockIcon")
+ control.check = control:GetNamedChild("Check")
+ control.text = control:GetNamedChild("Text")
+ local fontHeight = control.text:GetFontHeight()
+ control:SetDimensionConstraints(0, fontHeight, 0, 0)
+end
\ No newline at end of file