diff --git a/src/Classes/CalcSectionControl.lua b/src/Classes/CalcSectionControl.lua index 75be52c567..248ab19ab6 100644 --- a/src/Classes/CalcSectionControl.lua +++ b/src/Classes/CalcSectionControl.lua @@ -49,8 +49,20 @@ local CalcSectionClass = newClass("CalcSectionControl", "Control", "ControlHost" end end end + self.controls.popOut = new("ButtonControl", {"TOPRIGHT",self,"TOPRIGHT"}, {-22, 3, 16, 16}, "^", function() + self:ToggleOverlay() + end) + self.controls.popOut.shown = function() + return self.enabled and not self.isOverlay + end + self.isOverlay = false + self.overlayX = 320 + self.overlayY = 50 + self.dragging = false + self.dragOffX = 0 + self.dragOffY = 0 self.shown = function() - return self.enabled + return self.enabled and not self.isOverlay end end) @@ -160,7 +172,6 @@ end function CalcSectionClass:Draw(viewPort, noTooltip) local x, y = self:GetPos() local width, height = self:GetSize() - local cursorX, cursorY = GetCursorPos() local actor = self.calcsTab.input.showMinion and self.calcsTab.calcsEnv.minion or self.calcsTab.calcsEnv.player -- Draw border and background SetDrawLayer(nil, -10) @@ -168,35 +179,284 @@ function CalcSectionClass:Draw(viewPort, noTooltip) DrawImage(nil, x, y, width, height) SetDrawColor(0.10, 0.10, 0.10) DrawImage(nil, x + 2, y + 2, width - 4, height - 4) - + + self:DrawContent(x, y, width, actor, viewPort, false, noTooltip) +end + +function CalcSectionClass:OnKeyDown(key, doubleClick) + if not self:IsShown() or not self:IsEnabled() then + return + end + local mOverControl = self:GetMouseOverControl() + if mOverControl and mOverControl.OnKeyDown then + return mOverControl:OnKeyDown(key) + end + local mOver, mOverComp = self:IsMouseOver() + if key:match("BUTTON") then + if not mOver then + return + end + if mOverComp then + -- Pin the stat breakdown + self.calcsTab:SetDisplayStat(mOverComp, true) + return self.calcsTab.controls.breakdown + end + end + return +end + +function CalcSectionClass:OnKeyUp(key) + if not self:IsShown() or not self:IsEnabled() then + return + end + local mOverControl = self:GetMouseOverControl() + if mOverControl and mOverControl.OnKeyUp then + return mOverControl:OnKeyUp(key) + end + return +end + +function CalcSectionClass:ToggleOverlay() + self.isOverlay = not self.isOverlay + if self.isOverlay then + local x, y = self:GetPos() + self.overlayX = self.calcsTab.x + x + self.overlayY = self.calcsTab.y + y + t_insert(self.calcsTab.build.overlayPanes, self) + else + local panes = self.calcsTab.build.overlayPanes + for i, pane in ipairs(panes) do + if pane == self then + table.remove(panes, i) + break + end + end + self.dragging = false + end +end + +function CalcSectionClass:RaiseOverlay() + local panes = self.calcsTab.build.overlayPanes + for i, pane in ipairs(panes) do + if pane == self then + table.remove(panes, i) + t_insert(panes, self) + break + end + end +end + +function CalcSectionClass:IsMouseInOverlay(cursorX, cursorY) + if not self.isOverlay or not self.calcsTab.calcsEnv then return false end + local x = self.overlayX + local y = self.overlayY + if cursorX < x or cursorX > x + self.width or cursorY < y then return false end + local h = 28 + for i, subSec in ipairs(self.subSection) do + h = h + 22 + if not subSec.collapsed and self.calcsTab:CheckFlag(self) then + for _, rowData in ipairs(subSec.data) do + if self.calcsTab:CheckFlag(rowData) then + h = h + 18 + end + end + h = h + 2 + end + end + return cursorY < y + h +end + +function CalcSectionClass:HandleOverlayClick(key, cursorX, cursorY) + if key ~= "LEFTBUTTON" then return end + + self:RaiseOverlay() + + local x = self.overlayX + local y = self.overlayY + local overlayWidth = self.width + + -- Check close button + local closeX = x + overlayWidth - 18 + local closeY = y + 2 + if cursorX >= closeX and cursorX <= closeX + 14 and cursorY >= closeY and cursorY <= closeY + 16 then + self:ToggleOverlay() + return + end + + local lineY = y + 26 + local primary = true + for i, subSec in ipairs(self.subSection) do + local secEnabled = self.calcsTab:CheckFlag(self) + + -- Check subsection toggle + if secEnabled then + local toggleX = x + overlayWidth - 18 + local toggleY = lineY + 3 + if cursorX >= toggleX and cursorX <= toggleX + 14 and cursorY >= toggleY and cursorY <= toggleY + 16 then + subSec.collapsed = not subSec.collapsed + self.calcsTab.modFlag = true + return + end + end + + if subSec.collapsed or not secEnabled then + if primary then + break + else + lineY = lineY + 20 + primary = false + end + else + lineY = lineY + 20 + primary = false + for _, rowData in ipairs(subSec.data) do + if self.calcsTab:CheckFlag(rowData) then + for _, colData in ipairs(rowData) do + local cellX = x + (colData.xOffset or 134) + local cellW = colData.width or (overlayWidth - 136) + if cursorX >= cellX and cursorX <= cellX + cellW and cursorY >= lineY + 2 and cursorY <= lineY + 19 then + if colData.format and self.calcsTab:CheckFlag(colData) then + self.calcsTab:SetDisplayStat(colData, true) + end + return + end + end + lineY = lineY + 18 + end + end + lineY = lineY + 2 + end + end + + -- Start dragging + if cursorY >= y and cursorY <= y + 24 then + self.dragging = true + self.dragOffX = cursorX - self.overlayX + self.dragOffY = cursorY - self.overlayY + end +end + +function CalcSectionClass:HandleOverlayRelease(key) + if key == "LEFTBUTTON" then + self.dragging = false + end +end + +function CalcSectionClass:DrawOverlay(viewPort, inputEvents) + local cursorX, cursorY = GetCursorPos() + self.overlayBreakdownCell = nil + + if self.dragging then + self.overlayX = cursorX - self.dragOffX + self.overlayY = cursorY - self.dragOffY + end + + local x = self.overlayX + local y = self.overlayY + local overlayWidth = self.width + local actor = self.calcsTab.calcsEnv and (self.calcsTab.input.showMinion and self.calcsTab.calcsEnv.minion or self.calcsTab.calcsEnv.player) + + -- Calculate content height + local totalHeight = 28 + for i, subSec in ipairs(self.subSection) do + totalHeight = totalHeight + 22 + if actor and not subSec.collapsed and self.calcsTab:CheckFlag(self) then + for _, rowData in ipairs(subSec.data) do + if actor and self.calcsTab:CheckFlag(rowData) then + totalHeight = totalHeight + 18 + end + end + totalHeight = totalHeight + 2 + end + end + + -- Ensure it's above most other controls + SetDrawLayer(12) + + -- Draw background + SetDrawColor(0.08, 0.08, 0.08, 0.95) + DrawImage(nil, x, y, overlayWidth, totalHeight) + + -- Draw border + SetDrawColor(self.colour) + DrawImage(nil, x, y, overlayWidth, 1) + DrawImage(nil, x, y + totalHeight - 1, overlayWidth, 1) + DrawImage(nil, x, y, 1, totalHeight) + DrawImage(nil, x + overlayWidth - 1, y, 1, totalHeight) + + -- Draw header + SetDrawColor(0.18, 0.18, 0.18) + DrawImage(nil, x + 1, y + 1, overlayWidth - 2, 22) + SetDrawColor(1, 1, 1) + local cbx = x + overlayWidth - 16 + local cby = y + 3 + DrawImageQuad(nil, cbx + 3, cby + 5, cbx + 5, cby + 3, cbx + 13, cby + 11, cbx + 11, cby + 13) + DrawImageQuad(nil, cbx + 11, cby + 3, cbx + 13, cby + 5, cbx + 5, cby + 13, cbx + 3, cby + 11) + + -- Separator + SetDrawColor(self.colour) + DrawImage(nil, x + 2, y + 24, overlayWidth - 4, 1) + + -- Draw content + self:DrawContent(x, y + 26, overlayWidth, actor, viewPort, true) + + -- Draw stat breakdown + if self.calcsTab.displayData and (self.overlayBreakdownCell or self.calcsTab.displayPinned) then + local cd = self.calcsTab.displayData + local origX, origY = cd.x, cd.y + cd.x = x + (cd.xOffset or 134) + cd.y = y + 26 + (cd.yOffset or 0) + self.calcsTab.controls.breakdown:Draw(viewPort) + cd.x, cd.y = origX, origY + end + self.overlayBreakdownCell = nil + + SetDrawLayer(0) +end + +function CalcSectionClass:DrawContent(drawX, startLineY, drawWidth, actor, viewPort, isOverlay, noTooltip) + local cursorX, cursorY = GetCursorPos() + local lineY = startLineY local primary = true - local lineY = y + local enabled = isOverlay and (actor and self.calcsTab:CheckFlag(self)) or self.enabled + for _, subSec in ipairs(self.subSection) do - -- Draw line above label + -- Top border SetDrawColor(self.colour) - DrawImage(nil, x + 2, lineY, width - 4, 2) - SetDrawColor(0.10, 0.10, 0.10) - -- Draw label - if not self.enabled then - DrawString(x + 3, lineY + 3, "LEFT", 16, "VAR BOLD", "^8"..subSec.label) + DrawImage(nil, drawX + 2, lineY, drawWidth - 4, 2) + + -- Label + if not enabled then + local lx = isOverlay and drawX + 4 or drawX + 3 + DrawString(lx, lineY + 3, "LEFT", 16, "VAR BOLD", "^8"..(subSec.label or "")) else + local lx = isOverlay and drawX + 4 or drawX + 3 local textColor = "^7" - if self.calcsTab:SearchMatch(subSec.label) then + if not isOverlay and self.calcsTab:SearchMatch(subSec.label) then textColor = colorCodes.HIGHLIGHT end - DrawString(x + 3, lineY + 3, "LEFT", 16, "VAR BOLD", textColor..subSec.label..":") + DrawString(lx, lineY + 3, "LEFT", 16, "VAR BOLD", textColor..(subSec.label or "")..":") if subSec.data.extra then - local x = x + 3 + DrawStringWidth(16, "VAR BOLD", subSec.label) + 10 - DrawString(x, lineY + 3, "LEFT", 16, "VAR", "^7"..formatCalcStr(subSec.data.extra, actor)) + local ex = lx + DrawStringWidth(16, "VAR BOLD", subSec.label) + (isOverlay and 12 or 10) + DrawString(ex, lineY + 3, "LEFT", 16, "VAR", "^7"..formatCalcStr(subSec.data.extra, actor)) end end - -- Draw line below label + + -- Bottom border SetDrawColor(self.colour) - DrawImage(nil, x + 2, lineY + 20, width - 4, 2) - -- Draw controls - SetDrawLayer(nil, 0) - self:DrawControls(viewPort, noTooltip and self.calcsTab.selControl) - if subSec.collapsed or not self.enabled then + DrawImage(nil, drawX + 2, lineY + 20, drawWidth - 4, 2) + + -- Toggle + if isOverlay then + if enabled then + DrawString(drawX + drawWidth - 16, lineY + 3, "LEFT", 14, "VAR", "^7"..(subSec.collapsed and "+" or "-")) + end + else + SetDrawLayer(nil, 0) + self:DrawControls(viewPort, noTooltip and self.calcsTab.selControl) + end + + if subSec.collapsed or not enabled then if primary then return else @@ -206,86 +466,66 @@ function CalcSectionClass:Draw(viewPort, noTooltip) else lineY = lineY + 20 primary = false - local rows = 0; + local rows = 0 for _, rowData in ipairs(subSec.data) do - if rowData.enabled then + if (isOverlay and actor and self.calcsTab:CheckFlag(rowData)) or (not isOverlay and rowData.enabled) then rows = rows + 1 - local textColor = "^7" - if rowData.color then - textColor = rowData.color - end - if rowData.label then - SetDrawColor(rowData.bgCol or "^0") - DrawImage(nil, x + 2, lineY + 2, 130, 18) - if self.calcsTab:SearchMatch(rowData.label) then - textColor = colorCodes.HIGHLIGHT + if not isOverlay then + local textColor = rowData.color or "^7" + if rowData.label then + SetDrawColor(rowData.bgCol or "^0") + DrawImage(nil, drawX + 2, lineY + 2, 130, 18) + if self.calcsTab:SearchMatch(rowData.label) then + textColor = colorCodes.HIGHLIGHT + end + DrawString(drawX + 132, lineY + 2, "RIGHT_X", 16, "VAR", textColor..rowData.label.."^7:") end - DrawString(x + 132, lineY + 2, "RIGHT_X", 16, "VAR", textColor..rowData.label.."^7:") + elseif rowData.label then + DrawString(drawX + 132, lineY + 2, "RIGHT_X", 16, "VAR", "^7"..rowData.label.."^7:") end for colour, colData in ipairs(rowData) do - -- Draw column separator at the left end of the cell + local cellX = isOverlay and (drawX + (colData.xOffset or 134)) or colData.x + local cellW = isOverlay and (colData.width or (drawWidth - 136)) or colData.width + local cellY = isOverlay and lineY + 2 or colData.y + local cellH = colData.height or 18 + SetDrawColor(self.colour) - DrawImage(nil, colData.x, lineY + 2, 2, colData.height) - if colData.format and self.calcsTab:CheckFlag(colData) then - if cursorY >= viewPort.y and cursorY < viewPort.y + viewPort.height and cursorX >= colData.x and cursorY >= colData.y and cursorX < colData.x + colData.width and cursorY < colData.y + colData.height then - self.calcsTab:SetDisplayStat(colData) - end - if self.calcsTab.displayData == colData then - -- This is the display stat, draw a green border around this cell - SetDrawColor(0.25, 1, 0.25) - DrawImage(nil, colData.x + 2, colData.y, colData.width - 2, colData.height) - SetDrawColor(rowData.bgCol or "^0") - DrawImage(nil, colData.x + 3, colData.y + 1, colData.width - 4, colData.height - 2) - else - SetDrawColor(rowData.bgCol or "^0") - DrawImage(nil, colData.x + 2, colData.y, colData.width - 2, colData.height) + DrawImage(nil, cellX, lineY + 2, 2, cellH) + + if colData.format and (isOverlay or self.calcsTab:CheckFlag(colData)) then + if isOverlay and actor and self.calcsTab:CheckFlag(colData) or not isOverlay then + if isOverlay then + if cursorY >= lineY + 2 and cursorY < lineY + 20 and cursorX >= cellX and cursorX < cellX + cellW then + self.calcsTab:SetDisplayStat(colData) + self.overlayBreakdownCell = true + end + elseif cursorY >= viewPort.y and cursorY < viewPort.y + viewPort.height and cursorX >= cellX and cursorY >= cellY and cursorX < cellX + cellW and cursorY < cellY + cellH then + self.calcsTab:SetDisplayStat(colData) + end + + if not isOverlay and self.calcsTab.displayData == colData then + SetDrawColor(0.25, 1, 0.25) + DrawImage(nil, cellX + 2, cellY, cellW - 2, cellH) + SetDrawColor(rowData.bgCol or "^0") + DrawImage(nil, cellX + 3, cellY + 1, cellW - 4, cellH - 2) + else + SetDrawColor(rowData.bgCol or "^0") + DrawImage(nil, cellX + 2, lineY + 2, cellW - 2, 18) + end + + local textSize = rowData.textSize or 14 + SetViewport(cellX + 3, isOverlay and lineY + 2 or cellY, cellW - 4, 18) + DrawString(1, 9 - textSize / 2, "LEFT", textSize, "VAR", "^7"..formatCalcStr(colData.format, actor, colData)) + SetViewport() end - local textSize = rowData.textSize or 14 - SetViewport(colData.x + 3, colData.y, colData.width - 4, colData.height) - DrawString(1, 9 - textSize/2, "LEFT", textSize, "VAR", "^7"..formatCalcStr(colData.format, actor, colData)) - SetViewport() end end lineY = lineY + 18 end end - -- If there's at least one enabled row in this subsection, offset by the border for the subsection label if rows > 0 then lineY = lineY + 2 end end end -end - -function CalcSectionClass:OnKeyDown(key, doubleClick) - if not self:IsShown() or not self:IsEnabled() then - return - end - local mOverControl = self:GetMouseOverControl() - if mOverControl and mOverControl.OnKeyDown then - return mOverControl:OnKeyDown(key) - end - local mOver, mOverComp = self:IsMouseOver() - if key:match("BUTTON") then - if not mOver then - return - end - if mOverComp then - -- Pin the stat breakdown - self.calcsTab:SetDisplayStat(mOverComp, true) - return self.calcsTab.controls.breakdown - end - end - return -end - -function CalcSectionClass:OnKeyUp(key) - if not self:IsShown() or not self:IsEnabled() then - return - end - local mOverControl = self:GetMouseOverControl() - if mOverControl and mOverControl.OnKeyUp then - return mOverControl:OnKeyUp(key) - end - return end \ No newline at end of file diff --git a/src/Classes/CalcsTab.lua b/src/Classes/CalcsTab.lua index 53b379002e..f838ad25f8 100644 --- a/src/Classes/CalcsTab.lua +++ b/src/Classes/CalcsTab.lua @@ -231,7 +231,7 @@ function CalcsTabClass:Draw(viewPort, inputEvents) local maxY = 0 for _, section in ipairs(self.sectionList) do section:UpdateSize() - if section.enabled then + if section.enabled and not section.isOverlay then local col if section.group == 1 then -- Group 1: Offense or 3 wide sections @@ -280,7 +280,7 @@ function CalcsTabClass:Draw(viewPort, inputEvents) colY[c] = m_max(colY[1], colY[2], colY[3]) end for _, section in ipairs(self.sectionList) do - if section.enabled and (main.portraitMode and section.group == 2 or section.group == 3) then + if section.enabled and not section.isOverlay and (main.portraitMode and section.group == 2 or section.group == 3) then local col = 3 if colY[col] + section.height + 4 >= m_max(viewPort.y + viewPort.height, maxY) then -- No room in the 4th column, find the highest available location in columns 1-4 @@ -302,9 +302,11 @@ function CalcsTabClass:Draw(viewPort, inputEvents) self.controls.scrollBar.height = viewPort.height self.controls.scrollBar:SetContentDimension(maxY - (baseY - 26), viewPort.height) for _, section in ipairs(self.sectionList) do - -- Give sections their actual Y position and let them update - section.y = section.y - self.controls.scrollBar.offset - section:UpdatePos() + if not section.isOverlay then + -- Give sections their actual Y position and let them update + section.y = section.y - self.controls.scrollBar.offset + section:UpdatePos() + end end self.controls.search.y = 4 - self.controls.scrollBar.offset @@ -367,6 +369,10 @@ function CalcsTabClass:SetDisplayStat(displayData, pin) if not displayData or (not pin and self.displayPinned) then return end + if pin and self.displayPinned and self.displayData == displayData then + self:ClearDisplayStat() + return + end self.displayData = displayData self.displayPinned = pin self.controls.breakdown:SetBreakdownData(displayData, pin) diff --git a/src/Modules/Build.lua b/src/Modules/Build.lua index aa428db490..0f8e0b423f 100644 --- a/src/Modules/Build.lua +++ b/src/Modules/Build.lua @@ -604,6 +604,8 @@ function buildMode:Init(dbFileName, buildName, buildXML, convertBuild, importLin self.skillsTab = new("SkillsTab", self) self.calcsTab = new("CalcsTab", self) self.compareTab = new("CompareTab", self) + -- Used for pined calcs panes + self.overlayPanes = { } -- Load sections from the build file self.savers = { @@ -1173,6 +1175,29 @@ function buildMode:OnFrame(inputEvents) end end end + + -- Consume mouse events for overlay panes before normal input processing + local cursorX, cursorY = GetCursorPos() + for i = #inputEvents, 1, -1 do + local event = inputEvents[i] + if not event then break end + if event.type == "KeyDown" and event.key:match("BUTTON") then + for _, pane in ipairs(self.overlayPanes) do + if pane.isOverlay and pane:IsMouseInOverlay(cursorX, cursorY) then + pane:HandleOverlayClick(event.key, cursorX, cursorY) + inputEvents[i] = nil + break + end + end + elseif event.type == "KeyUp" and event.key:match("BUTTON") then + for _, pane in ipairs(self.overlayPanes) do + if pane.isOverlay then + pane:HandleOverlayRelease(event.key) + end + end + end + end + self:ProcessControlsInput(inputEvents, main.viewPort) self.controls.classDrop:SelByValue(self.spec.curClassId, "classId") @@ -1235,6 +1260,13 @@ function buildMode:OnFrame(inputEvents) self.compareTab:Draw(tabViewPort, inputEvents) end + -- Draw overlay panes on top of all tab content (last = topmost) + for _, pane in ipairs(self.overlayPanes) do + if pane.isOverlay then + pane:DrawOverlay(main.viewPort, inputEvents) + end + end + self.unsaved = self.modFlag or self.notesTab.modFlag or self.partyTab.modFlag or self.configTab.modFlag or self.treeTab.modFlag or self.treeTab.searchFlag or self.spec.modFlag or self.skillsTab.modFlag or self.itemsTab.modFlag or self.calcsTab.modFlag SetDrawLayer(5)