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