TradeSkillMaster/Core/UI/Elements/Graph.lua

616 lines
21 KiB
Lua
Raw Normal View History

2020-11-13 14:13:12 -05:00
-- ------------------------------------------------------------------------------ --
-- TradeSkillMaster --
-- https://tradeskillmaster.com --
-- All Rights Reserved - Detailed license information included with addon. --
-- ------------------------------------------------------------------------------ --
--- Graph UI Element Class.
-- The graph element allows for generating line graphs. It is a subclass of the @{Element} class.
-- @classmod Graph
local _, TSM = ...
local Math = TSM.Include("Util.Math")
local Theme = TSM.Include("Util.Theme")
local ScriptWrapper = TSM.Include("Util.ScriptWrapper")
local Graph = TSM.Include("LibTSMClass").DefineClass("Graph", TSM.UI.Element)
local UIElements = TSM.Include("UI.UIElements")
UIElements.Register(Graph)
TSM.UI.Graph = Graph
local private = {}
local PLOT_X_LABEL_WIDTH = 48
local PLOT_X_LABEL_HEIGHT = 16
local PLOT_X_LABEL_MARGIN = 6
local PLOT_Y_LABEL_WIDTH = 48
local PLOT_Y_LABEL_HEIGHT = 16
local PLOT_Y_LABEL_MARGIN = 4
local PLOT_HIGHLIGHT_TEXT_WIDTH = 80
local PLOT_HIGHLIGHT_TEXT_HEIGHT = 16
local PLOT_X_EXTRA_HIT_RECT = 4
local PLOT_Y_MARGIN = 4
local LINE_THICKNESS = 1
local LINE_THICKNESS_RATIO = 16
local PLOT_MIN_X_LINE_SPACING = PLOT_X_LABEL_WIDTH * 1.5 + 8
local PLOT_MIN_Y_LINE_SPACING = PLOT_Y_LABEL_HEIGHT * 1.5 + 8
local HOVER_LINE_THICKNESS = 1
local MAX_FILL_ALPHA = 0.5
local SELECTION_ALPHA = 0.2
local MAX_PLOT_POINTS = 300
-- ============================================================================
-- Public Class Methods
-- ============================================================================
function Graph.__init(self)
local frame = UIElements.CreateFrame(self, "Frame", nil, nil, TSM.IsShadowlands() and "BackdropTemplate" or nil)
self.__super:__init(frame)
frame:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8X8" })
frame.plot = CreateFrame("Frame", nil, frame, nil)
frame.plot:SetPoint("BOTTOMLEFT", PLOT_Y_LABEL_WIDTH, PLOT_X_LABEL_HEIGHT)
frame.plot:SetPoint("TOPRIGHT", -PLOT_X_EXTRA_HIT_RECT, -PLOT_HIGHLIGHT_TEXT_HEIGHT - PLOT_Y_MARGIN)
frame.plot:SetHitRectInsets(-PLOT_X_EXTRA_HIT_RECT, -PLOT_X_EXTRA_HIT_RECT, 0, 0)
frame.plot:EnableMouse(true)
ScriptWrapper.Set(frame.plot, "OnEnter", private.PlotFrameOnEnter, self)
ScriptWrapper.Set(frame.plot, "OnLeave", private.PlotFrameOnLeave, self)
ScriptWrapper.Set(frame.plot, "OnMouseDown", private.PlotFrameOnMouseDown, self)
ScriptWrapper.Set(frame.plot, "OnMouseUp", private.PlotFrameOnMouseUp, self)
frame.plot.dot = frame.plot:CreateTexture(nil, "ARTWORK", nil, 3)
TSM.UI.TexturePacks.SetTextureAndSize(frame.plot.dot, "uiFrames.HighlightDot")
frame.plot.hoverLine = frame.plot:CreateTexture(nil, "ARTWORK", nil, 2)
frame.plot.hoverLine:SetWidth(HOVER_LINE_THICKNESS)
frame.plot.hoverLine:Hide()
frame.plot.hoverText = frame.plot:CreateFontString()
frame.plot.hoverText:SetSize(PLOT_HIGHLIGHT_TEXT_WIDTH, PLOT_HIGHLIGHT_TEXT_HEIGHT)
frame.plot.hoverText:Hide()
frame.plot.selectionBox = frame.plot:CreateTexture(nil, "ARTWORK", nil, 2)
frame.plot.selectionBox:Hide()
self._usedTextures = {}
self._freeTextures = {}
self._usedFontStrings = {}
self._freeFontStrings = {}
self._xValuesFiltered = {}
self._yLookup = {}
self._yValueFunc = nil
self._xFormatFunc = nil
self._yFormatFunc = nil
self._xStepFunc = nil
self._yStepFunc = nil
self._xMin = nil
self._xMax = nil
self._yMin = nil
self._yMax = nil
self._isMouseOver = false
self._selectionStartX = nil
self._zoomStart = nil
self._zoomEnd = nil
self._onZoomChanged = nil
self._onHoverUpdate = nil
end
function Graph.Release(self)
self:_ReleaseAllTextures()
self:_ReleaseAllFontStrings()
wipe(self._xValuesFiltered)
wipe(self._yLookup)
self._yValueFunc = nil
self._xFormatFunc = nil
self._yFormatFunc = nil
self._xStepFunc = nil
self._yStepFunc = nil
self._xMin = nil
self._xMax = nil
self._yMin = nil
self._yMax = nil
self._isMouseOver = false
self._selectionStartX = nil
self._zoomStart = nil
self._zoomEnd = nil
self._onZoomChanged = nil
self._onHoverUpdate = nil
self.__super:Release()
end
--- Sets the step size of the axes.
-- @tparam Graph self The graph object
-- @tparam function x A function which gets the next x-axis step value
-- @tparam function y A function which gets the next y-axis step value
-- @treturn Graph The graph object
function Graph.SetAxisStepFunctions(self, x, y)
self._xStepFunc = x
self._yStepFunc = y
return self
end
function Graph.SetXRange(self, xMin, xMax, stepInterval)
assert(xMin <= xMax)
self._xMin = xMin
self._xMax = xMax
self._xStepInterval = stepInterval
self._zoomStart = xMin
self._zoomEnd = xMax
return self
end
function Graph.SetZoom(self, zoomStart, zoomEnd)
self._zoomStart = zoomStart
self._zoomEnd = zoomEnd
return self
end
function Graph.GetZoom(self)
return self._zoomStart, self._zoomEnd
end
function Graph.GetXRange(self)
local yMin, yMax = nil, nil
for _, x in ipairs(self._xValuesFiltered) do
local y = self._yValueFunc(x)
yMin = min(yMin or math.huge, y)
yMax = max(yMax or -math.huge, y)
end
return self._xMin, self._xMax
end
function Graph.GetYRange(self)
local yMin, yMax = nil, nil
for _, x in ipairs(self._xValuesFiltered) do
local y = self._yValueFunc(x)
yMin = min(yMin or math.huge, y)
yMax = max(yMax or -math.huge, y)
end
return yMin, yMax
end
function Graph.SetYValueFunction(self, func)
self._yValueFunc = func
return self
end
--- Sets functions for formatting values.
-- @tparam Graph self The graph object
-- @tparam function xFormatFunc A function which is passed an x value and returns a formatted string
-- @tparam function yFormatFunc A function which is passed a y value and returns a formatted string
-- @treturn Graph The graph object
function Graph.SetFormatFunctions(self, xFormatFunc, yFormatFunc)
self._xFormatFunc = xFormatFunc
self._yFormatFunc = yFormatFunc
return self
end
--- Registers a script handler.
-- @tparam ScrollingTable self The graph object
-- @tparam string script The script to register for (supported scripts: `OnZoomChanged`)
-- @tparam function handler The script handler which will be called with the graph object followed by any
-- arguments to the script
-- @treturn Graph The graph object
function Graph.SetScript(self, script, handler)
if script == "OnZoomChanged" then
self._onZoomChanged = handler
elseif script == "OnHoverUpdate" then
self._onHoverUpdate = handler
else
error("Unknown Graph script: "..tostring(script))
end
return self
end
function Graph.Draw(self)
self.__super:Draw()
self:_ReleaseAllTextures()
self:_ReleaseAllFontStrings()
local frame = self:_GetBaseFrame()
frame:SetBackdropColor(Theme.GetColor("PRIMARY_BG"):GetFractionalRGBA())
local plot = frame.plot
plot.hoverText:SetFont(Theme.GetFont("TABLE_TABLE1"):GetWowFont())
plot.hoverText:SetTextColor(Theme.GetColor("INDICATOR_ALT"):GetFractionalRGBA())
local plotWidth = plot:GetWidth()
local plotHeight = plot:GetHeight()
-- update the filtered set of x values to show and the bounds of the plot data
self:_PopulateFilteredData(plotWidth)
-- calculate the min and max y values which should be shown
self._yMin, self._yMax = self._yStepFunc("RANGE", self._yMin, self._yMax, floor(plotHeight / PLOT_MIN_Y_LINE_SPACING))
if Math.IsNan(self._yMax) then
-- this happens when we're resizing the application frame
return
end
-- draw the y axis lines and labels
local prevYAxisOffset = -math.huge
local yAxisValue = self._yMin
while yAxisValue <= self._yMax do
local yAxisOffset = Math.Scale(yAxisValue, self._yMin, self._yMax, 0, plotHeight)
if not prevYAxisOffset or (yAxisOffset - prevYAxisOffset) >= PLOT_MIN_Y_LINE_SPACING then
self:_DrawYAxisLine(yAxisOffset, yAxisValue, plotWidth, plotHeight)
prevYAxisOffset = yAxisOffset
end
yAxisValue = self._yStepFunc("NEXT", yAxisValue, self._yMax)
end
-- draw the x axis lines and labels
local xSuggestedStep = Math.Scale(PLOT_MIN_X_LINE_SPACING, 0, plotWidth, 0, self._zoomEnd - self._zoomStart)
local prevXAxisOffset = -math.huge
local xAxisValue = self._xStepFunc(self._zoomStart, xSuggestedStep)
while xAxisValue <= self._zoomEnd do
local xAxisOffset = Math.Scale(xAxisValue, self._zoomStart, self._zoomEnd, 0, plotWidth)
if not prevXAxisOffset or (xAxisOffset - prevXAxisOffset) > PLOT_MIN_X_LINE_SPACING then
self:_DrawXAxisLine(xAxisOffset, xAxisValue, plotWidth, plotHeight, xSuggestedStep)
prevXAxisOffset = xAxisOffset
end
xAxisValue = self._xStepFunc(xAxisValue, xSuggestedStep)
end
-- draw all the lines
local color = nil
if self._isMouseOver or self._selectionStartX then
color = Theme.GetColor("INDICATOR_ALT")
elseif self._yLookup[self._xValuesFiltered[1]] <= self._yLookup[self._xValuesFiltered[#self._xValuesFiltered]] then
color = Theme.GetFeedbackColor("GREEN")
else
color = Theme.GetFeedbackColor("RED")
end
local xPrev, yPrev = nil, nil
for _, x in ipairs(self._xValuesFiltered) do
local y = self._yLookup[x]
local xCoord = Math.Scale(x, self._zoomStart, self._zoomEnd, 0, plotWidth)
local yCoord = Math.Scale(y, self._yMin, self._yMax, 0, plotHeight)
if xPrev then
self:_DrawFillLine(xPrev, yPrev, xCoord, yCoord, LINE_THICKNESS, plotHeight, color)
end
xPrev = xCoord
yPrev = yCoord
end
end
-- ============================================================================
-- Private Class Methods
-- ============================================================================
function Graph._PopulateFilteredData(self, plotWidth)
wipe(self._xValuesFiltered)
wipe(self._yLookup)
self._yMin = math.huge
self._yMax = -math.huge
local minStep = Math.Ceil((self._zoomEnd - self._zoomStart) / min(plotWidth / 3, MAX_PLOT_POINTS), self._xStepInterval)
local x = self._zoomStart
while x <= self._zoomEnd do
local prevX = self._xValuesFiltered[#self._xValuesFiltered]
if not prevX or x == self._zoomEnd or (x - prevX > minStep and self._zoomEnd - x > minStep) then
-- this is either the first / last point or a middle point which is sufficiently far from the previous and last points
tinsert(self._xValuesFiltered, x)
local y = self._yValueFunc(x)
self._yMin = min(self._yMin, y)
self._yMax = max(self._yMax, y)
self._yLookup[x] = y
end
if x == self._zoomEnd then
break
end
x = min(x + minStep, self._zoomEnd)
end
end
function Graph._DrawYAxisLine(self, yOffset, yValue, plotWidth, plotHeight, ySuggestedStep)
local line = self:_AcquireLine("ARTWORK")
local thickness = LINE_THICKNESS
local textureHeight = thickness * LINE_THICKNESS_RATIO
-- trim the texture a bit on the left/right since it's not completely filled to the edges which is noticeable on long lines
line:SetTexCoord(0.1, 1, 0.1, 0, 0.9, 1, 0.9, 0)
line:SetPoint("BOTTOMLEFT", 0 - thickness / 2, yOffset - textureHeight / 2)
line:SetPoint("TOPRIGHT", line:GetParent(), "BOTTOMLEFT", plotWidth + thickness / 2, yOffset + textureHeight / 2)
line:SetVertexColor(Theme.GetColor("ACTIVE_BG"):GetFractionalRGBA())
line:SetDrawLayer("BACKGROUND", 0)
local text = self:_AcquireFontString(Theme.GetFont("TABLE_TABLE1"))
text:SetJustifyH("RIGHT")
local textYOffset = 0
if PLOT_Y_LABEL_HEIGHT / 2 > yOffset then
text:SetJustifyV("BOTTOM")
textYOffset = max(PLOT_Y_LABEL_HEIGHT / 2 - yOffset, 0)
elseif yOffset + PLOT_Y_LABEL_HEIGHT / 2 > plotHeight then
text:SetJustifyV("TOP")
textYOffset = plotHeight - yOffset - PLOT_Y_LABEL_HEIGHT / 2
else
text:SetJustifyV("MIDDLE")
end
text:SetPoint("RIGHT", line, "LEFT", -PLOT_Y_LABEL_MARGIN, textYOffset)
text:SetSize(PLOT_Y_LABEL_WIDTH, PLOT_Y_LABEL_HEIGHT)
text:SetText(self._yFormatFunc(yValue, ySuggestedStep))
end
function Graph._DrawXAxisLine(self, xOffset, xValue, plotWidth, plotHeight, xSuggestedStep)
local line = self:_AcquireLine("ARTWORK")
local thickness = LINE_THICKNESS
local textureHeight = thickness * LINE_THICKNESS_RATIO
-- trim the texture a bit on the left/right since it's not completely filled to the edges which is noticeable on long lines
line:SetTexCoord(0.9, 1, 0.1, 1, 0.9, 0, 0.1, 0)
line:SetPoint("BOTTOMLEFT", xOffset - textureHeight / 2, thickness / 2)
line:SetPoint("TOPRIGHT", line:GetParent(), "BOTTOMLEFT", xOffset + textureHeight / 2, plotHeight + thickness / 2)
line:SetVertexColor(Theme.GetColor("ACTIVE_BG"):GetFractionalRGBA())
line:SetDrawLayer("BACKGROUND", 0)
local text = self:_AcquireFontString(Theme.GetFont("BODY_BODY3_MEDIUM"))
text:ClearAllPoints()
text:SetJustifyV("TOP")
local textXOffset = 0
if PLOT_X_LABEL_WIDTH / 2 > xOffset then
text:SetJustifyH("LEFT")
textXOffset = max(PLOT_X_LABEL_WIDTH / 2 - xOffset, 0)
elseif xOffset + PLOT_X_LABEL_WIDTH / 2 > plotWidth then
text:SetJustifyH("RIGHT")
textXOffset = plotWidth - xOffset - PLOT_X_LABEL_WIDTH / 2
else
text:SetJustifyH("CENTER")
end
text:SetPoint("TOP", line, "BOTTOM", textXOffset, -PLOT_X_LABEL_MARGIN)
text:SetSize(PLOT_X_LABEL_WIDTH, PLOT_X_LABEL_HEIGHT)
text:SetText(self._xFormatFunc(xValue, xSuggestedStep))
end
function Graph._DrawFillLine(self, xFrom, yFrom, xTo, yTo, thickness, plotHeight, color)
assert(xFrom <= xTo)
local line = self:_AcquireLine("ARTWORK")
local textureHeight = thickness * LINE_THICKNESS_RATIO
local xDiff = xTo - xFrom
local yDiff = yTo - yFrom
local length = sqrt(xDiff * xDiff + yDiff * yDiff)
local sinValue = -yDiff / length
local cosValue = xDiff / length
local sinCosValue = sinValue * cosValue
local aspectRatio = length / textureHeight
local invAspectRatio = textureHeight / length
-- calculate and set tex coords
local LLx, LLy, ULx, ULy, URx, URy, LRx, LRy = nil, nil, nil, nil, nil, nil, nil, nil
if yDiff >= 0 then
LLx = invAspectRatio * sinCosValue
LLy = sinValue * sinValue
LRy = aspectRatio * sinCosValue
LRx = 1 - LLy
ULx = LLy
ULy = 1 - LRy
URx = 1 - LLx
URy = LRx
else
LLx = sinValue * sinValue
LLy = -aspectRatio * sinCosValue
LRx = 1 + invAspectRatio * sinCosValue
LRy = LLx
ULx = 1 - LRx
ULy = 1 - LLx
URy = 1 - LLy
URx = ULy
end
line:SetTexCoord(ULx, ULy, LLx, LLy, URx, URy, LRx, LRy)
-- calculate and set texture anchors
local xCenter = (xFrom + xTo) / 2
local yCenter = (yFrom + yTo) / 2
local halfWidth = (xDiff + invAspectRatio * abs(yDiff) + thickness) / 2
local halfHeight = (abs(yDiff) + invAspectRatio * xDiff + thickness) / 2
line:SetPoint("BOTTOMLEFT", xCenter - halfWidth, yCenter - halfHeight)
line:SetPoint("TOPRIGHT", line:GetParent(), "BOTTOMLEFT", xCenter + halfWidth, yCenter + halfHeight)
local minY = min(yFrom, yTo)
local maxY = max(yFrom, yTo)
local r, g, b, a = color:GetFractionalRGBA()
local barMaxAlpha = Math.Scale(minY, 0, plotHeight, 0, MAX_FILL_ALPHA * a)
local topMaxAlpha = Math.Scale(maxY, 0, plotHeight, 0, MAX_FILL_ALPHA * a)
line:SetVertexColor(r, g, b, a)
local fillTop = self:_AcquireTexture("ARTWORK", -1)
fillTop:SetTexture("Interface\\AddOns\\TradeSkillMaster\\Media\\triangle")
if yFrom < yTo then
fillTop:SetTexCoord(0, 0, 0, 1, 1, 0, 1, 1)
else
fillTop:SetTexCoord(1, 0, 1, 1, 0, 0, 0, 1)
end
fillTop:SetGradientAlpha("VERTICAL", r, g, b, barMaxAlpha, r, g, b, topMaxAlpha)
fillTop:SetPoint("BOTTOMLEFT", xFrom, minY)
fillTop:SetPoint("TOPRIGHT", fillTop:GetParent(), "BOTTOMLEFT", xTo, maxY)
local fillBar = self:_AcquireTexture("ARTWORK", -1)
fillBar:SetTexture("Interface\\Buttons\\WHITE8X8")
fillBar:SetGradientAlpha("VERTICAL", r, g, b, 0, r, g, b, barMaxAlpha)
fillBar:SetPoint("BOTTOMLEFT", xFrom, 0)
fillBar:SetPoint("TOPRIGHT", fillBar:GetParent(), "BOTTOMLEFT", xTo, minY)
return line
end
function Graph._AcquireLine(self, layer, subLayer)
local line = self:_AcquireTexture(layer, subLayer)
line:SetTexture("Interface\\AddOns\\TradeSkillMaster\\Media\\line.tga")
return line
end
function Graph._AcquireTexture(self, layer, subLayer)
local plot = self:_GetBaseFrame().plot
local result = tremove(self._freeTextures) or plot:CreateTexture()
tinsert(self._usedTextures, result)
result:SetParent(plot)
result:Show()
result:SetDrawLayer(layer, subLayer)
return result
end
function Graph._ReleaseAllTextures(self)
while #self._usedTextures > 0 do
local texture = tremove(self._usedTextures)
texture:SetTexture(nil)
texture:SetVertexColor(0, 0, 0, 0)
texture:SetTexCoord(0, 0, 0, 1, 1, 0, 1, 1)
texture:SetWidth(0)
texture:SetHeight(0)
texture:ClearAllPoints()
texture:Hide()
tinsert(self._freeTextures, texture)
end
end
function Graph._AcquireFontString(self, font)
local plot = self:_GetBaseFrame().plot
local result = tremove(self._freeFontStrings) or plot:CreateFontString()
tinsert(self._usedFontStrings, result)
result:SetParent(plot)
result:Show()
result:SetFont(font:GetWowFont())
result:SetTextColor(Theme.GetColor("TEXT"):GetFractionalRGBA())
return result
end
function Graph._ReleaseAllFontStrings(self)
while #self._usedFontStrings > 0 do
local fontString = tremove(self._usedFontStrings)
fontString:SetWidth(0)
fontString:SetHeight(0)
fontString:ClearAllPoints()
fontString:Hide()
tinsert(self._freeFontStrings, fontString)
end
end
function Graph._GetCursorClosestPoint(self)
local plotFrame = self:_GetBaseFrame().plot
local xPos = GetCursorPosition() / plotFrame:GetEffectiveScale()
local fromMin = plotFrame:GetLeft()
local fromMax = plotFrame:GetRight()
-- Convert the cursor position to be relative to the plotted x values
xPos = Math.Scale(Math.Bound(xPos, fromMin, fromMax), fromMin, fromMax, self._zoomStart, self._zoomEnd)
-- Find the closest point to the cursor (based on the x distance)
local closestX, closestY = nil, nil
for _, x in ipairs(self._xValuesFiltered) do
local y = self._yLookup[x]
local xDist = abs(x - xPos)
if not closestX or xDist < abs(closestX - xPos) then
closestX = x
closestY = y
end
end
assert(closestY)
return closestX, closestY
end
function Graph._XValueToPlotCoord(self, xValue)
local plotFrame = self:_GetBaseFrame().plot
return Math.Scale(xValue, self._zoomStart, self._zoomEnd, 0, plotFrame:GetWidth())
end
-- ============================================================================
-- Private Helper Functions
-- ============================================================================
function private.PlotFrameOnEnter(self)
self._isMouseOver = true
self:Draw()
local plotFrame = self:_GetBaseFrame().plot
ScriptWrapper.Set(plotFrame, "OnUpdate", private.PlotFrameOnUpdate, self)
end
function private.PlotFrameOnLeave(self)
self._isMouseOver = false
end
function private.PlotFrameOnUpdate(self)
local plotFrame = self:_GetBaseFrame().plot
local closestX, closestY = self:_GetCursorClosestPoint()
local xCoord = self:_XValueToPlotCoord(closestX)
local yCoord = Math.Scale(closestY, self._yMin, self._yMax, 0, plotFrame:GetHeight())
if self._isMouseOver then
plotFrame.dot:Show()
plotFrame.dot:ClearAllPoints()
plotFrame.dot:SetPoint("CENTER", plotFrame, "BOTTOMLEFT", xCoord, yCoord)
plotFrame.hoverLine:Show()
plotFrame.hoverLine:SetColorTexture(Theme.GetColor("INDICATOR_ALT"):GetFractionalRGBA())
plotFrame.hoverLine:ClearAllPoints()
plotFrame.hoverLine:SetPoint("TOP", plotFrame, "TOPLEFT", xCoord, 0)
plotFrame.hoverLine:SetPoint("BOTTOM", plotFrame, "BOTTOMLEFT", xCoord, 0)
plotFrame.hoverText:Show()
plotFrame.hoverText:SetWidth(1000)
plotFrame.hoverText:SetText(self._yFormatFunc(closestY, nil, true))
local textWidth = plotFrame.hoverText:GetStringWidth()
plotFrame.hoverText:SetWidth(textWidth)
plotFrame.hoverText:ClearAllPoints()
if xCoord - textWidth / 2 < 0 then
plotFrame.hoverText:SetPoint("BOTTOMLEFT", plotFrame, "TOPLEFT", 0, PLOT_Y_MARGIN)
elseif textWidth / 2 + xCoord > plotFrame:GetWidth() then
plotFrame.hoverText:SetPoint("BOTTOMRIGHT", plotFrame, "TOPRIGHT", 0, PLOT_Y_MARGIN)
else
plotFrame.hoverText:SetPoint("BOTTOM", plotFrame, "TOPLEFT", xCoord, PLOT_Y_MARGIN)
end
else
plotFrame.dot:Hide()
plotFrame.hoverLine:Hide()
plotFrame.hoverText:Hide()
end
if self._selectionStartX then
local startXCoord = self:_XValueToPlotCoord(self._selectionStartX)
local selectionMinX = min(startXCoord, xCoord)
local selectionMaxX = max(startXCoord, xCoord)
plotFrame.selectionBox:Show()
local r, g, b, a = Theme.GetColor("INDICATOR_ALT"):GetFractionalRGBA()
assert(a == 1)
plotFrame.selectionBox:SetColorTexture(r, g, b, SELECTION_ALPHA)
plotFrame.selectionBox:ClearAllPoints()
plotFrame.selectionBox:SetPoint("TOPLEFT", plotFrame, selectionMinX, 0)
plotFrame.selectionBox:SetPoint("BOTTOMRIGHT", plotFrame, "BOTTOMLEFT", selectionMaxX, 0)
else
plotFrame.selectionBox:Hide()
end
local isHovered = self._isMouseOver or self._selectionStartX
if not isHovered then
self:Draw()
ScriptWrapper.Clear(plotFrame, "OnUpdate")
end
if self._onHoverUpdate then
self:_onHoverUpdate(isHovered and closestX or nil)
end
end
function private.PlotFrameOnMouseDown(self, mouseButton)
if mouseButton ~= "LeftButton" then
return
end
assert(self._isMouseOver)
self._selectionStartX = self:_GetCursorClosestPoint()
end
function private.PlotFrameOnMouseUp(self, mouseButton)
if mouseButton ~= "LeftButton" then
return
end
local currentX = self:_GetCursorClosestPoint()
local startX = min(self._selectionStartX, currentX)
local endX = max(self._selectionStartX, currentX)
self._selectionStartX = nil
local plotFrame = self:_GetBaseFrame().plot
plotFrame.selectionBox:Hide()
if startX ~= endX and (startX ~= self._zoomStart or endX ~= self._zoomEnd) then
self._zoomStart = startX
self._zoomEnd = endX
self:Draw()
if self._onZoomChanged then
self:_onZoomChanged()
end
end
end