From 6f3056b7ccc3bc82a1a12f56b79bde3e4df72f1e Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Sun, 19 Apr 2026 20:14:03 +1000 Subject: [PATCH 01/15] Change Copy+Use to Equip Think using equip is more obvious for users and also makes the button smaller --- src/Classes/CompareTab.lua | 58 ++++++++++++++--------------- src/Classes/CompareTradeHelpers.lua | 24 ++++++------ 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/Classes/CompareTab.lua b/src/Classes/CompareTab.lua index 2e4fba9776..e2dec82997 100644 --- a/src/Classes/CompareTab.lua +++ b/src/Classes/CompareTab.lua @@ -65,7 +65,7 @@ local LAYOUT = { -- Items view itemsCheckboxOffset = 60, itemsCopyBtnW = 60, - itemsCopyUseBtnW = 78, + itemsEquipBtnW = 60, itemsCopyBtnH = 18, itemsBuyBtnW = 60, itemsMinColWidth = 700, @@ -3541,15 +3541,15 @@ function CompareTabClass:DrawItems(vp, compareEntry, inputEvents) -- Track item copy button clicks local clickedCopySlot = nil - local clickedCopyUseSlot = nil + local clickedEquipSlot = nil local clickedBuySlot = nil local clickedBuyItem = nil - -- Track Copy+Use button hover for stat comparison tooltip - local hoverCopyUseItem = nil - local hoverCopyUseSlotName = nil - local hoverCopyUseBtnX, hoverCopyUseBtnY = 0, 0 - local hoverCopyUseBtnW, hoverCopyUseBtnH = 0, 0 + -- Track Equip button hover for stat comparison tooltip + local hoverEquipItem = nil + local hoverEquipSlotName = nil + local hoverEquipBtnX, hoverEquipBtnY = 0, 0 + local hoverEquipBtnW, hoverEquipBtnH = 0, 0 -- Headers SetDrawColor(1, 1, 1) @@ -3558,13 +3558,13 @@ function CompareTabClass:DrawItems(vp, compareEntry, inputEvents) drawY = drawY + 24 -- Helper: process copy/buy button hover state and click events for a slot. - -- Closes over hoverCopyUse*/clicked* locals above. - local function processSlotButtons(b1Hover, b2Hover, b3Hover, b2X, b2Y, b2W, b2H, cItem, copySlotName, copyUseSlotName) + -- Closes over hoverEquip*/clicked* locals above. + local function processSlotButtons(b1Hover, b2Hover, b3Hover, b2X, b2Y, b2W, b2H, cItem, copySlotName, equipSlotName) if b2Hover and cItem then - hoverCopyUseItem = cItem - hoverCopyUseSlotName = copyUseSlotName - hoverCopyUseBtnX, hoverCopyUseBtnY = b2X, b2Y - hoverCopyUseBtnW, hoverCopyUseBtnH = b2W, b2H + hoverEquipItem = cItem + hoverEquipSlotName = equipSlotName + hoverEquipBtnX, hoverEquipBtnY = b2X, b2Y + hoverEquipBtnW, hoverEquipBtnH = b2W, b2H end if cItem and inputEvents then for id, event in ipairs(inputEvents) do @@ -3573,10 +3573,10 @@ function CompareTabClass:DrawItems(vp, compareEntry, inputEvents) clickedCopySlot = copySlotName inputEvents[id] = nil elseif b2Hover then - clickedCopyUseSlot = copyUseSlotName + clickedEquipSlot = equipSlotName inputEvents[id] = nil elseif b3Hover then - clickedBuySlot = copyUseSlotName + clickedBuySlot = equipSlotName clickedBuyItem = cItem inputEvents[id] = nil end @@ -3587,7 +3587,7 @@ function CompareTabClass:DrawItems(vp, compareEntry, inputEvents) -- Helper: draw a single slot entry (expanded or compact mode). -- Closes over drawY, colWidth, cursorX/Y, vp, self, compareEntry, hoverItem/hoverX/Y/W/H/hoverItemsTab. - local function drawSlotEntry(label, pItem, cItem, copySlotName, copyUseSlotName, labelW, pWarn, cWarn, slotMissing) + local function drawSlotEntry(label, pItem, cItem, copySlotName, equipSlotName, labelW, pWarn, cWarn, slotMissing) if self.itemsExpandedMode then -- === EXPANDED MODE === SetDrawColor(1, 1, 1) @@ -3596,8 +3596,8 @@ function CompareTabClass:DrawItems(vp, compareEntry, inputEvents) DrawString(scrollOffsetX + 10 + labelEndW + 8, drawY + 2, "LEFT", 14, "VAR", tradeHelpers.getSlotDiffLabel(pItem, cItem)) if cItem then - local b1Hover, b2Hover, b3Hover, b2X, b2Y, b2W, b2H = tradeHelpers.drawCopyButtons(cursorX, cursorY, scrollOffsetX + contentWidth - 214, drawY + 21, slotMissing, LAYOUT.itemsCopyBtnW, LAYOUT.itemsCopyBtnH, LAYOUT.itemsBuyBtnW, LAYOUT.itemsCopyUseBtnW) - processSlotButtons(b1Hover, b2Hover, b3Hover, b2X, b2Y, b2W, b2H, cItem, copySlotName, copyUseSlotName) + local b1Hover, b2Hover, b3Hover, b2X, b2Y, b2W, b2H = tradeHelpers.drawCopyButtons(cursorX, cursorY, scrollOffsetX + contentWidth - 214, drawY + 21, slotMissing, LAYOUT.itemsCopyBtnW, LAYOUT.itemsCopyBtnH, LAYOUT.itemsBuyBtnW, LAYOUT.itemsEquipBtnW) + processSlotButtons(b1Hover, b2Hover, b3Hover, b2X, b2Y, b2W, b2H, cItem, copySlotName, equipSlotName) end drawY = drawY + 20 @@ -3620,7 +3620,7 @@ function CompareTabClass:DrawItems(vp, compareEntry, inputEvents) tradeHelpers.drawCompactSlotRow(drawY, label, pItem, cItem, colWidth, cursorX, cursorY, labelW, self.primaryBuild.itemsTab, compareEntry.itemsTab, pWarn, cWarn, slotMissing, - LAYOUT.itemsCopyBtnW, LAYOUT.itemsCopyBtnH, LAYOUT.itemsBuyBtnW, LAYOUT.itemsCopyUseBtnW, scrollOffsetX) + LAYOUT.itemsCopyBtnW, LAYOUT.itemsCopyBtnH, LAYOUT.itemsBuyBtnW, LAYOUT.itemsEquipBtnW, scrollOffsetX) if rowHoverItem then hoverItem = rowHoverItem @@ -3629,7 +3629,7 @@ function CompareTabClass:DrawItems(vp, compareEntry, inputEvents) hoverW, hoverH = rowHoverW, rowHoverH end - processSlotButtons(b1Hover, b2Hover, b3Hover, b2X, b2Y, b2W, b2H, cItem, copySlotName, copyUseSlotName) + processSlotButtons(b1Hover, b2Hover, b3Hover, b2X, b2Y, b2W, b2H, cItem, copySlotName, equipSlotName) drawY = drawY + 20 end @@ -3694,8 +3694,8 @@ function CompareTabClass:DrawItems(vp, compareEntry, inputEvents) -- Process item copy button clicks if clickedCopySlot then self:CopyCompareItemToPrimary(clickedCopySlot, compareEntry, false) - elseif clickedCopyUseSlot then - self:CopyCompareItemToPrimary(clickedCopyUseSlot, compareEntry, true) + elseif clickedEquipSlot then + self:CopyCompareItemToPrimary(clickedEquipSlot, compareEntry, true) end -- Process buy button click @@ -3712,21 +3712,21 @@ function CompareTabClass:DrawItems(vp, compareEntry, inputEvents) SetDrawLayer(nil, 0) end - -- Draw stat comparison tooltip when hovering Copy+Use button - if hoverCopyUseItem and hoverCopyUseSlotName and not hoverItem then + -- Draw stat comparison tooltip when hovering Equip button + if hoverEquipItem and hoverEquipSlotName and not hoverItem then self.itemTooltip:Clear() local calcFunc, calcBase = self.calcs.getMiscCalculator(self.primaryBuild) if calcFunc then -- Create a fresh item to evaluate - local newItem = new("Item", hoverCopyUseItem.raw) + local newItem = new("Item", hoverEquipItem.raw) newItem:NormaliseQuality() -- Determine what's currently in the target slot - local pSlot = self.primaryBuild.itemsTab.slots[hoverCopyUseSlotName] + local pSlot = self.primaryBuild.itemsTab.slots[hoverEquipSlotName] local selItem = pSlot and self.primaryBuild.itemsTab.items[pSlot.selItemId] -- For jewel sockets that aren't allocated, temporarily allocate the node - local override = { repSlotName = hoverCopyUseSlotName, repItem = newItem } + local override = { repSlotName = hoverEquipSlotName, repItem = newItem } if pSlot and pSlot.nodeId then local pSpec = self.primaryBuild.spec if pSpec and pSpec.allocNodes and not pSpec.allocNodes[pSlot.nodeId] then @@ -3738,7 +3738,7 @@ function CompareTabClass:DrawItems(vp, compareEntry, inputEvents) end local output = calcFunc(override) - local slotLabel = pSlot and pSlot.label or hoverCopyUseSlotName + local slotLabel = pSlot and pSlot.label or hoverEquipSlotName local header if selItem then header = string.format("^7Equipping this item in %s will give you:\n(replacing %s%s^7)", slotLabel, colorCodes[selItem.rarity] or "^7", selItem.name) @@ -3754,7 +3754,7 @@ function CompareTabClass:DrawItems(vp, compareEntry, inputEvents) SetDrawLayer(nil, 100) -- Force tooltip to the left of the button by passing a large width -- so the right-side placement overflows and the Draw logic flips to left - self.itemTooltip:Draw(hoverCopyUseBtnX, hoverCopyUseBtnY, vp.width, hoverCopyUseBtnH, vp) + self.itemTooltip:Draw(hoverEquipBtnX, hoverEquipBtnY, vp.width, hoverEquipBtnH, vp) SetDrawLayer(nil, 0) end diff --git a/src/Classes/CompareTradeHelpers.lua b/src/Classes/CompareTradeHelpers.lua index bfbf64be04..eb1802348d 100644 --- a/src/Classes/CompareTradeHelpers.lua +++ b/src/Classes/CompareTradeHelpers.lua @@ -249,15 +249,15 @@ function M.getSlotDiffLabel(pItem, cItem) end end --- Helper: draw Copy, Copy+Use, and Buy buttons at the given position. +-- Helper: draw Copy, Equip, and Buy buttons at the given position. -- btnStartX is the left edge where the first button (Buy) should appear. -- copyBtnW, copyBtnH, buyBtnW are button dimensions (passed from LAYOUT by caller). --- Returns copyHovered, copyUseHovered, buyHovered booleans. -function M.drawCopyButtons(cursorX, cursorY, btnStartX, btnY, slotMissing, copyBtnW, copyBtnH, buyBtnW, copyUseBtnW) +-- Returns copyHovered, equipHovered, buyHovered booleans. +function M.drawCopyButtons(cursorX, cursorY, btnStartX, btnY, slotMissing, copyBtnW, copyBtnH, buyBtnW, equipBtnW) local btnW = copyBtnW local btnH = copyBtnH local buyW = buyBtnW - local copyUseW = copyUseBtnW + local equipW = equipBtnW local btn3X = btnStartX local btn1X = btn3X + buyW + 4 local btn2X = btn1X + btnW + 4 @@ -297,18 +297,18 @@ function M.drawCopyButtons(cursorX, cursorY, btnStartX, btnY, slotMissing, copyB local b2Hover if slotMissing then - -- Show "Missing slot" label instead of Copy+Use button + -- Show "Missing slot" label instead of Equip button SetDrawColor(1, 1, 1) - DrawString(btn2X + copyUseW / 2, btnY + 1, "CENTER_X", 14, "VAR", "^xBBBBBBMissing slot") + DrawString(btn2X + equipW / 2, btnY + 1, "CENTER_X", 14, "VAR", "^xBBBBBBMissing slot") b2Hover = false else - -- "Copy+Use" button - b2Hover = cursorX >= btn2X and cursorX < btn2X + copyUseW + -- "Equip" button + b2Hover = cursorX >= btn2X and cursorX < btn2X + equipW and cursorY >= btnY and cursorY < btnY + btnH - drawBtn(btn2X, copyUseW, b2Hover, "^7Copy+Use") + drawBtn(btn2X, equipW, b2Hover, "^7Equip") end - return b1Hover, b2Hover, b3Hover, btn2X, btnY, copyUseW, btnH + return b1Hover, b2Hover, b3Hover, btn2X, btnY, equipW, btnH end -- Helper: fit a colored item name within maxW pixels, truncating with "..." if needed. @@ -338,7 +338,7 @@ local ITEM_BOX_H = 20 function M.drawCompactSlotRow(drawY, slotLabel, pItem, cItem, colWidth, cursorX, cursorY, maxLabelW, primaryItemsTab, compareItemsTab, pWarn, cWarn, slotMissing, - copyBtnW, copyBtnH, buyBtnW, copyUseBtnW, xOffset) + copyBtnW, copyBtnH, buyBtnW, equipBtnW, xOffset) xOffset = xOffset or 0 local pName = pItem and pItem.name or "(empty)" @@ -396,7 +396,7 @@ function M.drawCompactSlotRow(drawY, slotLabel, pItem, cItem, if cItem then local btnStartX = cBoxX + cBoxW + 6 b1Hover, b2Hover, b3Hover, b2X, b2Y, b2W, b2H = - M.drawCopyButtons(cursorX, cursorY, btnStartX, drawY + 1, slotMissing, copyBtnW, copyBtnH, buyBtnW, copyUseBtnW) + M.drawCopyButtons(cursorX, cursorY, btnStartX, drawY + 1, slotMissing, copyBtnW, copyBtnH, buyBtnW, equipBtnW) end -- Determine hovered item and tooltip anchor position From 5ef8c2c068b495c466014bcaf51a1a4497cd8e40 Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Sun, 19 Apr 2026 20:52:11 +1000 Subject: [PATCH 02/15] Change gem quality display Gem level + quality used to look like Lv20/20q and would not display the quality if it was 0 It now shows it like we have in the skills tab of just 20/20 --- src/Classes/CompareTab.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Classes/CompareTab.lua b/src/Classes/CompareTab.lua index e2dec82997..c9162b6af7 100644 --- a/src/Classes/CompareTab.lua +++ b/src/Classes/CompareTab.lua @@ -3915,8 +3915,8 @@ function CompareTabClass:DrawSkills(vp, compareEntry) elseif entry.gem then local gemName = entry.gem.grantedEffect and entry.gem.grantedEffect.name or entry.gem.nameSpec or "?" local gemColor = entry.gem.color or colorCodes.GEM - local levelStr = entry.gem.level and (" Lv" .. entry.gem.level) or "" - local qualStr = entry.gem.quality and entry.gem.quality > 0 and ("/" .. entry.gem.quality .. "q") or "" + local levelStr = entry.gem.level and (" " .. entry.gem.level) or "" + local qualStr = entry.gem.quality and ("/" .. entry.gem.quality .. "") or "" local prefix = "" if entry.status == "additional" then prefix = colorCodes.POSITIVE .. "+ " From 5f2e4911504b8850037431a0aa2d5db6e4e336c6 Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Sun, 19 Apr 2026 22:07:07 +1000 Subject: [PATCH 03/15] Add gear icons to skill tab Adds the gear icons to the stat of each group label Increase the font size of the header too as it didn't stand out before Adjust the indenting of the list so they all line up nicely --- src/Classes/CompareTab.lua | 42 ++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/src/Classes/CompareTab.lua b/src/Classes/CompareTab.lua index c9162b6af7..e37698cd81 100644 --- a/src/Classes/CompareTab.lua +++ b/src/Classes/CompareTab.lua @@ -4047,6 +4047,30 @@ function CompareTabClass:DrawSkills(vp, compareEntry) end return groupLabel end + local function getGroupSlotIcon(skillsTab, idx, group) + local groupList = skillsTab and skillsTab.controls and skillsTab.controls.groupList + if not groupList or not groupList.GetRowIcon or not group then + return nil + end + return groupList:GetRowIcon(1, idx, group) + end + local groupHeaderX = 10 + local groupHeaderIconSize = 16 + local groupHeaderIconGap = 2 + local groupHeaderTextIndent = groupHeaderIconSize + groupHeaderIconGap + local groupHeaderTextX = groupHeaderX + groupHeaderTextIndent + local function getGroupHeaderWidth(group, idx) + return groupHeaderTextX + DrawStringWidth(18, "VAR", "^7" .. getGroupLabel(group, idx)) + end + local function drawGroupHeader(skillsTab, group, idx, x, y) + local icon = getGroupSlotIcon(skillsTab, idx, group) + local textX = x + groupHeaderTextIndent + if icon then + SetDrawColor(1, 1, 1) + DrawImage(icon, x, y, groupHeaderIconSize, groupHeaderIconSize) + end + DrawString(textX, y, "LEFT", 18, "VAR", "^7" .. getGroupLabel(group, idx)) + end local displayListsByPair = {} local maxPrimaryW = 0 @@ -4059,12 +4083,12 @@ function CompareTabClass:DrawSkills(vp, compareEntry) displayListsByPair[idx] = { p = pDisplay, c = cDisplay } if pGroup then - local w = 10 + DrawStringWidth(16, "VAR", "^7" .. getGroupLabel(pGroup, pair.pIdx)) + local w = getGroupHeaderWidth(pGroup, pair.pIdx) if w > maxPrimaryW then maxPrimaryW = w end end for _, entry in ipairs(pDisplay) do local line = buildGemDisplayString(entry) - local w = 20 + DrawStringWidth(gemFontSize, "VAR", line) + local w = groupHeaderTextX + DrawStringWidth(gemFontSize, "VAR", line) if w > maxPrimaryW then maxPrimaryW = w end end end @@ -4076,7 +4100,7 @@ function CompareTabClass:DrawSkills(vp, compareEntry) local colWidth = maxPrimaryW + LAYOUT.compareColGap local contentWidth = colWidth * 2 local needsHScroll = contentWidth > vp.width - local gemTextWidth = colWidth - 30 + local gemTextWidth = colWidth - (groupHeaderTextX + 10) -- Configure horizontal scrollbar local hBar = self.controls.skillsHScrollBar @@ -4108,8 +4132,8 @@ function CompareTabClass:DrawSkills(vp, compareEntry) local pGroup = pair.pIdx and pGroups[pair.pIdx] local cGroup = pair.cIdx and cGroups[pair.cIdx] - local pGemY = collectGemEntries(gemEntries, pDisplayList, scrollOffsetX + 20, preY + lineHeight, pGroup) - local cGemY = collectGemEntries(gemEntries, cDisplayList, scrollOffsetX + colWidth + 20, preY + lineHeight, cGroup) + local pGemY = collectGemEntries(gemEntries, pDisplayList, scrollOffsetX + groupHeaderTextX, preY + lineHeight, pGroup) + local cGemY = collectGemEntries(gemEntries, cDisplayList, scrollOffsetX + colWidth + groupHeaderTextX, preY + lineHeight, cGroup) preY = preY + m_max(pGemY - preY, cGemY - preY) + 6 end @@ -4163,14 +4187,14 @@ function CompareTabClass:DrawSkills(vp, compareEntry) local cGroup = pair.cIdx and cGroups[pair.cIdx] if pGroup then - DrawString(scrollOffsetX + 10, drawY, "LEFT", 16, "VAR", "^7" .. getGroupLabel(pGroup, pair.pIdx)) + drawGroupHeader(pSkillsTab, pGroup, pair.pIdx, scrollOffsetX + groupHeaderX, drawY) end if cGroup then - DrawString(scrollOffsetX + colWidth + 10, drawY, "LEFT", 16, "VAR", "^7" .. getGroupLabel(cGroup, pair.cIdx)) + drawGroupHeader(cSkillsTab, cGroup, pair.cIdx, scrollOffsetX + colWidth + groupHeaderX, drawY) end - pFinalGemY = drawGemList(pDisplayList, scrollOffsetX + 20, drawY + lineHeight, highlightSet, gemTextWidth) - cFinalGemY = drawGemList(cDisplayList, scrollOffsetX + colWidth + 20, drawY + lineHeight, highlightSet, gemTextWidth) + pFinalGemY = drawGemList(pDisplayList, scrollOffsetX + groupHeaderTextX, drawY + lineHeight, highlightSet, gemTextWidth) + cFinalGemY = drawGemList(cDisplayList, scrollOffsetX + colWidth + groupHeaderTextX, drawY + lineHeight, highlightSet, gemTextWidth) drawY = drawY + m_max(pFinalGemY - drawY, cFinalGemY - drawY) + 6 end From d937b71b92e78b45c0e9ddbcbdc2fedf3bb7d55c Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Sun, 19 Apr 2026 22:11:48 +1000 Subject: [PATCH 04/15] Use background colour of other tabs The background colour was being set to pitch black instead of the slightly lighter block that we use in the other tabs --- src/Classes/CompareTab.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Classes/CompareTab.lua b/src/Classes/CompareTab.lua index e37698cd81..a4a76bb197 100644 --- a/src/Classes/CompareTab.lua +++ b/src/Classes/CompareTab.lua @@ -1566,6 +1566,7 @@ end -- DRAW - Main render method -- ============================================================ function CompareTabClass:Draw(viewPort, inputEvents) + main:DrawBackground(viewPort) -- Position top-bar controls self.controls.subTabAnchor.x = viewPort.x + 4 self.controls.subTabAnchor.y = viewPort.y + 96 From 3e1ce4e65457077a5494ba401d66ff299cfd8eab Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Sun, 19 Apr 2026 22:31:05 +1000 Subject: [PATCH 05/15] Ctrl + F hotkey + reorder config header Ctrl + f now selects the search box on the configs tab Changed the order of the buttons so it uses the same layout as the main configs tab --- src/Classes/CompareTab.lua | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/src/Classes/CompareTab.lua b/src/Classes/CompareTab.lua index a4a76bb197..3ecf0fd431 100644 --- a/src/Classes/CompareTab.lua +++ b/src/Classes/CompareTab.lua @@ -911,7 +911,7 @@ function CompareTabClass:InitControls() end -- Config view: search bar - self.controls.configSearchEdit = new("EditControl", nil, {0, 0, 200, 20}, "", "Search", "%c", 100, nil, nil, nil, true) + self.controls.configSearchEdit = new("EditControl", nil, {0, 0, 240, 20}, "", "Search", "%c", 100, nil, nil, nil, true) self.controls.configSearchEdit.shown = function() return self.compareViewMode == "CONFIG" and self:GetActiveCompare() ~= nil end @@ -1605,6 +1605,13 @@ function CompareTabClass:Draw(viewPort, inputEvents) if compareEntry then self:UpdateSetSelectors(compareEntry) end + for _, event in ipairs(inputEvents) do + if event.type == "KeyDown" and event.key == "f" and IsKeyDown("CTRL") then + if self.compareViewMode == "CONFIG" and compareEntry then + self:SelectControl(self.controls.configSearchEdit) + end + end + end -- Layout and refresh calcs skill detail controls self.calcsSkillHeaderHeight = 0 if self.compareViewMode == "CALCS" and compareEntry then @@ -1898,12 +1905,19 @@ function CompareTabClass:LayoutConfigView(contentVP, compareEntry) -- Position header controls local row1Y = contentVP.y + 4 local row2Y = contentVP.y + 28 - self.controls.copyConfigBtn.x = contentVP.x + 10 + local configHeaderLeftX = contentVP.x + 10 + local configSetSelectX = contentVP.x + 80 + local configSetSelectW = 225 + local inputEndX = configSetSelectX + configSetSelectW + local actionX = inputEndX + 10 + + self.controls.copyConfigBtn.x = actionX self.controls.copyConfigBtn.y = row1Y - self.controls.configToggleBtn.x = contentVP.x + 260 - self.controls.configToggleBtn.y = row1Y + self.controls.configToggleBtn.x = actionX + self.controls.configToggleBtn.y = row2Y - self.controls.configSearchEdit.x = contentVP.x + 10 + self.controls.configSearchEdit.x = configHeaderLeftX + self.controls.configSearchEdit.width = inputEndX - configHeaderLeftX self.controls.configSearchEdit.y = row2Y -- Update primary config set dropdown list @@ -1917,10 +1931,11 @@ function CompareTabClass:LayoutConfigView(contentVP, compareEntry) end end self.controls.configPrimarySetSelect:SetList(pSetList) - self.controls.configPrimarySetLabel.x = contentVP.x + 220 - self.controls.configPrimarySetLabel.y = row2Y + 2 - self.controls.configPrimarySetSelect.x = contentVP.x + 290 - self.controls.configPrimarySetSelect.y = row2Y + self.controls.configPrimarySetLabel.x = configHeaderLeftX + self.controls.configPrimarySetLabel.y = row1Y + 2 + self.controls.configPrimarySetSelect.x = configSetSelectX + self.controls.configPrimarySetSelect.width = configSetSelectW + self.controls.configPrimarySetSelect.y = row1Y -- Build section layout: multi-column grid, mirroring regular ConfigTab local rowHeight = LAYOUT.configRowHeight From d1656aa807c11fd59319466f5088d15d97258514 Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Sun, 19 Apr 2026 22:38:05 +1000 Subject: [PATCH 06/15] Widen skill selection box --- src/Classes/CompareTab.lua | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Classes/CompareTab.lua b/src/Classes/CompareTab.lua index 3ecf0fd431..dca9eb66db 100644 --- a/src/Classes/CompareTab.lua +++ b/src/Classes/CompareTab.lua @@ -343,7 +343,7 @@ function CompareTabClass:InitControls() self.controls.cmpSkillLabel.shown = setsEnabled -- Socket group dropdown - self.controls.cmpSocketGroup = new("DropDownControl", {"LEFT", self.controls.cmpSkillLabel, "RIGHT"}, {2, 0, 200, 20}, {}, function(index, value) + self.controls.cmpSocketGroup = new("DropDownControl", {"LEFT", self.controls.cmpSkillLabel, "RIGHT"}, {4, 0, 200, 20}, {}, function(index, value) local entry = self:GetActiveCompare() if entry then entry:SetMainSocketGroup(index) @@ -354,7 +354,7 @@ function CompareTabClass:InitControls() self.controls.cmpSocketGroup.enableDroppedWidth = true -- Active skill within group - self.controls.cmpMainSkill = new("DropDownControl", {"LEFT", self.controls.cmpSocketGroup, "RIGHT"}, {2, 0, 150, 20}, {}, function(index, value) + self.controls.cmpMainSkill = new("DropDownControl", {"LEFT", self.controls.cmpSocketGroup, "RIGHT"}, {4, 0, 225, 20}, {}, function(index, value) local entry = self:GetActiveCompare() if entry then local mainSocketGroup = entry.skillsTab.socketGroupList[entry.mainSocketGroup] @@ -367,7 +367,7 @@ function CompareTabClass:InitControls() self.controls.cmpMainSkill.shown = false -- Skill part (multi-part skills) - self.controls.cmpSkillPart = new("DropDownControl", {"LEFT", self.controls.cmpMainSkill, "RIGHT"}, {2, 0, 100, 20}, {}, function(index, value) + self.controls.cmpSkillPart = new("DropDownControl", {"LEFT", self.controls.cmpMainSkill, "RIGHT"}, {4, 0, 200, 20}, {}, function(index, value) local entry = self:GetActiveCompare() if entry then local mainSocketGroup = entry.skillsTab.socketGroupList[entry.mainSocketGroup] @@ -384,9 +384,9 @@ function CompareTabClass:InitControls() self.controls.cmpSkillPart.shown = false -- Stage count - self.controls.cmpStageCountLabel = new("LabelControl", {"LEFT", self.controls.cmpSkillPart, "RIGHT"}, {4, 0, 0, 16}, "^7Stages:") + self.controls.cmpStageCountLabel = new("LabelControl", {"LEFT", self.controls.cmpSkillPart, "RIGHT"}, {6, 0, 0, 16}, "^7Stages:") self.controls.cmpStageCountLabel.shown = function() return self.controls.cmpStageCount.shown end - self.controls.cmpStageCount = new("EditControl", {"LEFT", self.controls.cmpStageCountLabel, "RIGHT"}, {2, 0, 52, 20}, "", nil, "%D", 5, function(buf) + self.controls.cmpStageCount = new("EditControl", {"LEFT", self.controls.cmpStageCountLabel, "RIGHT"}, {4, 0, 52, 20}, "", nil, "%D", 5, function(buf) local entry = self:GetActiveCompare() if entry then local mainSocketGroup = entry.skillsTab.socketGroupList[entry.mainSocketGroup] @@ -403,9 +403,9 @@ function CompareTabClass:InitControls() self.controls.cmpStageCount.shown = false -- Mine count - self.controls.cmpMineCountLabel = new("LabelControl", {"LEFT", self.controls.cmpStageCount, "RIGHT"}, {4, 0, 0, 16}, "^7Mines:") + self.controls.cmpMineCountLabel = new("LabelControl", {"LEFT", self.controls.cmpStageCount, "RIGHT"}, {6, 0, 0, 16}, "^7Mines:") self.controls.cmpMineCountLabel.shown = function() return self.controls.cmpMineCount.shown end - self.controls.cmpMineCount = new("EditControl", {"LEFT", self.controls.cmpMineCountLabel, "RIGHT"}, {2, 0, 52, 20}, "", nil, "%D", 5, function(buf) + self.controls.cmpMineCount = new("EditControl", {"LEFT", self.controls.cmpMineCountLabel, "RIGHT"}, {4, 0, 52, 20}, "", nil, "%D", 5, function(buf) local entry = self:GetActiveCompare() if entry then local mainSocketGroup = entry.skillsTab.socketGroupList[entry.mainSocketGroup] @@ -422,7 +422,7 @@ function CompareTabClass:InitControls() self.controls.cmpMineCount.shown = false -- Minion selector - self.controls.cmpMinion = new("DropDownControl", {"LEFT", self.controls.cmpMineCount, "RIGHT"}, {4, 0, 140, 20}, {}, function(index, value) + self.controls.cmpMinion = new("DropDownControl", {"LEFT", self.controls.cmpMineCount, "RIGHT"}, {6, 0, 140, 20}, {}, function(index, value) local entry = self:GetActiveCompare() if entry then local mainSocketGroup = entry.skillsTab.socketGroupList[entry.mainSocketGroup] @@ -446,7 +446,7 @@ function CompareTabClass:InitControls() self.controls.cmpMinion.shown = false -- Minion skill selector - self.controls.cmpMinionSkill = new("DropDownControl", {"LEFT", self.controls.cmpMinion, "RIGHT"}, {2, 0, 140, 20}, {}, function(index, value) + self.controls.cmpMinionSkill = new("DropDownControl", {"LEFT", self.controls.cmpMinion, "RIGHT"}, {4, 0, 140, 20}, {}, function(index, value) local entry = self:GetActiveCompare() if entry then local mainSocketGroup = entry.skillsTab.socketGroupList[entry.mainSocketGroup] From 53b74f337cffed535167dc402ebd9a80105be088 Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Sun, 19 Apr 2026 23:03:36 +1000 Subject: [PATCH 07/15] Fix scrolling on control elements Trying to scroll on dropdowns or input boxes was not working as it was always trying to handle the scroll input before the control input Also change to use KeyUp from the scroll events as that's what we do elsewhere --- src/Classes/CompareTab.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Classes/CompareTab.lua b/src/Classes/CompareTab.lua index dca9eb66db..61dc0ab152 100644 --- a/src/Classes/CompareTab.lua +++ b/src/Classes/CompareTab.lua @@ -1617,7 +1617,6 @@ function CompareTabClass:Draw(viewPort, inputEvents) if self.compareViewMode == "CALCS" and compareEntry then self.calcsSkillHeaderHeight = self:LayoutCalcsSkillControls(contentVP, compareEntry) end - self:HandleScrollInput(contentVP, inputEvents) -- Draw calcs skill header background if self.compareViewMode == "CALCS" and self.calcsSkillHeaderHeight > 0 then @@ -1627,6 +1626,7 @@ function CompareTabClass:Draw(viewPort, inputEvents) -- Process input events for our controls (including footer controls) self:ProcessControlsInput(inputEvents, viewPort) + self:HandleScrollInput(contentVP, inputEvents) -- Draw TREE view BEFORE controls so header dropdowns render on top of the tree if self.compareViewMode == "TREE" and compareEntry then @@ -2249,7 +2249,7 @@ function CompareTabClass:HandleScrollInput(contentVP, inputEvents) local mouseOverList = listControl:IsShown() and listControl:IsMouseOver() for id, event in ipairs(inputEvents) do - if event.type == "KeyDown" and mouseInContent and not mouseOverList then + if event.type == "KeyUp" and mouseInContent and not mouseOverList then if self.compareViewMode == "CALCS" then if event.key == "WHEELUP" then self.controls.calcsScrollBar:Scroll(-1) From 6672d4a6c0715723e3f149e760520ea5043193b9 Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Mon, 20 Apr 2026 00:50:09 +1000 Subject: [PATCH 08/15] Add scrollbars to tabs Scrollbars were not appearing in the summary, skill, items and config tabs --- src/Classes/ComparePowerReportListControl.lua | 4 +- src/Classes/CompareTab.lua | 166 ++++++++++++------ 2 files changed, 110 insertions(+), 60 deletions(-) diff --git a/src/Classes/ComparePowerReportListControl.lua b/src/Classes/ComparePowerReportListControl.lua index 1df2699180..00e21a2711 100644 --- a/src/Classes/ComparePowerReportListControl.lua +++ b/src/Classes/ComparePowerReportListControl.lua @@ -16,8 +16,8 @@ local ComparePowerReportListClass = newClass("ComparePowerReportListControl", "L { width = width * 0.10, label = "Category", sortable = true }, { width = width * 0.44, label = "Name" }, self.impactColumn, - { width = width * 0.08, label = "Points", sortable = true }, - { width = width * 0.16, label = "Per Point", sortable = true }, + { width = width * 0.06, label = "Points", sortable = true }, + { width = width * 0.14, label = "Per Point", sortable = true }, } self.colLabels = true self.showRowSeparators = true diff --git a/src/Classes/CompareTab.lua b/src/Classes/CompareTab.lua index 61dc0ab152..0aefa43ac1 100644 --- a/src/Classes/CompareTab.lua +++ b/src/Classes/CompareTab.lua @@ -1018,6 +1018,16 @@ function CompareTabClass:InitControls() return self.compareViewMode == "CALCS" and self:GetActiveCompare() ~= nil and calcsScrollBar.enabled end + -- Shared vertical scrollbar for Summary/Items/Skills/Config sub-tabs + self.controls.viewScrollBar = new("ScrollBarControl", nil, {0, 0, 18, 0}, 50, "VERTICAL", true) + local viewScrollBar = self.controls.viewScrollBar + self.controls.viewScrollBar.shown = function() + return self:GetActiveCompare() ~= nil + and self.compareViewMode ~= "TREE" + and self.compareViewMode ~= "CALCS" + and viewScrollBar.enabled + end + -- Horizontal scrollbar for Items sub-tab self.controls.itemsHScrollBar = new("ScrollBarControl", nil, {0, 0, 0, LAYOUT.itemsHScrollBarHeight}, 60, "HORIZONTAL", true) local itemsHScrollBar = self.controls.itemsHScrollBar @@ -1624,12 +1634,49 @@ function CompareTabClass:Draw(viewPort, inputEvents) DrawImage(nil, contentVP.x, contentVP.y, contentVP.width, self.calcsSkillHeaderHeight) end + -- Layout shared vertical scrollbar for Summary/Items/Skills/Config. + local mode = self.compareViewMode + local viewScrollBar = self.controls.viewScrollBar + if compareEntry and mode ~= "TREE" and mode ~= "CALCS" then + local topReserve = 0 + local bottomReserve = 0 + local contentHeight = 0 + if mode == "SUMMARY" then + contentHeight = self.summaryTotalContentHeight or 0 + elseif mode == "CONFIG" then + topReserve = LAYOUT.configFixedHeaderHeight + contentHeight = self.configTotalContentHeight or 0 + elseif mode == "ITEMS" then + topReserve = LAYOUT.itemsCheckboxOffset + bottomReserve = self.controls.itemsHScrollBar.enabled and LAYOUT.itemsHScrollBarHeight or 0 + contentHeight = self.itemsTotalContentHeight or 0 + elseif mode == "SKILLS" then + bottomReserve = self.controls.skillsHScrollBar.enabled and LAYOUT.skillsHScrollBarHeight or 0 + contentHeight = self.skillsTotalContentHeight or 0 + end + local viewHeight = m_max(contentVP.height - topReserve - bottomReserve, 0) + viewScrollBar.x = contentVP.x + contentVP.width - 18 + viewScrollBar.y = contentVP.y + topReserve + viewScrollBar.height = viewHeight + viewScrollBar:SetContentDimension(contentHeight, viewHeight) + viewScrollBar:SetOffset(self.scrollY) + self.scrollY = viewScrollBar.offset + else + viewScrollBar:SetContentDimension(0, contentVP.height) + viewScrollBar:SetOffset(0) + end + -- Process input events for our controls (including footer controls) self:ProcessControlsInput(inputEvents, viewPort) self:HandleScrollInput(contentVP, inputEvents) + if self.controls.viewScrollBar:IsShown() then + self.scrollY = self.controls.viewScrollBar.offset + end + + local drawingTree = self.compareViewMode == "TREE" and compareEntry ~= nil -- Draw TREE view BEFORE controls so header dropdowns render on top of the tree - if self.compareViewMode == "TREE" and compareEntry then + if drawingTree then self:DrawTree(contentVP, inputEvents, compareEntry) -- Elevate to main draw layer 1 (matching TreeTab pattern) so controls @@ -1651,14 +1698,6 @@ function CompareTabClass:Draw(viewPort, inputEvents) end end - -- Draw controls (at main layer 1 when in TREE mode, above all tree content) - self:DrawControls(viewPort) - - -- Reset to default draw layer after controls - if self.compareViewMode == "TREE" and compareEntry then - SetDrawLayer(0) - end - if not compareEntry then -- No comparison build loaded - show instructions SetViewport(contentVP.x, contentVP.y, contentVP.width, contentVP.height) @@ -1668,57 +1707,63 @@ function CompareTabClass:Draw(viewPort, inputEvents) DrawString(0, 70, "CENTER", 16, "VAR", "^7Click " .. colorCodes.POSITIVE .. "Import..." .. "^7 above to import a build to compare against.") SetViewport() - return - end - - -- Position items expanded mode checkbox and item set dropdowns (inside content area, top-left) - -- Label draws to the left of the checkbox, so offset x by labelWidth to keep it visible - if self.compareViewMode == "ITEMS" then - self.controls.itemsExpandedCheck.x = contentVP.x + 10 + self.controls.itemsExpandedCheck.labelWidth - self.controls.itemsExpandedCheck.y = contentVP.y + 8 - - local colWidth = self.itemsColWidth or m_max(m_floor(contentVP.width / 2), LAYOUT.itemsMinColWidth) - local itemSetLabelW = DrawStringWidth(16, "VAR", "^7Item set:") + 4 - local scrollOffsetX = -((self.controls.itemsHScrollBar and self.controls.itemsHScrollBar.offset) or 0) - - -- Item set dropdowns - local row1Y = contentVP.y + 34 - - -- Primary build item set dropdown - self.controls.primaryItemSetLabel.x = contentVP.x + scrollOffsetX + 10 - self.controls.primaryItemSetLabel.y = row1Y + 2 - self.controls.primaryItemSetSelect.x = contentVP.x + scrollOffsetX + 10 + itemSetLabelW - self.controls.primaryItemSetSelect.y = row1Y - - -- Compare build item set dropdown - self.controls.compareItemSetLabel2.x = contentVP.x + scrollOffsetX + colWidth + 10 - self.controls.compareItemSetLabel2.y = row1Y + 2 - self.controls.compareItemSetSelect2.x = contentVP.x + scrollOffsetX + colWidth + 10 + itemSetLabelW - self.controls.compareItemSetSelect2.y = row1Y + else + -- Position items expanded mode checkbox and item set dropdowns (inside content area, top-left) + -- Label draws to the left of the checkbox, so offset x by labelWidth to keep it visible + if self.compareViewMode == "ITEMS" then + self.controls.itemsExpandedCheck.x = contentVP.x + 10 + self.controls.itemsExpandedCheck.labelWidth + self.controls.itemsExpandedCheck.y = contentVP.y + 8 + + local colWidth = self.itemsColWidth or m_max(m_floor(contentVP.width / 2), LAYOUT.itemsMinColWidth) + local itemSetLabelW = DrawStringWidth(16, "VAR", "^7Item set:") + 4 + local scrollOffsetX = -((self.controls.itemsHScrollBar and self.controls.itemsHScrollBar.offset) or 0) + + -- Item set dropdowns + local row1Y = contentVP.y + 34 + + -- Primary build item set dropdown + self.controls.primaryItemSetLabel.x = contentVP.x + scrollOffsetX + 10 + self.controls.primaryItemSetLabel.y = row1Y + 2 + self.controls.primaryItemSetSelect.x = contentVP.x + scrollOffsetX + 10 + itemSetLabelW + self.controls.primaryItemSetSelect.y = row1Y + + -- Compare build item set dropdown + self.controls.compareItemSetLabel2.x = contentVP.x + scrollOffsetX + colWidth + 10 + self.controls.compareItemSetLabel2.y = row1Y + 2 + self.controls.compareItemSetSelect2.x = contentVP.x + scrollOffsetX + colWidth + 10 + itemSetLabelW + self.controls.compareItemSetSelect2.y = row1Y + + -- Populate primary build item set list + if self.primaryBuild.itemsTab and self.primaryBuild.itemsTab.itemSetOrderList then + self:PopulateSetDropdown(self.primaryBuild.itemsTab, "itemSetOrderList", "itemSets", "activeItemSetId", self.controls.primaryItemSetSelect) + end - -- Populate primary build item set list - if self.primaryBuild.itemsTab and self.primaryBuild.itemsTab.itemSetOrderList then - self:PopulateSetDropdown(self.primaryBuild.itemsTab, "itemSetOrderList", "itemSets", "activeItemSetId", self.controls.primaryItemSetSelect) + -- Populate compare build item set list + if compareEntry and compareEntry.itemsTab and compareEntry.itemsTab.itemSetOrderList then + self:PopulateSetDropdown(compareEntry.itemsTab, "itemSetOrderList", "itemSets", "activeItemSetId", self.controls.compareItemSetSelect2) + end end - -- Populate compare build item set list - if compareEntry and compareEntry.itemsTab and compareEntry.itemsTab.itemSetOrderList then - self:PopulateSetDropdown(compareEntry.itemsTab, "itemSetOrderList", "itemSets", "activeItemSetId", self.controls.compareItemSetSelect2) + -- Dispatch to sub-view (TREE already drawn above) + if self.compareViewMode == "SUMMARY" then + self:DrawSummary(contentVP, compareEntry) + elseif self.compareViewMode == "ITEMS" then + self:DrawItems(contentVP, compareEntry, inputEvents) + elseif self.compareViewMode == "SKILLS" then + self:DrawSkills(contentVP, compareEntry) + elseif self.compareViewMode == "CALCS" then + self:DrawCalcs(contentVP, compareEntry) + elseif self.compareViewMode == "CONFIG" then + self:DrawConfig(contentVP, compareEntry) end - end - -- Dispatch to sub-view (TREE already drawn above) - if self.compareViewMode == "SUMMARY" then - self:DrawSummary(contentVP, compareEntry) - elseif self.compareViewMode == "ITEMS" then - self:DrawItems(contentVP, compareEntry, inputEvents) - elseif self.compareViewMode == "SKILLS" then - self:DrawSkills(contentVP, compareEntry) - elseif self.compareViewMode == "CALCS" then - self:DrawCalcs(contentVP, compareEntry) - elseif self.compareViewMode == "CONFIG" then - self:DrawConfig(contentVP, compareEntry) + self:DrawControls(viewPort) + if drawingTree then + SetDrawLayer(0) + end + if self.controls.viewScrollBar:IsShown() then + self.scrollY = self.controls.viewScrollBar.offset end end @@ -2247,6 +2292,7 @@ function CompareTabClass:HandleScrollInput(contentVP, inputEvents) local listControl = self.controls.comparePowerReportList local mouseOverList = listControl:IsShown() and listControl:IsMouseOver() + local mouseOverViewScrollBar = self.controls.viewScrollBar:IsShown() and self.controls.viewScrollBar:IsMouseOver() for id, event in ipairs(inputEvents) do if event.type == "KeyUp" and mouseInContent and not mouseOverList then @@ -2258,10 +2304,12 @@ function CompareTabClass:HandleScrollInput(contentVP, inputEvents) self.controls.calcsScrollBar:Scroll(1) inputEvents[id] = nil end - elseif event.key == "WHEELUP" and self.compareViewMode ~= "TREE" then + elseif event.key == "WHEELUP" and self.compareViewMode ~= "TREE" and not mouseOverViewScrollBar then self.scrollY = m_max(self.scrollY - 40, 0) + self.controls.viewScrollBar:SetOffset(self.scrollY) + self.scrollY = self.controls.viewScrollBar.offset inputEvents[id] = nil - elseif event.key == "WHEELDOWN" and self.compareViewMode ~= "TREE" then + elseif event.key == "WHEELDOWN" and self.compareViewMode ~= "TREE" and not mouseOverViewScrollBar then local maxScroll = 0 local viewportH = contentVP.height if self.compareViewMode == "CONFIG" and self.configTotalContentHeight then @@ -2278,6 +2326,8 @@ function CompareTabClass:HandleScrollInput(contentVP, inputEvents) maxScroll = m_max(self.skillsTotalContentHeight - (viewportH - hBarReserve), 0) end self.scrollY = m_min(self.scrollY + 40, maxScroll) + self.controls.viewScrollBar:SetOffset(self.scrollY) + self.scrollY = self.controls.viewScrollBar.offset inputEvents[id] = nil end end @@ -3066,9 +3116,9 @@ function CompareTabClass:DrawSummary(vp, compareEntry) -- Position the list control (absolute screen coordinates). -- The list has a fixed height and its own internal scrollbar for rows. - -- Width matches the table columns (750) plus scrollbar (20px border/scroll area). + -- Width matches the table columns (720) plus scrollbar (20px border/scroll area). local listHeight = 250 - local listWidth = 770 + local listWidth = 740 listControl.x = vp.x + LAYOUT.powerReportLeft listControl.y = vp.y + drawY listControl.width = listWidth From 22787b6af93001e667b834df13abd07ceedaadf5 Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Mon, 20 Apr 2026 01:09:06 +1000 Subject: [PATCH 09/15] Fix tooltip display on small horizontal viewport Tooltips would clip off the side on smaller widths. Now passes in a max width value to compact the size of the tooltips to fit more info on screen --- src/Classes/CompareTab.lua | 9 ++++++--- src/Classes/ItemsTab.lua | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Classes/CompareTab.lua b/src/Classes/CompareTab.lua index 0aefa43ac1..73f2e38884 100644 --- a/src/Classes/CompareTab.lua +++ b/src/Classes/CompareTab.lua @@ -3770,17 +3770,20 @@ function CompareTabClass:DrawItems(vp, compareEntry, inputEvents) end -- Draw item tooltip on hover (compact mode only, on top of everything) + SetViewport() + local maxTooltipWidth = m_min(600, m_max(260, vp.width - 24)) if hoverItem and hoverItemsTab then self.itemTooltip:Clear() - hoverItemsTab:AddItemTooltip(self.itemTooltip, hoverItem, nil) + hoverItemsTab:AddItemTooltip(self.itemTooltip, hoverItem, nil, nil, maxTooltipWidth) SetDrawLayer(nil, 100) - self.itemTooltip:Draw(hoverX, hoverY, hoverW, hoverH, vp) + self.itemTooltip:Draw(vp.x + hoverX, vp.y + checkboxOffset + hoverY, hoverW, hoverH, vp) SetDrawLayer(nil, 0) end -- Draw stat comparison tooltip when hovering Equip button if hoverEquipItem and hoverEquipSlotName and not hoverItem then self.itemTooltip:Clear() + self.itemTooltip.maxWidth = maxTooltipWidth local calcFunc, calcBase = self.calcs.getMiscCalculator(self.primaryBuild) if calcFunc then -- Create a fresh item to evaluate @@ -3820,7 +3823,7 @@ function CompareTabClass:DrawItems(vp, compareEntry, inputEvents) SetDrawLayer(nil, 100) -- Force tooltip to the left of the button by passing a large width -- so the right-side placement overflows and the Draw logic flips to left - self.itemTooltip:Draw(hoverEquipBtnX, hoverEquipBtnY, vp.width, hoverEquipBtnH, vp) + self.itemTooltip:Draw(vp.x + hoverEquipBtnX, vp.y + checkboxOffset + hoverEquipBtnY, vp.width, hoverEquipBtnH, vp) SetDrawLayer(nil, 0) end diff --git a/src/Classes/ItemsTab.lua b/src/Classes/ItemsTab.lua index c0fa7b14d5..3825af2d33 100644 --- a/src/Classes/ItemsTab.lua +++ b/src/Classes/ItemsTab.lua @@ -3394,12 +3394,12 @@ function ItemsTabClass:FormatItemSource(text) :gsub("prophecy{([^}]+)}",colorCodes.PROPHECY.."%1"..colorCodes.SOURCE) end -function ItemsTabClass:AddItemTooltip(tooltip, item, slot, dbMode) +function ItemsTabClass:AddItemTooltip(tooltip, item, slot, dbMode, maxWidth) local fontSizeSmall = main.showFlavourText and 16 or 14 local fontSizeBig = main.showFlavourText and 18 or 16 local fontSizeTitle = main.showFlavourText and 22 or 20 local rarityCode = colorCodes[item.rarity] - tooltip.maxWidth = 600 -- Should instead get the longest mod and set the width to that. Some flavour text is way too long so we need a cap of sorts. + tooltip.maxWidth = m_min(maxWidth or 600, 600) -- Cap very long lines. Can use a narrower width for small viewports tooltip.tooltipHeader = item.rarity tooltip.foilType = item.foilType tooltip.center = true From 301f06c534fe11820c75a70f4ea49e88014d1ff7 Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Mon, 20 Apr 2026 01:53:23 +1000 Subject: [PATCH 10/15] Improve top header sections Changes the header for summary, skills and items tab to no move when scrolled Tree tab now shows the build name for each dropdown --- src/Classes/CompareTab.lua | 104 ++++++++++++++++++++++--------------- 1 file changed, 62 insertions(+), 42 deletions(-) diff --git a/src/Classes/CompareTab.lua b/src/Classes/CompareTab.lua index 73f2e38884..03ea67c919 100644 --- a/src/Classes/CompareTab.lua +++ b/src/Classes/CompareTab.lua @@ -52,7 +52,7 @@ local LAYOUT = { controlBarHeight = 126, -- Tree view header/footer - treeHeaderHeight = 58, + treeHeaderHeight = 74, treeFooterHeight = 30, treeOverlayCheckX = 155, @@ -61,9 +61,10 @@ local LAYOUT = { summaryCol2Right = 440, summaryCol3Right = 580, summaryCol4 = 600, + summaryHeaderHeight = 38, -- Items view - itemsCheckboxOffset = 60, + itemsCheckboxOffset = 78, itemsCopyBtnW = 60, itemsEquipBtnW = 60, itemsCopyBtnH = 18, @@ -71,6 +72,7 @@ local LAYOUT = { itemsMinColWidth = 700, itemsHScrollBarHeight = 16, compareColGap = 48, + skillsHeaderHeight = 24, skillsHScrollBarHeight = 16, -- Calcs view @@ -1642,6 +1644,7 @@ function CompareTabClass:Draw(viewPort, inputEvents) local bottomReserve = 0 local contentHeight = 0 if mode == "SUMMARY" then + topReserve = LAYOUT.summaryHeaderHeight contentHeight = self.summaryTotalContentHeight or 0 elseif mode == "CONFIG" then topReserve = LAYOUT.configFixedHeaderHeight @@ -1651,6 +1654,7 @@ function CompareTabClass:Draw(viewPort, inputEvents) bottomReserve = self.controls.itemsHScrollBar.enabled and LAYOUT.itemsHScrollBarHeight or 0 contentHeight = self.itemsTotalContentHeight or 0 elseif mode == "SKILLS" then + topReserve = LAYOUT.skillsHeaderHeight bottomReserve = self.controls.skillsHScrollBar.enabled and LAYOUT.skillsHScrollBarHeight or 0 contentHeight = self.skillsTotalContentHeight or 0 end @@ -1696,6 +1700,11 @@ function CompareTabClass:Draw(viewPort, inputEvents) SetDrawColor(0.85, 0.85, 0.85) DrawImage(nil, contentVP.x, layout.footerY, contentVP.width, 2) end + SetDrawColor(1, 1, 1) + DrawString(self.controls.leftSpecSelect.x, contentVP.y + 4, "LEFT", 18, "VAR", + colorCodes.POSITIVE .. self:GetShortBuildName(self.primaryBuild.buildName)) + DrawString(self.controls.rightSpecSelect.x, contentVP.y + 4, "LEFT", 18, "VAR", + colorCodes.WARNING .. (compareEntry.label or "Compare Build")) end if not compareEntry then @@ -1719,7 +1728,8 @@ function CompareTabClass:Draw(viewPort, inputEvents) local scrollOffsetX = -((self.controls.itemsHScrollBar and self.controls.itemsHScrollBar.offset) or 0) -- Item set dropdowns - local row1Y = contentVP.y + 34 + local buildLabelY = contentVP.y + 30 + local row1Y = contentVP.y + 52 -- Primary build item set dropdown self.controls.primaryItemSetLabel.x = contentVP.x + scrollOffsetX + 10 @@ -1733,6 +1743,14 @@ function CompareTabClass:Draw(viewPort, inputEvents) self.controls.compareItemSetSelect2.x = contentVP.x + scrollOffsetX + colWidth + 10 + itemSetLabelW self.controls.compareItemSetSelect2.y = row1Y + SetDrawColor(1, 1, 1) + DrawString(self.controls.primaryItemSetLabel.x, buildLabelY, "LEFT", 18, "VAR", + colorCodes.POSITIVE .. self:GetShortBuildName(self.primaryBuild.buildName)) + DrawString(self.controls.compareItemSetLabel2.x, buildLabelY, "LEFT", 18, "VAR", + colorCodes.WARNING .. (compareEntry.label or "Compare Build")) + SetDrawColor(0.5, 0.5, 0.5) + DrawImage(nil, contentVP.x + 4, contentVP.y + LAYOUT.itemsCheckboxOffset - 2, contentVP.width - 8, 2) + -- Populate primary build item set list if self.primaryBuild.itemsTab and self.primaryBuild.itemsTab.itemSetOrderList then self:PopulateSetDropdown(self.primaryBuild.itemsTab, "itemSetOrderList", "itemSets", "activeItemSetId", self.controls.primaryItemSetSelect) @@ -1873,6 +1891,9 @@ function CompareTabClass:LayoutTreeView(contentVP, compareEntry) self.controls.rightFooterAnchor.y = footerY + 4 self.controls.rightTreeSearch.width = halfWidth - 8 end + self.controls.leftSpecSelect.y = contentVP.y + 22 + self.controls.rightSpecSelect.y = contentVP.y + 22 + self.controls.treeOverlayCheck.y = contentVP.y + 48 -- (Common) Update spec dropdown lists if self.primaryBuild.treeTab then @@ -2316,14 +2337,16 @@ function CompareTabClass:HandleScrollInput(contentVP, inputEvents) local scrollViewH = viewportH - LAYOUT.configFixedHeaderHeight maxScroll = m_max(self.configTotalContentHeight - scrollViewH, 0) elseif self.compareViewMode == "SUMMARY" and self.summaryTotalContentHeight > 0 then - maxScroll = m_max(self.summaryTotalContentHeight - viewportH, 0) + local scrollViewH = viewportH - LAYOUT.summaryHeaderHeight + maxScroll = m_max(self.summaryTotalContentHeight - scrollViewH, 0) elseif self.compareViewMode == "ITEMS" and self.itemsTotalContentHeight > 0 then local hBarReserve = self.controls.itemsHScrollBar.enabled and LAYOUT.itemsHScrollBarHeight or 0 local scrollViewH = viewportH - LAYOUT.itemsCheckboxOffset - hBarReserve maxScroll = m_max(self.itemsTotalContentHeight - scrollViewH, 0) elseif self.compareViewMode == "SKILLS" and self.skillsTotalContentHeight > 0 then local hBarReserve = self.controls.skillsHScrollBar.enabled and LAYOUT.skillsHScrollBarHeight or 0 - maxScroll = m_max(self.skillsTotalContentHeight - (viewportH - hBarReserve), 0) + local scrollViewH = viewportH - LAYOUT.skillsHeaderHeight - hBarReserve + maxScroll = m_max(self.skillsTotalContentHeight - scrollViewH, 0) end self.scrollY = m_min(self.scrollY + 40, maxScroll) self.controls.viewScrollBar:SetOffset(self.scrollY) @@ -3003,6 +3026,8 @@ function CompareTabClass:DrawSummary(vp, compareEntry) local lineHeight = 18 local headerHeight = 22 + local headerReserve = LAYOUT.summaryHeaderHeight + local scrollViewH = vp.height - headerReserve -- Column positions (col3R and col4 shift right dynamically to avoid name overlap) local col1 = LAYOUT.summaryCol1 @@ -3018,22 +3043,18 @@ function CompareTabClass:DrawSummary(vp, compareEntry) local col3R = m_min(m_max(LAYOUT.summaryCol3Right, minCol3R), maxCol3R) local col4 = col3R + 20 - SetViewport(vp.x, vp.y, vp.width, vp.height) - local drawY = 4 - self.scrollY - - -- Headers + SetViewport(vp.x, vp.y, vp.width, headerReserve) SetDrawColor(1, 1, 1) - DrawString(col1, drawY, "LEFT", headerHeight, "VAR", "^7Stat") - DrawString(col2R, drawY, "RIGHT_X", headerHeight, "VAR", colorCodes.POSITIVE .. primaryName) - DrawString(col3R, drawY, "RIGHT_X", headerHeight, "VAR", + DrawString(col1, 4, "LEFT", headerHeight, "VAR", "^7Stat") + DrawString(col2R, 4, "RIGHT_X", headerHeight, "VAR", colorCodes.POSITIVE .. primaryName) + DrawString(col3R, 4, "RIGHT_X", headerHeight, "VAR", colorCodes.WARNING .. compareName) - DrawString(col4, drawY, "LEFT", headerHeight, "VAR", "^7Difference") - drawY = drawY + headerHeight + 4 - - -- Separator + DrawString(col4, 4, "LEFT", headerHeight, "VAR", "^7Difference") SetDrawColor(0.5, 0.5, 0.5) - DrawImage(nil, 4, drawY, vp.width - 8, 2) - drawY = drawY + 6 + DrawImage(nil, 4, headerHeight + 8, vp.width - 8, 2) + + SetViewport(vp.x, vp.y + headerReserve, vp.width, scrollViewH) + local drawY = -self.scrollY -- Stat comparison local displayStats = summaryUseMinion and self.primaryBuild.minionDisplayStats or self.primaryBuild.displayStats @@ -3062,8 +3083,7 @@ function CompareTabClass:DrawSummary(vp, compareEntry) -- Position controls dynamically based on drawY -- The controls need absolute screen positions (vp.x/vp.y offset + viewport-local drawY) - -- drawY already includes the scroll offset (starts at 4 - self.scrollY) - local controlY = vp.y + drawY + local controlY = vp.y + headerReserve + drawY local ctrlBaseX = vp.x + LAYOUT.powerReportLeft -- Metric dropdown @@ -3120,7 +3140,7 @@ function CompareTabClass:DrawSummary(vp, compareEntry) local listHeight = 250 local listWidth = 740 listControl.x = vp.x + LAYOUT.powerReportLeft - listControl.y = vp.y + drawY + listControl.y = vp.y + headerReserve + drawY listControl.width = listWidth listControl.height = listHeight @@ -3617,12 +3637,6 @@ function CompareTabClass:DrawItems(vp, compareEntry, inputEvents) local hoverEquipBtnX, hoverEquipBtnY = 0, 0 local hoverEquipBtnW, hoverEquipBtnH = 0, 0 - -- Headers - SetDrawColor(1, 1, 1) - DrawString(scrollOffsetX + 10, drawY, "LEFT", 18, "VAR", colorCodes.POSITIVE .. self:GetShortBuildName(self.primaryBuild.buildName)) - DrawString(scrollOffsetX + colWidth + 10, drawY, "LEFT", 18, "VAR", colorCodes.WARNING .. (compareEntry.label or "Compare Build")) - drawY = drawY + 24 - -- Helper: process copy/buy button hover state and click events for a slot. -- Closes over hoverEquip*/clicked* locals above. local function processSlotButtons(b1Hover, b2Hover, b3Hover, b2X, b2Y, b2W, b2H, cItem, copySlotName, equipSlotName) @@ -4170,6 +4184,7 @@ function CompareTabClass:DrawSkills(vp, compareEntry) local contentWidth = colWidth * 2 local needsHScroll = contentWidth > vp.width local gemTextWidth = colWidth - (groupHeaderTextX + 10) + local headerReserve = LAYOUT.skillsHeaderHeight -- Configure horizontal scrollbar local hBar = self.controls.skillsHScrollBar @@ -4180,22 +4195,25 @@ function CompareTabClass:DrawSkills(vp, compareEntry) self.skillsScrollX = hBar.offset local bottomReserve = needsHScroll and LAYOUT.skillsHScrollBarHeight or 0 - local scrollViewH = vp.height - bottomReserve - SetViewport(vp.x, vp.y, vp.width, scrollViewH) - local drawY = 4 - self.scrollY + local scrollViewH = vp.height - headerReserve - bottomReserve local scrollOffsetX = -self.skillsScrollX - -- Headers SetDrawColor(1, 1, 1) - DrawString(scrollOffsetX + 10, drawY, "LEFT", 18, "VAR", colorCodes.POSITIVE .. self:GetShortBuildName(self.primaryBuild.buildName)) - DrawString(scrollOffsetX + colWidth + 10, drawY, "LEFT", 18, "VAR", colorCodes.WARNING .. (compareEntry.label or "Compare Build")) - drawY = drawY + 24 + DrawString(vp.x + scrollOffsetX + 10, vp.y + 4, "LEFT", 18, "VAR", colorCodes.POSITIVE .. self:GetShortBuildName(self.primaryBuild.buildName)) + DrawString(vp.x + scrollOffsetX + colWidth + 10, vp.y + 4, "LEFT", 18, "VAR", colorCodes.WARNING .. (compareEntry.label or "Compare Build")) + SetDrawColor(0.5, 0.5, 0.5) + DrawImage(nil, vp.x + 4, vp.y + headerReserve, vp.width - 8, 2) + + SetViewport(vp.x, vp.y + headerReserve, vp.width, scrollViewH) + local drawY = 4 - self.scrollY -- Position pre-pass: compute gem positions for hover hit-testing local gemEntries = {} -- { gem, x, y, group } - local preY = 4 - self.scrollY + 24 -- after headers + local preY = 4 - self.scrollY for idx, pair in ipairs(renderPairs) do - preY = preY + 2 -- separator + if idx > 1 then + preY = preY + 2 + end local pDisplayList = displayListsByPair[idx].p local cDisplayList = displayListsByPair[idx].c local pGroup = pair.pIdx and pGroups[pair.pIdx] @@ -4210,9 +4228,9 @@ function CompareTabClass:DrawSkills(vp, compareEntry) -- Hit-test: find hovered gem local cursorX, cursorY = GetCursorPos() local localCursorX = cursorX - vp.x - local localCursorY = cursorY - vp.y + local localCursorY = cursorY - (vp.y + headerReserve) local hoveredEntry = nil - if localCursorX >= 0 and localCursorX < vp.width and localCursorY >= 0 and localCursorY < vp.height then + if localCursorX >= 0 and localCursorX < vp.width and localCursorY >= 0 and localCursorY < scrollViewH then for _, entry in ipairs(gemEntries) do if localCursorX >= entry.x and localCursorX < entry.x + gemTextWidth and localCursorY >= entry.y and localCursorY < entry.y + gemLineHeight then @@ -4242,10 +4260,12 @@ function CompareTabClass:DrawSkills(vp, compareEntry) -- Draw pass for idx, pair in ipairs(renderPairs) do - SetDrawColor(0.3, 0.3, 0.3) - -- Divider spans the viewport width, independent of horizontal scroll - DrawImage(nil, 0, drawY, vp.width, 1) - drawY = drawY + 2 + if idx > 1 then + SetDrawColor(0.3, 0.3, 0.3) + -- Divider spans the viewport width, independent of horizontal scroll + DrawImage(nil, 0, drawY, vp.width, 1) + drawY = drawY + 2 + end local pDisplayList = displayListsByPair[idx].p local cDisplayList = displayListsByPair[idx].c From 221794b245bbaa36d9b579c032256f052bb86e8b Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Mon, 20 Apr 2026 01:59:07 +1000 Subject: [PATCH 11/15] Add config tooltips Hovering over the checkboxes or input boxes wasn't showing the tooltip for that part --- src/Classes/CompareTab.lua | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Classes/CompareTab.lua b/src/Classes/CompareTab.lua index 03ea67c919..784bec3cc0 100644 --- a/src/Classes/CompareTab.lua +++ b/src/Classes/CompareTab.lua @@ -1105,7 +1105,7 @@ function CompareTabClass:NormalizeConfigVals(varData, pVal, cVal) end -- Create a single config control for a given varData, writing to the specified input/configTab/build -local function makeConfigControl(varData, inputTable, configTab, buildObj) +local function makeConfigControl(varData, inputTable, configTab, buildObj, sourceControl) local control local pVal = inputTable[varData.var] if varData.type == "check" then @@ -1140,6 +1140,12 @@ local function makeConfigControl(varData, inputTable, configTab, buildObj) end if control then control.shown = function() return false end + -- Reuse tooltip behavior from the source ConfigTab control so compare view + -- matches normal Config tab hover help (including dynamic tooltip funcs). + if sourceControl then + control.tooltipText = sourceControl.tooltipText + control.tooltipFunc = sourceControl.tooltipFunc + end end return control end @@ -1173,8 +1179,10 @@ function CompareTabClass:RebuildConfigControls(compareEntry) currentSection = nil end elseif currentSection and varData.var and varData.type ~= "text" then - local pCtrl = makeConfigControl(varData, pInput, self.primaryBuild.configTab, primaryBuild) - local cCtrl = makeConfigControl(varData, cInput, compareEntry.configTab, compareEntry) + local pSource = self.primaryBuild.configTab.varControls and self.primaryBuild.configTab.varControls[varData.var] + local cSource = compareEntry.configTab.varControls and compareEntry.configTab.varControls[varData.var] + local pCtrl = makeConfigControl(varData, pInput, self.primaryBuild.configTab, primaryBuild, pSource) + local cCtrl = makeConfigControl(varData, cInput, compareEntry.configTab, compareEntry, cSource) if pCtrl and cCtrl then self.controls["cfg_p_" .. varData.var] = pCtrl From f162dfc5cfcf5fccd8e6eb28e427fe1e25da3f86 Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Mon, 20 Apr 2026 02:03:55 +1000 Subject: [PATCH 12/15] Fix build list scrolling When you select the search bar you can't use the scroll wheel to scroll the build list --- src/Classes/CompareTab.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Classes/CompareTab.lua b/src/Classes/CompareTab.lua index 784bec3cc0..c5a1b25bba 100644 --- a/src/Classes/CompareTab.lua +++ b/src/Classes/CompareTab.lua @@ -1559,6 +1559,11 @@ function CompareTabClass:OpenImportFolderPopup() self:LoadBuild(build) end end + function controls.buildList:OnHoverKeyUp(key) + if self.controls.scrollBarV:IsScrollDownKey(key) or self.controls.scrollBarV:IsScrollUpKey(key) then + self:OnKeyUp(key) + end + end function controls.buildList:CanReceiveDrag() return false end function controls.buildList:OnSelCopy() end function controls.buildList:OnSelCut() end From 649760f318bedd831bf28232f47eb63419ede994 Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Mon, 20 Apr 2026 06:40:06 +1000 Subject: [PATCH 13/15] Only compare calcs that are different checkbox Adds a checkbox that is on by default and it now only shows the rows in the calcs section that are different between the 2 builds You can disable this and see all the rows again by unchecking the box --- src/Classes/CompareTab.lua | 82 ++++++++++++++++++++++++++++++++++---- 1 file changed, 75 insertions(+), 7 deletions(-) diff --git a/src/Classes/CompareTab.lua b/src/Classes/CompareTab.lua index c5a1b25bba..4fc30a4ac9 100644 --- a/src/Classes/CompareTab.lua +++ b/src/Classes/CompareTab.lua @@ -79,7 +79,7 @@ local LAYOUT = { calcsMaxCardWidth = 400, calcsLabelWidth = 132, calcsSepW = 2, - calcsHeaderBarHeight = 24, + calcsHeaderBarHeight = 12, -- Power report section (inside Summary view) powerReportLeft = 10, @@ -162,6 +162,7 @@ local CompareTabClass = newClass("CompareTab", "ControlHost", "Control", functio -- Tooltip for calcs hover breakdown self.calcsTooltip = new("Tooltip") + self.calcsShowOnlyDifferences = true -- Interactive config controls state self.configControls = {} -- { var -> { control, varData } } @@ -704,6 +705,13 @@ function CompareTabClass:InitControls() end) self.controls.cmpCalcsMode.shown = false + self.controls.calcsShowOnlyDifferencesCheck = new("CheckBoxControl", nil, {0, 0, 18}, "Show only differences", function(state) + self.calcsShowOnlyDifferences = state + end, "Show only rows that differ between both builds. Disable to include unchanged rows.") + self.controls.calcsShowOnlyDifferencesCheck.shown = function() + return self.compareViewMode == "CALCS" and self:GetActiveCompare() ~= nil + end + -- ============================================================ -- Tree footer controls (visible only in TREE view mode with a comparison loaded) -- ============================================================ @@ -2314,7 +2322,12 @@ function CompareTabClass:LayoutCalcsSkillControls(vp, compareEntry) end end - local headerHeight = m_max(leftY, rightY) - vp.y + textLinesHeight + 4 -- +4 for separator padding + local textBaseY = m_max(leftY, rightY) + local headerHeight = textBaseY - vp.y + textLinesHeight + 4 -- + separator padding + local showOnlyDiffCheck = self.controls.calcsShowOnlyDifferencesCheck + showOnlyDiffCheck.state = self.calcsShowOnlyDifferences and true or false + showOnlyDiffCheck.x = leftX + showOnlyDiffCheck.labelWidth + 2 + showOnlyDiffCheck.y = vp.y + headerHeight + 4 return headerHeight end @@ -4449,7 +4462,52 @@ function CompareTabClass:DrawCalcsSkillHeader(vp, compareEntry, headerHeight, pr -- Separator line SetDrawColor(0.4, 0.4, 0.4) - DrawImage(nil, vp.x + 2, vp.y + headerHeight - 2, vp.width - 4, 1) + DrawImage(nil, vp.x + 2, vp.y + headerHeight - 2, vp.width - 4, 2) +end + +local function calcRowMatchesBetweenBuilds(self, colData, primaryActor, compareActor, compareEntry) + if not colData or not colData.format then return false end + local primaryFormatOk, primaryFormattedValue = pcall(formatCalcStr, colData.format, primaryActor, colData) + local compareFormatOk, compareFormattedValue = pcall(formatCalcStr, colData.format, compareActor, colData) + if not primaryFormatOk or not compareFormatOk or tostring(primaryFormattedValue or "") ~= tostring(compareFormattedValue or "") then + return false + end + for _, sectionData in ipairs(colData) do + if sectionData.modName then -- Compare mod rows to see if they match + local primaryRows = calcsHelpers.TabulateMods(sectionData, primaryActor) + local compareRows = calcsHelpers.TabulateMods(sectionData, compareActor) + if #primaryRows ~= #compareRows then return false end + local counts = {} + for _, row in ipairs(primaryRows) do + local key = calcsHelpers.ModRowKey(row) .. "|" .. tostring(row.value) + counts[key] = (counts[key] or 0) + 1 + end + for _, row in ipairs(compareRows) do + local key = calcsHelpers.ModRowKey(row) .. "|" .. tostring(row.value) + local count = counts[key] + if not count then return false end + counts[key] = count > 1 and count - 1 or nil + end + end + if sectionData.breakdown then -- Compare breakdown to see if they are the same + local primaryBreakdownLines = calcsHelpers.GetBreakdownLines(sectionData, self.primaryBuild) + local compareBreakdownLines = calcsHelpers.GetBreakdownLines(sectionData, compareEntry) + local primaryLineCount = primaryBreakdownLines and #primaryBreakdownLines or 0 + local compareLineCount = compareBreakdownLines and #compareBreakdownLines or 0 + if primaryLineCount ~= compareLineCount then return false end + for i = 1, primaryLineCount do + if primaryBreakdownLines[i] ~= compareBreakdownLines[i] then return false end + end + end + end + return true +end + +local function subSectionExtraMatches(subSecData, primaryActor, compareActor) + if not subSecData or not subSecData.extra then return true end + local primaryExtraOk, primaryExtraText = pcall(formatCalcStr, subSecData.extra, primaryActor) + local compareExtraOk, compareExtraText = pcall(formatCalcStr, subSecData.extra, compareActor) + return primaryExtraOk and compareExtraOk and tostring(primaryExtraText or "") == tostring(compareExtraText or "") end function CompareTabClass:DrawCalcs(vp, compareEntry) @@ -4486,7 +4544,8 @@ function CompareTabClass:DrawCalcs(vp, compareEntry) local maxCol = m_max(1, m_floor(gridWidth / (cardWidth + 8))) local baseX = 4 local headerBarHeight = LAYOUT.calcsHeaderBarHeight - local baseY = headerBarHeight + local filterRowOffset = self.controls.calcsShowOnlyDifferencesCheck:IsShown() and 12 or 0 + local baseY = headerBarHeight + filterRowOffset -- Pre-compute section visibility and heights local sections = {} @@ -4502,12 +4561,21 @@ function CompareTabClass:DrawCalcs(vp, compareEntry) for _, rowData in ipairs(subSec.data) do -- Only include rows with a label and a first column with a format string if rowData.label and rowData[1] and rowData[1].format then - if self.primaryBuild.calcsTab:CheckFlag(rowData, primaryActor) or self.primaryBuild.calcsTab:CheckFlag(rowData, compareActor) then - t_insert(rows, rowData) + local primaryVisible = self.primaryBuild.calcsTab:CheckFlag(rowData, primaryActor) + local compareVisible = self.primaryBuild.calcsTab:CheckFlag(rowData, compareActor) + if primaryVisible or compareVisible then + local keepRow = true + if self.calcsShowOnlyDifferences and primaryVisible and compareVisible then + keepRow = not calcRowMatchesBetweenBuilds(self, rowData[1], primaryActor, compareActor, compareEntry) + end + if keepRow then + t_insert(rows, rowData) + end end end end - if #rows > 0 then + local keepSubSection = #rows > 0 or (self.calcsShowOnlyDifferences and not subSectionExtraMatches(subSec.data, primaryActor, compareActor)) + if keepSubSection then t_insert(subSecInfo, { label = subSec.label, rows = rows, data = subSec.data }) sectionHasRows = true end From aad276c8c637f4e4f3f929f250738ce2d288f57f Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Mon, 20 Apr 2026 06:54:16 +1000 Subject: [PATCH 14/15] Update variable names --- src/Classes/CompareTab.lua | 64 +++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/src/Classes/CompareTab.lua b/src/Classes/CompareTab.lua index 4fc30a4ac9..46ffd08f50 100644 --- a/src/Classes/CompareTab.lua +++ b/src/Classes/CompareTab.lua @@ -2300,9 +2300,9 @@ function CompareTabClass:LayoutCalcsSkillControls(vp, compareEntry) local textLinesHeight = 2 -- padding before text local primaryEnv = self.primaryBuild.calcsTab and self.primaryBuild.calcsTab.calcsEnv local compareEnv = compareEntry.calcsTab and compareEntry.calcsTab.calcsEnv - local pOutput = primaryEnv and primaryEnv.player and primaryEnv.player.output - local cOutput = compareEnv and compareEnv.player and compareEnv.player.output - if pOutput or cOutput then + local primaryOutput = primaryEnv and primaryEnv.player and primaryEnv.player.output + local compareOutput = compareEnv and compareEnv.player and compareEnv.player.output + if primaryOutput or compareOutput then local wrapWidth = colWidth - 8 local infoLabels = { BuffList = "Aura/Buff Skills", @@ -2311,13 +2311,13 @@ function CompareTabClass:LayoutCalcsSkillControls(vp, compareEntry) } local infoKeys = { "BuffList", "CombatList", "CurseList" } for _, key in ipairs(infoKeys) do - local pVal = pOutput and pOutput[key] - local cVal = cOutput and cOutput[key] - if (pVal and pVal ~= "") or (cVal and cVal ~= "") then + local primaryValue = primaryOutput and primaryOutput[key] + local compareValue = compareOutput and compareOutput[key] + if (primaryValue and primaryValue ~= "") or (compareValue and compareValue ~= "") then local label = infoLabels[key] - local pLines = (pVal and pVal ~= "") and #wrapInfoLine(label .. ": " .. pVal, wrapWidth) or 0 - local cLines = (cVal and cVal ~= "") and #wrapInfoLine(label .. ": " .. cVal, wrapWidth) or 0 - textLinesHeight = textLinesHeight + m_max(pLines, cLines, 1) * 18 + local primaryLineCount = (primaryValue and primaryValue ~= "") and #wrapInfoLine(label .. ": " .. primaryValue, wrapWidth) or 0 + local compareLineCount = (compareValue and compareValue ~= "") and #wrapInfoLine(label .. ": " .. compareValue, wrapWidth) or 0 + textLinesHeight = textLinesHeight + m_max(primaryLineCount, compareLineCount, 1) * 18 end end end @@ -4405,10 +4405,10 @@ function CompareTabClass:DrawCalcsSkillHeader(vp, compareEntry, headerHeight, pr -- Text info lines (Aura/Buffs, Combat Buffs, Curses) local textY = m_max(leftY, rightY) + 2 - local pOutput = primaryEnv.player and primaryEnv.player.output - local cOutput = compareEnv.player and compareEnv.player.output + local primaryOutput = primaryEnv.player and primaryEnv.player.output + local compareOutput = compareEnv.player and compareEnv.player.output self.calcsSkillHeaderHover = nil -- Reset hover state - if pOutput or cOutput then + if primaryOutput or compareOutput then local cursorX, cursorY = GetCursorPos() local wrapWidth = colWidth - 8 local infoLines = { @@ -4417,42 +4417,42 @@ function CompareTabClass:DrawCalcsSkillHeader(vp, compareEntry, headerHeight, pr { label = "Curses/Debuffs", key = "CurseList", breakdown = "SkillDebuffs" }, } for _, info in ipairs(infoLines) do - local pVal = pOutput and pOutput[info.key] - local cVal = cOutput and cOutput[info.key] - if (pVal and pVal ~= "") or (cVal and cVal ~= "") then - local pLines = (pVal and pVal ~= "") and wrapInfoLine(info.label .. ": " .. pVal, wrapWidth) or {} - local cLines = (cVal and cVal ~= "") and wrapInfoLine(info.label .. ": " .. cVal, wrapWidth) or {} - local pH = #pLines * 18 - local cH = #cLines * 18 - local rowH = m_max(pH, cH, 18) + local primaryValue = primaryOutput and primaryOutput[info.key] + local compareValue = compareOutput and compareOutput[info.key] + if (primaryValue and primaryValue ~= "") or (compareValue and compareValue ~= "") then + local primaryLines = (primaryValue and primaryValue ~= "") and wrapInfoLine(info.label .. ": " .. primaryValue, wrapWidth) or {} + local compareLines = (compareValue and compareValue ~= "") and wrapInfoLine(info.label .. ": " .. compareValue, wrapWidth) or {} + local primaryHeight = #primaryLines * 18 + local compareHeight = #compareLines * 18 + local rowH = m_max(primaryHeight, compareHeight, 18) -- Check hover per-side for lines that have breakdown data if info.breakdown and cursorY >= textY and cursorY < textY + rowH then - local onLeft = cursorX >= leftX and cursorX < rightX and pH > 0 and cursorY < textY + pH - local onRight = cursorX >= rightX and cursorX < vp.x + vp.width and cH > 0 and cursorY < textY + cH + local onLeft = cursorX >= leftX and cursorX < rightX and primaryHeight > 0 and cursorY < textY + primaryHeight + local onRight = cursorX >= rightX and cursorX < vp.x + vp.width and compareHeight > 0 and cursorY < textY + compareHeight if onLeft then SetDrawColor(0.15, 0.25, 0.15) - DrawImage(nil, leftX, textY, colWidth, pH) + DrawImage(nil, leftX, textY, colWidth, primaryHeight) self.calcsSkillHeaderHover = { breakdown = info.breakdown, label = info.label, build = self.primaryBuild, - x = leftX, y = textY, w = colWidth, h = pH, + x = leftX, y = textY, w = colWidth, h = primaryHeight, } elseif onRight then SetDrawColor(0.15, 0.25, 0.15) - DrawImage(nil, rightX, textY, colWidth, cH) + DrawImage(nil, rightX, textY, colWidth, compareHeight) self.calcsSkillHeaderHover = { breakdown = info.breakdown, label = info.label, build = compareEntry, - x = rightX, y = textY, w = colWidth, h = cH, + x = rightX, y = textY, w = colWidth, h = compareHeight, } end end - for i, line in ipairs(pLines) do + for i, line in ipairs(primaryLines) do DrawString(leftX, textY + 1 + (i - 1) * 18, "LEFT", 14, "VAR", "^7" .. line) end - for i, line in ipairs(cLines) do + for i, line in ipairs(compareLines) do DrawString(rightX, textY + 1 + (i - 1) * 18, "LEFT", 14, "VAR", "^7" .. line) end textY = textY + rowH @@ -4661,11 +4661,11 @@ function CompareTabClass:DrawCalcs(vp, compareEntry) if subSec.data and subSec.data.extra then local extraTextW = DrawStringWidth(16, "VAR BOLD", subSec.label .. ":") local extraX = x + 3 + extraTextW + 8 - local ok1, pExtra = pcall(formatCalcStr, subSec.data.extra, primaryActor) - local ok2, cExtra = pcall(formatCalcStr, subSec.data.extra, compareActor) - if ok1 and ok2 then + local primaryExtraOk, primaryExtraText = pcall(formatCalcStr, subSec.data.extra, primaryActor) + local compareExtraOk, compareExtraText = pcall(formatCalcStr, subSec.data.extra, compareActor) + if primaryExtraOk and compareExtraOk then DrawString(extraX, lineY + 3, "LEFT", 16, "VAR", - colorCodes.POSITIVE .. pExtra .. " ^8| " .. colorCodes.WARNING .. cExtra) + colorCodes.POSITIVE .. primaryExtraText .. " ^8| " .. colorCodes.WARNING .. compareExtraText) end end -- Separator below header From 5e86aad3ef061a37e55100ec3d6dbc7986ba5012 Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Mon, 20 Apr 2026 07:02:29 +1000 Subject: [PATCH 15/15] Reduce spacing for skill dropdowns --- src/Classes/CompareTab.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Classes/CompareTab.lua b/src/Classes/CompareTab.lua index 46ffd08f50..0e2869f0fb 100644 --- a/src/Classes/CompareTab.lua +++ b/src/Classes/CompareTab.lua @@ -2249,7 +2249,7 @@ function CompareTabClass:LayoutCalcsSkillControls(vp, compareEntry) local colWidth = m_floor((vp.width - 20) / 2) local leftX = vp.x + 4 local rightX = leftX + colWidth + 12 - local labelW = 140 + local labelW = 120 local controlW = colWidth - labelW - 8 local rowH = 22 local y = vp.y + 4