ElvUI/Core/Movers.lua

489 lines
13 KiB
Lua
Raw Permalink Normal View History

2020-11-13 14:27:50 -05:00
local E, L, V, P, G = unpack(select(2, ...)); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
local Sticky = E.Libs.SimpleSticky
local _G = _G
local type, unpack, pairs, error, ipairs = type, unpack, pairs, error, ipairs
local format, split, find, strupper = format, strsplit, strfind, strupper
local CreateFrame = CreateFrame
local IsShiftKeyDown = IsShiftKeyDown
local InCombatLockdown = InCombatLockdown
local IsControlKeyDown = IsControlKeyDown
local ERR_NOT_IN_COMBAT = ERR_NOT_IN_COMBAT
local hooksecurefunc = hooksecurefunc
E.CreatedMovers = {}
E.DisabledMovers = {}
local function SizeChanged(frame, width, height)
if InCombatLockdown() then return end
frame.mover:SetSize(width, height)
end
local function WidthChanged(frame, width)
if InCombatLockdown() then return end
frame.mover:SetWidth(width)
end
local function HeightChanged(frame, height)
if InCombatLockdown() then return end
frame.mover:SetHeight(height)
end
local function GetPoint(obj)
local point, anchor, secondaryPoint, x, y = obj:GetPoint()
if not anchor then anchor = E.UIParent end
return format('%s,%s,%s,%d,%d', point, anchor:GetName(), secondaryPoint, x and E:Round(x) or 0, y and E:Round(y) or 0)
end
local function GetSettingPoints(name)
local db = E.db.movers and E.db.movers[name]
if db then
local delim = (find(db, '\031') and '\031') or ','
return split(delim, db)
end
end
local function UpdateCoords(self)
local mover = self.child
local x, y, _, nudgePoint, nudgeInversePoint = E:CalculateMoverPoints(mover)
local coordX, coordY = E:GetXYOffset(nudgeInversePoint, 1)
local nudgeFrame = _G.ElvUIMoverNudgeWindow
nudgeFrame:ClearAllPoints()
nudgeFrame:SetPoint(nudgePoint, mover, nudgeInversePoint, coordX, coordY)
E:UpdateNudgeFrame(mover, x, y)
end
function E:SetMoverPoints(name, parent)
local holder = E.CreatedMovers[name]
if not holder then return end
local point1, relativeTo1, relativePoint1, xOffset1, yOffset1 = unpack(holder.parentPoint)
local point2, relativeTo2, relativePoint2, xOffset2, yOffset2 = GetSettingPoints(name)
if not _G[relativeTo2] then -- fallback to the parents original point (on create) if the setting doesn't exist
point2, relativeTo2, relativePoint2, xOffset2, yOffset2 = point1, relativeTo1, relativePoint1, xOffset1, yOffset1
end
if point2 then
holder.mover:ClearAllPoints()
holder.mover:SetPoint(point2, relativeTo2, relativePoint2, xOffset2, yOffset2)
end
if parent then
parent:ClearAllPoints()
parent:SetPoint(point1, parent.mover, 0, 0)
end
end
local isDragging = false
local coordFrame = CreateFrame('Frame')
coordFrame:SetScript('OnUpdate', UpdateCoords)
coordFrame:Hide()
local function HandlePostDrag(self, event)
if self.postdrag and type(self.postdrag) == 'function' then
self.postdrag(self, E:GetScreenQuadrant(self))
end
if event then
self:UnregisterAllEvents()
end
end
local function OnDragStart(self)
if InCombatLockdown() then E:Print(ERR_NOT_IN_COMBAT) return end
if _G.ElvUIGrid then
E:UIFrameFadeIn(_G.ElvUIGrid, 0.75, _G.ElvUIGrid:GetAlpha(), 1)
end
if E.db.general.stickyFrames then
Sticky:StartMoving(self, E.snapBars, self.snapOffset, self.snapOffset, self.snapOffset, self.snapOffset)
else
self:StartMoving()
end
coordFrame.child = self
coordFrame:Show()
isDragging = true
end
local function OnDragStop(self)
if InCombatLockdown() then E:Print(ERR_NOT_IN_COMBAT) return end
if _G.ElvUIGrid and E.ConfigurationMode then
E:UIFrameFadeOut(_G.ElvUIGrid, 0.75, _G.ElvUIGrid:GetAlpha(), 0.4)
end
if E.db.general.stickyFrames then
Sticky:StopMoving(self)
else
self:StopMovingOrSizing()
end
local x2, y2, p2 = E:CalculateMoverPoints(self)
self:ClearAllPoints()
self:SetPoint(p2, E.UIParent, p2, x2, y2)
E:SaveMoverPosition(self.name)
coordFrame.child = nil
coordFrame:Hide()
isDragging = false
HandlePostDrag(self)
self:SetUserPlaced(false)
end
local function OnEnter(self)
if isDragging then return end
for _, frame in pairs(E.CreatedMovers) do
local mover = frame.mover
if mover:IsShown() and mover ~= self then
E:UIFrameFadeOut(mover, 0.75, mover:GetAlpha(), 0.5)
end
end
E.AssignFrameToNudge(self)
coordFrame.child = self
coordFrame:GetScript('OnUpdate')(coordFrame)
self.text:SetTextColor(1, 1, 1)
end
local function OnLeave(self)
if isDragging then return end
for _, frame in pairs(E.CreatedMovers) do
local mover = frame.mover
if mover:IsShown() and mover ~= self then
E:UIFrameFadeIn(mover, 0.75, mover:GetAlpha(), 1)
end
end
self.text:SetTextColor(unpack(E.media.rgbvaluecolor))
end
local function OnMouseUp(_, button)
if button == 'LeftButton' and not isDragging then
_G.ElvUIMoverNudgeWindow:SetShown(not _G.ElvUIMoverNudgeWindow:IsShown())
end
end
local function OnMouseDown(self, button)
if isDragging then
OnDragStop(self)
elseif button == 'RightButton' then
if IsControlKeyDown() and self.textString then
E:ResetMovers(self.textString) --Allow resetting of anchor by Ctrl+RightClick
elseif IsShiftKeyDown() then
self:Hide() --Allow hiding a mover temporarily
elseif self.configString then
E:ToggleOptionsUI(self.configString) --OpenConfig
end
end
end
local function OnMouseWheel(_, delta)
if IsShiftKeyDown() then
E:NudgeMover(delta)
else
E:NudgeMover(nil, delta)
end
end
local function OnShow(self, r, g, b)
if not r then r, g, b = unpack(E.media.rgbvaluecolor) end
self.text:FontTemplate()
self.text:SetTextColor(r, g, b)
self:SetBackdropBorderColor(r, g, b)
self.forcedBorderColors = {r, g, b}
end
local function UpdateColors()
local r, g, b = unpack(E.media.rgbvaluecolor)
for _, holder in pairs(E.CreatedMovers) do
OnShow(holder.mover, r, g, b)
end
end
E.valueColorUpdateFuncs[UpdateColors] = true
local function UpdateMover(name, parent, textString, overlay, snapOffset, postdrag, shouldDisable, configString, perferCorners, ignoreSizeChanged)
if not (name and parent) then return end --If for some reason the parent isnt loaded yet, also require a name
local holder = E.CreatedMovers[name]
if holder.Created then return end
holder.Created = true
if overlay == nil then overlay = true end
local f = CreateFrame('Button', name, E.UIParent, 'BackdropTemplate')
f:SetClampedToScreen(true)
f:RegisterForDrag('LeftButton', 'RightButton')
f:SetFrameLevel(parent:GetFrameLevel() + 1)
f:SetFrameStrata(overlay and 'DIALOG' or 'BACKGROUND')
f:EnableMouseWheel(true)
f:SetMovable(true)
f:SetTemplate('Transparent', nil, nil, true)
f:SetSize(parent:GetSize())
f:Hide()
local fs = f:CreateFontString(nil, 'OVERLAY')
fs:FontTemplate()
fs:SetPoint('CENTER')
fs:SetText(textString or name)
fs:SetJustifyH('CENTER')
fs:SetTextColor(unpack(E.media.rgbvaluecolor))
f:SetFontString(fs)
f.text = fs
f.name = name
f.parent = parent
f.overlay = overlay
f.postdrag = postdrag
f.textString = textString or name
f.snapOffset = snapOffset or -2
f.shouldDisable = shouldDisable
f.configString = configString
f.perferCorners = perferCorners
f.ignoreSizeChanged = ignoreSizeChanged
holder.mover = f
parent.mover = f
E.snapBars[#E.snapBars+1] = f
if not ignoreSizeChanged then
hooksecurefunc(parent, 'SetSize', SizeChanged)
hooksecurefunc(parent, 'SetWidth', WidthChanged)
hooksecurefunc(parent, 'SetHeight', HeightChanged)
end
E:SetMoverPoints(name, parent)
f:SetScript('OnDragStart', OnDragStart)
f:SetScript('OnDragStop', OnDragStop)
f:SetScript('OnEnter', OnEnter)
f:SetScript('OnLeave', OnLeave)
f:SetScript('OnMouseDown', OnMouseDown)
f:SetScript('OnMouseUp', OnMouseUp)
f:SetScript('OnMouseWheel', OnMouseWheel)
f:SetScript('OnShow', OnShow)
f:SetScript('OnEvent', HandlePostDrag)
f:RegisterEvent('PLAYER_ENTERING_WORLD')
end
function E:CalculateMoverPoints(mover, nudgeX, nudgeY)
local centerX, centerY = E.UIParent:GetCenter()
local width = E.UIParent:GetRight()
local x, y = mover:GetCenter()
local point, nudgePoint, nudgeInversePoint = 'BOTTOM', 'BOTTOM', 'TOP'
if y >= centerY then -- TOP: 1080p = 540
point, nudgePoint, nudgeInversePoint = 'TOP', 'TOP', 'BOTTOM'
y = -(E.UIParent:GetTop() - mover:GetTop())
else
y = mover:GetBottom()
end
if x >= (width * 2 / 3) then -- RIGHT: 1080p = 1280
point, nudgePoint, nudgeInversePoint = point..'RIGHT', 'RIGHT', 'LEFT'
x = mover:GetRight() - width
elseif x <= (width / 3) or mover.perferCorners then -- LEFT: 1080p = 640
point, nudgePoint, nudgeInversePoint = point..'LEFT', 'LEFT', 'RIGHT'
x = mover:GetLeft()
else
x = x - centerX
end
--Update coordinates if nudged
x = x + (nudgeX or 0)
y = y + (nudgeY or 0)
return x, y, point, nudgePoint, nudgeInversePoint
end
function E:HasMoverBeenMoved(name)
return E.db.movers and E.db.movers[name]
end
function E:SaveMoverPosition(name)
local holder = E.CreatedMovers[name]
if not holder then return end
if not E.db.movers then E.db.movers = {} end
E.db.movers[name] = GetPoint(holder.mover)
end
function E:SetMoverSnapOffset(name, offset)
local holder = E.CreatedMovers[name]
if not holder then return end
holder.mover.snapOffset = offset or -2
holder.snapoffset = offset or -2
end
function E:SetMoverLayoutPositionPoint(holder, name, parent)
local layout = E.LayoutMoverPositions[E.db.layoutSetting]
local layoutPoint = (layout and layout[name]) or E.LayoutMoverPositions.ALL[name]
holder.layoutPoint = layoutPoint
holder.point = layoutPoint or GetPoint(parent or holder.mover)
if parent then -- CreateMover call
holder.parentPoint = {parent:GetPoint()}
end
end
function E:SaveMoverDefaultPosition(name)
local holder = E.CreatedMovers[name]
if not holder then return end
E:SetMoverLayoutPositionPoint(holder, name)
HandlePostDrag(holder.mover)
end
function E:CreateMover(parent, name, textString, overlay, snapoffset, postdrag, types, shouldDisable, configString, perferCorners, ignoreSizeChanged)
local holder = E.CreatedMovers[name]
if holder == nil then
holder = {}
holder.types = {}
if types then
for _, x in ipairs({split(',', types)}) do
holder.types[x] = true
end
else
holder.types.ALL = true
holder.types.GENERAL = true
end
E:SetMoverLayoutPositionPoint(holder, name, parent)
E.CreatedMovers[name] = holder
end
UpdateMover(name, parent, textString, overlay, snapoffset, postdrag, shouldDisable, configString, perferCorners, ignoreSizeChanged)
end
function E:ToggleMovers(show, which)
self.configMode = show
local upperText = strupper(which)
for _, holder in pairs(E.CreatedMovers) do
local isName = (holder.mover.name == which) or strupper(holder.mover.textString) == upperText
if show and (isName or holder.types[upperText]) then
holder.mover:Show()
else
holder.mover:Hide()
end
end
end
function E:GetMoverHolder(name)
local created = self.CreatedMovers[name]
local disabled = self.DisabledMovers[name]
return created or disabled, not not disabled
end
function E:DisableMover(name)
if self.DisabledMovers[name] then return end
local holder = self.CreatedMovers[name]
if not holder then
error(format('mover %s doesnt exist', name or 'nil'))
end
self.DisabledMovers[name] = {}
for x, y in pairs(holder) do
self.DisabledMovers[name][x] = y
end
if self.configMode then
holder.mover:Hide()
end
self.CreatedMovers[name] = nil
end
function E:EnableMover(name)
if self.CreatedMovers[name] then return end
local holder = self.DisabledMovers[name]
if not holder then
error(format('mover %s doesnt exist', name or 'nil'))
end
self.CreatedMovers[name] = {}
for x, y in pairs(holder) do
self.CreatedMovers[name][x] = y
end
if self.configMode then
holder.mover:Show()
end
self.DisabledMovers[name] = nil
end
function E:ResetMovers(arg)
local all = not arg or arg == ''
if all then self.db.movers = nil end
for name, holder in pairs(E.CreatedMovers) do
if all or (holder.mover and holder.mover.textString == arg) then
local point, anchor, secondaryPoint, x, y = split(',', holder.point)
local frame = holder.mover
if point then
frame:ClearAllPoints()
frame:SetPoint(point, anchor, secondaryPoint, x, y)
end
HandlePostDrag(frame)
if all then
E:SaveMoverPosition(name)
else
if holder.layoutPoint then
E:SaveMoverPosition(name)
elseif self.db.movers then
self.db.movers[name] = nil
end
break
end
end
end
end
--Profile Change
function E:SetMoversPositions()
--E:SetMoversPositions() is the first function called in E:StaggeredUpdateAll().
--Because of that, we can allow ourselves to re-enable all disabled movers here,
--as the subsequent updates to these elements will disable them again if needed.
for name in pairs(E.DisabledMovers) do
local disable = E.DisabledMovers[name].shouldDisable
local shouldDisable = (disable and disable()) or false
if not shouldDisable then E:EnableMover(name) end
end
for name in pairs(E.CreatedMovers) do
E:SetMoverPoints(name)
end
end
function E:SetMoversClampedToScreen(value)
for _, holder in pairs(E.CreatedMovers) do
holder.mover:SetClampedToScreen(value)
end
end
function E:LoadMovers()
for n, t in pairs(E.CreatedMovers) do
UpdateMover(n, t.parent, t.textString, t.overlay, t.snapoffset, t.postdrag, t.shouldDisable, t.configString, t.perferCorners, t.ignoreSizeChanged)
end
end