From cd7e074f102aca11381cb75b7ae2345d35b4de3b Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Wed, 29 Jul 2026 13:36:39 +1000 Subject: [PATCH] Fix various issues with the gem dropdown list The new sorting list change had a bunch of issues with clicking on existing gems or not selecting a gem at all Clicking on an existing gem now tracks that gem in the list Pressing escape or clicking outside the dropdown no longer removes an existing gem Typing while the gem list is sorting would highlight the wrong gem --- spec/System/TestGemSelectControl_spec.lua | 72 +++++++++++++++++++++++ src/Classes/GemSelectControl.lua | 58 ++++++++++-------- 2 files changed, 104 insertions(+), 26 deletions(-) create mode 100644 spec/System/TestGemSelectControl_spec.lua diff --git a/spec/System/TestGemSelectControl_spec.lua b/spec/System/TestGemSelectControl_spec.lua new file mode 100644 index 0000000000..2c45e9494b --- /dev/null +++ b/spec/System/TestGemSelectControl_spec.lua @@ -0,0 +1,72 @@ +describe("GemSelectControl", function() + local function makeControl() + local change + local skillsTab = { + showSupportGemTypes = "ALL", + defaultGemLevel = 20, + build = { + characterLevel = 100, + data = { gems = { } }, + }, + } + local control = new("GemSelectControl", nil, { 0, 0, 300, 20 }, skillsTab, 1, function(...) + change = { ... } + end) + control.gems = { + ["Default:Arc"] = { name = "Arc", grantedEffect = { } }, + ["Default:Fireball"] = { name = "Fireball", grantedEffect = { } }, + } + control.sortCache = { + canSupport = { }, + dps = { + ["Default:Arc"] = 2, + ["Default:Fireball"] = 1, + }, + } + control.skillsTab.sortGemsByDPS = true + control.list = { "Default:Fireball", "Default:Arc" } + control.searchStr = "" + control.ScrollSelIntoView = function() end + return control, function() return change end + end + + it("keeps the selected gem aligned with a lazy DPS sort", function() + local control = makeControl() + control.buf = "Fireball" + + control:SortCurrentList() + + assert.are.equal("Default:Arc", control.list[1]) + assert.are.equal(2, control.selIndex) + end) + + it("restores and releases focus on a cancelled outside click", function() + local control, getChange = makeControl() + control.buf = "Arc" + control.initialBuf = "Fireball" + control.dropped = true + control.IsShown = function() return true end + control.IsEnabled = function() return true end + control.IsMouseOver = function() return false end + + assert.is_nil(control:OnKeyDown("LEFTBUTTON")) + assert.are.equal("Fireball", control.buf) + assert.are.equal(2, control.selIndex) + assert.are.same({ "Fireball", false, true, true }, getChange()) + end) + + it("allows click-through after selecting a gem", function() + local control, getChange = makeControl() + control.buf = "Fireball" + control.initialBuf = "Fireball" + control.dropped = true + control.hoverSel = 2 + control.IsShown = function() return true end + control.IsEnabled = function() return true end + control.IsMouseOver = function() return true end + + assert.is_nil(control:OnKeyDown("LEFTBUTTON")) + assert.are.equal("Arc", control.buf) + assert.are.same({ "Arc", true, nil, true }, getChange()) + end) +end) diff --git a/src/Classes/GemSelectControl.lua b/src/Classes/GemSelectControl.lua index 75de66341c..f3534f0204 100644 --- a/src/Classes/GemSelectControl.lua +++ b/src/Classes/GemSelectControl.lua @@ -383,6 +383,24 @@ function GemSelectClass:SortGemList(gemList) end) end +function GemSelectClass:SyncSelection() + self.selIndex = 0 + for index, gemId in ipairs(self.list) do + if self.gems[gemId] and self.gems[gemId].name:lower() == self.buf:lower() then + self.selIndex = index + self:ScrollSelIntoView() + break + end + end +end + +function GemSelectClass:SortCurrentList() + if #self.searchStr == 0 then + self:SortGemList(self.list) + self:SyncSelection() + end +end + function GemSelectClass:DPSBuilder() local sortCache = self.sortCache if not sortCache or not sortCache.pendingGems then return end @@ -409,9 +427,7 @@ function GemSelectClass:DPSBuilder() end local now = GetTime() if now - start > 50 then - if #self.searchStr == 0 then - self:SortGemList(self.list) - end + self:SortCurrentList() if self.dpsBuilderCallback then self.dpsBuilderCallback(m_floor(index/#pending*100)) end @@ -420,9 +436,7 @@ function GemSelectClass:DPSBuilder() end end - if #self.searchStr == 0 then - self:SortGemList(self.list) - end + self:SortCurrentList() sortCache.pendingGems = nil end @@ -685,27 +699,23 @@ end function GemSelectClass:OnFocusGained() self.EditControl:OnFocusGained() self.dropped = true - self.selIndex = 0 self:UpdateSortCache() self:BuildList("") - for index, gemId in pairs(self.list) do - if self.gems[gemId] and self.gems[gemId].name == self.buf then - self.selIndex = index - self:ScrollSelIntoView() - break - end - end + self:SyncSelection() self.initialBuf = self.buf - self.initialIndex = self.selIndex +end + +function GemSelectClass:CancelSelection() + self.dropped = false + self.buf = self.initialBuf + self:BuildList("") + self:SyncSelection() + self:UpdateGem(false, true, true) end function GemSelectClass:OnFocusLost() if self.dropped then - self.dropped = false - if self.noMatches then - self:SetText("") - end - self:UpdateGem(true,true, true) + self:CancelSelection() end end @@ -739,6 +749,7 @@ function GemSelectClass:OnKeyDown(key, doubleClick) end if self.dropped then if key:match("BUTTON") and not self:IsMouseOver() then + self:CancelSelection() return end if key == "LEFTBUTTON" then @@ -761,11 +772,7 @@ function GemSelectClass:OnKeyDown(key, doubleClick) self:UpdateGem(true, true, true) return elseif key == "ESCAPE" then - self.dropped = false - self:BuildList("") - self.buf = self.initialBuf - self.selIndex = self.initialIndex - self:UpdateGem(false,true, true) + self:CancelSelection() return elseif self.controls.scrollBar:IsScrollUpKey(key) then self.controls.scrollBar:Scroll(-1) @@ -793,7 +800,6 @@ function GemSelectClass:OnKeyDown(key, doubleClick) elseif key == "RETURN" or key == "RIGHTBUTTON" then self.dropped = true self:UpdateSortCache() - self.initialIndex = self.selIndex self.initialBuf = self.buf return self end