Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions spec/System/TestCustomModControl_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ describe("Custom modifier controls", function()
end
end)

local configModBrowser = require("Modules.ConfigModBrowser")

local function openModBrowser()
local configTab = build.configTab
local blockData = configTab.configSets[configTab.activeConfigSetId].customModsList[1]
configTab:OpenAddModPopup(blockData)
configModBrowser.OpenAddModPopup(configTab, blockData)
return main.popups[1], blockData
end

Expand All @@ -31,31 +33,31 @@ describe("Custom modifier controls", function()

assert.is_nil(main.selControl)
assert.are_not.equal(popup, main.popups[1])
assert.are.equal(selectedMod, blockData.text)
assert.are.equal(selectedMod.text, blockData.text)
end)

it("collapses numeric tiers and imports the first range value", function()
it("collapses numeric tiers into a single entry and imports it", function()
local popup, blockData = openModBrowser()
local minionDamageEntries = { }
local minionDamageIndex
for index, modText in ipairs(popup.controls.listControl.list) do
if modText:match("^Minions deal .-%% increased Damage$") then
table.insert(minionDamageEntries, modText)
for index, mod in ipairs(popup.controls.listControl.list) do
if mod.text:match("^Minions deal .-%% increased Damage$") then
table.insert(minionDamageEntries, mod.text)
minionDamageIndex = index
end
end

assert.are.same({ "Minions deal 10% increased Damage" }, minionDamageEntries)
assert.are.same({ "Minions deal 12% increased Damage" }, minionDamageEntries)
popup.controls.listControl.selIndex = minionDamageIndex
popup.controls.save.onClick()
assert.are.equal("Minions deal 10% increased Damage", blockData.text)
assert.are.equal("Minions deal 12% increased Damage", blockData.text)
end)

it("orders modifiers alphabetically while ignoring numeric values", function()
local popup = openModBrowser()
local previousSortKey
for _, modText in ipairs(popup.controls.listControl.list) do
local sortKey = modText
for _, mod in ipairs(popup.controls.listControl.list) do
local sortKey = mod.text
:gsub("([%+-]?)%((%-?%d+%.?%d*)%-(%-?%d+%.?%d*)%)", "%1#")
:gsub("%d+%.?%d*", "#")
:lower()
Expand Down Expand Up @@ -92,7 +94,7 @@ describe("Custom modifier controls", function()
local searchWidth = popup.controls.search:GetSize()

assert.are.equal(720, popup.width)
assert.are.equal(540, popup.height)
assert.are.equal(566, popup.height)
assert.are.equal(700, listWidth)
assert.are.equal(454, listHeight)
assert.are.equal(640, searchWidth)
Expand Down
250 changes: 2 additions & 248 deletions src/Classes/ConfigTab.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ local s_upper = string.upper

local varList = LoadModule("Modules/ConfigOptions")
local configVisibility = LoadModule("Modules/ConfigVisibility")
local configModBrowser = require("Modules.ConfigModBrowser")

local CustomModBlockClass = newClass("CustomModBlockControl", "Control", "ControlHost", function(self, anchor, rect, configTab, blockIndex, blockData)
self.Control(anchor, rect)
Expand Down Expand Up @@ -40,7 +41,7 @@ local CustomModBlockClass = newClass("CustomModBlockControl", "Control", "Contro
end)

self.controls.addModBtn = new("ButtonControl", {"LEFT", self.controls.titleEdit, "RIGHT"}, {6, 0, 58, 18}, "^7Add Mod", function()
configTab:OpenAddModPopup(blockData)
configModBrowser.OpenAddModPopup(self.configTab, blockData)
end)

self.controls.enableCheck = new("CheckBoxControl", {"TOPRIGHT", self, "TOPRIGHT"}, {0, 0, 18}, "", function(state)
Expand Down Expand Up @@ -1301,250 +1302,3 @@ function ConfigTabClass:SetActiveConfigSet(configSetId, init)
self.build.buildFlag = true
self.build:SyncLoadouts()
end

function ConfigTabClass:OpenAddModPopup(blockData)
local bData = (self.build and self.build.data) or data
local allModsList = { }
local seen = { }

local function addModEntry(mod)
local function registerMod(str)
local stripped = StripEscapes(str):match("^%s*(.-)%s*$")
if #stripped > 0 and not seen[stripped] then
seen[stripped] = true
t_insert(allModsList, stripped)
end
end

if type(mod) == "string" then
registerMod(mod)
elseif type(mod) == "table" then
for i = 1, #mod do
if type(mod[i]) == "string" then
registerMod(mod[i])
end
end
end
end

if bData then
if bData.masterMods then
for _, mod in pairs(bData.masterMods) do
addModEntry(mod)
end
end
if bData.itemMods then
for catName, catMods in pairs(bData.itemMods) do
if catName ~= "Item" and type(catMods) == "table" then
for _, mod in pairs(catMods) do
addModEntry(mod)
end
end
end
end
if bData.veiledMods then
for _, mod in pairs(bData.veiledMods) do
addModEntry(mod)
end
end
if bData.beastCraft then
for _, mod in pairs(bData.beastCraft) do
addModEntry(mod)
end
end
end

local modTemplateCache = { }
local function getModTemplate(modText)
if not modTemplateCache[modText] then
modTemplateCache[modText] = modText
:gsub("([%+-]?)%((%-?%d+%.?%d*)%-(%-?%d+%.?%d*)%)", "%1#")
:gsub("%d+%.?%d*", "#")
:lower()
end
return modTemplateCache[modText]
end
local alphabeticalSortKeyCache = { }
local function getAlphabeticalSortKey(modText)
if not alphabeticalSortKeyCache[modText] then
alphabeticalSortKeyCache[modText] = getModTemplate(modText)
:gsub("#", " ")
:gsub("[^%a]+", " ")
:match("^%s*(.-)%s*$")
end
return alphabeticalSortKeyCache[modText]
end
local function sortAlphabeticallyIgnoringValues(a, b)
local aSortKey = getAlphabeticalSortKey(a)
local bSortKey = getAlphabeticalSortKey(b)
if aSortKey ~= bSortKey then
return aSortKey < bSortKey
end
local aTemplate = getModTemplate(a)
local bTemplate = getModTemplate(b)
if aTemplate ~= bTemplate then
return aTemplate < bTemplate
end
local aLower = a:lower()
local bLower = b:lower()
if aLower ~= bLower then
return aLower < bLower
end
return a < b
end

table.sort(allModsList, sortAlphabeticallyIgnoringValues)

-- Collapse affix tiers that only differ by their numeric values. The list is
-- sorted first so the retained representative is deterministic.
wipeTable(seen)
local deduplicatedModsList = { }
for _, modText in ipairs(allModsList) do
local modTemplate = getModTemplate(modText)
if not seen[modTemplate] then
seen[modTemplate] = true
t_insert(deduplicatedModsList, itemLib.applyRange(modText, 0))
end
end
table.sort(deduplicatedModsList, sortAlphabeticallyIgnoringValues)
allModsList = deduplicatedModsList

local displayList = { }
local controls = { }

local function fuzzyScore(modText, searchStr, words)
local modLower = modText:lower()
if modLower:find(searchStr, 1, true) then
return 1
end
if #words > 1 then
local allFound = true
for i = 1, #words do
if not modLower:find(words[i], 1, true) then
allFound = false
break
end
end
if allFound then
return 2
end
end
if #words == 1 and #searchStr >= 3 then
local textWords = {}
for word in modLower:gmatch("%w+") do
t_insert(textWords, word)
end
for i = 1, #textWords do
for len1 = 2, #searchStr - 1 do
local part1 = searchStr:sub(1, len1)
local part2 = searchStr:sub(len1 + 1)
if textWords[i]:sub(1, #part1) == part1 and textWords[i + 1] and textWords[i + 1]:sub(1, #part2) == part2 then
return 3
end
end
end
end
return nil
end

local function updateDisplayList()
wipeTable(displayList)
local searchStr = controls.search.buf:lower():gsub("^[%s]+", ""):gsub("[%s]+$", "")

if #searchStr == 0 then
for _, modText in ipairs(allModsList) do
t_insert(displayList, modText)
end
else
local words = {}
for word in searchStr:gmatch("%S+") do
t_insert(words, word)
end
local matches = {}
for _, modText in ipairs(allModsList) do
local rank = fuzzyScore(modText, searchStr, words)
if rank then
t_insert(matches, { text = modText, rank = rank })
end
end
table.sort(matches, function(a, b)
if a.rank ~= b.rank then
return a.rank < b.rank
end
return sortAlphabeticallyIgnoringValues(a.text, b.text)
end)
for _, match in ipairs(matches) do
t_insert(displayList, match.text)
end
end

if #displayList == 0 then
t_insert(displayList, "No matching modifiers found")
end
if controls.listControl then
controls.listControl.selIndex = 1
controls.listControl.selValue = displayList[1]
controls.listControl.controls.scrollBarV.offset = 0
end
end

controls.listControl = new("ListControl", {"TOPLEFT", nil, "TOPLEFT"}, {10, 20, 700, 454}, 16, "VERTICAL", false, displayList)
controls.listControl.font = "VAR"
controls.listControl.GetRowValue = function(self, column, index, value)
return value or ""
end
controls.listControl.AddValueTooltip = function(self, tooltip, index, value)
tooltip:Clear(true)
if value and #value > 0 and value ~= "No matching modifiers found" then
local mods, extra = modLib.parseMod(value)
if mods and not extra then
tooltip:AddLine(14, "^7Supported: ^2Yes")
else
tooltip:AddLine(14, "^7Supported: ^1No")
end
end
end
controls.listControl.OnSelClick = function(self, index, value, doubleClick)
self:SelectIndex(index)
if doubleClick and controls.save:IsEnabled() then
controls.save.onClick()
end
end

controls.searchLabel = new("LabelControl", {"TOPRIGHT", nil, "TOPLEFT"}, {65, 482, 0, 16}, "^7Search:")
controls.search = new("EditControl", {"TOPLEFT", nil, "TOPLEFT"}, {70, 482, 640, 18}, "", nil, "%c", 100, function()
updateDisplayList()
end, nil, nil, true)
controls.search.controls.buttonClear.shown = function()
return #controls.search.buf > 0
end

updateDisplayList()

controls.save = new("ButtonControl", nil, {-45, 512, 80, 20}, "Add", function()
local selIndex = controls.listControl.selIndex or 1
local selected = displayList[selIndex]
if selected and selected ~= "No matching modifiers found" then
if blockData.text and #blockData.text > 0 and not blockData.text:match("\n$") then
blockData.text = blockData.text .. "\n"
end
blockData.text = (blockData.text or "") .. selected
self:UpdateCustomModsControls()
self:AddUndoState()
self:BuildModList()
self.build.buildFlag = true
end
main:ClosePopup()
end)
controls.save.enabled = function()
local selIndex = controls.listControl and controls.listControl.selIndex or 1
local selected = displayList[selIndex]
return selected ~= nil and selected ~= "No matching modifiers found"
end

controls.close = new("ButtonControl", nil, {45, 512, 80, 20}, "Cancel", function()
main:ClosePopup()
end)

main:OpenPopup(720, 540, "Mod Browser", controls, "save", "search", "close")
end
2 changes: 1 addition & 1 deletion src/Classes/PassiveTree.lua
Original file line number Diff line number Diff line change
Expand Up @@ -804,7 +804,7 @@ function PassiveTreeClass:ProcessStats(node, startIndex)
if list and not extra then
-- Success, add dummy mod lists to the other lines that were combined with this one
for ci = i + 1, endI do
node.mods[ci] = { list = { } }
node.mods[ci] = { list = {}, combined = true }
end
break
end
Expand Down
Loading
Loading