Blizzard_TradeSkillUI/Blizzard_TradeSkillRecipeList.lua

Blizzard_TradeSkillUI/Blizzard_TradeSkillRecipeList.lua (8.3.7.35284; unchanged since 8.0.1.27101)
  1. local ROW_HEIGHT = 16;
  2. local LIST_FULL_HEIGHT = 405;
  3. local FAVORITES_CATEGORY_ID = -1;
  4. TradeSkillRecipeListMixin = {};
  5. local LEARNED_TAB = 1;
  6. local UNLEARNED_TAB = 2;
  7. function TradeSkillRecipeListMixin:OnLoad()
  8. HybridScrollFrame_CreateButtons(self, "TradeSkillRowButtonTemplate", 0, 0);
  9. self.update = self.RefreshDisplay;
  10. self.stepSize = ROW_HEIGHT * 2;
  11. self.dataList = {};
  12. PanelTemplates_SetNumTabs(self, 2);
  13. UIDropDownMenu_Initialize(self.RecipeOptionsMenu, function(...) self:InitRecipeOptionsMenu(...) end, "MENU");
  14. self:OnLearnedTabClicked();
  15. end
  16. function TradeSkillRecipeListMixin:InitRecipeOptionsMenu(dropdown, level)
  17. local info = UIDropDownMenu_CreateInfo();
  18. info.notCheckable = true;
  19. info.disabled = nil;
  20. local isFavorite = self.contextMenuRecipeID and C_TradeSkillUI.IsRecipeFavorite(self.contextMenuRecipeID);
  21. if isFavorite then
  22. info.text = BATTLE_PET_UNFAVORITE;
  23. info.func = function()
  24. C_TradeSkillUI.SetRecipeFavorite(self.contextMenuRecipeID, false);
  25. end
  26. else
  27. info.text = BATTLE_PET_FAVORITE;
  28. info.func = function()
  29. C_TradeSkillUI.SetRecipeFavorite(self.contextMenuRecipeID, true);
  30. end
  31. end
  32. UIDropDownMenu_AddButton(info, level);
  33. info.disabled = nil;
  34. info.text = CANCEL
  35. info.func = nil
  36. UIDropDownMenu_AddButton(info, level)
  37. end
  38. function TradeSkillRecipeListMixin:OnUpdate()
  39. if self.pendingRefresh then
  40. if self.tradeSkillChanged then
  41. self.collapsedCategories = {};
  42. self.tradeSkillChanged = nil;
  43. end
  44. if C_TradeSkillUI.IsRecipeSearchInProgress() then
  45. return;
  46. end
  47. if not C_TradeSkillUI.IsTradeSkillReady() then
  48. wipe(self.dataList);
  49. self:SetSelectedRecipeID(nil);
  50. self:RefreshDisplay();
  51. return;
  52. end
  53. self:RebuildDataList();
  54. if self:VerifySelection() then
  55. self:RefreshDisplay();
  56. end
  57. self.pendingRefresh = nil;
  58. if self.pendingForceRecipeIDIntoView then
  59. self:SelectedAndForceRecipeIDIntoView(self.pendingForceRecipeIDIntoView);
  60. self.pendingForceRecipeIDIntoView = nil;
  61. end
  62. end
  63. end
  64. function TradeSkillRecipeListMixin:OnDataSourceChanging()
  65. wipe(self.dataList);
  66. self:SetSelectedRecipeID(nil);
  67. for i, tradeSkillButton in ipairs(self.buttons) do
  68. tradeSkillButton:Clear();
  69. end
  70. self:Refresh();
  71. end
  72. function TradeSkillRecipeListMixin:OnDataSourceChanged(tradeSkillChanged)
  73. self.selectedRecipeID = nil;
  74. self.tradeSkillChanged = self.tradeSkillChanged or tradeSkillChanged;
  75. local isNPCCrafting = C_TradeSkillUI.IsNPCCrafting();
  76. self:OnLearnedTabClicked();
  77. self.LearnedTab:SetShown(not isNPCCrafting);
  78. self.UnlearnedTab:SetShown(not isNPCCrafting);
  79. if (not isNPCCrafting and not GetCVarBitfield("closedInfoFrames", LE_FRAME_TUTORIAL_TRADESKILL_UNLEARNED_TAB)) then
  80. SetButtonPulse(self.UnlearnedTab, 60, 1);
  81. end
  82. self:Refresh();
  83. end
  84. function TradeSkillRecipeListMixin:OnHeaderButtonClicked(categoryButton, categoryInfo, mouseButton)
  85. self:SetCategoryCollapsed(categoryInfo.categoryID, not self:IsCategoryCollapsed(categoryInfo.categoryID));
  86. end
  87. function TradeSkillRecipeListMixin:SetCategoryCollapsed(categoryID, collapsed)
  88. if self.collapsedCategories[categoryID] ~= collapsed then
  89. self.collapsedCategories[categoryID] = collapsed;
  90. self:Refresh();
  91. end
  92. end
  93. function TradeSkillRecipeListMixin:IsCategoryCollapsed(categoryID)
  94. return self.collapsedCategories[categoryID] == true;
  95. end
  96. function TradeSkillRecipeListMixin:OnRecipeButtonClicked(recipeButton, recipeInfo, mouseButton)
  97. if mouseButton == "LeftButton" then
  98. self:SetSelectedRecipeID(recipeInfo.recipeID);
  99. elseif mouseButton == "RightButton" then
  100. if recipeInfo.learned and not C_TradeSkillUI.IsTradeSkillGuild() and not C_TradeSkillUI.IsNPCCrafting() and not C_TradeSkillUI.IsTradeSkillLinked() then
  101. self.contextMenuRecipeID = recipeInfo.recipeID;
  102. ToggleDropDownMenu(1, nil, self.RecipeOptionsMenu, recipeButton, 0, 0);
  103. PlaySound(SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON);
  104. end
  105. end
  106. end
  107. function TradeSkillRecipeListMixin:OnLearnedTabClicked()
  108. PanelTemplates_SetTab(self, LEARNED_TAB);
  109. C_TradeSkillUI.SetOnlyShowLearnedRecipes(true);
  110. C_TradeSkillUI.SetOnlyShowUnlearnedRecipes(false);
  111. self:Refresh();
  112. end
  113. function TradeSkillRecipeListMixin:OnUnlearnedTabClicked()
  114. PanelTemplates_SetTab(self, UNLEARNED_TAB);
  115. C_TradeSkillUI.SetOnlyShowLearnedRecipes(false);
  116. C_TradeSkillUI.SetOnlyShowUnlearnedRecipes(true);
  117. SetButtonPulse(self.UnlearnedTab, 0, 1);
  118. SetCVarBitfield("closedInfoFrames", LE_FRAME_TUTORIAL_TRADESKILL_UNLEARNED_TAB, true);
  119. self:Refresh();
  120. end
  121. function TradeSkillRecipeListMixin:Refresh()
  122. self.pendingRefresh = true;
  123. end
  124. function TradeSkillRecipeListMixin:SetRecipeChangedCallback(recipeChangedCallback)
  125. self.recipeChangedCallback = recipeChangedCallback;
  126. end
  127. function TradeSkillRecipeListMixin:FindBestStarRankLinksForRecipe(recipeInfo)
  128. local startingRecipeInfo = recipeInfo;
  129. while startingRecipeInfo.previousRecipeInfo do
  130. startingRecipeInfo = startingRecipeInfo.previousRecipeInfo;
  131. end
  132. local bestRecipeInfo = startingRecipeInfo;
  133. while bestRecipeInfo.nextRecipeInfo and bestRecipeInfo.nextRecipeInfo.learned do
  134. bestRecipeInfo = bestRecipeInfo.nextRecipeInfo;
  135. end
  136. return bestRecipeInfo;
  137. end
  138. local function IsFavoriteRecipesCategoriesVisible(recipeInfo)
  139. local categoryData = C_TradeSkillUI.GetCategoryInfo(recipeInfo.categoryID);
  140. if categoryData.enabled then
  141. if categoryData.parentCategoryID then
  142. local parentCategoryData = C_TradeSkillUI.GetCategoryInfo(recipeInfo.categoryID);
  143. return parentCategoryData.enabled;
  144. end
  145. return true;
  146. end
  147. return false;
  148. end
  149. function TradeSkillRecipeListMixin:RebuildDataList()
  150. wipe(self.dataList);
  151. self.recipeIDs = C_TradeSkillUI.GetFilteredRecipeIDs(self.recipeIDs);
  152. local currentCategoryID, currentParentCategoryID;
  153. local isCurrentCategoryEnabled, isCurrentParentCategoryEnabled = true, true;
  154. local favoritesIndex = nil;
  155. local starRankLinks = {};
  156. for i, recipeID in ipairs(self.recipeIDs) do
  157. if not starRankLinks[recipeID] then
  158. local recipeInfo = C_TradeSkillUI.GetRecipeInfo(recipeID);
  159. TradeSkillFrame_GenerateRankLinks(recipeInfo, starRankLinks);
  160. recipeInfo = self:FindBestStarRankLinksForRecipe(recipeInfo);
  161. if recipeInfo.favorite then
  162. if IsFavoriteRecipesCategoriesVisible(recipeInfo) then
  163. if not favoritesIndex then
  164. -- Insert the special favorites category header
  165. favoritesIndex = 1;
  166. table.insert(self.dataList, favoritesIndex, { type = "header", numIndents = 0, name = FAVORITES, categoryID = FAVORITES_CATEGORY_ID, });
  167. favoritesIndex = favoritesIndex + 1; -- Start inserting new favorites here
  168. end
  169. if not self:IsCategoryCollapsed(FAVORITES_CATEGORY_ID) then
  170. recipeInfo.numIndents = 0; -- No subcategories under favorites, force no indents
  171. table.insert(self.dataList, favoritesIndex, recipeInfo);
  172. favoritesIndex = favoritesIndex + 1;
  173. end
  174. end
  175. else
  176. if recipeInfo.categoryID ~= currentCategoryID then
  177. local categoryData = C_TradeSkillUI.GetCategoryInfo(recipeInfo.categoryID);
  178. isCurrentCategoryEnabled = categoryData.enabled;
  179. if categoryData.parentCategoryID ~= currentParentCategoryID then
  180. currentParentCategoryID = categoryData.parentCategoryID;
  181. if currentParentCategoryID then
  182. local parentCategoryData = C_TradeSkillUI.GetCategoryInfo(currentParentCategoryID);
  183. isCurrentParentCategoryEnabled = parentCategoryData.enabled;
  184. if isCurrentParentCategoryEnabled then
  185. table.insert(self.dataList, parentCategoryData);
  186. end
  187. else
  188. isCurrentParentCategoryEnabled = true;
  189. end
  190. end
  191. if isCurrentCategoryEnabled and isCurrentParentCategoryEnabled and (not currentParentCategoryID or not self:IsCategoryCollapsed(currentParentCategoryID)) then
  192. table.insert(self.dataList, categoryData);
  193. currentCategoryID = recipeInfo.categoryID;
  194. end
  195. end
  196. if isCurrentCategoryEnabled and isCurrentParentCategoryEnabled and (not currentParentCategoryID or not self:IsCategoryCollapsed(currentParentCategoryID)) and not self:IsCategoryCollapsed(currentCategoryID) then
  197. table.insert(self.dataList, recipeInfo);
  198. end
  199. end
  200. end
  201. end
  202. if (not C_TradeSkillUI.GetOnlyShowUnlearnedRecipes()) then
  203. local categories = {C_TradeSkillUI.GetCategories()};
  204. for i, categoryID in ipairs(categories) do
  205. if (C_TradeSkillUI.IsEmptySkillLineCategory(categoryID)) then
  206. local categoryData = C_TradeSkillUI.GetCategoryInfo(categoryID);
  207. table.insert(self.dataList, categoryData);
  208. end
  209. end
  210. end
  211. end
  212. function TradeSkillRecipeListMixin:VerifySelection()
  213. local firstVisibleRecipeID = nil;
  214. if self.selectedRecipeID then
  215. for i, listData in ipairs(self.dataList) do
  216. if listData.type == "recipe" then
  217. if self.selectedRecipeID == listData.recipeID then
  218. -- Recipe is still valid, do nothing
  219. return true;
  220. end
  221. if not firstVisibleRecipeID then
  222. firstVisibleRecipeID = listData.recipeID;
  223. end
  224. local currentRecipeInfo = listData.previousRecipeInfo;
  225. while currentRecipeInfo do
  226. if self.selectedRecipeID == currentRecipeInfo.recipeID then
  227. -- Recipe rank changed, switch to the new rank
  228. return not self:SetSelectedRecipeID(listData.recipeID);
  229. end
  230. currentRecipeInfo = currentRecipeInfo.previousRecipeInfo;
  231. end
  232. local currentRecipeInfo = listData.nextRecipeInfo;
  233. while currentRecipeInfo do
  234. if self.selectedRecipeID == currentRecipeInfo.recipeID then
  235. -- Recipe rank changed, switch to the new rank
  236. return not self:SetSelectedRecipeID(listData.recipeID);
  237. end
  238. currentRecipeInfo = currentRecipeInfo.nextRecipeInfo;
  239. end
  240. end
  241. end
  242. else
  243. for i, listData in ipairs(self.dataList) do
  244. if listData.type == "recipe" then
  245. firstVisibleRecipeID = listData.recipeID;
  246. break;
  247. end
  248. end
  249. end
  250. -- Couldn't find recipe, probably filtered
  251. -- Try using the first visible recipe
  252. return not self:SetSelectedRecipeID(firstVisibleRecipeID);
  253. end
  254. local function GetUnfilteredSubCategoryName(categoryID, ...)
  255. local areAllUnfiltered = true;
  256. for i = 1, select("#", ...) do
  257. local subCategoryID = select(i, ...);
  258. if C_TradeSkillUI.IsRecipeCategoryFiltered(categoryID, subCategoryID) then
  259. areAllUnfiltered = false;
  260. break;
  261. end
  262. end
  263. if areAllUnfiltered then
  264. return nil;
  265. end
  266. for i = 1, select("#", ...) do
  267. local subCategoryID = select(i, ...);
  268. if not C_TradeSkillUI.IsRecipeCategoryFiltered(categoryID, subCategoryID) then
  269. local subCategoryData = C_TradeSkillUI.GetCategoryInfo(subCategoryID);
  270. return subCategoryData.name;
  271. end
  272. end
  273. end
  274. local function GetUnfilteredCategoryName(...)
  275. -- Try subCategories first
  276. for i = 1, select("#", ...) do
  277. local categoryID = select(i, ...);
  278. local subCategoryName = GetUnfilteredSubCategoryName(categoryID, C_TradeSkillUI.GetSubCategories(categoryID));
  279. if subCategoryName then
  280. return subCategoryName;
  281. end
  282. end
  283. for i = 1, select("#", ...) do
  284. local categoryID = select(i, ...);
  285. if not C_TradeSkillUI.IsRecipeCategoryFiltered(categoryID) then
  286. local categoryData = C_TradeSkillUI.GetCategoryInfo(categoryID);
  287. return categoryData.name;
  288. end
  289. end
  290. return nil;
  291. end
  292. local function GetUnfilteredInventorySlotName(...)
  293. for i = 1, select("#", ...) do
  294. if not C_TradeSkillUI.IsInventorySlotFiltered(i) then
  295. local inventorySlot = select(i, ...);
  296. return inventorySlot;
  297. end
  298. end
  299. return nil;
  300. end
  301. function TradeSkillRecipeListMixin:UpdateFilterBar()
  302. local filters = nil;
  303. if C_TradeSkillUI.GetOnlyShowMakeableRecipes() then
  304. filters = filters or {};
  305. filters[#filters + 1] = CRAFT_IS_MAKEABLE;
  306. end
  307. if C_TradeSkillUI.GetOnlyShowSkillUpRecipes() then
  308. filters = filters or {};
  309. filters[#filters + 1] = TRADESKILL_FILTER_HAS_SKILL_UP;
  310. end
  311. if C_TradeSkillUI.AnyRecipeCategoriesFiltered() then
  312. local categoryName = GetUnfilteredCategoryName(C_TradeSkillUI.GetCategories());
  313. if categoryName then
  314. filters = filters or {};
  315. filters[#filters + 1] = categoryName;
  316. end
  317. end
  318. if C_TradeSkillUI.AreAnyInventorySlotsFiltered() then
  319. local inventorySlot = GetUnfilteredInventorySlotName(C_TradeSkillUI.GetAllFilterableInventorySlots());
  320. if inventorySlot then
  321. filters = filters or {};
  322. filters[#filters + 1] = inventorySlot;
  323. end
  324. end
  325. if not TradeSkillFrame_AreAllSourcesUnfiltered() then
  326. local numSources = C_PetJournal.GetNumPetSources();
  327. for i = 1, numSources do
  328. if C_TradeSkillUI.IsAnyRecipeFromSource(i) and C_TradeSkillUI.IsRecipeSourceTypeFiltered(i) then
  329. filters = filters or {};
  330. filters[#filters + 1] = _G["BATTLE_PET_SOURCE_"..i];
  331. end
  332. end
  333. end
  334. if filters == nil then
  335. self.FilterBar:Hide();
  336. self:SetHeight(LIST_FULL_HEIGHT);
  337. self:SetPoint("TOPLEFT", 7, -83);
  338. self.LearnedTab:SetPoint("BOTTOMLEFT", self, "TOPLEFT", 10, 3);
  339. self.scrollBar:SetPoint("TOPLEFT", self, "TOPRIGHT", 1, -14);
  340. else
  341. self:SetHeight(LIST_FULL_HEIGHT - ROW_HEIGHT);
  342. self.FilterBar:Show();
  343. self:SetPoint("TOPLEFT", 7, -83 - ROW_HEIGHT);
  344. self.LearnedTab:SetPoint("BOTTOMLEFT", self, "TOPLEFT", 10, 3 + ROW_HEIGHT);
  345. self.scrollBar:SetPoint("TOPLEFT", self, "TOPRIGHT", 1, -14 + ROW_HEIGHT);
  346. self.FilterBar.Text:SetFormattedText("%s: %s", FILTER, table.concat(filters, PLAYER_LIST_DELIMITER));
  347. end
  348. end
  349. function TradeSkillRecipeListMixin:RefreshDisplay()
  350. self:UpdateFilterBar();
  351. local offset = HybridScrollFrame_GetOffset(self);
  352. for i, tradeSkillButton in ipairs(self.buttons) do
  353. local dataIndex = offset + i;
  354. local tradeSkillInfo = self.dataList[dataIndex];
  355. if tradeSkillInfo then
  356. if tradeSkillInfo.type == "header" or tradeSkillInfo.type == "subheader" then
  357. tradeSkillInfo.collapsed = self:IsCategoryCollapsed(tradeSkillInfo.categoryID);
  358. end
  359. tradeSkillButton:SetUp(tradeSkillInfo);
  360. if tradeSkillInfo.type == "recipe" then
  361. tradeSkillButton:SetSelected(self.selectedRecipeID == tradeSkillInfo.recipeID);
  362. end
  363. else
  364. tradeSkillButton:Clear();
  365. end
  366. end
  367. HybridScrollFrame_Update(self, #self.dataList * ROW_HEIGHT, 389);
  368. end
  369. function TradeSkillRecipeListMixin:FindRecipeIndexInCurrentList(recipeID)
  370. for i, listData in ipairs(self.dataList) do
  371. if listData.type == "recipe" and recipeID == listData.recipeID then
  372. return i;
  373. end
  374. end
  375. return nil;
  376. end
  377. function TradeSkillRecipeListMixin:IsRecipeInCurrentList(recipeID)
  378. return self:FindRecipeIndexInCurrentList(recipeID) ~= nil;
  379. end
  380. function TradeSkillRecipeListMixin:ForceRecipeIntoView(recipeID)
  381. local dataIndex = self:FindRecipeIndexInCurrentList(recipeID);
  382. if dataIndex then
  383. local offset = HybridScrollFrame_GetOffset(self);
  384. if dataIndex - 1 < offset or dataIndex - 1 > offset + math.floor(self:GetHeight() / ROW_HEIGHT) then
  385. local centerish = ((dataIndex - 1) - math.floor((self:GetHeight() * .5) / ROW_HEIGHT)) * ROW_HEIGHT;
  386. self.scrollBar:SetValue(centerish);
  387. end
  388. end
  389. end
  390. function TradeSkillRecipeListMixin:SelectedAndForceRecipeIDIntoView(recipeID)
  391. if self.pendingRefresh then
  392. self.pendingForceRecipeIDIntoView = recipeID;
  393. elseif self:IsRecipeInCurrentList(recipeID) then
  394. self:SetSelectedRecipeID(recipeID);
  395. self:ForceRecipeIntoView(recipeID);
  396. end
  397. end
  398. function TradeSkillRecipeListMixin:SetSelectedRecipeID(recipeID)
  399. if self.selectedRecipeID ~= recipeID and (not recipeID or self:IsRecipeInCurrentList(recipeID)) then
  400. self.selectedRecipeID = recipeID;
  401. self:RefreshDisplay();
  402. if self.recipeChangedCallback then
  403. self.recipeChangedCallback(recipeID);
  404. end
  405. return true;
  406. end
  407. return false;
  408. end
  409. function TradeSkillRecipeListMixin:GetSelectedRecipeID()
  410. return self.selectedRecipeID;
  411. end