-- ------------------------------------------------------------------------------ -- -- TradeSkillMaster -- -- https://tradeskillmaster.com -- -- All Rights Reserved - Detailed license information included with addon. -- -- ------------------------------------------------------------------------------ -- --- Divided Container UI Element Class. -- A divided container is a container with two children with a divider between them. It is a subclass of the @{Frame} class. -- @classmod DividedContainer local _, TSM = ... local UIElements = TSM.Include("UI.UIElements") local DividedContainer = TSM.Include("LibTSMClass").DefineClass("DividedContainer", TSM.UI.Frame) UIElements.Register(DividedContainer) TSM.UI.DividedContainer = DividedContainer local private = {} local DIVIDER_SIZE = 2 -- ============================================================================ -- Public Class Methods -- ============================================================================ function DividedContainer.__init(self) self.__super:__init() self._leftChild = nil self._rightChild = nil self._resizeStartX = nil self._resizeOffset = 0 self._contextTable = nil self._defaultContextTable = nil self._minLeftWidth = nil self._minRightWidth = nil end function DividedContainer.Acquire(self) self.__super:AddChildNoLayout(UIElements.New("Frame", "leftEmpty") :AddAnchor("TOPLEFT") :AddAnchor("BOTTOMRIGHT", "divider", "BOTTOMLEFT") ) self.__super:AddChild(UIElements.New("Button", "divider") :SetSize(DIVIDER_SIZE, nil) :SetHitRectInsets(-2, -2, 0, 0) :SetRelativeLevel(2) :EnableRightClick() :SetScript("OnMouseDown", private.HandleOnMouseDown) :SetScript("OnMouseUp", private.HandleOnMouseUp) :SetScript("OnClick", private.HandleOnClick) :SetScript("OnUpdate", private.HandleOnUpdate) ) self.__super:AddChildNoLayout(UIElements.New("Frame", "rightEmpty") :AddAnchor("TOPLEFT", "divider", "TOPRIGHT") :AddAnchor("BOTTOMRIGHT") ) self.__super:Acquire() self.__super:SetLayout("HORIZONTAL") end function DividedContainer.Release(self) self._isVertical = false self._leftChild = nil self._rightChild = nil self._resizeStartX = nil self._resizeOffset = 0 self._contextTable = nil self._defaultContextTable = nil self._minLeftWidth = nil self._minRightWidth = nil self.__super:Release() end function DividedContainer.SetVertical(self) assert(not self._leftChild and not self._rightChild and not self._isVertical) self._isVertical = true self:GetElement("leftEmpty") :WipeAnchors() :AddAnchor("TOPLEFT") :AddAnchor("BOTTOMRIGHT", "divider", "TOPRIGHT") self:GetElement("divider") :SetSize(nil, DIVIDER_SIZE) :SetHitRectInsets(0, 0, -2, -2) self:GetElement("rightEmpty") :WipeAnchors() :AddAnchor("TOPLEFT", "divider", "BOTTOMLEFT") :AddAnchor("BOTTOMRIGHT") self.__super:SetLayout("VERTICAL") return self end function DividedContainer.SetLayout(self, layout) error("DividedContainer doesn't support this method") end function DividedContainer.AddChild(self, child) error("DividedContainer doesn't support this method") end function DividedContainer.AddChildBeforeById(self, beforeId, child) error("DividedContainer doesn't support this method") end --- Sets the context table. -- This table can be used to preserve the divider position across lifecycles of the divided container and even WoW -- sessions if it's within the settings DB. The position is stored as the width of the left child element. -- @tparam DividedContainer self The divided container object -- @tparam table tbl The context table -- @tparam table defaultTbl The default table (required fields: `leftWidth`) -- @treturn DividedContainer The divided container object function DividedContainer.SetContextTable(self, tbl, defaultTbl) assert(defaultTbl.leftWidth > 0) tbl.leftWidth = tbl.leftWidth or defaultTbl.leftWidth self._contextTable = tbl self._defaultContextTable = defaultTbl return self end --- Sets the context table from a settings object. -- @tparam DividedContainer self The divided container object -- @tparam Settings settings The settings object -- @tparam string key The setting key -- @treturn DividedContainer The divided container object function DividedContainer.SetSettingsContext(self, settings, key) return self:SetContextTable(settings[key], settings:GetDefaultReadOnly(key)) end --- Sets the minimum width of the child element. -- @tparam DividedContainer self The divided container object -- @tparam number minLeftWidth The minimum width of the left child element -- @tparam number minRightWidth The minimum width of the right child element -- @treturn DividedContainer The divided container object function DividedContainer.SetMinWidth(self, minLeftWidth, minRightWidth) self._minLeftWidth = minLeftWidth self._minRightWidth = minRightWidth return self end --- Sets the left child element. -- @tparam DividedContainer self The divided container object -- @tparam Element child The left child element -- @treturn DividedContainer The divided container object function DividedContainer.SetLeftChild(self, child) assert(not self._isVertical and not self._leftChild and child) self._leftChild = child self.__super:AddChildBeforeById("divider", child) return self end --- Sets the right child element. -- @tparam DividedContainer self The divided container object -- @tparam Element child The right child element -- @treturn DividedContainer The divided container object function DividedContainer.SetRightChild(self, child) assert(not self._isVertical and not self._rightChild and child) self._rightChild = child self.__super:AddChild(child) return self end --- Sets the top child element in vertical mode. -- @tparam DividedContainer self The divided container object -- @tparam Element child The top child element -- @treturn DividedContainer The divided container object function DividedContainer.SetTopChild(self, child) assert(self._isVertical and not self._leftChild and child) self._leftChild = child self.__super:AddChildBeforeById("divider", child) return self end --- Sets the bottom child element in vertical mode. -- @tparam DividedContainer self The divided container object -- @tparam Element child The bottom child element -- @treturn DividedContainer The divided container object function DividedContainer.SetBottomChild(self, child) assert(self._isVertical and not self._rightChild and child) self._rightChild = child self.__super:AddChild(child) return self end function DividedContainer.Draw(self) assert(self._contextTable and self._minLeftWidth and self._minRightWidth) self.__super.__super.__super:Draw() self:GetElement("divider") :SetBackground("ACTIVE_BG") :SetHighlightEnabled(true) local width = self:_GetDimension(self._isVertical and "HEIGHT" or "WIDTH") - DIVIDER_SIZE local leftWidth = self._contextTable.leftWidth + self._resizeOffset local rightWidth = width - leftWidth if rightWidth < self._minRightWidth then leftWidth = width - self._minRightWidth assert(leftWidth >= self._minLeftWidth) elseif leftWidth < self._minLeftWidth then leftWidth = self._minLeftWidth end self._contextTable.leftWidth = leftWidth - self._resizeOffset local leftChild = self._leftChild local rightChild = self._rightChild local leftEmpty = self:GetElement("leftEmpty") local rightEmpty = self:GetElement("rightEmpty") if self._isVertical then leftEmpty:SetHeight(leftWidth) leftChild:SetHeight(leftWidth) else leftEmpty:SetWidth(leftWidth) leftChild:SetWidth(leftWidth) end if self._resizeStartX then leftChild:_GetBaseFrame():SetAlpha(0) leftChild:_GetBaseFrame():SetFrameStrata("LOW") rightChild:_GetBaseFrame():SetAlpha(0) rightChild:_GetBaseFrame():SetFrameStrata("LOW") leftEmpty:Show() rightEmpty:Show() else leftChild:_GetBaseFrame():SetAlpha(1) leftChild:_GetBaseFrame():SetFrameStrata(self:_GetBaseFrame():GetFrameStrata()) rightChild:_GetBaseFrame():SetAlpha(1) rightChild:_GetBaseFrame():SetFrameStrata(self:_GetBaseFrame():GetFrameStrata()) leftEmpty:Hide() rightEmpty:Hide() end self.__super:Draw() end -- ============================================================================ -- Private Helper Functions -- ============================================================================ function private.HandleOnUpdate(handle) local self = handle:GetParentElement() if self._resizeStartX then if self._isVertical then local currY = select(2, GetCursorPosition()) / self:_GetBaseFrame():GetEffectiveScale() self._resizeOffset = self._resizeStartX - currY else local currX = GetCursorPosition() / self:_GetBaseFrame():GetEffectiveScale() self._resizeOffset = currX - self._resizeStartX end self:Draw() end end function private.HandleOnMouseDown(handle, mouseButton) if mouseButton ~= "LeftButton" then return end local self = handle:GetParentElement() if self._isVertical then self._resizeStartX = select(2, GetCursorPosition()) / self:_GetBaseFrame():GetEffectiveScale() else self._resizeStartX = GetCursorPosition() / self:_GetBaseFrame():GetEffectiveScale() end self._resizeOffset = 0 end function private.HandleOnMouseUp(handle, mouseButton) if mouseButton ~= "LeftButton" then return end local self = handle:GetParentElement() self._contextTable.leftWidth = max(self._contextTable.leftWidth + self._resizeOffset, self._minLeftWidth) self._resizeOffset = 0 self._resizeStartX = nil self:Draw() end function private.HandleOnClick(handle, mouseButton) if mouseButton ~= "RightButton" then return end local self = handle:GetParentElement() self._contextTable.leftWidth = self._defaultContextTable.leftWidth self:Draw() end