-- ------------------------------------------------------------------------------ -- -- 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