From 9b207cc8c5653404a8f421d00affe3505f728999 Mon Sep 17 00:00:00 2001 From: vaisest <4550061+vaisest@users.noreply.github.com> Date: Tue, 4 Aug 2026 21:35:34 +0300 Subject: [PATCH 1/5] Add sorting for modifier dropdowns in crafting UI --- src/Classes/ItemsTab.lua | 162 ++++++++++++++++++++++++++++++--------- 1 file changed, 125 insertions(+), 37 deletions(-) diff --git a/src/Classes/ItemsTab.lua b/src/Classes/ItemsTab.lua index deb1fcc43f..2c73946508 100644 --- a/src/Classes/ItemsTab.lua +++ b/src/Classes/ItemsTab.lua @@ -626,7 +626,7 @@ holding Shift will put it in the second.]]) self.controls.displayItemSectionQuality = new("Control", {"TOPLEFT",self.controls.displayItemSectionInfluence,"BOTTOMLEFT"}, {0, 0, 0, function() return (self.controls.displayItemQuality:IsShown() and self.controls.displayItemQualityEdit:IsShown()) and 28 or 0 end}) - self.controls.displayItemQuality = new("LabelControl", {"TOPLEFT",self.controls.displayItemSectionQuality,"TOPRIGHT"}, {-4, 0, 0, 16}, "^7Quality:") + self.controls.displayItemQuality = new("LabelControl", { "TOPLEFT", self.controls.displayItemSectionQuality, "TOPRIGHT" }, { 0, 0, 0, 16 }, "^7Quality:") self.controls.displayItemQuality.shown = function() return self.displayItem and self.displayItem.quality and (self.displayItem.base.type ~= "Amulet" or self.displayItem.base.type ~= "Belt" or self.displayItem.base.type ~= "Jewel" or self.displayItem.base.type ~= "Quiver" or self.displayItem.base.type ~= "Ring" or self.displayItem.type ~= "Graft") end @@ -640,6 +640,14 @@ holding Shift will put it in the second.]]) return self.displayItem and self.displayItem.quality and (self.displayItem.base.type ~= "Amulet" or self.displayItem.base.type ~= "Belt" or self.displayItem.base.type ~= "Jewel" or self.displayItem.base.type ~= "Quiver" or self.displayItem.base.type ~= "Ring" or self.displayItem.type ~= "Graft") end + local sortingOptions = { + { stat = nil, label = "Default" } + } + for _, option in ipairs(data.powerStatList) do + if not option.ignoreForItems and label ~= "Name" then + table.insert(sortingOptions, option) + end + end -- Section: Catalysts self.controls.displayItemSectionCatalyst = new("Control", {"TOPLEFT",self.controls.displayItemSectionQuality,"BOTTOMLEFT"}, {0, 0, 0, function() return (self.controls.displayItemCatalyst:IsShown() or self.controls.displayItemCatalystQualityEdit:IsShown()) and 28 or 0 @@ -703,9 +711,16 @@ holding Shift will put it in the second.]]) self:CraftClusterJewel() end) + self.controls.craftingSortingLabel = new("LabelControl", { "TOPLEFT", self.controls.displayItemSectionClusterJewel, "BOTTOMLEFT" }, { 0, 0, 0, 16 }, "^7Modifier sorting:") + self.controls.craftingSorting = new("DropDownControl", { "LEFT", self.controls.craftingSortingLabel, "RIGHT" }, { 4, 0, 120, 20 }, sortingOptions, function() + self:UpdateAffixControls() + end) + self.controls.craftingSorting.shown = function() + return self.displayItem and self.displayItem.crafted + end -- Section: Affix Selection local maxModCount = 9 - self.controls.displayItemSectionAffix = new("Control", {"TOPLEFT",self.controls.displayItemSectionClusterJewel,"BOTTOMLEFT"}, {0, 0, 0, function() + self.controls.displayItemSectionAffix = new("Control", { "TOPLEFT", self.controls.craftingSortingLabel, "BOTTOMLEFT" }, { 0, 8, 0, function() if not self.displayItem or not self.displayItem.crafted then return 0 end @@ -720,6 +735,7 @@ holding Shift will put it in the second.]]) end return h end}) + for i = 1, maxModCount do local prev = self.controls["displayItemAffix"..(i-1)] or self.controls.displayItemSectionAffix local drop, slider @@ -2159,50 +2175,122 @@ function ItemsTabClass:UpdateAffixControl(control, item, affixType, outputTable, control.slider.shown = false control.slider.val = main.defaultItemAffixQuality or 0.5 local selAffix = item[outputTable][outputIndex].modId - if (item.type == "Jewel" and item.base.subType ~= "Abyss") then - for i, modId in pairs(affixList) do - local mod = item.affixes[modId] - if selAffix == modId then - control.selIndex = i + 1 - end + local lastSeries + -- combine runs of modifiers to one group, which will only take up one row + -- in the list + for _, modId in ipairs(affixList) do + local mod = item.affixes[modId] + if not lastSeries or not tableDeepEquals(lastSeries.statOrder, mod.statOrder) then local modString = table.concat(mod, "/") - local label = modString - if retainedAffixes[modId] then - label = "^8[Retained] " .. modString - elseif item.type == "Flask" then - label = mod.affix .. " ^8[" .. modString .. "]" - end - control.list[i + 1] = { - label = label, - modList = { modId }, - modId = modId, + lastSeries = { + label = modString, + modList = {}, haveRange = modString:match("%(%-?[%d%.]+%-%-?[%d%.]+%)"), + statOrder = mod.statOrder, } + t_insert(control.list, lastSeries) end - else - local lastSeries - for _, modId in ipairs(affixList) do - local mod = item.affixes[modId] - if not lastSeries or not tableDeepEquals(lastSeries.statOrder, mod.statOrder) then - local modString = table.concat(mod, "/") - lastSeries = { - label = modString, - modList = { }, - haveRange = modString:match("%(%-?[%d%.]+%-%-?[%d%.]+%)"), - statOrder = mod.statOrder, - } - t_insert(control.list, lastSeries) + -- cluster jewel mods retained after changing the cluster type + if retainedAffixes[modId] then + lastSeries.label = "^8[Retained] " .. lastSeries.label + end + t_insert(lastSeries.modList, modId) + if #lastSeries.modList == 2 then + lastSeries.label = lastSeries.label:gsub("%(%-?[%d%.]+%-%-?[%d%.]+%)", "#"):gsub("%-?%d+%.?%d*", "#") + lastSeries.haveRange = true + end + end + + local sortOption = self.controls.craftingSorting:GetSelValue() + -- sort modifier groups by power + if sortOption.label ~= "Default" then + local calcFunc = self.build.calcsTab:GetMiscCalculator() + local slotName = self.displayItem:GetPrimarySlot() + local testSubject = new("Item", self.displayItem:BuildRaw()) + local function pickModifierFromList(modList) + -- pick mid tier modifier from a group + if #modList == 1 then + return modList[1] + else + return modList[1 + round((#modList - 1) * main.defaultItemAffixQuality)] + end + end + ---@type table + -- returns power for a given mod id + local powerOutputs = setmetatable({}, { + __index = function(t, k) + local mod = testSubject.affixes[k] + + local modCount = #mod + -- magnitude scaling happens during item parsing, which means we + -- can't use the faster path where items don't need to be + -- re-parsed. note that this wouldn't be correct if we were + -- adding a mod magnitude mod here, but currently all mod + -- magnitude mods are custom modifiers and so this works + local power + if (#testSubject.modMagnitudeMods > 0) or (testSubject.catalyst and testSubject.catalyst > 0) then + local originalItem = testSubject:BuildRaw() + for _, subMod in ipairs(mod) do + local modLine = { line = subMod, modTags = mod.modTags, [mod.type] = true } + t_insert(testSubject.explicitModLines, modLine) + end + testSubject:BuildAndParseRaw() + power = data.powerStatList.GetFromOutput( + calcFunc({ repSlotName = slotName, repItem = testSubject }), + sortOption + ) + testSubject = new("Item", originalItem) + else + for _, line in ipairs(mod) do + local rangedLine = itemLib.applyRange(line, main.defaultItemAffixQuality or 0.5, 1, 1) + local modList, extra = modLib.parseMod(rangedLine) + local modLine = { line = line, modList = modList, extra = extra, modTags = mod.modTags, [mod.type] = true } + t_insert(testSubject.explicitModLines, modLine) + end + + testSubject:BuildModList() + power = data.powerStatList.GetFromOutput( + calcFunc({ repSlotName = slotName, repItem = testSubject }), + sortOption + ) + for _ = 1, modCount do + t_remove(testSubject.explicitModLines, #testSubject.explicitModLines) + end + end + t[k] = power + return power end - if selAffix == modId then - control.selIndex = #control.list + }) + table.sort(control.list, function(a, b) + -- keep "None" as the first option + if not a.modList then + return true + elseif not b.modList then + return false end - t_insert(lastSeries.modList, modId) - if #lastSeries.modList == 2 then - lastSeries.label = lastSeries.label:gsub("%(%-?[%d%.]+%-%-?[%d%.]+%)","#"):gsub("%-?%d+%.?%d*","#") - lastSeries.haveRange = true + + local modIdA = pickModifierFromList(a.modList) + local modIdB = pickModifierFromList(b.modList) + + return powerOutputs[modIdA] > powerOutputs[modIdB] + end) + end + local function findSelectedIdx() + for i, entry in ipairs(control.list) do + if entry.modList then + for _, modId in ipairs(entry.modList) do + if selAffix == modId then + return i + end + end + else + if selAffix == entry then + return i + end end end end + control.selIndex = findSelectedIdx() if control.list[control.selIndex].haveRange then control.slider.divCount = #control.list[control.selIndex].modList local index = isValueInArray(control.list[control.selIndex].modList, selAffix) From 8a32c6154cd5b9ee8e44c71724c2f2f3f007e9dd Mon Sep 17 00:00:00 2001 From: vaisest <4550061+vaisest@users.noreply.github.com> Date: Tue, 4 Aug 2026 21:48:14 +0300 Subject: [PATCH 2/5] Hide sorting for clusters --- src/Classes/ItemsTab.lua | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/Classes/ItemsTab.lua b/src/Classes/ItemsTab.lua index 2c73946508..dd42d55df5 100644 --- a/src/Classes/ItemsTab.lua +++ b/src/Classes/ItemsTab.lua @@ -712,15 +712,24 @@ holding Shift will put it in the second.]]) end) self.controls.craftingSortingLabel = new("LabelControl", { "TOPLEFT", self.controls.displayItemSectionClusterJewel, "BOTTOMLEFT" }, { 0, 0, 0, 16 }, "^7Modifier sorting:") + self.controls.craftingSortingLabel.shown = function() + return self.displayItem and self.displayItem.crafted and + -- cluster jewels don't have good comparison support and sorting would be misleading + not (self.displayItem.base.type == "Jewel" and self.displayItem.base.subType == "Cluster") + end self.controls.craftingSorting = new("DropDownControl", { "LEFT", self.controls.craftingSortingLabel, "RIGHT" }, { 4, 0, 120, 20 }, sortingOptions, function() self:UpdateAffixControls() end) - self.controls.craftingSorting.shown = function() - return self.displayItem and self.displayItem.crafted - end + -- Section: Affix Selection local maxModCount = 9 - self.controls.displayItemSectionAffix = new("Control", { "TOPLEFT", self.controls.craftingSortingLabel, "BOTTOMLEFT" }, { 0, 8, 0, function() + self.controls.displayItemSectionAffix = new("Control", { "TOPLEFT", self.controls.craftingSortingLabel, "BOTTOMLEFT" }, { 0, function() + if self.controls.craftingSortingLabel.shown() then + return 8 + else + return -16 + end + end, 0, function() if not self.displayItem or not self.displayItem.crafted then return 0 end From ed3d54fcb17a0fedfc88a25abac6d5953c4b9888 Mon Sep 17 00:00:00 2001 From: vaisest <4550061+vaisest@users.noreply.github.com> Date: Tue, 4 Aug 2026 22:13:45 +0300 Subject: [PATCH 3/5] Use same cache for each affix control --- src/Classes/ItemsTab.lua | 99 ++++++++++++++++++++-------------------- 1 file changed, 50 insertions(+), 49 deletions(-) diff --git a/src/Classes/ItemsTab.lua b/src/Classes/ItemsTab.lua index dd42d55df5..02f6d0b80e 100644 --- a/src/Classes/ItemsTab.lua +++ b/src/Classes/ItemsTab.lua @@ -644,7 +644,7 @@ holding Shift will put it in the second.]]) { stat = nil, label = "Default" } } for _, option in ipairs(data.powerStatList) do - if not option.ignoreForItems and label ~= "Name" then + if not option.ignoreForItems and option.label ~= "Name" then table.insert(sortingOptions, option) end end @@ -912,7 +912,7 @@ holding Shift will put it in the second.]]) if value.modId or #modList == 1 then mod = self.displayItem.affixes[value.modId or modList[1]] else - mod = self.displayItem.affixes[modList[1 + round((#modList - 1) * main.defaultItemAffixQuality)]] + mod = self.displayItem.affixes[modList[1 + round((#modList - 1) * (main.defaultItemAffixQuality or 0.5))]] end -- Adding Mod @@ -2104,15 +2104,16 @@ function ItemsTabClass:UpdateAffixControls() local item = self.displayItem local prefixLimit = item.prefixes.limit or (item.affixLimit / 2) local ignoreModType = item.rareLikeUnique and item.rareLikeUnique.ignoreModType + local powerCache = {} for i = 1, item.affixLimit do if i <= prefixLimit then local modType = "Prefix" if ignoreModType then modType = nil end - self:UpdateAffixControl(self.controls["displayItemAffix" .. i], item, modType, "prefixes", i) + self:UpdateAffixControl(self.controls["displayItemAffix" .. i], item, modType, "prefixes", i, powerCache) else - self:UpdateAffixControl(self.controls["displayItemAffix"..i], item, "Suffix", "suffixes", i - prefixLimit) + self:UpdateAffixControl(self.controls["displayItemAffix" .. i], item, "Suffix", "suffixes", i - prefixLimit, powerCache) end end -- The custom affixes may have had their indexes changed, so the custom control UI is also rebuilt so that it will @@ -2120,7 +2121,7 @@ function ItemsTabClass:UpdateAffixControls() self:UpdateCustomControls() end -function ItemsTabClass:UpdateAffixControl(control, item, affixType, outputTable, outputIndex) +function ItemsTabClass:UpdateAffixControl(control, item, affixType, outputTable, outputIndex, powerCache) local extraTags = { } local excludeGroups = { } local allowDuplicateGroups = item.rareLikeUnique and item.rareLikeUnique.allowDuplicateGroups @@ -2224,52 +2225,51 @@ function ItemsTabClass:UpdateAffixControl(control, item, affixType, outputTable, return modList[1 + round((#modList - 1) * main.defaultItemAffixQuality)] end end - ---@type table - -- returns power for a given mod id - local powerOutputs = setmetatable({}, { - __index = function(t, k) - local mod = testSubject.affixes[k] - - local modCount = #mod - -- magnitude scaling happens during item parsing, which means we - -- can't use the faster path where items don't need to be - -- re-parsed. note that this wouldn't be correct if we were - -- adding a mod magnitude mod here, but currently all mod - -- magnitude mods are custom modifiers and so this works - local power - if (#testSubject.modMagnitudeMods > 0) or (testSubject.catalyst and testSubject.catalyst > 0) then - local originalItem = testSubject:BuildRaw() - for _, subMod in ipairs(mod) do - local modLine = { line = subMod, modTags = mod.modTags, [mod.type] = true } - t_insert(testSubject.explicitModLines, modLine) - end - testSubject:BuildAndParseRaw() - power = data.powerStatList.GetFromOutput( - calcFunc({ repSlotName = slotName, repItem = testSubject }), - sortOption - ) - testSubject = new("Item", originalItem) - else - for _, line in ipairs(mod) do - local rangedLine = itemLib.applyRange(line, main.defaultItemAffixQuality or 0.5, 1, 1) - local modList, extra = modLib.parseMod(rangedLine) - local modLine = { line = line, modList = modList, extra = extra, modTags = mod.modTags, [mod.type] = true } - t_insert(testSubject.explicitModLines, modLine) - end + local function getPower(modId) + if powerCache[modId] then + return powerCache[modId] + end + local mod = testSubject.affixes[modId] + + local modCount = #mod + -- magnitude scaling happens during item parsing, which means we + -- can't use the faster path where items don't need to be + -- re-parsed. note that this wouldn't be correct if we were + -- adding a mod magnitude mod here, but currently all mod + -- magnitude mods are custom modifiers and so this works + local power + if (#testSubject.modMagnitudeMods > 0) or (testSubject.catalyst and testSubject.catalyst > 0) then + local originalItem = testSubject:BuildRaw() + for _, subMod in ipairs(mod) do + local modLine = { line = subMod, modTags = mod.modTags, [mod.type] = true } + t_insert(testSubject.explicitModLines, modLine) + end + testSubject:BuildAndParseRaw() + power = data.powerStatList.GetFromOutput( + calcFunc({ repSlotName = slotName, repItem = testSubject }), + sortOption + ) + testSubject = new("Item", originalItem) + else + for _, line in ipairs(mod) do + local rangedLine = itemLib.applyRange(line, main.defaultItemAffixQuality or 0.5, 1, 1) + local modList, extra = modLib.parseMod(rangedLine) + local modLine = { line = line, modList = modList, extra = extra, modTags = mod.modTags, [mod.type] = true } + t_insert(testSubject.explicitModLines, modLine) + end - testSubject:BuildModList() - power = data.powerStatList.GetFromOutput( - calcFunc({ repSlotName = slotName, repItem = testSubject }), - sortOption - ) - for _ = 1, modCount do - t_remove(testSubject.explicitModLines, #testSubject.explicitModLines) - end + testSubject:BuildModList() + power = data.powerStatList.GetFromOutput( + calcFunc({ repSlotName = slotName, repItem = testSubject }), + sortOption + ) + for _ = 1, modCount do + t_remove(testSubject.explicitModLines, #testSubject.explicitModLines) end - t[k] = power - return power end - }) + powerCache[modId] = power + return power + end table.sort(control.list, function(a, b) -- keep "None" as the first option if not a.modList then @@ -2281,7 +2281,7 @@ function ItemsTabClass:UpdateAffixControl(control, item, affixType, outputTable, local modIdA = pickModifierFromList(a.modList) local modIdB = pickModifierFromList(b.modList) - return powerOutputs[modIdA] > powerOutputs[modIdB] + return getPower(modIdA) > getPower(modIdB) end) end local function findSelectedIdx() @@ -2298,6 +2298,7 @@ function ItemsTabClass:UpdateAffixControl(control, item, affixType, outputTable, end end end + return 1 end control.selIndex = findSelectedIdx() if control.list[control.selIndex].haveRange then From 02c3c51b40da7518cbd8c6a8528d3f8f0e9e72f1 Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Wed, 5 Aug 2026 16:48:53 +1000 Subject: [PATCH 4/5] Fix sort on existing mods The sorted list wasn't removing the current mod from the item before sorting the list so it could be skewed by the current selection Also disables the sort from running if the selection is hidden. The sort was running in the background for the cluster jewels even though it was hidden --- spec/System/TestItemMods_spec.lua | 74 +++++++++++++++++++++++++++++++ src/Classes/ItemsTab.lua | 14 ++++-- 2 files changed, 84 insertions(+), 4 deletions(-) diff --git a/spec/System/TestItemMods_spec.lua b/spec/System/TestItemMods_spec.lua index 6577021509..a3beafb143 100644 --- a/spec/System/TestItemMods_spec.lua +++ b/spec/System/TestItemMods_spec.lua @@ -803,6 +803,80 @@ describe("TetsItemMods", function() end assert.are.equals(2, count) end) + + it("does not sort cluster jewel modifiers when the sorting control is hidden", function() + local item = new("Item", [[ + Rarity: RARE + New Item + Large Cluster Jewel + Crafted: true + Prefix: {range:0.5}AfflictionNotableWickedPall_ + Prefix: {range:0.5}AfflictionNotableMiseryEverlasting + Suffix: {range:0.5}AfflictionNotableUnholyGrace_ + Suffix: None + Cluster Jewel Skill: affliction_chaos_damage + Cluster Jewel Node Count: 8 + Quality: 0 + LevelReq: 40 + Implicits: 3 + {crafted}Adds 8 Passive Skills + {crafted}2 Added Passive Skills are Jewel Sockets + {crafted}Added Small Passive Skills grant: 12% increased Chaos Damage + 1 Added Passive Skill is Misery Everlasting + 1 Added Passive Skill is Unholy Grace + 1 Added Passive Skill is Wicked Pall + ]]) + local calcCount = 0 + build.itemsTab.displayItem = item + build.itemsTab.controls.craftingSorting:SetSel(2, true) + build.calcsTab.GetMiscCalculator = function() + return function() + calcCount = calcCount + 1 + return { } + end + end + + assert.is_false(build.itemsTab.controls.craftingSortingLabel.shown()) + build.itemsTab:UpdateAffixControls() + assert.are.equals(0, calcCount) + end) + + it("sorts crafted modifier replacements without retaining the selected modifier", function() + local item = new("Item", [[ + Rarity: RARE + New Item + Cobalt Jewel + Crafted: true + Prefix: {range:1}PercentIncreasedLifeJewel + Prefix: None + Suffix: None + Suffix: None + Quality: 0 + LevelReq: 0 + Implicits: 0 + 7% increased maximum Life + ]]) + local calcCount = 0 + local retainedCount = 0 + build.itemsTab.displayItem = item + build.itemsTab.controls.craftingSorting:SetSel(2, true) + build.calcsTab.GetMiscCalculator = function() + return function(args) + calcCount = calcCount + 1 + for _, modLine in ipairs(args.repItem.explicitModLines) do + if modLine.line == "7% increased maximum Life" then + retainedCount = retainedCount + 1 + break + end + end + return { } + end + end + + build.itemsTab:UpdateAffixControl(build.itemsTab.controls.displayItemAffix1, item, "Prefix", "prefixes", 1, { }) + assert.is_true(calcCount > 1) + assert.are.equals(0, retainedCount) + end) it("shows a fallback tooltip when an item's base is no longer supported", function() local item = new("Item", [[ diff --git a/src/Classes/ItemsTab.lua b/src/Classes/ItemsTab.lua index 02f6d0b80e..c0f5ee0873 100644 --- a/src/Classes/ItemsTab.lua +++ b/src/Classes/ItemsTab.lua @@ -2213,10 +2213,16 @@ function ItemsTabClass:UpdateAffixControl(control, item, affixType, outputTable, local sortOption = self.controls.craftingSorting:GetSelValue() -- sort modifier groups by power - if sortOption.label ~= "Default" then + if sortOption.stat and self.controls.craftingSortingLabel.shown() then local calcFunc = self.build.calcsTab:GetMiscCalculator() local slotName = self.displayItem:GetPrimarySlot() local testSubject = new("Item", self.displayItem:BuildRaw()) + local controlPowerCache = powerCache + if selAffix and selAffix ~= "None" then + testSubject[outputTable][outputIndex] = { modId = "None" } + testSubject:Craft() + controlPowerCache = { } + end local function pickModifierFromList(modList) -- pick mid tier modifier from a group if #modList == 1 then @@ -2226,8 +2232,8 @@ function ItemsTabClass:UpdateAffixControl(control, item, affixType, outputTable, end end local function getPower(modId) - if powerCache[modId] then - return powerCache[modId] + if controlPowerCache[modId] then + return controlPowerCache[modId] end local mod = testSubject.affixes[modId] @@ -2267,7 +2273,7 @@ function ItemsTabClass:UpdateAffixControl(control, item, affixType, outputTable, t_remove(testSubject.explicitModLines, #testSubject.explicitModLines) end end - powerCache[modId] = power + controlPowerCache[modId] = power return power end table.sort(control.list, function(a, b) From 6214aef12c45c5f9f3a1eaa8967be89d4da068bf Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Wed, 5 Aug 2026 16:51:34 +1000 Subject: [PATCH 5/5] Widen dropdown The dropdown cut off the longer named stats --- src/Classes/ItemsTab.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Classes/ItemsTab.lua b/src/Classes/ItemsTab.lua index c0f5ee0873..a8fe3acddc 100644 --- a/src/Classes/ItemsTab.lua +++ b/src/Classes/ItemsTab.lua @@ -717,7 +717,7 @@ holding Shift will put it in the second.]]) -- cluster jewels don't have good comparison support and sorting would be misleading not (self.displayItem.base.type == "Jewel" and self.displayItem.base.subType == "Cluster") end - self.controls.craftingSorting = new("DropDownControl", { "LEFT", self.controls.craftingSortingLabel, "RIGHT" }, { 4, 0, 120, 20 }, sortingOptions, function() + self.controls.craftingSorting = new("DropDownControl", { "LEFT", self.controls.craftingSortingLabel, "RIGHT" }, { 4, 0, 200, 20 }, sortingOptions, function() self:UpdateAffixControls() end)