----------------------------------------------------------------------- -- LibDBIcon-1.0 -- -- Allows addons to easily create a lightweight minimap icon as an alternative to heavier LDB displays. -- local DBICON10 = "LibDBIcon-1.0" local DBICON10_MINOR = 44 -- Bump on changes if not LibStub then error(DBICON10 .. " requires LibStub.") end local ldb = LibStub("LibDataBroker-1.1", true) if not ldb then error(DBICON10 .. " requires LibDataBroker-1.1.") end local lib = LibStub:NewLibrary(DBICON10, DBICON10_MINOR) if not lib then return end lib.objects = lib.objects or {} lib.callbackRegistered = lib.callbackRegistered or nil lib.callbacks = lib.callbacks or LibStub("CallbackHandler-1.0"):New(lib) lib.notCreated = lib.notCreated or {} lib.radius = lib.radius or 5 local next, Minimap, CreateFrame = next, Minimap, CreateFrame lib.tooltip = lib.tooltip or CreateFrame("GameTooltip", "LibDBIconTooltip", UIParent, "GameTooltipTemplate") local isDraggingButton = false function lib:IconCallback(event, name, key, value) if lib.objects[name] then if key == "icon" then lib.objects[name].icon:SetTexture(value) elseif key == "iconCoords" then lib.objects[name].icon:UpdateCoord() elseif key == "iconR" then local _, g, b = lib.objects[name].icon:GetVertexColor() lib.objects[name].icon:SetVertexColor(value, g, b) elseif key == "iconG" then local r, _, b = lib.objects[name].icon:GetVertexColor() lib.objects[name].icon:SetVertexColor(r, value, b) elseif key == "iconB" then local r, g = lib.objects[name].icon:GetVertexColor() lib.objects[name].icon:SetVertexColor(r, g, value) end end end if not lib.callbackRegistered then ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__icon", "IconCallback") ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__iconCoords", "IconCallback") ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__iconR", "IconCallback") ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__iconG", "IconCallback") ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__iconB", "IconCallback") lib.callbackRegistered = true end local function getAnchors(frame) local x, y = frame:GetCenter() if not x or not y then return "CENTER" end local hhalf = (x > UIParent:GetWidth()*2/3) and "RIGHT" or (x < UIParent:GetWidth()/3) and "LEFT" or "" local vhalf = (y > UIParent:GetHeight()/2) and "TOP" or "BOTTOM" return vhalf..hhalf, frame, (vhalf == "TOP" and "BOTTOM" or "TOP")..hhalf end local function onEnter(self) if isDraggingButton then return end for _, button in next, lib.objects do if button.showOnMouseover then button.fadeOut:Stop() button:SetAlpha(1) end end local obj = self.dataObject if obj.OnTooltipShow then lib.tooltip:SetOwner(self, "ANCHOR_NONE") lib.tooltip:SetPoint(getAnchors(self)) obj.OnTooltipShow(lib.tooltip) lib.tooltip:Show() elseif obj.OnEnter then obj.OnEnter(self) end end local function onLeave(self) lib.tooltip:Hide() if not isDraggingButton then for _, button in next, lib.objects do if button.showOnMouseover then button.fadeOut:Play() end end end local obj = self.dataObject if obj.OnLeave then obj.OnLeave(self) end end -------------------------------------------------------------------------------- local onDragStart, updatePosition do local minimapShapes = { ["ROUND"] = {true, true, true, true}, ["SQUARE"] = {false, false, false, false}, ["CORNER-TOPLEFT"] = {false, false, false, true}, ["CORNER-TOPRIGHT"] = {false, false, true, false}, ["CORNER-BOTTOMLEFT"] = {false, true, false, false}, ["CORNER-BOTTOMRIGHT"] = {true, false, false, false}, ["SIDE-LEFT"] = {false, true, false, true}, ["SIDE-RIGHT"] = {true, false, true, false}, ["SIDE-TOP"] = {false, false, true, true}, ["SIDE-BOTTOM"] = {true, true, false, false}, ["TRICORNER-TOPLEFT"] = {false, true, true, true}, ["TRICORNER-TOPRIGHT"] = {true, false, true, true}, ["TRICORNER-BOTTOMLEFT"] = {true, true, false, true}, ["TRICORNER-BOTTOMRIGHT"] = {true, true, true, false}, } local rad, cos, sin, sqrt, max, min = math.rad, math.cos, math.sin, math.sqrt, math.max, math.min function updatePosition(button, position) local angle = rad(position or 225) local x, y, q = cos(angle), sin(angle), 1 if x < 0 then q = q + 1 end if y > 0 then q = q + 2 end local minimapShape = GetMinimapShape and GetMinimapShape() or "ROUND" local quadTable = minimapShapes[minimapShape] local w = (Minimap:GetWidth() / 2) + lib.radius local h = (Minimap:GetHeight() / 2) + lib.radius if quadTable[q] then x, y = x*w, y*h else local diagRadiusW = sqrt(2*(w)^2)-10 local diagRadiusH = sqrt(2*(h)^2)-10 x = max(-w, min(x*diagRadiusW, w)) y = max(-h, min(y*diagRadiusH, h)) end button:SetPoint("CENTER", Minimap, "CENTER", x, y) end end local function onClick(self, b) if self.dataObject.OnClick then self.dataObject.OnClick(self, b) end end local function onMouseDown(self) self.isMouseDown = true self.icon:UpdateCoord() end local function onMouseUp(self) self.isMouseDown = false self.icon:UpdateCoord() end do local deg, atan2 = math.deg, math.atan2 local function onUpdate(self) local mx, my = Minimap:GetCenter() local px, py = GetCursorPosition() local scale = Minimap:GetEffectiveScale() px, py = px / scale, py / scale local pos = 225 if self.db then pos = deg(atan2(py - my, px - mx)) % 360 self.db.minimapPos = pos else pos = deg(atan2(py - my, px - mx)) % 360 self.minimapPos = pos end updatePosition(self, pos) end function onDragStart(self) self:LockHighlight() self.isMouseDown = true self.icon:UpdateCoord() self:SetScript("OnUpdate", onUpdate) isDraggingButton = true lib.tooltip:Hide() for _, button in next, lib.objects do if button.showOnMouseover then button.fadeOut:Stop() button:SetAlpha(1) end end end end local function onDragStop(self) self:SetScript("OnUpdate", nil) self.isMouseDown = false self.icon:UpdateCoord() self:UnlockHighlight() isDraggingButton = false for _, button in next, lib.objects do if button.showOnMouseover then button.fadeOut:Play() end end end local defaultCoords = {0, 1, 0, 1} local function updateCoord(self) local coords = self:GetParent().dataObject.iconCoords or defaultCoords local deltaX, deltaY = 0, 0 if not self:GetParent().isMouseDown then deltaX = (coords[2] - coords[1]) * 0.05 deltaY = (coords[4] - coords[3]) * 0.05 end self:SetTexCoord(coords[1] + deltaX, coords[2] - deltaX, coords[3] + deltaY, coords[4] - deltaY) end local function createButton(name, object, db) local button = CreateFrame("Button", "LibDBIcon10_"..name, Minimap) button.dataObject = object button.db = db button:SetFrameStrata("MEDIUM") if button.SetFixedFrameStrata then -- Classic support button:SetFixedFrameStrata(true) end button:SetFrameLevel(8) if button.SetFixedFrameLevel then -- Classic support button:SetFixedFrameLevel(true) end button:SetSize(31, 31) button:RegisterForClicks("anyUp") button:RegisterForDrag("LeftButton") button:SetHighlightTexture(136477) --"Interface\\Minimap\\UI-Minimap-ZoomButton-Highlight" local overlay = button:CreateTexture(nil, "OVERLAY") overlay:SetSize(53, 53) overlay:SetTexture(136430) --"Interface\\Minimap\\MiniMap-TrackingBorder" overlay:SetPoint("TOPLEFT") local background = button:CreateTexture(nil, "BACKGROUND") background:SetSize(20, 20) background:SetTexture(136467) --"Interface\\Minimap\\UI-Minimap-Background" background:SetPoint("TOPLEFT", 7, -5) local icon = button:CreateTexture(nil, "ARTWORK") icon:SetSize(17, 17) icon:SetTexture(object.icon) icon:SetPoint("TOPLEFT", 7, -6) button.icon = icon button.isMouseDown = false local r, g, b = icon:GetVertexColor() icon:SetVertexColor(object.iconR or r, object.iconG or g, object.iconB or b) icon.UpdateCoord = updateCoord icon:UpdateCoord() button:SetScript("OnEnter", onEnter) button:SetScript("OnLeave", onLeave) button:SetScript("OnClick", onClick) if not db or not db.lock then button:SetScript("OnDragStart", onDragStart) button:SetScript("OnDragStop", onDragStop) end button:SetScript("OnMouseDown", onMouseDown) button:SetScript("OnMouseUp", onMouseUp) button.fadeOut = button:CreateAnimationGroup() local animOut = button.fadeOut:CreateAnimation("Alpha") animOut:SetOrder(1) animOut:SetDuration(0.2) animOut:SetFromAlpha(1) animOut:SetToAlpha(0) animOut:SetStartDelay(1) button.fadeOut:SetToFinalAlpha(true) lib.objects[name] = button if lib.loggedIn then updatePosition(button, db and db.minimapPos) if not db or not db.hide then button:Show() else button:Hide() end end lib.callbacks:Fire("LibDBIcon_IconCreated", button, name) -- Fire 'Icon Created' callback end -- We could use a metatable.__index on lib.objects, but then we'd create -- the icons when checking things like :IsRegistered, which is not necessary. local function check(name) if lib.notCreated[name] then createButton(name, lib.notCreated[name][1], lib.notCreated[name][2]) lib.notCreated[name] = nil end end -- Wait a bit with the initial positioning to let any GetMinimapShape addons -- load up. if not lib.loggedIn then local f = CreateFrame("Frame") f:SetScript("OnEvent", function(f) for _, button in next, lib.objects do updatePosition(button, button.db and button.db.minimapPos) if not button.db or not button.db.hide then button:Show() else button:Hide() end end lib.loggedIn = true f:SetScript("OnEvent", nil) end) f:RegisterEvent("PLAYER_LOGIN") end local function getDatabase(name) return lib.notCreated[name] and lib.notCreated[name][2] or lib.objects[name].db end function lib:Register(name, object, db) if not object.icon then error("Can't register LDB objects without icons set!") end if lib.objects[name] or lib.notCreated[name] then error(DBICON10.. ": Object '".. name .."' is already registered.") end if not db or not db.hide then createButton(name, object, db) else lib.notCreated[name] = {object, db} end end function lib:Lock(name) if not lib:IsRegistered(name) then return end if lib.objects[name] then lib.objects[name]:SetScript("OnDragStart", nil) lib.objects[name]:SetScript("OnDragStop", nil) end local db = getDatabase(name) if db then db.lock = true end end function lib:Unlock(name) if not lib:IsRegistered(name) then return end if lib.objects[name] then lib.objects[name]:SetScript("OnDragStart", onDragStart) lib.objects[name]:SetScript("OnDragStop", onDragStop) end local db = getDatabase(name) if db then db.lock = nil end end function lib:Hide(name) if not lib.objects[name] then return end lib.objects[name]:Hide() end function lib:Show(name) check(name) local button = lib.objects[name] if button then button:Show() updatePosition(button, button.db and button.db.minimapPos or button.minimapPos) end end function lib:IsRegistered(name) return (lib.objects[name] or lib.notCreated[name]) and true or false end function lib:Refresh(name, db) check(name) local button = lib.objects[name] if db then button.db = db end updatePosition(button, button.db and button.db.minimapPos or button.minimapPos) if not button.db or not button.db.hide then button:Show() else button:Hide() end if not button.db or not button.db.lock then button:SetScript("OnDragStart", onDragStart) button:SetScript("OnDragStop", onDragStop) else button:SetScript("OnDragStart", nil) button:SetScript("OnDragStop", nil) end end function lib:GetMinimapButton(name) return lib.objects[name] end do local function OnMinimapEnter() if isDraggingButton then return end for _, button in next, lib.objects do if button.showOnMouseover then button.fadeOut:Stop() button:SetAlpha(1) end end end local function OnMinimapLeave() if isDraggingButton then return end for _, button in next, lib.objects do if button.showOnMouseover then button.fadeOut:Play() end end end Minimap:HookScript("OnEnter", OnMinimapEnter) Minimap:HookScript("OnLeave", OnMinimapLeave) function lib:ShowOnEnter(name, value) local button = lib.objects[name] if button then if value then button.showOnMouseover = true button.fadeOut:Stop() button:SetAlpha(0) else button.showOnMouseover = false button.fadeOut:Stop() button:SetAlpha(1) end end end end function lib:GetButtonList() local t = {} for name in next, lib.objects do t[#t+1] = name end return t end function lib:SetButtonRadius(radius) if type(radius) == "number" then lib.radius = radius for _, button in next, lib.objects do updatePosition(button, button.db and button.db.minimapPos or button.minimapPos) end end end function lib:SetButtonToPosition(button, position) updatePosition(lib.objects[button] or button, position) end -- Upgrade! for name, button in next, lib.objects do local db = getDatabase(name) if not db or not db.lock then button:SetScript("OnDragStart", onDragStart) button:SetScript("OnDragStop", onDragStop) end button:SetScript("OnEnter", onEnter) button:SetScript("OnLeave", onLeave) button:SetScript("OnClick", onClick) button:SetScript("OnMouseDown", onMouseDown) button:SetScript("OnMouseUp", onMouseUp) if not button.fadeOut then -- Upgrade to 39 button.fadeOut = button:CreateAnimationGroup() local animOut = button.fadeOut:CreateAnimation("Alpha") animOut:SetOrder(1) animOut:SetDuration(0.2) animOut:SetFromAlpha(1) animOut:SetToAlpha(0) animOut:SetStartDelay(1) button.fadeOut:SetToFinalAlpha(true) end end lib:SetButtonRadius(lib.radius) -- Upgrade to 40