Quantcast
local mod = StarTip:NewModule("Position", "AceEvent-3.0", "AceHook-3.0")
mod.name = "Positioning"
local LibTimer = LibStub("LibScriptableUtilsTimer-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

	}
}

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

	},
	--[[anchorXOffset = {
		name = "X-axis offset",
		desc = "The x-axis offset used to position the tooltip in relationship to the anchor point",
		type = "range",
		min = minX,
		max = maxX,
		step = 1,
		bigStep = 5,
		get = get,
		set = set,
		order = 5
	},
	anchorYOffset = {
		name = "Y-axis offset",
		desc = "The y-axis offset used to position the tooltip in relationship to the anchor point",
		type = "range",
		min = minY,
		max = maxY,
		step = 1,
		bigStep = 5,
		get = get,
		set = set,
		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,
	},
	--[[inCombatXOffset = {
		name = "X-axis offset",
		desc = "The x-axis offset used to position the tooltip in relationship to the anchor point",
		type = "range",
		min = minX,
		max = maxX,
		step = 1,
		bigStep = 5,
		get = get,
		set = set,
		order = 9
	},
	inCombatYOffset = {
		name = "Y-axis offset",
		desc = "The y-axis offset used to position the tooltip in relationship to the anchor point",
		type = "range",
		min = minY,
		max = maxY,
		step = 1,
		bigStep = 5,
		get = get,
		set = set,
		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
	},
	--[[unitFramesXOffset = {
		name = "X-axis offset",
		desc = "The x-axis offset used to position the tooltip in relationship to the anchor point",
		type = "range",
		min = minX,
		max = maxX,
		step = 1,
		bigStep = 5,
		get = get,
		set = set,
		order = 13
	},
	unitFramesYOffset = {
		name = "Y-axis offset",
		desc = "The y-axis offset used to position the tooltip in relationship to the anchor point",
		type = "range",
		min = minY,
		max = maxY,
		step = 1,
		bigStep = 5,
		get = get,
		set = set,
		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
	},
	--[[otherXOffset = {
		name = "X-axis offset",
		desc = "The x-axis offset used to position the tooltip in relationship to the anchor point",
		type = "range",
		min = minX,
		max = maxX,
		step = 1,
		bigStep = 5,
		get = get,
		set = set,
		order = 17
	},
	otherYOffset = {
		name = "Y-axis offset",
		desc = "The y-axis offset used to position the tooltip in relationship to the anchor point",
		type = "range",
		min = minY,
		max = maxY,
		step = 1,
		bigStep = 5,
		get = get,
		set = set,
		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
	}
}

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 getIndex = function(owner)
	local index
	if GameTooltip:GetUnit() and UnitExists(StarTip.unit) then
		if InCombatLockdown() then
			index = self.db.profile.inCombat
		elseif owner == UIParent then
			index = self.db.profile.anchor
		else
			index = self.db.profile.unitFrames
		end
	else
		index = self.db.profile.other
	end
	return index
end

local function hideGameTooltip()
	GameTooltip:ClearAllPoints()
	GameTooltip:SetClampRectInsets(10000, 0, 0, 0)
	GameTooltip:SetPoint("RIGHT", UIParent, "LEFT")
end

local isUnitTooltip
local updateFrame = CreateFrame("Frame")
local fakeUpdateFrame = CreateFrame("Frame")
local currentAnchor = "BOTTOM"
local xoffset, yoffset = 0, 0
local positionTooltip = function()
	local x, y = GetCursorPosition()

	local effScale = GameTooltip:GetEffectiveScale()

	if not isUnitTooltip then
		GameTooltip:ClearAllPoints()
		GameTooltip:SetPoint(currentAnchor, UIParent, "BOTTOMLEFT", (x + xoffset) / effScale, (y + yoffset) / effScale + 5)
	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 x, y = GetCursorPosition()

	local index = getIndex(UIParent)
	local currentAnchor = StarTip.opposites[StarTip.anchors[index]:sub(8)]
	local tooltip = StarTip.tooltipMain
	local effScale = tooltip:GetEffectiveScale()

	tooltip:ClearAllPoints()
	tooltip:SetPoint(currentAnchor, UIParent, "BOTTOMLEFT",
		(x + xoffset) / effScale, (y + yoffset) / effScale)
end


local setOffsets = function(owner)
	if owner == 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 currentOwner
local currentThis
local delayFrame = CreateFrame("Frame")
local function delayAnchor()
	delayFrame:SetScript("OnUpdate", nil)

	local this = currentThis
	local owner = currentOwner
	setOffsets(owner)
	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
			fakeUpdateFrame:SetScript("OnUpdate", positionMainTooltip)
			positionMainTooltip()
		end
		updateFrame:SetScript("OnUpdate", positionTooltip)
		positionTooltip()
	elseif GameTooltip:GetUnit() then
		updateFrame:SetScript("OnUpdate", nil)
		fakeUpdateFrame:SetScript("OnUpdate", nil)
		StarTip.tooltipMain:ClearAllPoints()
		StarTip.tooltipMain:SetPoint(StarTip.anchors[index], UIParent, StarTip.anchors[index], xoffset, yoffset)
	else
		updateFrame:SetScript("OnUpdate", nil)
		fakeUpdateFrame:SetScript("OnUpdate", nil)
		GameTooltip:ClearAllPoints()
		GameTooltip:SetPoint(StarTip.anchors[index], UIParent, StarTip.anchors[index], xoffset, yoffset)
	end
end

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
		delayFrame:SetScript("OnUpdate", delayAnchor)
	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()
	updateFrame:SetScript("OnUpdate", nil)
	delayFrame:SetScript("OnUpdate", nil)
	fakeUpdateFrame:SetScript("OnUpdate", nil)
end

function mod:SetSpell()
	local index = getIndex(currentOwner)
	if StarTip.anchors[index]:find("^CURSOR_")  then
		updateFrame:SetScript("OnUpdate", nil)
		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
		updateFrame:SetScript("OnUpdate", nil)
		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