TradeSkillMaster/Core/UI/Elements/ApplicationFrame.lua

683 lines
26 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. --
-- ------------------------------------------------------------------------------ --
--- ApplicationFrame UI Element Class.
-- An application frame is the base frame of all of the TSM UIs. It is a subclass of the @{Frame} class.
-- @classmod ApplicationFrame
local _, TSM = ...
local L = TSM.Include("Locale").GetTable()
local Math = TSM.Include("Util.Math")
local TempTable = TSM.Include("Util.TempTable")
local Color = TSM.Include("Util.Color")
local NineSlice = TSM.Include("Util.NineSlice")
local Table = TSM.Include("Util.Table")
local ScriptWrapper = TSM.Include("Util.ScriptWrapper")
local Theme = TSM.Include("Util.Theme")
local Tooltip = TSM.Include("UI.Tooltip")
local UIElements = TSM.Include("UI.UIElements")
local ApplicationFrame = TSM.Include("LibTSMClass").DefineClass("ApplicationFrame", TSM.UI.Frame)
UIElements.Register(ApplicationFrame)
TSM.UI.ApplicationFrame = ApplicationFrame
local private = {
menuDialogContext = {},
}
local SECONDS_PER_HOUR = 60 * 60
local SECONDS_PER_DAY = 24 * SECONDS_PER_HOUR
local CONTENT_FRAME_OFFSET = 8
local DIALOG_RELATIVE_LEVEL = 18
local HEADER_HEIGHT = 40
local MIN_SCALE = 0.3
local DIALOG_OPACITY_PCT = 65
local MIN_ON_SCREEN_PX = 50
local function NoOp() end
-- ============================================================================
-- Public Class Methods
-- ============================================================================
function ApplicationFrame.__init(self)
self.__super:__init()
self._contentFrame = nil
self._contextTable = nil
self._defaultContextTable = nil
self._isScaling = nil
self._protected = nil
self._minWidth = 0
self._minHeight = 0
self._dialogStack = {}
local frame = self:_GetBaseFrame()
local globalFrameName = tostring(frame)
_G[globalFrameName] = frame
-- insert our frames before other addons (i.e. Skillet) to avoid conflicts
tinsert(UISpecialFrames, 1, globalFrameName)
self._nineSlice = NineSlice.New(frame)
self._nineSlice:SetStyle("outerFrame")
frame.resizeIcon = frame:CreateTexture(nil, "ARTWORK")
frame.resizeIcon:SetPoint("BOTTOMRIGHT")
TSM.UI.TexturePacks.SetTextureAndSize(frame.resizeIcon, "iconPack.14x14/Resize")
frame.resizeBtn = CreateFrame("Button", nil, frame)
frame.resizeBtn:SetAllPoints(frame.resizeIcon)
frame.resizeBtn:RegisterForClicks("LeftButtonUp", "RightButtonUp")
ScriptWrapper.Set(frame.resizeBtn, "OnEnter", private.ResizeButtonOnEnter, self)
ScriptWrapper.Set(frame.resizeBtn, "OnLeave", private.ResizeButtonOnLeave, self)
ScriptWrapper.Set(frame.resizeBtn, "OnMouseDown", private.ResizeButtonOnMouseDown, self)
ScriptWrapper.Set(frame.resizeBtn, "OnMouseUp", private.ResizeButtonOnMouseUp, self)
ScriptWrapper.Set(frame.resizeBtn, "OnClick", private.ResizeButtonOnClick, self)
Theme.RegisterChangeCallback(function()
if self:IsVisible() then
self:Draw()
end
end)
end
function ApplicationFrame.Acquire(self)
self:AddChildNoLayout(UIElements.New("Frame", "titleFrame")
:SetLayout("HORIZONTAL")
:SetHeight(24)
:AddAnchor("TOPLEFT", 8, -8)
:AddAnchor("TOPRIGHT", -8, -8)
:SetBackgroundColor("FRAME_BG")
:AddChild(UIElements.New("Texture", "icon")
:SetMargin(0, 16, 0, 0)
:SetTextureAndSize("uiFrames.SmallLogo")
)
:AddChild(UIElements.New("Text", "title")
:AddAnchor("CENTER")
:SetWidth("AUTO")
:SetFont("BODY_BODY2_BOLD")
:SetTextColor("TEXT_ALT")
)
:AddChild(UIElements.New("Spacer", "spacer"))
:AddChild(UIElements.New("Button", "closeBtn")
:SetBackgroundAndSize("iconPack.24x24/Close/Default")
:SetScript("OnClick", private.CloseButtonOnClick)
)
)
self.__super:Acquire()
local frame = self:_GetBaseFrame()
frame:EnableMouse(true)
frame:SetMovable(true)
frame:SetResizable(true)
frame:RegisterForDrag("LeftButton")
self:SetScript("OnDragStart", private.FrameOnDragStart)
self:SetScript("OnDragStop", private.FrameOnDragStop)
end
function ApplicationFrame.Release(self)
if self._protected then
tinsert(UISpecialFrames, 1, tostring(self:_GetBaseFrame()))
end
self._contentFrame = nil
self._contextTable = nil
self._defaultContextTable = nil
self:_GetBaseFrame():SetMinResize(0, 0)
self:_GetBaseFrame():SetMaxResize(0, 0)
self._isScaling = nil
self._protected = nil
self._minWidth = 0
self._minHeight = 0
self.__super:Release()
end
--- Adds player gold text to the title frame.
-- @tparam ApplicationFrame self The application frame object
-- @treturn ApplicationFrame The application frame object
function ApplicationFrame.AddPlayerGold(self)
local titleFrame = self:GetElement("titleFrame")
titleFrame:AddChildBeforeById(titleFrame:HasChildById("switchBtn") and "switchBtn" or "closeBtn", UIElements.New("PlayerGoldText", "playerGold")
:SetWidth("AUTO")
:SetMargin(0, 8, 0, 0)
)
return self
end
--- Adds the app status icon to the title frame.
-- @tparam ApplicationFrame self The application frame object
-- @treturn ApplicationFrame The application frame object
function ApplicationFrame.AddAppStatusIcon(self)
local color, texture = nil, nil
local appUpdateAge = time() - TSM.GetAppUpdateTime()
local auctionDBRealmTime, auctionDBRegionTime = TSM.AuctionDB.GetAppDataUpdateTimes()
local auctionDBRealmAge = time() - auctionDBRealmTime
local auctionDBRegionAge = time() - auctionDBRegionTime
if appUpdateAge >= 2 * SECONDS_PER_DAY or auctionDBRealmAge > 2 * SECONDS_PER_DAY or auctionDBRegionAge > 2 * SECONDS_PER_DAY then
color = "RED"
texture = "iconPack.14x14/Attention"
elseif appUpdateAge >= 2 * SECONDS_PER_HOUR or auctionDBRealmAge >= 4 * SECONDS_PER_HOUR then
color = "YELLOW"
texture = "iconPack.14x14/Attention"
else
color = "GREEN"
texture = "iconPack.14x14/Checkmark/Circle"
end
local titleFrame = self:GetElement("titleFrame")
titleFrame:AddChildBeforeById("playerGold", UIElements.New("Button", "playerGold")
:SetBackgroundAndSize(TSM.UI.TexturePacks.GetColoredKey(texture, Theme.GetFeedbackColor(color)))
:SetMargin(0, 8, 0, 0)
:SetTooltip(private.GetAppStatusTooltip)
)
return self
end
--- Adds a switch button to the title frame.
-- @tparam ApplicationFrame self The application frame object
-- @tparam function onClickHandler The handler for the OnClick script for the button
-- @treturn ApplicationFrame The application frame object
function ApplicationFrame.AddSwitchButton(self, onClickHandler)
local titleFrame = self:GetElement("titleFrame")
titleFrame:AddChildBeforeById("closeBtn", UIElements.New("ActionButton", "switchBtn")
:SetSize(95, 20)
:SetMargin(0, 8, 0, 0)
:SetFont("BODY_BODY3_MEDIUM")
:SetText(L["WOW UI"])
:SetScript("OnClick", onClickHandler)
)
return self
end
function ApplicationFrame.SetProtected(self, protected)
self._protected = protected
local globalFrameName = tostring(self:_GetBaseFrame())
if protected then
Table.RemoveByValue(UISpecialFrames, globalFrameName)
else
if not Table.KeyByValue(UISpecialFrames, globalFrameName) then
-- insert our frames before other addons (i.e. Skillet) to avoid conflicts
tinsert(UISpecialFrames, 1, globalFrameName)
end
end
return self
end
--- Sets the title text.
-- @tparam ApplicationFrame self The application frame object
-- @tparam string title The title text
-- @treturn ApplicationFrame The application frame object
function ApplicationFrame.SetTitle(self, title)
local titleFrame = self:GetElement("titleFrame")
titleFrame:GetElement("title"):SetText(title)
titleFrame:Draw()
return self
end
--- Sets the content frame.
-- @tparam ApplicationFrame self The application frame object
-- @tparam Frame frame The frame's content frame
-- @treturn ApplicationFrame The application frame object
function ApplicationFrame.SetContentFrame(self, frame)
assert(frame:__isa(TSM.UI.Frame))
frame:WipeAnchors()
frame:AddAnchor("TOPLEFT", CONTENT_FRAME_OFFSET, -HEADER_HEIGHT)
frame:AddAnchor("BOTTOMRIGHT", -CONTENT_FRAME_OFFSET, CONTENT_FRAME_OFFSET)
frame:SetPadding(2)
frame:SetBorderColor("ACTIVE_BG", 2)
self._contentFrame = frame
self:AddChildNoLayout(frame)
return self
end
--- Sets the context table.
-- This table can be used to preserve position and size information across lifecycles of the application frame and even
-- WoW sessions if it's within the settings DB.
-- @tparam ApplicationFrame self The application frame object
-- @tparam table tbl The context table
-- @tparam table defaultTbl Default values (required attributes: `width`, `height`, `centerX`, `centerY`)
-- @treturn ApplicationFrame The application frame object
function ApplicationFrame.SetContextTable(self, tbl, defaultTbl)
assert(defaultTbl.width > 0 and defaultTbl.height > 0)
assert(defaultTbl.centerX and defaultTbl.centerY)
tbl.width = tbl.width or defaultTbl.width
tbl.height = tbl.height or defaultTbl.height
tbl.centerX = tbl.centerX or defaultTbl.centerX
tbl.centerY = tbl.centerY or defaultTbl.centerY
tbl.scale = tbl.scale or defaultTbl.scale
self._contextTable = tbl
self._defaultContextTable = defaultTbl
return self
end
--- Sets the context table from a settings object.
-- @tparam ApplicationFrame self The application frame object
-- @tparam Settings settings The settings object
-- @tparam string key The setting key
-- @treturn ApplicationFrame The application frame object
function ApplicationFrame.SetSettingsContext(self, settings, key)
return self:SetContextTable(settings[key], settings:GetDefaultReadOnly(key))
end
--- Sets the minimum size the application frame can be resized to.
-- @tparam ApplicationFrame self The application frame object
-- @tparam number minWidth The minimum width
-- @tparam number minHeight The minimum height
-- @treturn ApplicationFrame The application frame object
function ApplicationFrame.SetMinResize(self, minWidth, minHeight)
self._minWidth = minWidth
self._minHeight = minHeight
return self
end
--- Shows a dialog frame.
-- @tparam ApplicationFrame self The application frame object
-- @tparam Element frame The element to show in a dialog
-- @param context The context to set on the dialog frame
function ApplicationFrame.ShowDialogFrame(self, frame, context)
local dialogFrame = UIElements.New("Frame", "_dialog_"..random())
:SetRelativeLevel(DIALOG_RELATIVE_LEVEL * (#self._dialogStack + 1))
:SetBackgroundColor(Color.GetFullBlack():GetOpacity(DIALOG_OPACITY_PCT))
:AddAnchor("TOPLEFT")
:AddAnchor("BOTTOMRIGHT")
:SetMouseEnabled(true)
:SetMouseWheelEnabled(true)
:SetContext(context)
:SetScript("OnMouseWheel", NoOp)
:SetScript("OnMouseUp", private.DialogOnMouseUp)
:SetScript("OnHide", private.DialogOnHide)
:AddChildNoLayout(frame)
tinsert(self._dialogStack, dialogFrame)
self._contentFrame:AddChildNoLayout(dialogFrame)
dialogFrame:Show()
dialogFrame:Draw()
end
--- Show a confirmation dialog.
-- @tparam ApplicationFrame self The application frame object
-- @tparam string title The title of the dialog
-- @tparam string subTitle The sub-title of the dialog
-- @tparam function callback The callback for when the dialog is closed
-- @tparam[opt] varag ... Arguments to pass to the callback
function ApplicationFrame.ShowConfirmationDialog(self, title, subTitle, callback, ...)
local context = TempTable.Acquire(...)
context.callback = callback
local frame = UIElements.New("Frame", "frame")
:SetLayout("VERTICAL")
:SetSize(328, 158)
:SetPadding(12, 12, 8, 12)
:AddAnchor("CENTER")
:SetBackgroundColor("FRAME_BG", true)
:SetMouseEnabled(true)
:AddChild(UIElements.New("Frame", "header")
:SetLayout("HORIZONTAL")
:SetHeight(24)
:AddChild(UIElements.New("Text", "title")
:SetHeight(20)
:SetMargin(32, 8, 0, 0)
:SetFont("BODY_BODY2_BOLD")
:SetJustifyH("CENTER")
:SetText(title)
)
:AddChild(UIElements.New("Button", "closeBtn")
:SetBackgroundAndSize("iconPack.24x24/Close/Default")
:SetScript("OnClick", private.DialogCancelBtnOnClick)
)
)
:AddChild(UIElements.New("Text", "desc")
:SetMargin(0, 0, 16, 16)
:SetFont("BODY_BODY3")
:SetJustifyH("LEFT")
:SetJustifyV("TOP")
:SetText(subTitle)
)
:AddChild(UIElements.New("ActionButton", "confirmBtn")
:SetHeight(24)
:SetText(L["Confirm"])
:SetScript("OnClick", private.DialogConfirmBtnOnClick)
)
self:ShowDialogFrame(frame, context)
end
--- Show a dialog triggered by a "more" button.
-- @tparam ApplicationFrame self The application frame object
-- @tparam Button moreBtn The "more" button
-- @tparam function iter A dialog menu row iterator with the following fields: `index, text, callback`
function ApplicationFrame.ShowMoreButtonDialog(self, moreBtn, iter)
local frame = UIElements.New("PopupFrame", "moreDialog")
:SetLayout("VERTICAL")
:SetWidth(200)
:SetPadding(0, 0, 8, 4)
:AddAnchor("TOPRIGHT", moreBtn:_GetBaseFrame(), "BOTTOM", 22, -16)
local numRows = 0
for i, text, callback in iter do
frame:AddChild(UIElements.New("Button", "row"..i)
:SetHeight(20)
:SetFont("BODY_BODY2_MEDIUM")
:SetText(text)
:SetScript("OnClick", callback)
)
numRows = numRows + 1
end
frame:SetHeight(12 + numRows * 20)
self:ShowDialogFrame(frame)
end
--- Show a menu dialog.
-- @tparam ApplicationFrame self The application frame object
-- @tparam string frame The frame to anchor the dialog to
-- @tparam function iter A menu row iterator with the following fields: `index, text, subIter`
-- @param context Context to pass to the iter / subIter
-- @tparam function clickCallback The function to be called when a menu row is clicked
-- @tparam boolean flip Flip the anchor to the other side
function ApplicationFrame.ShowMenuDialog(self, frame, iter, context, clickCallback, flip)
wipe(private.menuDialogContext)
private.menuDialogContext.context = context
private.menuDialogContext.clickCallback = clickCallback
self:ShowDialogFrame(private.CreateMenuDialogFrame("_menuDialog", iter)
:AddAnchor(flip and "TOPRIGHT" or "TOPLEFT", frame, flip and "BOTTOMRIGHT" or "BOTTOMLEFT", 2, -4)
)
end
--- Hides the current dialog.
-- @tparam ApplicationFrame self The application frame object
function ApplicationFrame.HideDialog(self)
local dialogFrame = tremove(self._dialogStack)
if not dialogFrame then
return
end
dialogFrame:GetParentElement():RemoveChild(dialogFrame)
dialogFrame:Hide()
dialogFrame:Release()
end
function ApplicationFrame.Draw(self)
local frame = self:_GetBaseFrame()
frame:SetToplevel(true)
frame:Raise()
self._nineSlice:SetVertexColor(Theme.GetColor("FRAME_BG"):GetFractionalRGBA())
-- update the size if it's less than the set min size
assert(self._minWidth > 0 and self._minHeight > 0)
self._contextTable.width = max(self._contextTable.width, self._minWidth)
self._contextTable.height = max(self._contextTable.height, self._minHeight)
self._contextTable.scale = max(self._contextTable.scale, MIN_SCALE)
-- set the frame size from the contextTable
self:SetScale(self._contextTable.scale)
self:SetSize(self._contextTable.width, self._contextTable.height)
-- make sure at least 50px of the frame is on the screen and offset by at least 1 scaled pixel to fix some rendering issues
local maxAbsCenterX = (UIParent:GetWidth() / self._contextTable.scale + self._contextTable.width) / 2 - MIN_ON_SCREEN_PX
local maxAbsCenterY = (UIParent:GetHeight() / self._contextTable.scale + self._contextTable.height) / 2 - MIN_ON_SCREEN_PX
local effectiveScale = UIParent:GetEffectiveScale()
if self._contextTable.centerX < 0 then
self._contextTable.centerX = min(max(self._contextTable.centerX, -maxAbsCenterX), -effectiveScale)
else
self._contextTable.centerX = max(min(self._contextTable.centerX, maxAbsCenterX), effectiveScale)
end
if self._contextTable.centerY < 0 then
self._contextTable.centerY = min(max(self._contextTable.centerY, -maxAbsCenterY), -effectiveScale)
else
self._contextTable.centerY = max(min(self._contextTable.centerY, maxAbsCenterY), effectiveScale)
end
-- adjust the position of the frame based on the UI scale to make rendering more consistent
self._contextTable.centerX = Math.Round(self._contextTable.centerX, effectiveScale)
self._contextTable.centerY = Math.Round(self._contextTable.centerY, effectiveScale)
-- set the frame position from the contextTable
self:WipeAnchors()
self:AddAnchor("CENTER", self._contextTable.centerX, self._contextTable.centerY)
self.__super:Draw()
end
-- ============================================================================
-- Private Class Methods
-- ============================================================================
function ApplicationFrame._SavePositionAndSize(self, wasScaling)
local frame = self:_GetBaseFrame()
local parentFrame = frame:GetParent()
local width = frame:GetWidth()
local height = frame:GetHeight()
if wasScaling then
-- the anchor is in our old frame's scale, so convert the parent measurements to our old scale and then the resuslt to our new scale
local scaleAdjustment = width / self._contextTable.width
local frameLeftOffset = frame:GetLeft() - parentFrame:GetLeft() / self._contextTable.scale
self._contextTable.centerX = (frameLeftOffset - (parentFrame:GetWidth() / self._contextTable.scale - width) / 2) / scaleAdjustment
local frameBottomOffset = frame:GetBottom() - parentFrame:GetBottom() / self._contextTable.scale
self._contextTable.centerY = (frameBottomOffset - (parentFrame:GetHeight() / self._contextTable.scale - height) / 2) / scaleAdjustment
self._contextTable.scale = self._contextTable.scale * scaleAdjustment
else
self._contextTable.width = width
self._contextTable.height = height
-- the anchor is in our frame's scale, so convert the parent measurements to our scale
local frameLeftOffset = frame:GetLeft() - parentFrame:GetLeft() / self._contextTable.scale
self._contextTable.centerX = (frameLeftOffset - (parentFrame:GetWidth() / self._contextTable.scale - width) / 2)
local frameBottomOffset = frame:GetBottom() - parentFrame:GetBottom() / self._contextTable.scale
self._contextTable.centerY = (frameBottomOffset - (parentFrame:GetHeight() / self._contextTable.scale - height) / 2)
end
end
function ApplicationFrame._SetResizing(self, resizing)
if resizing then
self:GetElement("titleFrame"):Hide()
self._contentFrame:_GetBaseFrame():SetAlpha(0)
self._contentFrame:_GetBaseFrame():SetFrameStrata("LOW")
self._contentFrame:Draw()
else
self:GetElement("titleFrame"):Show()
self._contentFrame:_GetBaseFrame():SetAlpha(1)
self._contentFrame:_GetBaseFrame():SetFrameStrata(self._strata)
end
end
-- ============================================================================
-- Local Script Handlers
-- ============================================================================
function private.CloseButtonOnClick(button)
button:GetElement("__parent.__parent"):Hide()
end
function private.ResizeButtonOnEnter(self)
local tooltip = L["Click and drag to resize this window."].."\n"..
L["Hold SHIFT while dragging to scale the window instead."].."\n"..
L["Right-Click to reset the window size, scale, and position to their defaults."]
Tooltip.Show(self:_GetBaseFrame().resizeBtn, tooltip, true)
end
function private.ResizeButtonOnLeave(self)
Tooltip.Hide()
end
function private.ResizeButtonOnMouseDown(self, mouseButton)
if mouseButton ~= "LeftButton" then
return
end
self._isScaling = IsShiftKeyDown()
local frame = self:_GetBaseFrame()
local width = frame:GetWidth()
local height = frame:GetHeight()
if self._isScaling then
local minWidth = width * MIN_SCALE / self._contextTable.scale
local minHeight = height * MIN_SCALE / self._contextTable.scale
frame:SetMinResize(minWidth, minHeight)
frame:SetMaxResize(width * 10, height * 10)
else
frame:SetMinResize(self._minWidth, self._minHeight)
frame:SetMaxResize(width * 10, height * 10)
end
self:_SetResizing(true)
frame:StartSizing("BOTTOMRIGHT")
-- force updating the size here, to prevent using cached values from previously opened application frames
frame:SetWidth(width)
frame:SetHeight(height)
end
function private.ResizeButtonOnMouseUp(self, mouseButton)
if mouseButton ~= "LeftButton" then
return
end
self:_GetBaseFrame():StopMovingOrSizing()
self:_SetResizing(false)
self:_SavePositionAndSize(self._isScaling)
self._isScaling = nil
self:Draw()
end
function private.ResizeButtonOnClick(self, mouseButton)
if mouseButton ~= "RightButton" then
return
end
self._contextTable.scale = self._defaultContextTable.scale
self._contextTable.width = self._defaultContextTable.width
self._contextTable.height = self._defaultContextTable.height
self._contextTable.centerX = self._defaultContextTable.centerX
self._contextTable.centerY = self._defaultContextTable.centerY
self:Draw()
end
function private.FrameOnDragStart(self)
self:_GetBaseFrame():StartMoving()
end
function private.FrameOnDragStop(self)
self:_GetBaseFrame():StopMovingOrSizing()
self:_SavePositionAndSize()
self:Draw()
end
function private.DialogOnMouseUp(dialog)
local self = dialog:GetParentElement():GetParentElement()
self:HideDialog()
end
function private.DialogOnHide(dialog)
local context = dialog:GetContext()
if context then
TempTable.Release(context)
end
end
function private.DialogCancelBtnOnClick(button)
local self = button:GetBaseElement()
self:HideDialog()
end
function private.DialogConfirmBtnOnClick(button)
local self = button:GetBaseElement()
local dialogFrame = button:GetParentElement():GetParentElement()
local context = dialogFrame:GetContext()
dialogFrame:SetContext(nil)
self:HideDialog()
context.callback(TempTable.UnpackAndRelease(context))
end
function private.CreateMenuDialogFrame(id, iter)
local frame = UIElements.New("Frame", id)
:SetLayout("VERTICAL")
:SetWidth(180)
:SetPadding(2)
:SetBackgroundColor("PRIMARY_BG_ALT")
:SetBorderColor("ACTIVE_BG_ALT")
local numRows = 0
for i, text, subIter in iter, private.menuDialogContext.context do
frame:AddChild(UIElements.New("Frame", "row"..i)
:SetLayout("HORIZONTAL")
:SetHeight(21)
:SetContext(subIter)
:AddChild(UIElements.New("Button", "btn")
:SetHeight(21)
:SetPadding(8, 0, 0, 0)
:SetFont("BODY_BODY3_MEDIUM")
:SetBackground("PRIMARY_BG_ALT")
:SetHighlightEnabled(true)
:SetJustifyH("LEFT")
:SetIcon(subIter and "iconPack.12x12/Chevron/Right" or nil, subIter and "RIGHT" or nil)
:SetText(text)
:SetContext(i)
:SetScript("OnEnter", subIter and private.MenuDialogRowSubIterOnEnter or private.MenuDialogRowDefaultOnEnter)
:SetScript("OnClick", not subIter and private.MenuDialogRowOnClick or nil)
)
)
numRows = numRows + 1
end
frame:SetHeight(4 + numRows * 21)
return frame
end
function private.MenuDialogRowOnClick(button)
local path = TempTable.Acquire()
tinsert(path, button:GetContext())
local parentFrame = button:GetParentElement():GetParentElement():GetParentElement()
local self = parentFrame:GetBaseElement()
while parentFrame:GetParentElement() ~= self._contentFrame do
local selectedButton = parentFrame:GetContext():GetElement("btn")
tinsert(path, 1, selectedButton:GetContext())
parentFrame = parentFrame:GetParentElement()
end
private.menuDialogContext.clickCallback(button, private.menuDialogContext.context, TempTable.UnpackAndRelease(path))
end
function private.MenuDialogRowDefaultOnEnter(button)
local frame = button:GetParentElement():GetParentElement()
if frame:HasChildById("subFrame") then
local subFrame = frame:GetElement("subFrame")
local prevRow = frame:GetContext()
frame:SetContext(nil)
if prevRow then
prevRow:GetElement("btn"):SetHighlightLocked(false)
end
frame:RemoveChild(subFrame)
subFrame:Release()
frame:Draw()
end
end
function private.MenuDialogRowSubIterOnEnter(button)
private.MenuDialogRowDefaultOnEnter(button)
button:SetHighlightLocked(true)
local row = button:GetParentElement()
local frame = row:GetParentElement()
frame:SetContext(row)
local subFrame = private.CreateMenuDialogFrame("subFrame", row:GetContext())
:AddAnchor("TOPLEFT", button:_GetBaseFrame(), "TOPRIGHT", 4, 2)
frame:AddChildNoLayout(subFrame)
subFrame:Draw()
end
function private.GetAppStatusTooltip()
local tooltipLines = TempTable.Acquire()
tinsert(tooltipLines, format(L["TSM Desktop App Status (%s)"], TSM.GetRegion().."-"..GetRealmName()))
local appUpdateAge = time() - TSM.GetAppUpdateTime()
if appUpdateAge < 2 * SECONDS_PER_HOUR then
tinsert(tooltipLines, Theme.GetFeedbackColor("GREEN"):ColorText(format(L["App Synced %s Ago"], SecondsToTime(appUpdateAge))))
elseif appUpdateAge < 2 * SECONDS_PER_DAY then
tinsert(tooltipLines, Theme.GetFeedbackColor("YELLOW"):ColorText(format(L["App Synced %s Ago"], SecondsToTime(appUpdateAge))))
else
tinsert(tooltipLines, Theme.GetFeedbackColor("RED"):ColorText(L["App Not Synced"]))
end
local auctionDBRealmTime, auctionDBRegionTime = TSM.AuctionDB.GetAppDataUpdateTimes()
local auctionDBRealmAge = time() - auctionDBRealmTime
local auctionDBRegionAge = time() - auctionDBRegionTime
if auctionDBRealmAge < 4 * SECONDS_PER_HOUR then
tinsert(tooltipLines, Theme.GetFeedbackColor("GREEN"):ColorText(format(L["AuctionDB Realm Data is %s Old"], SecondsToTime(auctionDBRealmAge))))
elseif auctionDBRealmAge < 2 * SECONDS_PER_DAY then
tinsert(tooltipLines, Theme.GetFeedbackColor("YELLOW"):ColorText(format(L["AuctionDB Realm Data is %s Old"], SecondsToTime(auctionDBRealmAge))))
else
tinsert(tooltipLines, Theme.GetFeedbackColor("RED"):ColorText(L["No AuctionDB Realm Data"]))
end
if auctionDBRegionAge < 2 * SECONDS_PER_DAY then
tinsert(tooltipLines, Theme.GetFeedbackColor("GREEN"):ColorText(format(L["AuctionDB Region Data is %s Old"], SecondsToTime(auctionDBRegionAge))))
else
tinsert(tooltipLines, Theme.GetFeedbackColor("RED"):ColorText(L["No AuctionDB Region Data"]))
end
return strjoin("\n", TempTable.UnpackAndRelease(tooltipLines)), true, 16
end