local mod = StarTip:NewModule("Position", "AceEvent-3.0", "AceHook-3.0") mod.name = "Positioning" local LibTimer = LibStub("LibScriptableUtilsTimer-1.0") local Evaluator = LibStub("LibScriptableUtilsEvaluator-1.0") local _G = _G local GameTooltip = _G.GameTooltip local StarTip = _G.StarTip local UIParent = _G.UIParent local self = mod local L = StarTip.L local square = {L["Left"], L["Right"], L["Top"], L["Bottom"], L["Offscreen"]} local squareDict = {[L["Left"]] = 1, [L["Right"]] = 2, [L["Top"]] = 3, [L["Bottom"]] = 4, [L["Offscreen"]] = 5} local squareNames = {"LEFT", "RIGHT", "TOP", "BOTTOM"} local defaults = { profile = { inCombat = 1, anchor = 1, unitFrames = 1, other = 1, inCombatXOffset = 10, inCombatYOffset = 0, anchorXOffset = 10, anchorYOffset = 0, unitFramesXOffset = 10, unitFramesYOffset = 0, otherXOffset = 10, otherYOffset = 0, defaultUnitTooltipPos = 5, anchorScript = [[ -- Modify locals x and y.They're set to mouse cursor position to start. local mod = StarTip:GetModule("Position") x = x + mod.db.profile.anchorXOffset y = y + mod.db.profile.anchorYOffset ]], inCombatScript = [[ -- Modify locals x and y.They're set to mouse cursor position to start. local mod = StarTip:GetModule("Position") x = x + mod.db.profile.inCombatXOffset y = y + mod.db.profile.inCombatYOffset ]], unitFramesScript = [[ -- Modify locals x and y.They're set to mouse cursor position to start. local mod = StarTip:GetModule("Position") x = x + mod.db.profile.unitFramesXOffset y = y + mod.db.profile.unitFramesYOffset ]], otherScript = [[ -- Modify locals x and y.They're set to mouse cursor position to start. local mod = StarTip:GetModule("Position") x = x + mod.db.profile.otherXOffset y = y + mod.db.profile.otherYOffset ]], } } mod.defaults = defaults local selections = {} for i, v in ipairs(StarTip.anchorText) do selections[i] = v end selections[#selections+1] = "Hide" local get = function(info) return self.db.profile[info[#info]] end local set = function(info,v) self.db.profile[info[#info]] = v end local inputGet = function(info) return tostring(self.db.profile[info[#info]] or 0) end local inputSet = function(info, v) self.db.profile[info[#info]] = tonumber(v) end local minX = -math.floor(GetScreenWidth()/5 + 0.5) * 5 local minY = -math.floor(GetScreenHeight()/5 + 0.5) * 5 local maxX = math.floor(GetScreenWidth()/5 + 0.5) * 5 local maxY = math.floor(GetScreenHeight()/5 + 0.5) * 5 local options = { anchor = { name = L["World Units"], desc = L["Where to anchor the tooltip when mousing over world characters"], type = "select", values = selections, get = get, set = set, order = 4 }, anchorXOffset = { name = format(L["X-axis offset: %d-%d"], minX, maxX), desc = L["The x-axis offset used to position the tooltip in relationship to the anchor point"], type = "input", pattern = "%d", get = inputGet, set = inputSet, validate = function(info, val) val = tonumber(val) return val >= minX and val <= maxX end, order = 5 }, anchorYOffset = { name = format(L["Y-axis offset: %d-%d"], minY, maxY), desc = L["The y-axis offset used to position the tooltip in relationship to the anchor point"], type = "input", pattern = "%d", get = inputGet, set = inputSet, validate = function(info, val) val = tonumber(val) return val >= minY and val <= maxY end, order = 6 }, inCombatHeader = { name = "", type = "header", order = 7 }, inCombat = { name = L["In Combat"], desc = L["Where to anchor the world unit tooltip while in combat"], type = "select", values = selections, get = get, set = set, order = 8 }, inCombatXOffset = { name = format(L["X-axis offset: %d-%d"], minX, maxX), desc = L["The x-axis offset used to position the tooltip in relationship to the anchor point"], type = "input", pattern = "%d", get = inputGet, set = inputSet, validate = function(info, val) val = tonumber(val) return val >= minX and val <= maxX end, order = 9 }, inCombatYOffset = { name = format(L["Y-axis offset: %d-%d"], minY, maxY), desc = L["The y-axis offset used to position the tooltip in relationship to the anchor point"], type = "input", pattern = "%d", get = inputGet, set = inputSet, validate = function(info, val) val = tonumber(val) return val >= minY and val <= maxX end, order = 10, }, unitFramesHeader = { name = "", type = "header", order = 11 }, unitFrames = { name = L["Unit Frames"], desc = L["Where to anchor the tooltip when mousing over a unit frame"], type = "select", values = selections, get = get, set = set, order = 12 }, unitFramesXOffset = { name = format(L["X-axis offset: %d-%d"], minX, maxX), desc = L["The x-axis offset used to position the tooltip in relationship to the anchor point"], type = "input", pattern = "%d", get = inputGet, set = inputSet, validate = function(info, val) val = tonumber(val) return val >= minX and val <= maxX end, order = 13 }, unitFramesYOffset = { name = format(L["Y-axis offset: %d-%d"], minY, maxY), desc = L["The y-axis offset used to position the tooltip in relationship to the anchor point"], type = "input", pattern = "%d", get = inputGet, set = inputSet, validate = function(info, val) val = tonumber(val) return val >= minY and val <= maxY end, order = 14 }, otherHeader = { name = "", type = "header", order = 15 }, other = { name = L["Other tooltips"], desc = L["Where to anchor most other tooltips"], type = "select", values = selections, get = get, set = set, order = 16 }, otherXOffset = { name = format(L["X-axis offset: %d-%d"], minX, maxX), desc = L["The x-axis offset used to position the tooltip in relationship to the anchor point"], type = "input", pattern = "%d", get = inputGet, set = inputSet, validate = function(info, val) val = tonumber(val) return val >= minX and val <= maxX end, order = 17 }, otherYOffset = { name = format(L["Y-axis offset: %d-%d"], minY, maxY), desc = L["The y-axis offset used to position the tooltip in relationship to the anchor point"], type = "input", pattern = "%d", get = inputGet, set = inputSet, validate = function(info, val) val = tonumber(val) return val >= minY and val <= maxY end, order = 18 }, defaultUnitTooltipPos = { name = "Position UI Tooltip", type = "select", values = square, get = function() return mod.db.profile.defaultUnitTooltipPos end, set = function(info, val) mod.db.profile.defaultUnitTooltipPos = val end, order = 20 }, anchorScript = { name = "Gametooltip Non-Unit", type = "input", multiline = true, width = "full", get = function() return mod.db.profile.anchorScript end, set = function(info, val) mod.db.profile.anchorScript = val end, order = 21 }, inCombatScript = { name = "Gametooltip Is-Unit", type = "input", multiline = true, width = "full", get = function() return mod.db.profile.inCombatScript end, set = function(info, val) mod.db.profile.inCombatScript = val end, order = 22 }, unitFramesScript = { name = "Tooltip Main", type = "input", multiline = true, width = "full", get = function() return mod.db.profile.unitFramesScript end, set = function(info, val) mod.db.profile.unitFramesScript = val end, order = 23 }, otherScript = { name = "Other objects", type = "input", multiline = true, width = "full", get = function() return mod.db.profile.otherScript end, set = function(info, val) mod.db.profile.otherScript = val end, order = 24 } } function mod:OnInitialize() self.db = StarTip.db:RegisterNamespace(self:GetName(), defaults) StarTip:SetOptionsDisabled(options, true) self.timer = LibTimer:New("Position.timer ", 100, false, positionTooltip) end function mod:OnEnable() self:RegisterEvent("REGEN_DISABLED") self:RegisterEvent("REGEN_ENABLED") self:SecureHook("GameTooltip_SetDefaultAnchor") StarTip:SetOptionsDisabled(options, false) end function mod:OnDisable() self:UnregisterEvent("REGEN_DISABLED") self:UnregisterEvent("REGEN_ENABLED") self:Unhook("GameTooltip_SetDefaultAnchor") StarTip:SetOptionsDisabled(options, true) end function mod:GetOptions() return options end local currentOwner local currentThis local getIndex = function() local index = self.db.profile.other if GameTooltip:GetUnit() and UnitExists(StarTip.unit) then if InCombatLockdown() then index = self.db.profile.inCombat elseif currentOwner == UIParent then index = self.db.profile.anchor else index = self.db.profile.unitFrames end else index = self.db.profile.other end return index end function mod:GetPosition(x, y) local environment = StarTip.environment environment.x = x environment.y = y if currentOwner == UIParent then if UnitExists(StarTip.unit) then if InCombatLockdown() then Evaluator.ExecuteCode(environment, "StarTip.Position.inCombat", self.db.profile.inCombatScript) else Evaluator.ExecuteCode(environment, "StarTip.Position.inCombat", self.db.profile.anchorScript) end else Evaluator.ExecuteCode(environment, "StarTip.Position.other", self.db.profile.otherScript) end else if UnitExists(StarTip.unit) then Evaluator.ExecuteCode(environment, "StarTip.Position.other", self.db.profile.unitFramesScript) else Evaluator.ExecuteCode(environment, "StarTip.Position.other", self.db.profile.otherScript) end end return environment.x, environment.y end local function hideGameTooltip() GameTooltip:ClearAllPoints() GameTooltip:SetClampRectInsets(10000, 0, 0, 0) GameTooltip:SetPoint("RIGHT", UIParent, "LEFT") end local isUnitTooltip local currentAnchor = "BOTTOM" local positionTooltip = function() local envirnment = StarTip.environment local effScale = GameTooltip:GetEffectiveScale() local x, y = GetCursorPosition() environment.anchorFrame = UIParent x, y = mod:GetPosition(x, y) -- execute user script local index = getIndex(environment.anchorFrame) local anchor = environment.anchor or StarTip.opposites[StarTip.anchors[index]:sub(8)] local relative = environment.anchorRelative or "BOTTOMLEFT" environment.anchor = false environment.anchorRelative = false if not isUnitTooltip then GameTooltip:ClearAllPoints() GameTooltip:SetPoint(anchor, environment.anchorFrame, relative, x / effScale, y / effScale) end if UnitExists(StarTip.unit or "mouseover") then if mod.db.profile.defaultUnitTooltipPos == 5 then hideGameTooltip() else local pos = squareNames[self.db.profile.defaultUnitTooltipPos] GameTooltip:ClearAllPoints() GameTooltip:SetPoint(StarTip.opposites[pos], StarTip.tooltipMain, pos) end end end local positionMainTooltip = function() local environment = StarTip.environment environment.anchorFrame = UIParent local x, y = GetCursorPosition() x, y = mod:GetPosition(x, y) -- execute user script local index = getIndex(environment.anchorFrame) local anchor = environment.anchor or StarTip.opposites[StarTip.anchors[index]:sub(8)] local relative = environment.anchorRelative or "BOTTOMLEFT" environment.anchor = false environment.anchorRelative = false local tooltip = StarTip.tooltipMain local effScale = tooltip:GetEffectiveScale() tooltip:ClearAllPoints() tooltip:SetPoint(anchor, UIParent, anchorRelative, x / effScale, y / effScale) end local updateTimer = LibTimer:New("Position timer", 30, true, positionTooltip) local fakeUpdateTimer = LibTimer:New("Position fake timer", 30, true, positionMainTooltip) --[[ mod:SetOffsets = function() if currentOwner == UIParent then if UnitExists(StarTip.unit) then if InCombatLockdown() then xoffset = self.db.profile.inCombatXOffset yoffset = self.db.profile.inCombatYOffset else xoffset = self.db.profile.anchorXOffset yoffset = self.db.profile.anchorYOffset end else xoffset = self.db.profile.otherXOffset yoffset = self.db.profile.otherYOffset end else if UnitExists(StarTip.unit) then xoffset = self.db.profile.unitFramesXOffset yoffset = self.db.profile.unitFramesYOffset else xoffset = self.db.profile.otherXOffset yoffset = self.db.profile.otherYOffset end end end ]] local function delayAnchor() local this = currentThis local owner = currentOwner local index = getIndex(owner) if index == #selections then this:Hide() return elseif StarTip.anchors[index]:find("^CURSOR_") then currentAnchor = StarTip.opposites[StarTip.anchors[index]:sub(8)] isUnitTooltip = false if GameTooltip:GetUnit() then isUnitTooltip = true fakeUpdateTimer:Start() positionMainTooltip() end updateTimer:Start() positionTooltip() elseif GameTooltip:GetUnit() then fakeUpdateTimer:Stop() updateTimer:Stop() StarTip.envirnoment.x = 0 StarTip.environment.y = 0 Evaluator.ExecuteCode(StarTip.environment, "StarTip.Position", mod.db.profile.anchorScript) StarTip.tooltipMain:ClearAllPoints() StarTip.tooltipMain:SetPoint(StarTip.anchors[index], UIParent, StarTip.anchors[index], StarTip.environment.x, StarTip.environment.y) else fakeUpdateTimer:Stop() updateTimer:Stop() GameTooltip:ClearAllPoints() StarTip.environment.x = 0 StarTip.environment.y = 0 StarTip.environment.i = index Evaluator.ExecuteCode(StarTip.environment, "StarTip.Position", mod.db.profile.otherScript) index = StarTip.envirnment.i local anchor = StarTip.anchor or StarTip.anchors[index] local relative = StarTip.anchorRelative or anchor GameTooltip:SetPoint(anchor, UIParent, anchorRelative, StarTip.environment.x, StarTip.environment.y) StarTip.anchor = false end end local delayTimer = LibTimer:New("Position delay timer", 30, false, delayAnchor) function mod:GameTooltip_SetDefaultAnchor(this, owner) currentOwner = owner currentThis = this local index = getIndex(owner) local ownername = owner:GetName() if owner == MainMenuMicroButton then -- This one is troublesome, so single it out and anchor right away. delayAnchor() else delayTimer:Start() end end function mod:REGEN_DISABLED() if not currentOwner then return end updateFrame:SetScript("OnUpdate", nil) self:GameTooltip_SetDefaultAnchor(GameTooltip, currentOwner) end mod.REGEN_ENABLED = mod.REGEN_DISABLED function mod:OnHide() updateTimer:Stop() delayTimer:Stop() fakeUpdateTimer:Stop() end function mod:SetSpell() local index = getIndex(currentOwner) if StarTip.anchors[index]:find("^CURSOR_") then updateTimer:Stop() positionTooltip() else GameTooltip:ClearAllPoints() GameTooltip:SetPoint(StarTip.anchors[index], UIParent, StarTip.anchors[index], xoffset, yoffset) end end function mod:SetItem() local index = getIndex(currentOwner) if StarTip.anchors[index]:find("^CURSOR_") then updateTimer:Stop() positionTooltip() else GameTooltip:ClearAllPoints() GameTooltip:SetPoint(StarTip.anchors[index], UIParent, StarTip.anchors[index], xoffset, yoffset) end end function mod:SetUnit() local index = getIndex(currentOwner) if not StarTip.anchors[index]:find("^CURSOR_") then hideGameTooltip() end end