TradeSkillMaster/Core/UI/Elements/DividedContainer.lua

282 lines
9.6 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. --
-- ------------------------------------------------------------------------------ --
--- 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