You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
418 lines
12 KiB
418 lines
12 KiB
|
5 years ago
|
-- ========================================================================= --
|
||
|
|
-- SylingTracker --
|
||
|
|
-- https://www.curseforge.com/wow/addons/sylingtracker --
|
||
|
|
-- --
|
||
|
|
-- Repository: --
|
||
|
|
-- https://github.com/Skamer/SylingTracker --
|
||
|
|
-- --
|
||
|
|
-- ========================================================================= --
|
||
|
|
Syling "SylingTracker.Quests.QuestCategoryView" ""
|
||
|
|
-- ========================================================================= --
|
||
|
|
namespace "SLT"
|
||
|
|
-- ========================================================================= --
|
||
|
|
-- Iterator helper for ignoring the children are used for backdrop, and avoiding
|
||
|
|
-- they are taken as account for their parent height
|
||
|
|
IterateFrameChildren = Utils.IterateFrameChildren
|
||
|
|
ResetStyles = Utils.ResetStyles
|
||
|
|
ValidateFlags = System.Toolset.validateflags
|
||
|
|
-- ========================================================================= --
|
||
|
|
__Recyclable__ "SylingTracker_QuestCategoryView%d"
|
||
|
|
class "QuestCategoryView" (function(_ENV)
|
||
|
|
inherit "Frame" extend "IView"
|
||
|
|
|
||
|
|
__Flags__()
|
||
|
|
enum "Flags" {
|
||
|
|
NONE = 0,
|
||
|
|
HAS_QUESTS = 1
|
||
|
|
}
|
||
|
|
-----------------------------------------------------------------------------
|
||
|
|
-- Methods --
|
||
|
|
-----------------------------------------------------------------------------
|
||
|
|
function OnViewUpdate(self, data)
|
||
|
|
local nameFS = self:GetChild("Name")
|
||
|
|
|
||
|
|
-- Determines the flags
|
||
|
|
local flags = Flags.NONE
|
||
|
|
if data then
|
||
|
|
flags = Flags.HAS_QUESTS
|
||
|
|
end
|
||
|
|
|
||
|
|
if flags ~= self.Flags then
|
||
|
|
ResetStyles(self)
|
||
|
|
ResetStyles(nameFS)
|
||
|
|
|
||
|
|
-- Is there quests
|
||
|
|
if ValidateFlags(Flags.HAS_QUESTS, flags) then
|
||
|
|
self:AcquireQuests()
|
||
|
|
else
|
||
|
|
self:ReleaseQuests()
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Styling stuff
|
||
|
|
if flags ~= Flags.NONE then
|
||
|
|
local styles = self.FlagsStyles and self.FlagsStyles[flags]
|
||
|
|
if styles then
|
||
|
|
Style[self] = styles
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if data then
|
||
|
|
-- Update the category name
|
||
|
|
local name
|
||
|
|
for _, v in pairs(data) do
|
||
|
|
name = v.category
|
||
|
|
break
|
||
|
|
end
|
||
|
|
|
||
|
|
Style[nameFS].text = name
|
||
|
|
|
||
|
|
|
||
|
|
local questsView = self:AcquireQuests()
|
||
|
|
questsView:UpdateView(data)
|
||
|
|
end
|
||
|
|
|
||
|
|
self.Flags = flags
|
||
|
|
end
|
||
|
|
|
||
|
|
function AcquireQuests(self)
|
||
|
|
local quests = self:GetChild("Quests")
|
||
|
|
if not quests then
|
||
|
|
quests = QuestListView.Acquire()
|
||
|
|
|
||
|
|
-- We need to keep the old name when we'll release it
|
||
|
|
self.__PreviousQuestsName = quests:GetName()
|
||
|
|
|
||
|
|
quests:SetParent(self)
|
||
|
|
quests:SetName("Quests")
|
||
|
|
quests:InstantApplyStyle()
|
||
|
|
|
||
|
|
-- Register the events
|
||
|
|
quests.OnSizeChanged = quests.OnSizeChanged + self.OnQuestsSizeChanged
|
||
|
|
|
||
|
|
self:AdjustHeight()
|
||
|
|
end
|
||
|
|
|
||
|
|
return quests
|
||
|
|
end
|
||
|
|
|
||
|
|
function ReleaseQuests(self)
|
||
|
|
local quests = self:GetChild("Quests")
|
||
|
|
if quests then
|
||
|
|
-- Give its old name (generated by the recycle system)
|
||
|
|
quests:SetName(self.__PreviousQuestsName)
|
||
|
|
self.__PreviousQuestsName = nil
|
||
|
|
|
||
|
|
-- Unregister the events
|
||
|
|
quests.OnSizeChanged = quests.OnSizeChanged - self.OnQuestsSizeChanged
|
||
|
|
|
||
|
|
-- It's better to release it after the event has been unregistered for avoiding
|
||
|
|
-- useless call
|
||
|
|
quests:Release()
|
||
|
|
|
||
|
|
self:AdjustHeight()
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function OnAdjustHeight(self, useAnimation)
|
||
|
|
local height = 0
|
||
|
|
local maxOuterBottom
|
||
|
|
for childName, child in IterateFrameChildren(self) do
|
||
|
|
|
||
|
|
local outerBottom = child:GetBottom()
|
||
|
|
local outerTop = child:GetTop()
|
||
|
|
if outerBottom then
|
||
|
|
if not maxOuterBottom or maxOuterBottom > outerBottom then
|
||
|
|
maxOuterBottom = outerBottom
|
||
|
|
maxChild = child
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if maxOuterBottom then
|
||
|
|
local computeHeight = (self:GetTop() - maxOuterBottom) + self.PaddingBottom
|
||
|
|
if useAnimation then
|
||
|
|
self:SetAnimatedHeight(computeHeight)
|
||
|
|
else
|
||
|
|
self:SetHeight(computeHeight)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function OnRelease(self)
|
||
|
|
-- Release first the children
|
||
|
|
self:ReleaseQuests()
|
||
|
|
|
||
|
|
self:Hide()
|
||
|
|
self:ClearAllPoints()
|
||
|
|
self:SetParent()
|
||
|
|
|
||
|
|
-- "CancelAdjustHeight" and "CancelAnimatingHeight" wiil cancel the pending
|
||
|
|
-- computing stuff for height, so they not prevent "SetHeight" here doing
|
||
|
|
-- its stuff.
|
||
|
|
self:CancelAdjustHeight()
|
||
|
|
self:CancelAnimatingHeight()
|
||
|
|
self:SetHeight(1)
|
||
|
|
|
||
|
|
-- Reset the class properties
|
||
|
|
self.Flags = nil
|
||
|
|
|
||
|
|
-- Will Remove all custom styles properties, so the next time the object will
|
||
|
|
-- be used, this one will be in a clean state
|
||
|
|
ResetStyles(self)
|
||
|
|
end
|
||
|
|
|
||
|
|
function OnAcquire(self)
|
||
|
|
-- Important ! We need the frame is instantly styled as this may affect
|
||
|
|
-- its height.
|
||
|
|
self:InstantApplyStyle()
|
||
|
|
|
||
|
|
self:Show()
|
||
|
|
self:AdjustHeight()
|
||
|
|
end
|
||
|
|
-----------------------------------------------------------------------------
|
||
|
|
-- Properties --
|
||
|
|
-----------------------------------------------------------------------------
|
||
|
|
property "FlagsStyles" {
|
||
|
|
type = Table
|
||
|
|
}
|
||
|
|
|
||
|
|
property "Flags" {
|
||
|
|
type = QuestCategoryView.Flags,
|
||
|
|
default = QuestCategoryView.Flags.NONE
|
||
|
|
}
|
||
|
|
|
||
|
|
property "PaddingBottom" {
|
||
|
|
type = Number,
|
||
|
|
default = 0
|
||
|
|
}
|
||
|
|
-----------------------------------------------------------------------------
|
||
|
|
-- Constructors --
|
||
|
|
-----------------------------------------------------------------------------
|
||
|
|
__Template__ {
|
||
|
|
Name = SLTFontString
|
||
|
|
}
|
||
|
|
function __ctor(self)
|
||
|
|
-- Important ! We need the frame is instantly styled as this may affect
|
||
|
|
-- its height.
|
||
|
|
self:InstantApplyStyle()
|
||
|
|
|
||
|
|
-- Important! As the frame ajusts its height depending of its children height
|
||
|
|
-- we need to set its height when contructed for the event "OnSizechanged" of
|
||
|
|
-- its children is triggered.
|
||
|
|
self:SetHeight(1)
|
||
|
|
|
||
|
|
self.OnQuestsSizeChanged = function() self:AdjustHeight() end
|
||
|
|
|
||
|
|
self:SetClipsChildren(true)
|
||
|
|
end
|
||
|
|
|
||
|
|
end)
|
||
|
|
|
||
|
|
__Recyclable__ "SylingTracker_QuestCategoryListView%d"
|
||
|
|
class "QuestCategoryListView" (function(_ENV)
|
||
|
|
inherit "Frame" extend "IView"
|
||
|
|
-----------------------------------------------------------------------------
|
||
|
|
-- Methods --
|
||
|
|
-----------------------------------------------------------------------------
|
||
|
|
function OnViewUpdate(self, data)
|
||
|
|
-- Clear the current achievement id list
|
||
|
|
wipe(self.categoriesID)
|
||
|
|
wipe(self.categoriesRange)
|
||
|
|
wipe(self.categoriesData)
|
||
|
|
wipe(self.categoriesOrder)
|
||
|
|
|
||
|
|
if data then
|
||
|
|
-- 1. Split the quests data in their respective category
|
||
|
|
for questID, questData in pairs(data) do
|
||
|
|
local categoryName = questData.category
|
||
|
|
local t = self.categoriesData[categoryName]
|
||
|
|
if not t then
|
||
|
|
t = setmetatable({}, { __mode = "v"})
|
||
|
|
self.categoriesData[categoryName] = t
|
||
|
|
tinsert(self.categoriesOrder, categoryName)
|
||
|
|
end
|
||
|
|
|
||
|
|
local bestRange = self.categoriesRange[categoryName]
|
||
|
|
if not bestRange or questData.distance < bestRange then
|
||
|
|
self.categoriesRange[categoryName] = questData.distance or 99999
|
||
|
|
end
|
||
|
|
|
||
|
|
t[questID] = questData
|
||
|
|
end
|
||
|
|
|
||
|
|
-- 2. Sort the category by their best range
|
||
|
|
table.sort(self.categoriesOrder, function(a, b)
|
||
|
|
return self.categoriesRange[a] < self.categoriesRange[b]
|
||
|
|
end)
|
||
|
|
|
||
|
|
-- 3. Update the category with their quests data
|
||
|
|
local previousCategory
|
||
|
|
for index, categoryName in ipairs(self.categoriesOrder) do
|
||
|
|
local category = self:AcquireCategory(categoryName)
|
||
|
|
|
||
|
|
if index > 1 then
|
||
|
|
category:SetPoint("TOP", previousCategory, "BOTTOM", 0, -self.CategorySpacing)
|
||
|
|
else
|
||
|
|
category:SetPoint("TOP")
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Update our category with data
|
||
|
|
category:UpdateView(self.categoriesData[categoryName])
|
||
|
|
|
||
|
|
-- Build the current quest id list
|
||
|
|
self.categoriesID[categoryName] = true
|
||
|
|
|
||
|
|
previousCategory = category
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
self:ReleaseUnusedCategories()
|
||
|
|
end
|
||
|
|
|
||
|
|
function AcquireCategory(self, id)
|
||
|
|
local category = self.categoriesCache[id]
|
||
|
|
if not category then
|
||
|
|
category = QuestCategoryView.Acquire()
|
||
|
|
category:SetParent(self)
|
||
|
|
category:SetPoint("LEFT")
|
||
|
|
category:SetPoint("RIGHT")
|
||
|
|
|
||
|
|
category.OnSizeChanged = category.OnSizeChanged + self.OnCategorySizeChanged
|
||
|
|
|
||
|
|
self.categoriesCache[id] = category
|
||
|
|
|
||
|
|
self:AdjustHeight()
|
||
|
|
end
|
||
|
|
|
||
|
|
return category
|
||
|
|
end
|
||
|
|
|
||
|
|
function ReleaseUnusedCategories(self)
|
||
|
|
for categoryName, category in pairs(self.categoriesCache) do
|
||
|
|
if not self.categoriesID[categoryName] then
|
||
|
|
self.categoriesCache[categoryName] = nil
|
||
|
|
|
||
|
|
category.OnSizeChanged = category.OnSizeChanged - self.OnCategorySizeChanged
|
||
|
|
|
||
|
|
category:Release()
|
||
|
|
|
||
|
|
self:AdjustHeight()
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function OnAdjustHeight(self, useAnimation)
|
||
|
|
local height = 0
|
||
|
|
local count = 0
|
||
|
|
|
||
|
|
for _, child in IterateFrameChildren(self) do
|
||
|
|
height = height + child:GetHeight()
|
||
|
|
count = count + 1
|
||
|
|
end
|
||
|
|
|
||
|
|
height = height + self.CategorySpacing * math.max(0, count-1)
|
||
|
|
|
||
|
|
if useAnimation then
|
||
|
|
self:SetAnimatedHeight(height)
|
||
|
|
else
|
||
|
|
self:SetHeight(height)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function OnRelease(self)
|
||
|
|
wipe(self.categoriesID)
|
||
|
|
wipe(self.categoriesRange)
|
||
|
|
wipe(self.categoriesData)
|
||
|
|
wipe(self.categoriesOrder)
|
||
|
|
self:ReleaseUnusedCategories()
|
||
|
|
|
||
|
|
self:Hide()
|
||
|
|
self:ClearAllPoints()
|
||
|
|
self:SetParent()
|
||
|
|
self:CancelAdjustHeight()
|
||
|
|
self:CancelAnimatingHeight()
|
||
|
|
|
||
|
|
self:SetHeight(1)
|
||
|
|
|
||
|
|
ResetStyles(self)
|
||
|
|
end
|
||
|
|
|
||
|
|
function OnAcquire(self)
|
||
|
|
-- Important ! We need the frame is instantly styled as this may affect
|
||
|
|
-- its height.
|
||
|
|
self:InstantApplyStyle()
|
||
|
|
|
||
|
|
self:Show()
|
||
|
|
|
||
|
|
self:AdjustHeight()
|
||
|
|
end
|
||
|
|
-----------------------------------------------------------------------------
|
||
|
|
-- Properties --
|
||
|
|
-----------------------------------------------------------------------------
|
||
|
|
property "CategorySpacing" {
|
||
|
|
type = Number,
|
||
|
|
default = 13
|
||
|
|
}
|
||
|
|
-----------------------------------------------------------------------------
|
||
|
|
-- Constructors --
|
||
|
|
-----------------------------------------------------------------------------
|
||
|
|
function __ctor(self)
|
||
|
|
-- Important ! We need the frame is instantly styled as this may affect
|
||
|
|
-- its height.
|
||
|
|
self:InstantApplyStyle()
|
||
|
|
|
||
|
|
-- Important! As the frame ajusts its height depending of its children height
|
||
|
|
-- we need to set its height when contructed for the event "OnSizechanged" of
|
||
|
|
-- its children is triggered.
|
||
|
|
self:SetHeight(1) -- !important
|
||
|
|
|
||
|
|
-- Keep in the cache the categories, to be reused.
|
||
|
|
-- use: self.categoriesCache[categoryName] = categoryObject
|
||
|
|
self.categoriesCache = setmetatable({}, { __mode = "v"})
|
||
|
|
|
||
|
|
-- Get the current categories id's list. Used internally to release the
|
||
|
|
-- unused categories
|
||
|
|
-- use: self.achievementsID[categoryName] = true or nil
|
||
|
|
self.categoriesID = {}
|
||
|
|
|
||
|
|
self.categoriesRange = {}
|
||
|
|
self.categoriesData = {}
|
||
|
|
self.categoriesOrder = {}
|
||
|
|
|
||
|
|
self.OnCategorySizeChanged = function() self:AdjustHeight() end
|
||
|
|
|
||
|
|
self:SetClipsChildren(true)
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
-------------------------------------------------------------------------------
|
||
|
|
-- Styles --
|
||
|
|
-------------------------------------------------------------------------------
|
||
|
|
Style.UpdateSkin("Default", {
|
||
|
|
[QuestCategoryView] = {
|
||
|
|
Name = {
|
||
|
|
sharedMediaFont = FontType("PT Sans Narrow Bold", 12),
|
||
|
|
textColor = Color(1, 0.38, 0),
|
||
|
|
textTransform = "UPPERCASE",
|
||
|
|
justifyH = "LEFT",
|
||
|
|
location = {
|
||
|
|
Anchor("TOP"),
|
||
|
|
Anchor("LEFT", 10, 0),
|
||
|
|
Anchor("RIGHT")
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
FlagsStyles = {
|
||
|
|
[QuestCategoryView.Flags.HAS_QUESTS] = {
|
||
|
|
Quests = {
|
||
|
|
location = {
|
||
|
|
Anchor("TOP", 0, -13, "Name", "BOTTOM"),
|
||
|
|
Anchor("LEFT"),
|
||
|
|
Anchor("RIGHT")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
})
|