|
|
|
|
if not WeakAuras.IsLibsOK() then return end
|
|
|
|
|
---@type string
|
|
|
|
|
local AddonName = ...
|
|
|
|
|
---@class OptionsPrivate
|
|
|
|
|
local OptionsPrivate = select(2, ...)
|
|
|
|
|
local GetAtlasInfo = C_Texture and C_Texture.GetAtlasInfo or GetAtlasInfo
|
|
|
|
|
|
|
|
|
|
-- Lua APIs
|
|
|
|
|
local wipe = wipe
|
|
|
|
|
local pairs, next, type = pairs, next, type
|
|
|
|
|
|
|
|
|
|
-- WoW APIs
|
|
|
|
|
local CreateFrame = CreateFrame
|
|
|
|
|
|
|
|
|
|
local AceGUI = LibStub("AceGUI-3.0")
|
|
|
|
|
|
|
|
|
|
---@class WeakAuras
|
|
|
|
|
local WeakAuras = WeakAuras
|
|
|
|
|
local L = WeakAuras.L
|
|
|
|
|
|
|
|
|
|
local function CompareValues(a, b)
|
|
|
|
|
if type(a) ~= type(b) then
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
if type(a) == "table" then
|
|
|
|
|
for k, v in pairs(a) do
|
|
|
|
|
if v ~= b[k] then
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
for k, v in pairs(b) do
|
|
|
|
|
if v ~= a[k] then
|
|
|
|
|
return false
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
else
|
|
|
|
|
return a == b
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function GetAll(baseObject, paths, property, default)
|
|
|
|
|
local valueFromPath = OptionsPrivate.Private.ValueFromPath
|
|
|
|
|
if not property then
|
|
|
|
|
return default
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local result = default
|
|
|
|
|
local first = true
|
|
|
|
|
for child in OptionsPrivate.Private.TraverseLeafsOrAura(baseObject) do
|
|
|
|
|
local childObject = valueFromPath(child, paths[child.id])
|
|
|
|
|
if childObject and childObject[property] then
|
|
|
|
|
if first then
|
|
|
|
|
result = childObject[property]
|
|
|
|
|
first = false
|
|
|
|
|
else
|
|
|
|
|
if not CompareValues(result, childObject[property]) then
|
|
|
|
|
return default
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return result
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function SetAll(baseObject, paths, property, value, width, height, adjustSize)
|
|
|
|
|
local valueFromPath = OptionsPrivate.Private.ValueFromPath
|
|
|
|
|
for child in OptionsPrivate.Private.TraverseLeafsOrAura(baseObject) do
|
|
|
|
|
local object = valueFromPath(child, paths[child.id])
|
|
|
|
|
if object then
|
|
|
|
|
object[property] = value
|
|
|
|
|
if adjustSize and width and height then
|
|
|
|
|
child.width = width
|
|
|
|
|
child.height = height
|
|
|
|
|
end
|
|
|
|
|
WeakAuras.Add(child)
|
|
|
|
|
WeakAuras.ClearAndUpdateOptions(child.id)
|
|
|
|
|
WeakAuras.UpdateThumbnail(child)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local texturePicker
|
|
|
|
|
|
|
|
|
|
local function ConstructTexturePicker(frame)
|
|
|
|
|
local group = AceGUI:Create("SimpleGroup");
|
|
|
|
|
group.frame:SetParent(frame);
|
|
|
|
|
group.frame:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -17, 46);
|
|
|
|
|
group.frame:SetPoint("TOPLEFT", frame, "TOPLEFT", 17, -50);
|
|
|
|
|
group.frame:Hide();
|
|
|
|
|
group.children = {};
|
|
|
|
|
group.categories = {};
|
|
|
|
|
group.textureWidgets = {}
|
|
|
|
|
|
|
|
|
|
local dropdown = AceGUI:Create("DropdownGroup");
|
|
|
|
|
dropdown:SetLayout("fill");
|
|
|
|
|
dropdown.width = "fill";
|
|
|
|
|
dropdown:SetHeight(390);
|
|
|
|
|
group:SetLayout("fill");
|
|
|
|
|
group:AddChild(dropdown);
|
|
|
|
|
dropdown.list = {};
|
|
|
|
|
dropdown:SetGroupList(dropdown.list);
|
|
|
|
|
|
|
|
|
|
local scroll = AceGUI:Create("WeakAurasScrollArea");
|
|
|
|
|
scroll:SetWidth(540);
|
|
|
|
|
dropdown:AddChild(scroll);
|
|
|
|
|
|
|
|
|
|
local function UpdateShownWidgets()
|
|
|
|
|
-- Acquires/Releases widgets based on the scroll position
|
|
|
|
|
for _, widget in ipairs(group.textureWidgets) do
|
|
|
|
|
widget.frame:Hide()
|
|
|
|
|
widget:Release()
|
|
|
|
|
end
|
|
|
|
|
wipe(group.textureWidgets)
|
|
|
|
|
local viewportWidth, viewportHeight = scroll:GetViewportSize()
|
|
|
|
|
|
|
|
|
|
local texturesPerRow = floor(viewportWidth / 128)
|
|
|
|
|
local topRow = floor(scroll:GetContentOffset() / 128)
|
|
|
|
|
local bottomRow = topRow + ceil(viewportHeight / 128)
|
|
|
|
|
|
|
|
|
|
local first = topRow * texturesPerRow + 1
|
|
|
|
|
local last = first + (bottomRow - topRow + 1) * texturesPerRow - 1
|
|
|
|
|
|
|
|
|
|
for i = first, last do
|
|
|
|
|
local data = group.selectedGroupSorted[i]
|
|
|
|
|
if data then
|
|
|
|
|
local texturePath, textureName = data[1], data[2]
|
|
|
|
|
local textureWidget = AceGUI:Create("WeakAurasTextureButton");
|
|
|
|
|
tinsert(group.textureWidgets, textureWidget)
|
|
|
|
|
if (group.SetTextureFunc) then
|
|
|
|
|
group.SetTextureFunc(textureWidget, texturePath, textureName);
|
|
|
|
|
else
|
|
|
|
|
textureWidget:SetTexture(texturePath, textureName);
|
|
|
|
|
local d = group.textureData;
|
|
|
|
|
textureWidget:ChangeTexture(d.r, d.g, d.b, d.a, d.texRotation, d.auraRotation, d.mirror, d.blendMode)
|
|
|
|
|
end
|
|
|
|
|
if group.selectedTextures[texturePath] then
|
|
|
|
|
textureWidget:Pick()
|
|
|
|
|
end
|
|
|
|
|
textureWidget:SetClick(function()
|
|
|
|
|
group:Pick(texturePath);
|
|
|
|
|
end);
|
|
|
|
|
|
|
|
|
|
local index = i - 1 -- Math is easier if we start counting at 0
|
|
|
|
|
local textureY = floor(index / texturesPerRow) * -128
|
|
|
|
|
local textureX = (index % texturesPerRow) * 128
|
|
|
|
|
|
|
|
|
|
textureWidget.frame:Show()
|
|
|
|
|
textureWidget.frame:SetParent(scroll.content)
|
|
|
|
|
textureWidget.frame:SetPoint("TOPLEFT", textureX, textureY)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
scroll:SetCallback("ContentScrolled", function(self)
|
|
|
|
|
UpdateShownWidgets()
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
local function texturePickerGroupSelected(widget, event, uniquevalue, filter)
|
|
|
|
|
group.selectedGroupSorted = {}
|
|
|
|
|
if filter then
|
|
|
|
|
filter = filter:lower()
|
|
|
|
|
end
|
|
|
|
|
for texturePath, textureName in pairs(group.textures[uniquevalue]) do
|
|
|
|
|
if filter == nil or filter == "" or textureName:lower():find(filter, 1, true) then
|
|
|
|
|
tinsert(group.selectedGroupSorted, {texturePath, textureName})
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
table.sort(group.selectedGroupSorted, function(a, b)
|
|
|
|
|
local aPath, bPath = a[1], b[1]
|
|
|
|
|
local aNum, bNum = tonumber(aPath:match("%d+")), tonumber(bPath:match("%d+"));
|
|
|
|
|
local aNonNumber, bNonNumber = aPath:match("[^%d]+"), bPath:match("[^%d]+")
|
|
|
|
|
if(aNum and bNum and aNonNumber == bNonNumber) then
|
|
|
|
|
return aNum < bNum;
|
|
|
|
|
else
|
|
|
|
|
return aPath < bPath;
|
|
|
|
|
end
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
local viewportWidth = scroll:GetViewportSize()
|
|
|
|
|
local texturesPerRow = floor(viewportWidth / 128)
|
|
|
|
|
if texturesPerRow == 0 then
|
|
|
|
|
texturesPerRow = 1
|
|
|
|
|
end
|
|
|
|
|
local totalHeight = ceil(#group.selectedGroupSorted / texturesPerRow) * 128
|
|
|
|
|
scroll:SetContentHeight(totalHeight)
|
|
|
|
|
|
|
|
|
|
UpdateShownWidgets()
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local input = CreateFrame("EditBox", nil, group.frame, "SearchBoxTemplate");
|
|
|
|
|
input:SetScript("OnTextChanged", function(self, ...)
|
|
|
|
|
SearchBoxTemplate_OnTextChanged(self)
|
|
|
|
|
local status = dropdown.status or dropdown.localstatus
|
|
|
|
|
texturePickerGroupSelected(nil, nil, status.selected, input:GetText())
|
|
|
|
|
end);
|
|
|
|
|
input:SetScript("OnEnterPressed", function(...)
|
|
|
|
|
local status = dropdown.status or dropdown.localstatus
|
|
|
|
|
texturePickerGroupSelected(nil, nil, status.selected, input:GetText())
|
|
|
|
|
end);
|
|
|
|
|
input:SetScript("OnEscapePressed", function(...)
|
|
|
|
|
input:SetText("");
|
|
|
|
|
local status = dropdown.status or dropdown.localstatus
|
|
|
|
|
texturePickerGroupSelected(nil, nil, status.selected, input:GetText())
|
|
|
|
|
end);
|
|
|
|
|
input:SetWidth(200);
|
|
|
|
|
input:SetHeight(15);
|
|
|
|
|
input:SetPoint("TOPRIGHT", group.frame, "TOPRIGHT", -3, -10);
|
|
|
|
|
|
|
|
|
|
dropdown:SetCallback("OnGroupSelected", function(widget, event, uniquevalue)
|
|
|
|
|
texturePickerGroupSelected(widget, event, uniquevalue, input:GetText())
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
function group.UpdateList(self)
|
|
|
|
|
dropdown.dropdown.pullout:Close()
|
|
|
|
|
wipe(dropdown.list);
|
|
|
|
|
for categoryName, category in pairs(self.textures) do
|
|
|
|
|
local match = false;
|
|
|
|
|
for texturePath, textureName in pairs(category) do
|
|
|
|
|
if(self.selectedTextures[texturePath]) then
|
|
|
|
|
match = true;
|
|
|
|
|
break;
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
dropdown.list[categoryName] = (match and "|cFF80A0FF" or "")..categoryName;
|
|
|
|
|
end
|
|
|
|
|
dropdown:SetGroupList(dropdown.list);
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function group.Pick(self, texturePath)
|
|
|
|
|
local pickedwidget;
|
|
|
|
|
for index, widget in ipairs(group.textureWidgets) do
|
|
|
|
|
widget:ClearPick();
|
|
|
|
|
if(widget:GetTexturePath() == texturePath) then
|
|
|
|
|
pickedwidget = widget;
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
local width, height, flipbookInfo
|
|
|
|
|
if(pickedwidget) then
|
|
|
|
|
pickedwidget:Pick();
|
|
|
|
|
if not pickedwidget.texture.IsStopMotion then
|
|
|
|
|
local atlasInfo = GetAtlasInfo(pickedwidget.texture.path)
|
|
|
|
|
if atlasInfo then
|
|
|
|
|
width = atlasInfo.width
|
|
|
|
|
height = atlasInfo.height
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
flipbookInfo = OptionsPrivate.GetFlipbookTileSize(pickedwidget.texture.path)
|
|
|
|
|
if flipbookInfo then
|
|
|
|
|
width = flipbookInfo.tileWidth
|
|
|
|
|
height = flipbookInfo.tileHeight
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
wipe(group.selectedTextures)
|
|
|
|
|
group.selectedTextures[texturePath] = true
|
|
|
|
|
|
|
|
|
|
SetAll(self.baseObject, self.paths, self.properties.texture, texturePath, width, height, self.adjustSize)
|
|
|
|
|
|
|
|
|
|
group:UpdateList();
|
|
|
|
|
local status = dropdown.status or dropdown.localstatus
|
|
|
|
|
dropdown.dropdown:SetText(dropdown.list[status.selected]);
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function group.Open(self, baseObject, paths, properties, textures, SetTextureFunc, adjustSize)
|
|
|
|
|
local valueFromPath = OptionsPrivate.Private.ValueFromPath
|
|
|
|
|
self.baseObject = baseObject
|
|
|
|
|
self.paths = paths
|
|
|
|
|
self.properties = properties
|
|
|
|
|
self.textures = textures;
|
|
|
|
|
self.SetTextureFunc = SetTextureFunc
|
|
|
|
|
self.givenPath = {};
|
|
|
|
|
self.selectedTextures = {}
|
|
|
|
|
self.adjustSize = adjustSize
|
|
|
|
|
|
|
|
|
|
for child in OptionsPrivate.Private.TraverseLeafsOrAura(baseObject) do
|
|
|
|
|
local object = valueFromPath(child, paths[child.id])
|
|
|
|
|
if object and object[properties.texture] then
|
|
|
|
|
local texture = object[properties.texture]
|
|
|
|
|
self.givenPath[child.id] = texture
|
|
|
|
|
self.selectedTextures[texture] = true
|
|
|
|
|
else
|
|
|
|
|
self.givenPath[child.id] = ""
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local colorAll = GetAll(baseObject, paths, properties.color, {1, 1, 1, 1});
|
|
|
|
|
self.textureData = {
|
|
|
|
|
r = colorAll[1] or 1,
|
|
|
|
|
g = colorAll[2] or 1,
|
|
|
|
|
b = colorAll[3] or 1,
|
|
|
|
|
a = colorAll[4] or 1,
|
|
|
|
|
auraRotation = GetAll(baseObject, paths, properties.auraRotation, 0),
|
|
|
|
|
texRotation = GetAll(baseObject, paths, properties.rotation, 0),
|
|
|
|
|
mirror = GetAll(baseObject, paths, properties.mirror, false),
|
|
|
|
|
blendMode = GetAll(baseObject, paths, properties.blendMode, "ADD")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
frame.window = "texture";
|
|
|
|
|
frame:UpdateFrameVisible()
|
|
|
|
|
group:UpdateList()
|
|
|
|
|
local picked = false;
|
|
|
|
|
for categoryName, category in pairs(self.textures) do
|
|
|
|
|
if not(picked) then
|
|
|
|
|
for texturePath, textureName in pairs(category) do
|
|
|
|
|
if(self.selectedTextures[texturePath]) then
|
|
|
|
|
dropdown:SetGroup(categoryName);
|
|
|
|
|
picked = true;
|
|
|
|
|
break;
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
if not(picked) then
|
|
|
|
|
local categoryName = next(self.textures)
|
|
|
|
|
if(categoryName) then
|
|
|
|
|
dropdown:SetGroup(categoryName);
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
UpdateShownWidgets()
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function group.Close()
|
|
|
|
|
frame.window = "default";
|
|
|
|
|
frame:UpdateFrameVisible()
|
|
|
|
|
WeakAuras.FillOptions()
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function group.CancelClose()
|
|
|
|
|
local valueFromPath = OptionsPrivate.Private.ValueFromPath
|
|
|
|
|
for child in OptionsPrivate.Private.TraverseLeafsOrAura(group.baseObject) do
|
|
|
|
|
local childObject = valueFromPath(child, group.paths[child.id])
|
|
|
|
|
if childObject then
|
|
|
|
|
childObject[group.properties.texture] = group.givenPath[child.id]
|
|
|
|
|
WeakAuras.Add(child);
|
|
|
|
|
WeakAuras.UpdateThumbnail(child);
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
group.Close();
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local cancel = CreateFrame("Button", nil, group.frame, "UIPanelButtonTemplate")
|
|
|
|
|
cancel:SetScript("OnClick", group.CancelClose)
|
|
|
|
|
cancel:SetPoint("BOTTOMRIGHT", -20, -24)
|
|
|
|
|
cancel:SetSize(100, 20)
|
|
|
|
|
cancel:SetText(L["Cancel"])
|
|
|
|
|
|
|
|
|
|
local close = CreateFrame("Button", nil, group.frame, "UIPanelButtonTemplate")
|
|
|
|
|
close:SetScript("OnClick", group.Close)
|
|
|
|
|
close:SetPoint("RIGHT", cancel, "LEFT", -10, 0)
|
|
|
|
|
close:SetSize(100, 20)
|
|
|
|
|
close:SetText(L["Okay"])
|
|
|
|
|
|
|
|
|
|
return group
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function OptionsPrivate.TexturePicker(frame, noConstruct)
|
|
|
|
|
texturePicker = texturePicker or (not noConstruct and ConstructTexturePicker(frame))
|
|
|
|
|
return texturePicker
|
|
|
|
|
end
|