Version 0.7.2-beta

- minimap button

- graphical move arrows

- crash on matches with 0-width
This commit is contained in:
2020-06-08 01:43:23 +02:00
parent 8e179692ee
commit 5b72ad3b78
18 changed files with 872 additions and 118 deletions

View File

@@ -0,0 +1,470 @@
-----------------------------------------------------------------------
-- 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 = 43 -- 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
lib.tooltip = lib.tooltip or CreateFrame("GameTooltip", "LibDBIconTooltip", UIParent, "GameTooltipTemplate")
local next, Minimap = next, Minimap
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")
button:SetSize(31, 31)
button:SetFrameLevel(8)
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

View File

@@ -0,0 +1,7 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
..\FrameXML\UI.xsd">
<Script file="LibDBIcon-1.0.lua"/>
</Ui>

View File

@@ -0,0 +1,90 @@
assert(LibStub, "LibDataBroker-1.1 requires LibStub")
assert(LibStub:GetLibrary("CallbackHandler-1.0", true), "LibDataBroker-1.1 requires CallbackHandler-1.0")
local lib, oldminor = LibStub:NewLibrary("LibDataBroker-1.1", 4)
if not lib then return end
oldminor = oldminor or 0
lib.callbacks = lib.callbacks or LibStub:GetLibrary("CallbackHandler-1.0"):New(lib)
lib.attributestorage, lib.namestorage, lib.proxystorage = lib.attributestorage or {}, lib.namestorage or {}, lib.proxystorage or {}
local attributestorage, namestorage, callbacks = lib.attributestorage, lib.namestorage, lib.callbacks
if oldminor < 2 then
lib.domt = {
__metatable = "access denied",
__index = function(self, key) return attributestorage[self] and attributestorage[self][key] end,
}
end
if oldminor < 3 then
lib.domt.__newindex = function(self, key, value)
if not attributestorage[self] then attributestorage[self] = {} end
if attributestorage[self][key] == value then return end
attributestorage[self][key] = value
local name = namestorage[self]
if not name then return end
callbacks:Fire("LibDataBroker_AttributeChanged", name, key, value, self)
callbacks:Fire("LibDataBroker_AttributeChanged_"..name, name, key, value, self)
callbacks:Fire("LibDataBroker_AttributeChanged_"..name.."_"..key, name, key, value, self)
callbacks:Fire("LibDataBroker_AttributeChanged__"..key, name, key, value, self)
end
end
if oldminor < 2 then
function lib:NewDataObject(name, dataobj)
if self.proxystorage[name] then return end
if dataobj then
assert(type(dataobj) == "table", "Invalid dataobj, must be nil or a table")
self.attributestorage[dataobj] = {}
for i,v in pairs(dataobj) do
self.attributestorage[dataobj][i] = v
dataobj[i] = nil
end
end
dataobj = setmetatable(dataobj or {}, self.domt)
self.proxystorage[name], self.namestorage[dataobj] = dataobj, name
self.callbacks:Fire("LibDataBroker_DataObjectCreated", name, dataobj)
return dataobj
end
end
if oldminor < 1 then
function lib:DataObjectIterator()
return pairs(self.proxystorage)
end
function lib:GetDataObjectByName(dataobjectname)
return self.proxystorage[dataobjectname]
end
function lib:GetNameByDataObject(dataobject)
return self.namestorage[dataobject]
end
end
if oldminor < 4 then
local next = pairs(attributestorage)
function lib:pairs(dataobject_or_name)
local t = type(dataobject_or_name)
assert(t == "string" or t == "table", "Usage: ldb:pairs('dataobjectname') or ldb:pairs(dataobject)")
local dataobj = self.proxystorage[dataobject_or_name] or dataobject_or_name
assert(attributestorage[dataobj], "Data object not found")
return next, attributestorage[dataobj], nil
end
local ipairs_iter = ipairs(attributestorage)
function lib:ipairs(dataobject_or_name)
local t = type(dataobject_or_name)
assert(t == "string" or t == "table", "Usage: ldb:ipairs('dataobjectname') or ldb:ipairs(dataobject)")
local dataobj = self.proxystorage[dataobject_or_name] or dataobject_or_name
assert(attributestorage[dataobj], "Data object not found")
return ipairs_iter, attributestorage[dataobj], 0
end
end

View File

@@ -0,0 +1,13 @@
LibDataBroker is a small WoW addon library designed to provide a "MVC":http://en.wikipedia.org/wiki/Model-view-controller interface for use in various addons.
LDB's primary goal is to "detach" plugins for TitanPanel and FuBar from the display addon.
Plugins can provide data into a simple table, and display addons can receive callbacks to refresh their display of this data.
LDB also provides a place for addons to register "quicklaunch" functions, removing the need for authors to embed many large libraries to create minimap buttons.
Users who do not wish to be "plagued" by these buttons simply do not install an addon to render them.
Due to it's simple generic design, LDB can be used for any design where you wish to have an addon notified of changes to a table.
h2. Links
* "API documentation":http://github.com/tekkub/libdatabroker-1-1/wikis/api
* "Data specifications":http://github.com/tekkub/libdatabroker-1-1/wikis/data-specifications
* "Addons using LDB":http://github.com/tekkub/libdatabroker-1-1/wikis/addons-using-ldb