TradeSkillMaster/Core/UI/Elements/ManagementGroupTree.lua

302 lines
10 KiB
Lua
Raw Normal View History

2020-11-13 14:13:12 -05:00
-- ------------------------------------------------------------------------------ --
-- TradeSkillMaster --
-- https://tradeskillmaster.com --
-- All Rights Reserved - Detailed license information included with addon. --
-- ------------------------------------------------------------------------------ --
--- ManagementGroupTree UI Element Class.
-- The management group tree allows for moving, adding, and deleting groups. It also only allows for a single group to
-- be selected. It is a subclass of the @{GroupTree} class.
-- @classmod ManagementGroupTree
local _, TSM = ...
local L = TSM.Include("Locale").GetTable()
local Analytics = TSM.Include("Util.Analytics")
local String = TSM.Include("Util.String")
local Theme = TSM.Include("Util.Theme")
local ScriptWrapper = TSM.Include("Util.ScriptWrapper")
local ManagementGroupTree = TSM.Include("LibTSMClass").DefineClass("ManagementGroupTree", TSM.UI.GroupTree)
local UIElements = TSM.Include("UI.UIElements")
UIElements.Register(ManagementGroupTree)
TSM.UI.ManagementGroupTree = ManagementGroupTree
local private = {}
local DRAG_SCROLL_SPEED_FACTOR = 12
local MOVE_FRAME_PADDING = 8
-- ============================================================================
-- Public Class Methods
-- ============================================================================
function ManagementGroupTree.__init(self)
self.__super:__init()
self._moveFrame = nil
self._selectedGroup = nil
self._onGroupSelectedHandler = nil
self._onNewGroupHandler = nil
self._scrollAmount = 0
end
function ManagementGroupTree.Acquire(self)
self._moveFrame = UIElements.New("Frame", self._id.."_MoveFrame")
:SetLayout("VERTICAL")
:SetHeight(20)
:SetStrata("TOOLTIP")
:SetBackgroundColor("PRIMARY_BG_ALT", true)
:SetBorderColor("INDICATOR")
:SetContext(self)
:AddChild(UIElements.New("Text", "text")
:SetFont("BODY_BODY3")
:SetJustifyH("CENTER")
)
self._moveFrame:SetParent(self:_GetBaseFrame())
self._moveFrame:Hide()
self._moveFrame:SetScript("OnShow", private.MoveFrameOnShow)
self._moveFrame:SetScript("OnUpdate", private.MoveFrameOnUpdate)
self.__super:Acquire()
self:GetScrollingTableInfo()
:GetColById("group")
:SetActionIconInfo(2, 14, private.GetActionIcon, true)
:SetActionIconClickHandler(private.OnActionIconClick)
:Commit()
:Commit()
end
function ManagementGroupTree.Release(self)
self._selectedGroup = nil
self._onGroupSelectedHandler = nil
self._onNewGroupHandler = nil
self._moveFrame:Release()
self._moveFrame = nil
for _, row in ipairs(self._rows) do
row._frame:RegisterForDrag()
ScriptWrapper.Clear(row._frame, "OnDragStart")
ScriptWrapper.Clear(row._frame, "OnDragStop")
for _, button in pairs(row._buttons) do
button:RegisterForDrag()
ScriptWrapper.Clear(button, "OnDragStart")
ScriptWrapper.Clear(button, "OnDragStop")
end
end
self.__super:Release()
end
--- Sets the selected group.
-- @tparam ManagementGroupTree self The management group tree object
-- @tparam string groupPath The selected group's path
-- @tparam boolean redraw Whether or not to redraw the management group tree
-- @treturn ManagementGroupTree The management group tree object
function ManagementGroupTree.SetSelectedGroup(self, groupPath, redraw)
self._selectedGroup = groupPath
if self._onGroupSelectedHandler then
self:_onGroupSelectedHandler(groupPath)
end
if redraw then
-- make sure this group is visible (its parent is expanded)
local parent = TSM.Groups.Path.GetParent(groupPath)
self._contextTable.collapsed[TSM.CONST.ROOT_GROUP_PATH] = nil
while parent and parent ~= TSM.CONST.ROOT_GROUP_PATH do
self._contextTable.collapsed[parent] = nil
parent = TSM.Groups.Path.GetParent(parent)
end
self:UpdateData(true)
self:_ScrollToData(self._selectedGroup)
end
return self
end
--- Registers a script handler.
-- @tparam ManagementGroupTree self The management group tree object
-- @tparam string script The script to register for (supported scripts: `OnGroupSelected`)
-- @tparam function handler The script handler which will be called with the management group tree object followed by
-- any arguments to the script
-- @treturn ManagementGroupTree The management group tree object
function ManagementGroupTree.SetScript(self, script, handler)
if script == "OnGroupSelected" then
self._onGroupSelectedHandler = handler
elseif script == "OnNewGroup" then
self._onNewGroupHandler = handler
else
error("Unknown ManagementGroupTree script: "..tostring(script))
end
return self
end
-- ============================================================================
-- Private Class Methods
-- ============================================================================
function ManagementGroupTree._GetTableRow(self, isHeader)
local row = self.__super:_GetTableRow(isHeader)
if not isHeader then
row._frame:RegisterForDrag("LeftButton")
ScriptWrapper.Set(row._frame, "OnDragStart", private.RowOnDragStart, row)
ScriptWrapper.Set(row._frame, "OnDragStop", private.RowOnDragStop, row)
for _, button in pairs(row._buttons) do
button:RegisterForDrag("LeftButton")
ScriptWrapper.Set(button, "OnDragStart", private.RowOnDragStart, row)
ScriptWrapper.Set(button, "OnDragStop", private.RowOnDragStop, row)
end
end
return row
end
function ManagementGroupTree._SetCollapsed(self, data, collapsed)
self.__super:_SetCollapsed(data, collapsed)
if collapsed and self._selectedGroup ~= data and strmatch(self._selectedGroup, "^"..String.Escape(data)) then
-- we collapsed a parent of the selected group, so select the group we just collapsed instead
self:SetSelectedGroup(data, true)
end
end
function ManagementGroupTree._IsSelected(self, data)
return data == self._selectedGroup
end
function ManagementGroupTree._HandleRowClick(self, data, mouseButton)
if mouseButton == "RightButton" then
self.__super:_HandleRowClick(data, mouseButton)
return
end
self:SetSelectedGroup(data, true)
end
-- ============================================================================
-- Private Helper Functions
-- ============================================================================
function private.GetActionIcon(self, data, iconIndex, isMouseOver)
if iconIndex == 1 then
local texturePack = "iconPack.14x14/Add/Circle"
return true, isMouseOver and TSM.UI.TexturePacks.GetColoredKey(texturePack, Theme.GetColor("INDICATOR")) or texturePack
elseif iconIndex == 2 then
if data ~= TSM.CONST.ROOT_GROUP_PATH then
local texturePack = "iconPack.14x14/Delete"
return true, isMouseOver and TSM.UI.TexturePacks.GetColoredKey(texturePack, Theme.GetColor("INDICATOR")) or texturePack
else
return false, nil
end
else
error("Invalid index: "..tostring(iconIndex))
end
end
function private.OnActionIconClick(self, data, iconIndex)
if iconIndex == 1 then
local newGroupPath = TSM.Groups.Path.Join(data, L["New Group"])
if TSM.Groups.Exists(newGroupPath) then
local num = 1
while TSM.Groups.Exists(newGroupPath.." "..num) do
num = num + 1
end
newGroupPath = newGroupPath.." "..num
end
TSM.Groups.Create(newGroupPath)
Analytics.Action("CREATED_GROUP", newGroupPath)
self:SetSelectedGroup(newGroupPath, true)
if self._onNewGroupHandler then
self:_onNewGroupHandler()
end
elseif iconIndex == 2 then
local groupColor = Theme.GetGroupColor(select('#', strsplit(TSM.CONST.GROUP_SEP, data)))
self:GetBaseElement():ShowConfirmationDialog(L["Delete Group?"], format(L["Deleting this group (%s) will also remove any sub-groups attached to this group."], groupColor:ColorText(TSM.Groups.Path.GetName(data))), private.DeleteConfirmed, self, data)
else
error("Invalid index: "..tostring(iconIndex))
end
end
function private.DeleteConfirmed(self, data)
TSM.Groups.Delete(data)
Analytics.Action("DELETED_GROUP", data)
self:SetSelectedGroup(TSM.CONST.ROOT_GROUP_PATH, true)
end
function private.MoveFrameOnShow(frame)
local self = frame:GetContext()
self._scrollAmount = 0
end
function private.MoveFrameOnUpdate(frame)
local self = frame:GetContext()
local uiScale = UIParent:GetEffectiveScale()
local x, y = GetCursorPosition()
x = x / uiScale
y = y / uiScale
frame:_GetBaseFrame():SetPoint("CENTER", UIParent, "BOTTOMLEFT", x, y)
-- figure out if we're above or below the frame for scrolling while dragging
local top = self:_GetBaseFrame():GetTop()
local bottom = self:_GetBaseFrame():GetBottom()
if y > top then
self._scrollAmount = top - y
elseif y < bottom then
self._scrollAmount = bottom - y
else
self._scrollAmount = 0
end
self._vScrollbar:SetValue(self._vScrollbar:GetValue() + self._scrollAmount / DRAG_SCROLL_SPEED_FACTOR)
end
function private.RowOnDragStart(row)
local self = row._scrollingTable
local groupPath = row:GetData()
if groupPath == TSM.CONST.ROOT_GROUP_PATH then
-- don't do anything for the root group
return
end
local level = select('#', strsplit(TSM.CONST.GROUP_SEP, groupPath))
local levelColor = Theme.GetGroupColor(level)
self._dragGroupPath = groupPath
self._moveFrame:Show()
self._moveFrame:SetHeight(self._rowHeight)
local moveFrameText = self._moveFrame:GetElement("text")
moveFrameText:SetTextColor(levelColor)
moveFrameText:SetText(TSM.Groups.Path.GetName(groupPath))
moveFrameText:SetWidth(1000)
moveFrameText:Draw()
self._moveFrame:SetWidth(moveFrameText:GetStringWidth() + MOVE_FRAME_PADDING * 2)
self._moveFrame:Draw()
end
function private.RowOnDragStop(row)
local self = row._scrollingTable
local groupPath = row:GetData()
if groupPath == TSM.CONST.ROOT_GROUP_PATH then
-- don't do anything for the root group
return
end
self._moveFrame:Hide()
local destPath = nil
for _, targetRow in ipairs(self._rows) do
if targetRow:IsMouseOver() then
destPath = targetRow:GetData()
break
end
end
local oldPath = self._dragGroupPath
self._dragGroupPath = nil
if not destPath or destPath == oldPath or TSM.Groups.Path.IsChild(destPath, oldPath) then
return
end
local newPath = TSM.Groups.Path.Join(destPath, TSM.Groups.Path.GetName(oldPath))
if oldPath == newPath then
return
elseif TSM.Groups.Exists(newPath) then
return
end
TSM.Groups.Move(oldPath, newPath)
Analytics.Action("MOVED_GROUP", oldPath, newPath)
self:SetSelectedGroup(newPath, true)
end