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.

417 lines
12 KiB

-- ========================================================================= --
-- 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")
}
}
}
}
}
})