683 lines
26 KiB
Lua
683 lines
26 KiB
Lua
|
-- ------------------------------------------------------------------------------ --
|
||
|
-- 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
|