302 lines
10 KiB
Lua
302 lines
10 KiB
Lua
|
-- ------------------------------------------------------------------------------ --
|
||
|
-- 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
|