1350 lines
49 KiB
Lua
1350 lines
49 KiB
Lua
local NanamiDPS = NanamiDPS
|
|
local DataStore = NanamiDPS.DataStore
|
|
local BarDisplay = NanamiDPS.BarDisplay
|
|
local TooltipMod = NanamiDPS.Tooltip
|
|
local L = NanamiDPS.L
|
|
|
|
local Window = {}
|
|
NanamiDPS.Window = Window
|
|
|
|
local MAX_WINDOWS = 5
|
|
local activeWindows = {}
|
|
|
|
local SNAP_THRESHOLD = 12
|
|
|
|
-----------------------------------------------------------------------
|
|
-- Scrollable dropdown menu
|
|
-----------------------------------------------------------------------
|
|
local MAX_DROPDOWN_VISIBLE = 8
|
|
|
|
local function ShowDropdown(parent, items, onClick, anchorPoint, relTo, relPoint, xOff, yOff)
|
|
if parent.dropdown and parent.dropdown:IsShown() then
|
|
parent.dropdown:Hide()
|
|
return
|
|
end
|
|
|
|
if not parent.dropdown then
|
|
parent.dropdown = CreateFrame("Frame", nil, parent)
|
|
parent.dropdown:SetFrameStrata("TOOLTIP")
|
|
SFrames:CreateBackdrop(parent.dropdown)
|
|
parent.dropdown:EnableMouseWheel(1)
|
|
end
|
|
|
|
local dd = parent.dropdown
|
|
if dd.buttons then
|
|
for _, btn in pairs(dd.buttons) do btn:Hide() end
|
|
end
|
|
dd.buttons = {}
|
|
dd.allItems = items
|
|
dd.onClickFn = onClick
|
|
dd.scrollOffset = 0
|
|
|
|
local itemH = 18
|
|
local padding = 4
|
|
local maxW = 80
|
|
local totalItems = table.getn(items)
|
|
local visibleCount = math.min(totalItems, MAX_DROPDOWN_VISIBLE)
|
|
|
|
for i = 1, visibleCount do
|
|
local btn = CreateFrame("Button", nil, dd)
|
|
btn:SetHeight(itemH)
|
|
btn.slotIndex = i
|
|
|
|
btn:SetBackdrop({
|
|
bgFile = "Interface\\Buttons\\WHITE8X8",
|
|
tile = false, edgeSize = 0,
|
|
})
|
|
btn:SetBackdropColor(0, 0, 0, 0)
|
|
|
|
local fs = SFrames:CreateFontString(btn, 9, "LEFT")
|
|
fs:SetPoint("LEFT", 4, 0)
|
|
fs:SetPoint("RIGHT", -4, 0)
|
|
btn.label = fs
|
|
|
|
local A = SFrames.ActiveTheme
|
|
btn:SetScript("OnEnter", function()
|
|
if A and A.buttonHoverBg then
|
|
this:SetBackdropColor(A.buttonHoverBg[1], A.buttonHoverBg[2], A.buttonHoverBg[3], 0.6)
|
|
else
|
|
this:SetBackdropColor(0.3, 0.3, 0.3, 0.6)
|
|
end
|
|
end)
|
|
btn:SetScript("OnLeave", function()
|
|
this:SetBackdropColor(0, 0, 0, 0)
|
|
end)
|
|
btn:SetScript("OnClick", function()
|
|
if this.itemData then
|
|
dd.onClickFn(this.itemData)
|
|
dd:Hide()
|
|
end
|
|
end)
|
|
|
|
table.insert(dd.buttons, btn)
|
|
end
|
|
|
|
local function RefreshDropdownButtons()
|
|
local offset = dd.scrollOffset
|
|
for idx, btn in ipairs(dd.buttons) do
|
|
local dataIdx = idx + offset
|
|
if dataIdx <= totalItems then
|
|
local item = dd.allItems[dataIdx]
|
|
btn.itemData = item
|
|
btn.label:SetText(item.text or item.name or "")
|
|
btn:ClearAllPoints()
|
|
btn:SetPoint("TOPLEFT", dd, "TOPLEFT", padding, -padding - (idx - 1) * itemH)
|
|
btn:SetPoint("TOPRIGHT", dd, "TOPRIGHT", -padding, -padding - (idx - 1) * itemH)
|
|
btn:Show()
|
|
|
|
local strW = btn.label:GetStringWidth()
|
|
if strW and strW + 20 > maxW then maxW = strW + 20 end
|
|
else
|
|
btn:Hide()
|
|
end
|
|
end
|
|
dd:SetWidth(math.max(maxW, 90))
|
|
end
|
|
|
|
RefreshDropdownButtons()
|
|
|
|
dd:SetScript("OnMouseWheel", function()
|
|
local maxOffset = math.max(0, totalItems - visibleCount)
|
|
if arg1 > 0 then
|
|
dd.scrollOffset = math.max(0, dd.scrollOffset - 1)
|
|
else
|
|
dd.scrollOffset = math.min(maxOffset, dd.scrollOffset + 1)
|
|
end
|
|
RefreshDropdownButtons()
|
|
end)
|
|
|
|
local ddHeight = visibleCount * itemH + padding * 2
|
|
if totalItems > MAX_DROPDOWN_VISIBLE then
|
|
ddHeight = ddHeight + 8
|
|
end
|
|
dd:SetHeight(ddHeight)
|
|
dd:ClearAllPoints()
|
|
dd:SetPoint(anchorPoint or "TOPLEFT", relTo or parent, relPoint or "BOTTOMLEFT", xOff or 0, yOff or -2)
|
|
|
|
-- Scroll indicator for long lists
|
|
if not dd.scrollUpIndicator then
|
|
dd.scrollUpIndicator = SFrames:CreateFontString(dd, 8, "CENTER")
|
|
dd.scrollUpIndicator:SetPoint("TOP", dd, "TOP", 0, -1)
|
|
dd.scrollUpIndicator:SetText("|cff888888▲")
|
|
end
|
|
if not dd.scrollDownIndicator then
|
|
dd.scrollDownIndicator = SFrames:CreateFontString(dd, 8, "CENTER")
|
|
dd.scrollDownIndicator:SetPoint("BOTTOM", dd, "BOTTOM", 0, 1)
|
|
dd.scrollDownIndicator:SetText("|cff888888▼")
|
|
end
|
|
|
|
if totalItems > MAX_DROPDOWN_VISIBLE then
|
|
dd.scrollUpIndicator:Show()
|
|
dd.scrollDownIndicator:Show()
|
|
else
|
|
dd.scrollUpIndicator:Hide()
|
|
dd.scrollDownIndicator:Hide()
|
|
end
|
|
|
|
dd:Show()
|
|
end
|
|
|
|
-----------------------------------------------------------------------
|
|
-- Magnetic snapping
|
|
-----------------------------------------------------------------------
|
|
local function SnapWindow(frame)
|
|
local left = frame:GetLeft()
|
|
local bottom = frame:GetBottom()
|
|
local right = frame:GetRight()
|
|
local top = frame:GetTop()
|
|
local w = frame:GetWidth()
|
|
local h = frame:GetHeight()
|
|
|
|
if not left or not bottom then return end
|
|
|
|
local snapX, snapY = nil, nil
|
|
local bestDistX, bestDistY = SNAP_THRESHOLD + 1, SNAP_THRESHOLD + 1
|
|
|
|
for _, other in pairs(activeWindows) do
|
|
if other and other ~= frame and other:IsShown() then
|
|
local oL = other:GetLeft()
|
|
local oB = other:GetBottom()
|
|
local oR = other:GetRight()
|
|
local oT = other:GetTop()
|
|
if oL and oB then
|
|
-- Snap right edge to other's left edge
|
|
local d = math.abs(right - oL)
|
|
if d < bestDistX then bestDistX = d; snapX = oL - w end
|
|
-- Snap left edge to other's right edge
|
|
d = math.abs(left - oR)
|
|
if d < bestDistX then bestDistX = d; snapX = oR end
|
|
-- Snap left to left
|
|
d = math.abs(left - oL)
|
|
if d < bestDistX then bestDistX = d; snapX = oL end
|
|
-- Snap right to right
|
|
d = math.abs(right - oR)
|
|
if d < bestDistX then bestDistX = d; snapX = oR - w end
|
|
|
|
-- Snap top to top
|
|
d = math.abs(top - oT)
|
|
if d < bestDistY then bestDistY = d; snapY = oT - h end
|
|
-- Snap bottom to bottom
|
|
d = math.abs(bottom - oB)
|
|
if d < bestDistY then bestDistY = d; snapY = oB end
|
|
-- Snap top to other's bottom
|
|
d = math.abs(top - oB)
|
|
if d < bestDistY then bestDistY = d; snapY = oB - h end
|
|
-- Snap bottom to other's top
|
|
d = math.abs(bottom - oT)
|
|
if d < bestDistY then bestDistY = d; snapY = oT end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Screen edge snapping
|
|
local screenW = GetScreenWidth()
|
|
local screenH = GetScreenHeight()
|
|
local uiScale = UIParent:GetEffectiveScale()
|
|
|
|
local d = math.abs(left)
|
|
if d < bestDistX then bestDistX = d; snapX = 0 end
|
|
d = math.abs(right - screenW)
|
|
if d < bestDistX then bestDistX = d; snapX = screenW - w end
|
|
|
|
d = math.abs(bottom)
|
|
if d < bestDistY then bestDistY = d; snapY = 0 end
|
|
d = math.abs(top - screenH)
|
|
if d < bestDistY then bestDistY = d; snapY = screenH - h end
|
|
|
|
if bestDistX <= SNAP_THRESHOLD or bestDistY <= SNAP_THRESHOLD then
|
|
local finalX = (bestDistX <= SNAP_THRESHOLD and snapX) or left
|
|
local finalY = (bestDistY <= SNAP_THRESHOLD and snapY) or bottom
|
|
frame:ClearAllPoints()
|
|
frame:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", finalX, finalY)
|
|
end
|
|
end
|
|
|
|
-----------------------------------------------------------------------
|
|
-- Window creation
|
|
-----------------------------------------------------------------------
|
|
function Window:Create(wid)
|
|
local cfg = NanamiDPS.config or {}
|
|
local barHeight = cfg.barHeight or 16
|
|
|
|
local frame = CreateFrame("Frame", "NanamiDPSWindow" .. wid, UIParent)
|
|
frame:SetWidth(220)
|
|
frame:SetHeight(barHeight * 8 + 26)
|
|
frame:SetClampedToScreen(true)
|
|
frame:SetMovable(true)
|
|
frame:SetResizable(true)
|
|
frame:SetMinResize(160, 50)
|
|
frame:EnableMouse(true)
|
|
frame:EnableMouseWheel(1)
|
|
|
|
SFrames:CreateRoundBackdrop(frame)
|
|
local A = SFrames.ActiveTheme
|
|
if A and A.panelBg then
|
|
frame:SetBackdropColor(A.panelBg[1], A.panelBg[2], A.panelBg[3], cfg.backdropAlpha or 0.92)
|
|
end
|
|
if A and A.panelBorder then
|
|
frame:SetBackdropBorderColor(A.panelBorder[1], A.panelBorder[2], A.panelBorder[3], A.panelBorder[4] or 0.9)
|
|
end
|
|
|
|
frame:SetID(wid)
|
|
frame.scroll = 0
|
|
frame.segmentIndex = 1
|
|
frame.activeModuleName = "DamageDone"
|
|
frame.bars = {}
|
|
|
|
---------------------------------------------------------------
|
|
-- Title bar
|
|
---------------------------------------------------------------
|
|
frame.titleBar = CreateFrame("Frame", nil, frame)
|
|
frame.titleBar:SetHeight(20)
|
|
frame.titleBar:SetPoint("TOPLEFT", frame, "TOPLEFT", 4, -4)
|
|
frame.titleBar:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -4, -4)
|
|
frame.titleBar:SetBackdrop({
|
|
bgFile = "Interface\\Buttons\\WHITE8X8",
|
|
tile = false, edgeSize = 0,
|
|
})
|
|
if A and A.headerBg then
|
|
frame.titleBar:SetBackdropColor(A.headerBg[1], A.headerBg[2], A.headerBg[3], A.headerBg[4] or 0.98)
|
|
else
|
|
frame.titleBar:SetBackdropColor(0.06, 0.06, 0.08, 0.98)
|
|
end
|
|
frame.titleBar:EnableMouse(true)
|
|
frame.titleBar:RegisterForDrag("LeftButton")
|
|
frame.titleBar:SetScript("OnDragStart", function()
|
|
if not cfg.locked then frame:StartMoving() end
|
|
end)
|
|
frame.titleBar:SetScript("OnDragStop", function()
|
|
frame:StopMovingOrSizing()
|
|
SnapWindow(frame)
|
|
Window:SavePosition(frame)
|
|
end)
|
|
|
|
-- Title label
|
|
frame.titleText = SFrames:CreateFontString(frame.titleBar, 10, "LEFT")
|
|
frame.titleText:SetPoint("LEFT", frame.titleBar, "LEFT", 6, 0)
|
|
local accentHex = (A and A.accentHex) or "ffFF88AA"
|
|
frame.titleText:SetText("|c" .. accentHex .. "Nanami|r DPS")
|
|
if A and A.title then
|
|
frame.titleText:SetTextColor(A.title[1], A.title[2], A.title[3])
|
|
end
|
|
|
|
---------------------------------------------------------------
|
|
-- Title bar buttons (right side)
|
|
---------------------------------------------------------------
|
|
-- Close / Hide button
|
|
local closeBtn = CreateFrame("Button", nil, frame.titleBar)
|
|
closeBtn:SetWidth(16)
|
|
closeBtn:SetHeight(16)
|
|
closeBtn:SetPoint("RIGHT", frame.titleBar, "RIGHT", -4, 0)
|
|
local closeIcon = SFrames:CreateIcon(closeBtn, "close", 12)
|
|
closeIcon:SetPoint("CENTER", 0, 0)
|
|
closeBtn:SetScript("OnClick", function()
|
|
if wid > 1 then
|
|
Window:DestroyWindow(wid)
|
|
else
|
|
frame:Hide()
|
|
end
|
|
end)
|
|
closeBtn:SetScript("OnEnter", function()
|
|
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
|
GameTooltip:AddLine(L["Hide Window"])
|
|
GameTooltip:Show()
|
|
end)
|
|
closeBtn:SetScript("OnLeave", function() GameTooltip:Hide() end)
|
|
|
|
-- New window button (+)
|
|
local newWinBtn = CreateFrame("Button", nil, frame.titleBar)
|
|
newWinBtn:SetWidth(16)
|
|
newWinBtn:SetHeight(16)
|
|
newWinBtn:SetPoint("RIGHT", closeBtn, "LEFT", -1, 0)
|
|
local newWinIcon = SFrames:CreateIcon(newWinBtn, "save", 12)
|
|
newWinIcon:SetPoint("CENTER", 0, 0)
|
|
newWinBtn:SetScript("OnClick", function()
|
|
Window:CreateNewWindow()
|
|
end)
|
|
newWinBtn:SetScript("OnEnter", function()
|
|
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
|
GameTooltip:AddLine(L["Create New Window"])
|
|
GameTooltip:Show()
|
|
if A and A.accent then
|
|
newWinIcon:SetVertexColor(A.accent[1], A.accent[2], A.accent[3])
|
|
end
|
|
end)
|
|
newWinBtn:SetScript("OnLeave", function()
|
|
GameTooltip:Hide()
|
|
newWinIcon:SetVertexColor(1, 1, 1)
|
|
end)
|
|
|
|
-- Settings button
|
|
local settingsBtn = CreateFrame("Button", nil, frame.titleBar)
|
|
settingsBtn:SetWidth(16)
|
|
settingsBtn:SetHeight(16)
|
|
settingsBtn:SetPoint("RIGHT", newWinBtn, "LEFT", -1, 0)
|
|
local settingsIcon = SFrames:CreateIcon(settingsBtn, "settings", 12)
|
|
settingsIcon:SetPoint("CENTER", 0, 0)
|
|
settingsBtn:SetScript("OnClick", function()
|
|
if NanamiDPS.Options then NanamiDPS.Options:Toggle() end
|
|
end)
|
|
settingsBtn:SetScript("OnEnter", function()
|
|
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
|
GameTooltip:AddLine(L["Settings"])
|
|
GameTooltip:Show()
|
|
end)
|
|
settingsBtn:SetScript("OnLeave", function() GameTooltip:Hide() end)
|
|
|
|
-- Reset button
|
|
local resetBtn = CreateFrame("Button", nil, frame.titleBar)
|
|
resetBtn:SetWidth(16)
|
|
resetBtn:SetHeight(16)
|
|
resetBtn:SetPoint("RIGHT", settingsBtn, "LEFT", -1, 0)
|
|
local resetIcon = SFrames:CreateIcon(resetBtn, "close", 10)
|
|
resetIcon:SetPoint("CENTER", 0, 0)
|
|
if A and A.accent then
|
|
resetIcon:SetVertexColor(A.accent[1], A.accent[2], A.accent[3])
|
|
else
|
|
resetIcon:SetVertexColor(1, 0.4, 0.4)
|
|
end
|
|
resetBtn:SetScript("OnClick", function()
|
|
if IsShiftKeyDown() then
|
|
DataStore:ResetAll()
|
|
else
|
|
local dialog = StaticPopupDialogs["NANAMI_DPS_CONFIRM"]
|
|
dialog.text = L["Reset Data?"]
|
|
dialog.OnAccept = function() DataStore:ResetAll() end
|
|
StaticPopup_Show("NANAMI_DPS_CONFIRM")
|
|
end
|
|
end)
|
|
resetBtn:SetScript("OnEnter", function()
|
|
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
|
GameTooltip:AddLine(L["Reset"])
|
|
GameTooltip:AddLine("|cffffffff" .. L["Shift-click to reset"])
|
|
GameTooltip:Show()
|
|
end)
|
|
resetBtn:SetScript("OnLeave", function() GameTooltip:Hide() end)
|
|
|
|
-- Report button
|
|
local reportBtn = CreateFrame("Button", nil, frame.titleBar)
|
|
reportBtn:SetWidth(16)
|
|
reportBtn:SetHeight(16)
|
|
reportBtn:SetPoint("RIGHT", resetBtn, "LEFT", -1, 0)
|
|
local reportIcon = SFrames:CreateFontString(reportBtn, 9, "CENTER")
|
|
reportIcon:SetAllPoints()
|
|
reportIcon:SetText("R")
|
|
if A and A.text then
|
|
reportIcon:SetTextColor(A.text[1], A.text[2], A.text[3])
|
|
end
|
|
reportBtn:SetScript("OnClick", function()
|
|
Window:ShowReport(frame)
|
|
end)
|
|
reportBtn:SetScript("OnEnter", function()
|
|
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
|
GameTooltip:AddLine(L["Report to Chat"])
|
|
GameTooltip:Show()
|
|
if A and A.accent then
|
|
reportIcon:SetTextColor(A.accent[1], A.accent[2], A.accent[3])
|
|
end
|
|
end)
|
|
reportBtn:SetScript("OnLeave", function()
|
|
GameTooltip:Hide()
|
|
if A and A.text then
|
|
reportIcon:SetTextColor(A.text[1], A.text[2], A.text[3])
|
|
else
|
|
reportIcon:SetTextColor(1, 1, 1)
|
|
end
|
|
end)
|
|
|
|
---------------------------------------------------------------
|
|
-- Segment / Mode selectors (below title)
|
|
---------------------------------------------------------------
|
|
frame.selectorBar = CreateFrame("Frame", nil, frame)
|
|
frame.selectorBar:SetHeight(16)
|
|
frame.selectorBar:SetPoint("TOPLEFT", frame.titleBar, "BOTTOMLEFT", 0, 0)
|
|
frame.selectorBar:SetPoint("TOPRIGHT", frame.titleBar, "BOTTOMRIGHT", 0, 0)
|
|
frame.selectorBar:SetBackdrop({
|
|
bgFile = "Interface\\Buttons\\WHITE8X8",
|
|
tile = false, edgeSize = 0,
|
|
})
|
|
if A and A.sectionBg then
|
|
frame.selectorBar:SetBackdropColor(A.sectionBg[1], A.sectionBg[2], A.sectionBg[3], A.sectionBg[4] or 0.8)
|
|
else
|
|
frame.selectorBar:SetBackdropColor(0.08, 0.04, 0.06, 0.8)
|
|
end
|
|
|
|
-- Segment button
|
|
frame.btnSegment = CreateFrame("Button", nil, frame.selectorBar)
|
|
frame.btnSegment:SetHeight(14)
|
|
frame.btnSegment:SetPoint("LEFT", frame.selectorBar, "LEFT", 4, 0)
|
|
frame.btnSegment:SetWidth(70)
|
|
frame.btnSegment:SetBackdrop({
|
|
bgFile = "Interface\\Buttons\\WHITE8X8",
|
|
edgeFile = "Interface\\Buttons\\WHITE8X8",
|
|
tile = false, edgeSize = 1,
|
|
insets = { left = 1, right = 1, top = 1, bottom = 1 },
|
|
})
|
|
if A and A.buttonBg then
|
|
frame.btnSegment:SetBackdropColor(A.buttonBg[1], A.buttonBg[2], A.buttonBg[3], A.buttonBg[4] or 0.9)
|
|
frame.btnSegment:SetBackdropBorderColor(A.buttonBorder[1], A.buttonBorder[2], A.buttonBorder[3], A.buttonBorder[4] or 0.8)
|
|
else
|
|
frame.btnSegment:SetBackdropColor(0.15, 0.08, 0.12, 0.9)
|
|
frame.btnSegment:SetBackdropBorderColor(0.4, 0.3, 0.35, 0.8)
|
|
end
|
|
|
|
frame.btnSegment.text = SFrames:CreateFontString(frame.btnSegment, 9, "CENTER")
|
|
frame.btnSegment.text:SetAllPoints()
|
|
frame.btnSegment.text:SetText(L["Current"])
|
|
|
|
frame.btnSegment:SetScript("OnClick", function()
|
|
local items = {}
|
|
local segList = DataStore:GetSegmentList()
|
|
for _, seg in ipairs(segList) do
|
|
table.insert(items, { text = seg.name, index = seg.index })
|
|
end
|
|
ShowDropdown(frame.btnSegment, items, function(item)
|
|
frame.segmentIndex = item.index
|
|
frame.btnSegment.text:SetText(item.text)
|
|
Window:RefreshWindow(frame)
|
|
end, "TOPLEFT", frame.btnSegment, "BOTTOMLEFT", 0, -2)
|
|
end)
|
|
|
|
-- Mode button
|
|
frame.btnMode = CreateFrame("Button", nil, frame.selectorBar)
|
|
frame.btnMode:SetHeight(14)
|
|
frame.btnMode:SetPoint("LEFT", frame.btnSegment, "RIGHT", 4, 0)
|
|
frame.btnMode:SetPoint("RIGHT", frame.selectorBar, "RIGHT", -4, 0)
|
|
frame.btnMode:SetBackdrop({
|
|
bgFile = "Interface\\Buttons\\WHITE8X8",
|
|
edgeFile = "Interface\\Buttons\\WHITE8X8",
|
|
tile = false, edgeSize = 1,
|
|
insets = { left = 1, right = 1, top = 1, bottom = 1 },
|
|
})
|
|
if A and A.buttonBg then
|
|
frame.btnMode:SetBackdropColor(A.buttonBg[1], A.buttonBg[2], A.buttonBg[3], A.buttonBg[4] or 0.9)
|
|
frame.btnMode:SetBackdropBorderColor(A.buttonBorder[1], A.buttonBorder[2], A.buttonBorder[3], A.buttonBorder[4] or 0.8)
|
|
else
|
|
frame.btnMode:SetBackdropColor(0.15, 0.08, 0.12, 0.9)
|
|
frame.btnMode:SetBackdropBorderColor(0.4, 0.3, 0.35, 0.8)
|
|
end
|
|
|
|
frame.btnMode.text = SFrames:CreateFontString(frame.btnMode, 9, "CENTER")
|
|
frame.btnMode.text:SetAllPoints()
|
|
frame.btnMode.text:SetText(L["Damage Done"])
|
|
|
|
frame.btnMode:SetScript("OnClick", function()
|
|
local items = {}
|
|
for _, name in ipairs(NanamiDPS.moduleOrder) do
|
|
local mod = NanamiDPS.modules[name]
|
|
if mod then
|
|
table.insert(items, { text = mod:GetName(), moduleName = name })
|
|
end
|
|
end
|
|
ShowDropdown(frame.btnMode, items, function(item)
|
|
frame.activeModuleName = item.moduleName
|
|
frame.btnMode.text:SetText(item.text)
|
|
frame.scroll = 0
|
|
Window:RefreshWindow(frame)
|
|
end, "TOPLEFT", frame.btnMode, "BOTTOMLEFT", 0, -2)
|
|
end)
|
|
|
|
---------------------------------------------------------------
|
|
-- Content area (bars go here)
|
|
---------------------------------------------------------------
|
|
frame.content = CreateFrame("Frame", nil, frame)
|
|
frame.content:SetPoint("TOPLEFT", frame.selectorBar, "BOTTOMLEFT", 0, -1)
|
|
frame.content:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -4, 4)
|
|
|
|
---------------------------------------------------------------
|
|
-- Scroll wheel
|
|
---------------------------------------------------------------
|
|
frame:SetScript("OnMouseWheel", function()
|
|
local delta = arg1
|
|
frame.scroll = frame.scroll + (delta > 0 and -1 or 1)
|
|
frame.scroll = math.max(frame.scroll, 0)
|
|
Window:RefreshWindow(frame)
|
|
end)
|
|
|
|
---------------------------------------------------------------
|
|
-- Resize handle
|
|
---------------------------------------------------------------
|
|
frame.resizeBtn = CreateFrame("Frame", nil, frame)
|
|
frame.resizeBtn:SetWidth(16)
|
|
frame.resizeBtn:SetHeight(16)
|
|
frame.resizeBtn:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -3, 3)
|
|
frame.resizeBtn:EnableMouse(true)
|
|
frame.resizeBtn:SetFrameLevel(frame:GetFrameLevel() + 10)
|
|
|
|
frame.resizeBtn.tex = frame.resizeBtn:CreateTexture(nil, "OVERLAY")
|
|
frame.resizeBtn.tex:SetAllPoints()
|
|
frame.resizeBtn.tex:SetTexture("Interface\\ChatFrame\\UI-ChatIM-SizeGrabber-Up")
|
|
if A and A.accent then
|
|
frame.resizeBtn.tex:SetVertexColor(A.accent[1], A.accent[2], A.accent[3], 0.6)
|
|
else
|
|
frame.resizeBtn.tex:SetVertexColor(0.6, 0.6, 0.6, 0.5)
|
|
end
|
|
|
|
frame.resizeBtn:SetScript("OnEnter", function()
|
|
if not cfg.locked then
|
|
this.tex:SetTexture("Interface\\ChatFrame\\UI-ChatIM-SizeGrabber-Highlight")
|
|
if A and A.accent then
|
|
this.tex:SetVertexColor(A.accent[1], A.accent[2], A.accent[3], 1)
|
|
else
|
|
this.tex:SetVertexColor(1, 1, 1, 0.9)
|
|
end
|
|
GameTooltip:SetOwner(this, "ANCHOR_TOPLEFT")
|
|
GameTooltip:AddLine(L["Drag to Resize"])
|
|
GameTooltip:Show()
|
|
end
|
|
end)
|
|
frame.resizeBtn:SetScript("OnLeave", function()
|
|
this.tex:SetTexture("Interface\\ChatFrame\\UI-ChatIM-SizeGrabber-Up")
|
|
if A and A.accent then
|
|
this.tex:SetVertexColor(A.accent[1], A.accent[2], A.accent[3], 0.6)
|
|
else
|
|
this.tex:SetVertexColor(0.6, 0.6, 0.6, 0.5)
|
|
end
|
|
GameTooltip:Hide()
|
|
end)
|
|
frame.resizeBtn:SetScript("OnMouseDown", function()
|
|
if not cfg.locked then
|
|
frame.sizing = true
|
|
this.tex:SetTexture("Interface\\ChatFrame\\UI-ChatIM-SizeGrabber-Down")
|
|
frame:StartSizing("BOTTOMRIGHT")
|
|
end
|
|
end)
|
|
frame.resizeBtn:SetScript("OnMouseUp", function()
|
|
frame.sizing = nil
|
|
this.tex:SetTexture("Interface\\ChatFrame\\UI-ChatIM-SizeGrabber-Highlight")
|
|
frame:StopMovingOrSizing()
|
|
SnapWindow(frame)
|
|
Window:SavePosition(frame)
|
|
Window:RefreshWindow(frame, true)
|
|
end)
|
|
|
|
---------------------------------------------------------------
|
|
-- Periodic refresh
|
|
---------------------------------------------------------------
|
|
frame:SetScript("OnUpdate", function()
|
|
if (this.tick or 0) > GetTime() then return end
|
|
this.tick = GetTime() + 0.3
|
|
if this.needsRefresh then
|
|
this.needsRefresh = nil
|
|
Window:RefreshWindow(this)
|
|
end
|
|
if cfg.locked then
|
|
this.resizeBtn:SetAlpha(0)
|
|
this.resizeBtn:EnableMouse(false)
|
|
else
|
|
this.resizeBtn:EnableMouse(true)
|
|
if MouseIsOver(this) then
|
|
this.resizeBtn:SetAlpha(1)
|
|
else
|
|
this.resizeBtn:SetAlpha(0.4)
|
|
end
|
|
end
|
|
end)
|
|
|
|
---------------------------------------------------------------
|
|
-- Position loading
|
|
---------------------------------------------------------------
|
|
frame:RegisterEvent("PLAYER_LOGIN")
|
|
frame:SetScript("OnEvent", function()
|
|
Window:LoadPosition(frame)
|
|
end)
|
|
|
|
return frame
|
|
end
|
|
|
|
function Window:SavePosition(frame)
|
|
if not NanamiDPS_DB then return end
|
|
NanamiDPS_DB.windowPositions = NanamiDPS_DB.windowPositions or {}
|
|
local wid = frame:GetID()
|
|
|
|
local l = frame:GetLeft()
|
|
local b = frame:GetBottom()
|
|
local screenW = UIParent:GetRight() or UIParent:GetWidth()
|
|
local screenH = UIParent:GetTop() or UIParent:GetHeight()
|
|
if not l or not b then return end
|
|
|
|
local fw = frame:GetWidth() or 0
|
|
local fh = frame:GetHeight() or 0
|
|
local cx = l + fw / 2
|
|
local cy = b + fh / 2
|
|
|
|
local point, relPoint, xOfs, yOfs
|
|
if cx < screenW / 3 then
|
|
if cy > screenH * 2 / 3 then
|
|
point = "TOPLEFT"; relPoint = "TOPLEFT"
|
|
xOfs = l; yOfs = -(screenH - (b + fh))
|
|
elseif cy < screenH / 3 then
|
|
point = "BOTTOMLEFT"; relPoint = "BOTTOMLEFT"
|
|
xOfs = l; yOfs = b
|
|
else
|
|
point = "LEFT"; relPoint = "LEFT"
|
|
xOfs = l; yOfs = cy - screenH / 2
|
|
end
|
|
elseif cx > screenW * 2 / 3 then
|
|
local r = frame:GetRight() or 0
|
|
if cy > screenH * 2 / 3 then
|
|
point = "TOPRIGHT"; relPoint = "TOPRIGHT"
|
|
xOfs = r - screenW; yOfs = -(screenH - (b + fh))
|
|
elseif cy < screenH / 3 then
|
|
point = "BOTTOMRIGHT"; relPoint = "BOTTOMRIGHT"
|
|
xOfs = r - screenW; yOfs = b
|
|
else
|
|
point = "RIGHT"; relPoint = "RIGHT"
|
|
xOfs = r - screenW; yOfs = cy - screenH / 2
|
|
end
|
|
else
|
|
if cy > screenH * 2 / 3 then
|
|
point = "TOP"; relPoint = "TOP"
|
|
xOfs = cx - screenW / 2; yOfs = -(screenH - (b + fh))
|
|
elseif cy < screenH / 3 then
|
|
point = "BOTTOM"; relPoint = "BOTTOM"
|
|
xOfs = cx - screenW / 2; yOfs = b
|
|
else
|
|
point = "CENTER"; relPoint = "CENTER"
|
|
xOfs = cx - screenW / 2; yOfs = cy - screenH / 2
|
|
end
|
|
end
|
|
|
|
NanamiDPS_DB.windowPositions[wid] = {
|
|
point = point, relativePoint = relPoint,
|
|
xOfs = xOfs, yOfs = yOfs,
|
|
w = fw, h = fh,
|
|
}
|
|
|
|
frame:ClearAllPoints()
|
|
frame:SetPoint(point, UIParent, relPoint, xOfs, yOfs)
|
|
end
|
|
|
|
function Window:LoadPosition(frame)
|
|
if not NanamiDPS_DB or not NanamiDPS_DB.windowPositions then
|
|
frame:ClearAllPoints()
|
|
frame:SetPoint("RIGHT", UIParent, "RIGHT", -80, -50)
|
|
return
|
|
end
|
|
local wid = frame:GetID()
|
|
local pos = NanamiDPS_DB.windowPositions[wid]
|
|
if pos and pos.point and pos.relativePoint then
|
|
frame:ClearAllPoints()
|
|
frame:SetPoint(pos.point, UIParent, pos.relativePoint, pos.xOfs or 0, pos.yOfs or 0)
|
|
if pos.w then frame:SetWidth(pos.w) end
|
|
if pos.h then frame:SetHeight(pos.h) end
|
|
elseif pos and pos.left and pos.bottom then
|
|
frame:ClearAllPoints()
|
|
frame:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", pos.left, pos.bottom)
|
|
if pos.w then frame:SetWidth(pos.w) end
|
|
if pos.h then frame:SetHeight(pos.h) end
|
|
elseif pos and pos.x and pos.y then
|
|
frame:ClearAllPoints()
|
|
frame:SetPoint("CENTER", UIParent, "BOTTOMLEFT", pos.x, pos.y)
|
|
if pos.w then frame:SetWidth(pos.w) end
|
|
if pos.h then frame:SetHeight(pos.h) end
|
|
else
|
|
frame:ClearAllPoints()
|
|
frame:SetPoint("RIGHT", UIParent, "RIGHT", -80, -50)
|
|
end
|
|
end
|
|
|
|
-----------------------------------------------------------------------
|
|
-- Refresh logic
|
|
-----------------------------------------------------------------------
|
|
function Window:RefreshWindow(frame, force)
|
|
if not frame then return end
|
|
local mod = NanamiDPS.modules[frame.activeModuleName]
|
|
if not mod then return end
|
|
|
|
local seg = DataStore:GetSegment(frame.segmentIndex or 1)
|
|
if not seg then return end
|
|
|
|
local barData = mod:GetBars(seg)
|
|
if not barData then barData = {} end
|
|
|
|
local maxBars = BarDisplay:GetMaxBars(frame)
|
|
local totalEntries = table.getn(barData)
|
|
|
|
frame.scroll = math.min(frame.scroll, math.max(0, totalEntries - maxBars))
|
|
|
|
local maxVal = 0
|
|
for _, d in ipairs(barData) do
|
|
if (d.value or 0) > maxVal then maxVal = d.value end
|
|
end
|
|
|
|
local barsNeeded = math.max(0, math.min(maxBars, totalEntries - frame.scroll))
|
|
while table.getn(frame.bars) < barsNeeded do
|
|
local bar = BarDisplay:CreateBar(frame, table.getn(frame.bars) + 1)
|
|
table.insert(frame.bars, bar)
|
|
end
|
|
|
|
for i = 1, math.max(table.getn(frame.bars), barsNeeded) do
|
|
local dataIdx = i + frame.scroll
|
|
local bar = frame.bars[i]
|
|
if not bar then break end
|
|
|
|
if dataIdx <= totalEntries then
|
|
local d = barData[dataIdx]
|
|
BarDisplay:UpdateBar(bar, d, maxVal, dataIdx)
|
|
BarDisplay:SetBarCallbacks(bar,
|
|
function(b) TooltipMod:ShowBar(b, frame) end,
|
|
function(b) TooltipMod:Hide() end,
|
|
function(b, btn)
|
|
if b.barData and NanamiDPS.DetailView then
|
|
NanamiDPS.DetailView:Show(b.barData, frame.activeModuleName, frame.segmentIndex or 1)
|
|
end
|
|
end
|
|
)
|
|
else
|
|
bar:Hide()
|
|
end
|
|
end
|
|
|
|
BarDisplay:LayoutBars(frame)
|
|
end
|
|
|
|
function Window:RefreshAll(force)
|
|
for _, win in pairs(activeWindows) do
|
|
if win and win:IsShown() then
|
|
self:RefreshWindow(win, force)
|
|
end
|
|
end
|
|
end
|
|
|
|
-----------------------------------------------------------------------
|
|
-- Initialization
|
|
-----------------------------------------------------------------------
|
|
NanamiDPS:RegisterCallback("INIT", "Window", function()
|
|
local win = Window:Create(1)
|
|
activeWindows[1] = win
|
|
NanamiDPS.windows = activeWindows
|
|
|
|
NanamiDPS:RegisterCallback("refresh", "WindowRefresh", function()
|
|
for _, w in pairs(activeWindows) do
|
|
if w then w.needsRefresh = true end
|
|
end
|
|
end)
|
|
|
|
Window:LoadPosition(win)
|
|
|
|
local cfg = NanamiDPS.config or {}
|
|
if cfg.visible ~= false then
|
|
win:Show()
|
|
else
|
|
win:Hide()
|
|
end
|
|
|
|
Window:RefreshWindow(win, true)
|
|
end)
|
|
|
|
-----------------------------------------------------------------------
|
|
-- Multi-window API
|
|
-----------------------------------------------------------------------
|
|
function Window:CreateNewWindow()
|
|
for i = 2, MAX_WINDOWS do
|
|
if not activeWindows[i] then
|
|
local win = self:Create(i)
|
|
activeWindows[i] = win
|
|
|
|
local prev = activeWindows[i - 1]
|
|
if prev and prev:IsShown() then
|
|
win:ClearAllPoints()
|
|
win:SetPoint("TOPLEFT", prev, "TOPRIGHT", 0, 0)
|
|
win:SetWidth(prev:GetWidth())
|
|
win:SetHeight(prev:GetHeight())
|
|
end
|
|
|
|
win:Show()
|
|
self:RefreshWindow(win, true)
|
|
self:SavePosition(win)
|
|
return win
|
|
end
|
|
end
|
|
local chatHex = (SFrames.ActiveTheme and SFrames.ActiveTheme.accentHex) or "ffFF88AA"
|
|
DEFAULT_CHAT_FRAME:AddMessage("|c" .. chatHex .. "Nanami DPS|r: " .. L["Max Segments"] .. " (" .. MAX_WINDOWS .. ")")
|
|
end
|
|
|
|
function Window:DestroyWindow(wid)
|
|
if wid == 1 then return end
|
|
if activeWindows[wid] then
|
|
activeWindows[wid]:Hide()
|
|
activeWindows[wid] = nil
|
|
end
|
|
end
|
|
|
|
function Window:ToggleVisibility()
|
|
for _, win in pairs(activeWindows) do
|
|
if win then
|
|
if win:IsShown() then win:Hide() else win:Show() end
|
|
end
|
|
end
|
|
end
|
|
|
|
-----------------------------------------------------------------------
|
|
-- Report to Chat Panel
|
|
-----------------------------------------------------------------------
|
|
local reportFrame = nil
|
|
local reportChannel = "SAY"
|
|
local reportCount = 5
|
|
local reportSourceWin = nil
|
|
local reportHeaderFn = nil
|
|
local reportLinesFn = nil
|
|
|
|
local CHANNEL_LIST = {
|
|
{ id = "SAY", key = "Say" },
|
|
{ id = "YELL", key = "Yell" },
|
|
{ id = "PARTY", key = "Party" },
|
|
{ id = "RAID", key = "Raid" },
|
|
{ id = "GUILD", key = "Guild" },
|
|
{ id = "OFFICER", key = "Officer" },
|
|
{ id = "WHISPER", key = "Whisper" },
|
|
}
|
|
|
|
local function UpdateReportChannelButtons()
|
|
if not reportFrame or not reportFrame.channelBtns then return end
|
|
local A = SFrames.ActiveTheme
|
|
|
|
for _, btn in ipairs(reportFrame.channelBtns) do
|
|
if btn.channelId == reportChannel then
|
|
if A and A.accent then
|
|
btn:SetBackdropColor(A.accent[1] * 0.3, A.accent[2] * 0.3, A.accent[3] * 0.3, 0.9)
|
|
btn:SetBackdropBorderColor(A.accent[1], A.accent[2], A.accent[3], 0.9)
|
|
else
|
|
btn:SetBackdropColor(0.3, 0.15, 0.2, 0.9)
|
|
btn:SetBackdropBorderColor(1, 0.53, 0.67, 0.9)
|
|
end
|
|
if A and A.accent then
|
|
btn.label:SetTextColor(A.accent[1], A.accent[2], A.accent[3])
|
|
else
|
|
btn.label:SetTextColor(1, 0.85, 0.9)
|
|
end
|
|
else
|
|
if A and A.buttonBg then
|
|
btn:SetBackdropColor(A.buttonBg[1], A.buttonBg[2], A.buttonBg[3], A.buttonBg[4] or 0.9)
|
|
else
|
|
btn:SetBackdropColor(0.15, 0.08, 0.12, 0.9)
|
|
end
|
|
if A and A.buttonBorder then
|
|
btn:SetBackdropBorderColor(A.buttonBorder[1], A.buttonBorder[2], A.buttonBorder[3], A.buttonBorder[4] or 0.8)
|
|
else
|
|
btn:SetBackdropBorderColor(0.4, 0.3, 0.35, 0.8)
|
|
end
|
|
if A and A.text then
|
|
btn.label:SetTextColor(A.text[1], A.text[2], A.text[3])
|
|
else
|
|
btn.label:SetTextColor(0.8, 0.8, 0.8)
|
|
end
|
|
end
|
|
end
|
|
|
|
if reportFrame.whisperRow then
|
|
if reportChannel == "WHISPER" then
|
|
reportFrame.whisperRow:Show()
|
|
else
|
|
reportFrame.whisperRow:Hide()
|
|
end
|
|
end
|
|
|
|
-- Reposition elements below whisper row
|
|
local linesY = (reportChannel == "WHISPER") and -118 or -92
|
|
if reportFrame.linesRow then
|
|
reportFrame.linesRow:ClearAllPoints()
|
|
reportFrame.linesRow:SetPoint("TOPLEFT", reportFrame.titleBar, "BOTTOMLEFT", 8, linesY)
|
|
reportFrame.linesRow:SetPoint("RIGHT", reportFrame, "RIGHT", -12, 0)
|
|
end
|
|
if reportFrame.sendBtn then
|
|
reportFrame.sendBtn:ClearAllPoints()
|
|
reportFrame.sendBtn:SetPoint("TOPLEFT", reportFrame.titleBar, "BOTTOMLEFT", 60, linesY - 36)
|
|
end
|
|
|
|
local frameH = (reportChannel == "WHISPER") and 230 or 200
|
|
reportFrame:SetHeight(frameH)
|
|
end
|
|
|
|
local function GetSegmentName(win)
|
|
if not win then return "" end
|
|
local idx = win.segmentIndex or 1
|
|
if idx == 0 then return L["Total"] end
|
|
if idx == 1 then return L["Current"] end
|
|
local segList = DataStore:GetSegmentList()
|
|
for _, s in ipairs(segList) do
|
|
if s.index == idx then return s.name end
|
|
end
|
|
return "Segment " .. idx
|
|
end
|
|
|
|
local function DoSendReport()
|
|
if not reportLinesFn then return end
|
|
|
|
local lines = reportLinesFn(reportCount)
|
|
if not lines or table.getn(lines) == 0 then
|
|
local chatHex = (SFrames.ActiveTheme and SFrames.ActiveTheme.accentHex) or "ffFF88AA"
|
|
DEFAULT_CHAT_FRAME:AddMessage("|c" .. chatHex .. "Nanami DPS|r: " .. L["No Report Data"])
|
|
return
|
|
end
|
|
|
|
local channel = reportChannel
|
|
local target = nil
|
|
if channel == "WHISPER" then
|
|
target = reportFrame.whisperEdit:GetText()
|
|
if not target or NanamiDPS.trim(target) == "" then
|
|
reportFrame.whisperEdit:SetFocus()
|
|
return
|
|
end
|
|
target = NanamiDPS.trim(target)
|
|
end
|
|
|
|
local header = reportHeaderFn and reportHeaderFn() or "Nanami DPS:"
|
|
SendChatMessage(header, channel, nil, target)
|
|
for _, line in ipairs(lines) do
|
|
SendChatMessage(line, channel, nil, target)
|
|
end
|
|
|
|
local chatHex = (SFrames.ActiveTheme and SFrames.ActiveTheme.accentHex) or "ffFF88AA"
|
|
DEFAULT_CHAT_FRAME:AddMessage("|c" .. chatHex .. "Nanami DPS|r: " .. L["Report Sent"])
|
|
reportFrame:Hide()
|
|
end
|
|
|
|
local function EnsureReportFrame()
|
|
if reportFrame then return reportFrame end
|
|
|
|
local A = SFrames.ActiveTheme
|
|
|
|
reportFrame = CreateFrame("Frame", "NanamiDPSReportFrame", UIParent)
|
|
reportFrame:SetWidth(280)
|
|
reportFrame:SetHeight(200)
|
|
reportFrame:SetPoint("CENTER", UIParent, "CENTER", 0, 50)
|
|
reportFrame:SetMovable(true)
|
|
reportFrame:EnableMouse(true)
|
|
reportFrame:SetClampedToScreen(true)
|
|
reportFrame:SetFrameStrata("DIALOG")
|
|
|
|
SFrames:CreateRoundBackdrop(reportFrame)
|
|
|
|
-- Title bar
|
|
local titleBar = CreateFrame("Frame", nil, reportFrame)
|
|
titleBar:SetHeight(22)
|
|
titleBar:SetPoint("TOPLEFT", reportFrame, "TOPLEFT", 4, -4)
|
|
titleBar:SetPoint("TOPRIGHT", reportFrame, "TOPRIGHT", -4, -4)
|
|
titleBar:SetBackdrop({
|
|
bgFile = "Interface\\Buttons\\WHITE8X8",
|
|
tile = false, edgeSize = 0,
|
|
})
|
|
if A and A.headerBg then
|
|
titleBar:SetBackdropColor(A.headerBg[1], A.headerBg[2], A.headerBg[3], A.headerBg[4] or 0.98)
|
|
else
|
|
titleBar:SetBackdropColor(0.06, 0.06, 0.08, 0.98)
|
|
end
|
|
titleBar:EnableMouse(true)
|
|
titleBar:RegisterForDrag("LeftButton")
|
|
titleBar:SetScript("OnDragStart", function() reportFrame:StartMoving() end)
|
|
titleBar:SetScript("OnDragStop", function() reportFrame:StopMovingOrSizing() end)
|
|
reportFrame.titleBar = titleBar
|
|
|
|
local accentHex = (A and A.accentHex) or "ffFF88AA"
|
|
local titleText = SFrames:CreateFontString(titleBar, 11, "LEFT")
|
|
titleText:SetPoint("LEFT", titleBar, "LEFT", 8, 0)
|
|
titleText:SetPoint("RIGHT", titleBar, "RIGHT", -28, 0)
|
|
titleText:SetText("|c" .. accentHex .. "Nanami|r DPS - " .. L["Report to Chat"])
|
|
|
|
local closeBtn = CreateFrame("Button", nil, titleBar)
|
|
closeBtn:SetWidth(18)
|
|
closeBtn:SetHeight(18)
|
|
closeBtn:SetPoint("RIGHT", titleBar, "RIGHT", -3, 0)
|
|
local closeIcon = SFrames:CreateIcon(closeBtn, "close", 12)
|
|
closeIcon:SetPoint("CENTER", 0, 0)
|
|
closeBtn:SetScript("OnClick", function() reportFrame:Hide() end)
|
|
closeBtn:SetScript("OnEnter", function() closeIcon:SetVertexColor(1, 0.3, 0.3) end)
|
|
closeBtn:SetScript("OnLeave", function() closeIcon:SetVertexColor(1, 1, 1) end)
|
|
|
|
-- Info line: module + segment
|
|
reportFrame.infoText = SFrames:CreateFontString(reportFrame, 9, "LEFT")
|
|
reportFrame.infoText:SetPoint("TOPLEFT", titleBar, "BOTTOMLEFT", 8, -8)
|
|
reportFrame.infoText:SetPoint("RIGHT", reportFrame, "RIGHT", -12, 0)
|
|
if A and A.text then
|
|
reportFrame.infoText:SetTextColor(A.text[1], A.text[2], A.text[3])
|
|
end
|
|
|
|
-- Channel section header
|
|
local channelLabel = SFrames:CreateFontString(reportFrame, 9, "LEFT")
|
|
channelLabel:SetPoint("TOPLEFT", titleBar, "BOTTOMLEFT", 8, -28)
|
|
if A and A.sectionTitle then
|
|
channelLabel:SetTextColor(A.sectionTitle[1], A.sectionTitle[2], A.sectionTitle[3])
|
|
end
|
|
channelLabel:SetText(L["Channel"])
|
|
|
|
-- Channel buttons (4 per row, 2 rows)
|
|
reportFrame.channelBtns = {}
|
|
local btnW = 60
|
|
local btnH = 18
|
|
local btnGap = 3
|
|
local col, row = 0, 0
|
|
|
|
for _, ch in ipairs(CHANNEL_LIST) do
|
|
local channelId = ch.id
|
|
local channelKey = ch.key
|
|
|
|
local btn = CreateFrame("Button", nil, reportFrame)
|
|
btn:SetWidth(btnW)
|
|
btn:SetHeight(btnH)
|
|
btn:SetPoint("TOPLEFT", titleBar, "BOTTOMLEFT",
|
|
8 + col * (btnW + btnGap),
|
|
-44 - row * (btnH + btnGap))
|
|
|
|
btn:SetBackdrop({
|
|
bgFile = "Interface\\Buttons\\WHITE8X8",
|
|
edgeFile = "Interface\\Buttons\\WHITE8X8",
|
|
tile = false, edgeSize = 1,
|
|
insets = { left = 1, right = 1, top = 1, bottom = 1 },
|
|
})
|
|
|
|
btn.label = SFrames:CreateFontString(btn, 9, "CENTER")
|
|
btn.label:SetAllPoints()
|
|
btn.label:SetText(L[channelKey])
|
|
btn.channelId = channelId
|
|
|
|
btn:SetScript("OnClick", function()
|
|
reportChannel = channelId
|
|
UpdateReportChannelButtons()
|
|
end)
|
|
|
|
btn:SetScript("OnEnter", function()
|
|
if channelId ~= reportChannel then
|
|
if A and A.buttonHoverBg then
|
|
this:SetBackdropColor(A.buttonHoverBg[1], A.buttonHoverBg[2], A.buttonHoverBg[3], 0.3)
|
|
else
|
|
this:SetBackdropColor(0.3, 0.3, 0.3, 0.2)
|
|
end
|
|
end
|
|
end)
|
|
btn:SetScript("OnLeave", function()
|
|
if channelId ~= reportChannel then
|
|
if A and A.buttonBg then
|
|
this:SetBackdropColor(A.buttonBg[1], A.buttonBg[2], A.buttonBg[3], A.buttonBg[4] or 0.9)
|
|
else
|
|
this:SetBackdropColor(0.15, 0.08, 0.12, 0.9)
|
|
end
|
|
end
|
|
end)
|
|
|
|
table.insert(reportFrame.channelBtns, btn)
|
|
col = col + 1
|
|
if col >= 4 then
|
|
col = 0
|
|
row = row + 1
|
|
end
|
|
end
|
|
|
|
-- Whisper target row (hidden by default)
|
|
reportFrame.whisperRow = CreateFrame("Frame", nil, reportFrame)
|
|
reportFrame.whisperRow:SetHeight(22)
|
|
reportFrame.whisperRow:SetPoint("TOPLEFT", titleBar, "BOTTOMLEFT", 8, -88)
|
|
reportFrame.whisperRow:SetPoint("RIGHT", reportFrame, "RIGHT", -12, 0)
|
|
|
|
local whisperLabel = SFrames:CreateFontString(reportFrame.whisperRow, 9, "LEFT")
|
|
whisperLabel:SetPoint("LEFT", reportFrame.whisperRow, "LEFT", 0, 0)
|
|
whisperLabel:SetWidth(40)
|
|
if A and A.sectionTitle then
|
|
whisperLabel:SetTextColor(A.sectionTitle[1], A.sectionTitle[2], A.sectionTitle[3])
|
|
end
|
|
whisperLabel:SetText(L["Whisper"] .. ":")
|
|
|
|
reportFrame.whisperEdit = CreateFrame("EditBox", "NanamiDPSWhisperTarget", reportFrame.whisperRow)
|
|
reportFrame.whisperEdit:SetPoint("LEFT", whisperLabel, "RIGHT", 4, 0)
|
|
reportFrame.whisperEdit:SetPoint("RIGHT", reportFrame.whisperRow, "RIGHT", 0, 0)
|
|
reportFrame.whisperEdit:SetHeight(18)
|
|
reportFrame.whisperEdit:SetFontObject(ChatFontNormal)
|
|
reportFrame.whisperEdit:SetAutoFocus(false)
|
|
reportFrame.whisperEdit:SetMaxLetters(50)
|
|
reportFrame.whisperEdit:SetBackdrop({
|
|
bgFile = "Interface\\Buttons\\WHITE8X8",
|
|
edgeFile = "Interface\\Buttons\\WHITE8X8",
|
|
tile = false, edgeSize = 1,
|
|
insets = { left = 1, right = 1, top = 1, bottom = 1 },
|
|
})
|
|
if A and A.checkBg then
|
|
reportFrame.whisperEdit:SetBackdropColor(A.checkBg[1], A.checkBg[2], A.checkBg[3], A.checkBg[4] or 0.9)
|
|
else
|
|
reportFrame.whisperEdit:SetBackdropColor(0.12, 0.06, 0.09, 0.9)
|
|
end
|
|
if A and A.buttonBorder then
|
|
reportFrame.whisperEdit:SetBackdropBorderColor(A.buttonBorder[1], A.buttonBorder[2], A.buttonBorder[3], A.buttonBorder[4] or 0.8)
|
|
else
|
|
reportFrame.whisperEdit:SetBackdropBorderColor(0.4, 0.3, 0.35, 0.8)
|
|
end
|
|
reportFrame.whisperEdit:SetTextInsets(4, 4, 0, 0)
|
|
reportFrame.whisperEdit:SetScript("OnEscapePressed", function() this:ClearFocus() end)
|
|
reportFrame.whisperEdit:SetScript("OnEnterPressed", function()
|
|
this:ClearFocus()
|
|
DoSendReport()
|
|
end)
|
|
reportFrame.whisperRow:Hide()
|
|
|
|
-- Lines count row
|
|
reportFrame.linesRow = CreateFrame("Frame", nil, reportFrame)
|
|
reportFrame.linesRow:SetHeight(22)
|
|
reportFrame.linesRow:SetPoint("TOPLEFT", titleBar, "BOTTOMLEFT", 8, -92)
|
|
reportFrame.linesRow:SetPoint("RIGHT", reportFrame, "RIGHT", -12, 0)
|
|
|
|
local linesLabel = SFrames:CreateFontString(reportFrame.linesRow, 9, "LEFT")
|
|
linesLabel:SetPoint("LEFT", reportFrame.linesRow, "LEFT", 0, 0)
|
|
linesLabel:SetWidth(36)
|
|
if A and A.sectionTitle then
|
|
linesLabel:SetTextColor(A.sectionTitle[1], A.sectionTitle[2], A.sectionTitle[3])
|
|
end
|
|
linesLabel:SetText(L["Report Lines"] .. ":")
|
|
|
|
local countText = SFrames:CreateFontString(reportFrame.linesRow, 10, "CENTER")
|
|
countText:SetWidth(30)
|
|
|
|
local minusBtn = CreateFrame("Button", nil, reportFrame.linesRow)
|
|
minusBtn:SetWidth(20)
|
|
minusBtn:SetHeight(18)
|
|
minusBtn:SetPoint("LEFT", linesLabel, "RIGHT", 4, 0)
|
|
minusBtn:SetBackdrop({
|
|
bgFile = "Interface\\Buttons\\WHITE8X8",
|
|
edgeFile = "Interface\\Buttons\\WHITE8X8",
|
|
tile = false, edgeSize = 1,
|
|
insets = { left = 1, right = 1, top = 1, bottom = 1 },
|
|
})
|
|
if A and A.buttonBg then
|
|
minusBtn:SetBackdropColor(A.buttonBg[1], A.buttonBg[2], A.buttonBg[3], A.buttonBg[4] or 0.9)
|
|
else
|
|
minusBtn:SetBackdropColor(0.15, 0.08, 0.12, 0.9)
|
|
end
|
|
if A and A.buttonBorder then
|
|
minusBtn:SetBackdropBorderColor(A.buttonBorder[1], A.buttonBorder[2], A.buttonBorder[3], A.buttonBorder[4] or 0.8)
|
|
else
|
|
minusBtn:SetBackdropBorderColor(0.4, 0.3, 0.35, 0.8)
|
|
end
|
|
local minusLabel = SFrames:CreateFontString(minusBtn, 10, "CENTER")
|
|
minusLabel:SetAllPoints()
|
|
minusLabel:SetText("-")
|
|
|
|
countText:SetPoint("LEFT", minusBtn, "RIGHT", 2, 0)
|
|
countText:SetText(tostring(reportCount))
|
|
if A and A.accent then
|
|
countText:SetTextColor(A.accent[1], A.accent[2], A.accent[3])
|
|
end
|
|
|
|
local plusBtn = CreateFrame("Button", nil, reportFrame.linesRow)
|
|
plusBtn:SetWidth(20)
|
|
plusBtn:SetHeight(18)
|
|
plusBtn:SetPoint("LEFT", countText, "RIGHT", 2, 0)
|
|
plusBtn:SetBackdrop({
|
|
bgFile = "Interface\\Buttons\\WHITE8X8",
|
|
edgeFile = "Interface\\Buttons\\WHITE8X8",
|
|
tile = false, edgeSize = 1,
|
|
insets = { left = 1, right = 1, top = 1, bottom = 1 },
|
|
})
|
|
if A and A.buttonBg then
|
|
plusBtn:SetBackdropColor(A.buttonBg[1], A.buttonBg[2], A.buttonBg[3], A.buttonBg[4] or 0.9)
|
|
else
|
|
plusBtn:SetBackdropColor(0.15, 0.08, 0.12, 0.9)
|
|
end
|
|
if A and A.buttonBorder then
|
|
plusBtn:SetBackdropBorderColor(A.buttonBorder[1], A.buttonBorder[2], A.buttonBorder[3], A.buttonBorder[4] or 0.8)
|
|
else
|
|
plusBtn:SetBackdropBorderColor(0.4, 0.3, 0.35, 0.8)
|
|
end
|
|
local plusLabel = SFrames:CreateFontString(plusBtn, 10, "CENTER")
|
|
plusLabel:SetAllPoints()
|
|
plusLabel:SetText("+")
|
|
|
|
minusBtn:SetScript("OnClick", function()
|
|
reportCount = math.max(1, reportCount - 1)
|
|
countText:SetText(tostring(reportCount))
|
|
end)
|
|
plusBtn:SetScript("OnClick", function()
|
|
reportCount = math.min(25, reportCount + 1)
|
|
countText:SetText(tostring(reportCount))
|
|
end)
|
|
|
|
local function StepperHover(btn)
|
|
btn:SetScript("OnEnter", function()
|
|
if A and A.buttonHoverBg then
|
|
this:SetBackdropColor(A.buttonHoverBg[1], A.buttonHoverBg[2], A.buttonHoverBg[3], 0.3)
|
|
else
|
|
this:SetBackdropColor(0.3, 0.3, 0.3, 0.2)
|
|
end
|
|
if A and A.accent then
|
|
this:SetBackdropBorderColor(A.accent[1], A.accent[2], A.accent[3], 0.9)
|
|
end
|
|
end)
|
|
btn:SetScript("OnLeave", function()
|
|
if A and A.buttonBg then
|
|
this:SetBackdropColor(A.buttonBg[1], A.buttonBg[2], A.buttonBg[3], A.buttonBg[4] or 0.9)
|
|
else
|
|
this:SetBackdropColor(0.15, 0.08, 0.12, 0.9)
|
|
end
|
|
if A and A.buttonBorder then
|
|
this:SetBackdropBorderColor(A.buttonBorder[1], A.buttonBorder[2], A.buttonBorder[3], A.buttonBorder[4] or 0.8)
|
|
else
|
|
this:SetBackdropBorderColor(0.4, 0.3, 0.35, 0.8)
|
|
end
|
|
end)
|
|
end
|
|
StepperHover(minusBtn)
|
|
StepperHover(plusBtn)
|
|
|
|
-- Send Report button
|
|
reportFrame.sendBtn = CreateFrame("Button", nil, reportFrame)
|
|
reportFrame.sendBtn:SetWidth(160)
|
|
reportFrame.sendBtn:SetHeight(24)
|
|
reportFrame.sendBtn:SetPoint("TOPLEFT", titleBar, "BOTTOMLEFT", 60, -128)
|
|
reportFrame.sendBtn:SetBackdrop({
|
|
bgFile = "Interface\\Buttons\\WHITE8X8",
|
|
edgeFile = "Interface\\Buttons\\WHITE8X8",
|
|
tile = false, edgeSize = 1,
|
|
insets = { left = 1, right = 1, top = 1, bottom = 1 },
|
|
})
|
|
if A and A.accent then
|
|
reportFrame.sendBtn:SetBackdropColor(A.accent[1] * 0.25, A.accent[2] * 0.25, A.accent[3] * 0.25, 0.95)
|
|
reportFrame.sendBtn:SetBackdropBorderColor(A.accent[1], A.accent[2], A.accent[3], 0.8)
|
|
else
|
|
reportFrame.sendBtn:SetBackdropColor(0.2, 0.1, 0.15, 0.95)
|
|
reportFrame.sendBtn:SetBackdropBorderColor(1, 0.53, 0.67, 0.8)
|
|
end
|
|
|
|
local sendLabel = SFrames:CreateFontString(reportFrame.sendBtn, 10, "CENTER")
|
|
sendLabel:SetAllPoints()
|
|
sendLabel:SetText(L["Send Report"])
|
|
if A and A.accent then
|
|
sendLabel:SetTextColor(A.accent[1], A.accent[2], A.accent[3])
|
|
end
|
|
|
|
reportFrame.sendBtn:SetScript("OnClick", function()
|
|
DoSendReport()
|
|
end)
|
|
reportFrame.sendBtn:SetScript("OnEnter", function()
|
|
if A and A.accent then
|
|
this:SetBackdropColor(A.accent[1] * 0.4, A.accent[2] * 0.4, A.accent[3] * 0.4, 0.95)
|
|
else
|
|
this:SetBackdropColor(0.35, 0.15, 0.25, 0.95)
|
|
end
|
|
end)
|
|
reportFrame.sendBtn:SetScript("OnLeave", function()
|
|
if A and A.accent then
|
|
this:SetBackdropColor(A.accent[1] * 0.25, A.accent[2] * 0.25, A.accent[3] * 0.25, 0.95)
|
|
else
|
|
this:SetBackdropColor(0.2, 0.1, 0.15, 0.95)
|
|
end
|
|
end)
|
|
|
|
UpdateReportChannelButtons()
|
|
reportFrame:Hide()
|
|
return reportFrame
|
|
end
|
|
|
|
local function ShowReportPanel(anchorFrame)
|
|
local frame = EnsureReportFrame()
|
|
|
|
frame:ClearAllPoints()
|
|
if anchorFrame and anchorFrame:GetRight() and (anchorFrame:GetRight() + 290) < GetScreenWidth() then
|
|
frame:SetPoint("TOPLEFT", anchorFrame, "TOPRIGHT", 4, 0)
|
|
else
|
|
frame:SetPoint("TOPRIGHT", anchorFrame, "TOPLEFT", -4, 0)
|
|
end
|
|
|
|
UpdateReportChannelButtons()
|
|
|
|
if frame:IsShown() then
|
|
frame:Hide()
|
|
else
|
|
frame:Show()
|
|
end
|
|
end
|
|
|
|
function Window:ShowReport(sourceWindow)
|
|
local frame = EnsureReportFrame()
|
|
reportSourceWin = sourceWindow
|
|
|
|
local mod = NanamiDPS.modules[sourceWindow.activeModuleName]
|
|
local modName = mod and mod:GetName() or sourceWindow.activeModuleName
|
|
local segName = GetSegmentName(sourceWindow)
|
|
local accentHex = (SFrames.ActiveTheme and SFrames.ActiveTheme.accentHex) or "ffFF88AA"
|
|
frame.infoText:SetText("|c" .. accentHex .. modName .. "|r - " .. segName)
|
|
|
|
reportHeaderFn = function()
|
|
return "Nanami DPS - " .. segName .. " " .. modName .. ":"
|
|
end
|
|
reportLinesFn = function(count)
|
|
local seg = DataStore:GetSegment(sourceWindow.segmentIndex or 1)
|
|
if mod and mod.GetReportLines then
|
|
return mod:GetReportLines(seg, count)
|
|
end
|
|
return {}
|
|
end
|
|
|
|
ShowReportPanel(sourceWindow)
|
|
end
|
|
|
|
function Window:ShowReportCustom(anchorFrame, infoStr, headerFn, linesFn)
|
|
local frame = EnsureReportFrame()
|
|
reportSourceWin = nil
|
|
reportHeaderFn = headerFn
|
|
reportLinesFn = linesFn
|
|
frame.infoText:SetText(infoStr)
|
|
|
|
ShowReportPanel(anchorFrame)
|
|
end
|