You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
327 lines
12 KiB
327 lines
12 KiB
--========================================================--
|
|
-- Scorpio Element Panel --
|
|
-- --
|
|
-- Author : kurapica125@outlook.com --
|
|
-- Create Date : 2020/11/12 --
|
|
--========================================================--
|
|
|
|
--========================================================--
|
|
Scorpio "Scorpio.UI.ElementPanel" "1.0.0"
|
|
--========================================================--
|
|
|
|
__Sealed__() class "ElementPanel" (function(_ENV)
|
|
inherit "Frame" extend "ICountable"
|
|
|
|
export{ max = math.max }
|
|
|
|
local function onSizeChanged(self)
|
|
return self:Refresh()
|
|
end
|
|
|
|
local function adjustElement(element, self)
|
|
local id = element:GetID()
|
|
if not id then return end
|
|
|
|
element:SetSize(self.ElementWidth, self.ElementHeight)
|
|
|
|
local posX = (self.Orientation == Orientation.HORIZONTAL and (id - 1) % self.ColumnCount or floor((id - 1) / self.RowCount)) * (self.ElementWidth + self.HSpacing)
|
|
local posY = (self.Orientation == Orientation.HORIZONTAL and floor((id - 1) / self.ColumnCount) or (id - 1) % self.RowCount) * (self.ElementHeight + self.VSpacing)
|
|
|
|
element:ClearAllPoints()
|
|
|
|
if self.TopToBottom then
|
|
element:SetPoint("TOP", 0, - posY - self.MarginTop)
|
|
else
|
|
element:SetPoint("BOTTOM", 0, posY + self.MarginBottom)
|
|
end
|
|
|
|
if self.LeftToRight then
|
|
element:SetPoint("LEFT", posX + self.MarginLeft, 0)
|
|
else
|
|
element:SetPoint("RIGHT", - posX - self.MarginRight, 0)
|
|
end
|
|
end
|
|
|
|
local function adjustPanel(self)
|
|
if self.KeepMaxSize then
|
|
if not self.FixedWidth then
|
|
self:SetWidth(self.ColumnCount * self.ElementWidth + (self.ColumnCount - 1) * self.HSpacing + self.MarginLeft + self.MarginRight)
|
|
end
|
|
|
|
if not self.FixedHeight then
|
|
self:SetHeight(self.RowCount * self.ElementHeight + (self.RowCount - 1) * self.VSpacing + self.MarginTop + self.MarginBottom)
|
|
end
|
|
else
|
|
local i = self.Count
|
|
|
|
while i > 0 do
|
|
if self[i]:IsShown() then break end
|
|
i = i - 1
|
|
end
|
|
|
|
local row
|
|
local column
|
|
|
|
if self.Orientation == Orientation.HORIZONTAL then
|
|
row = ceil(i / self.ColumnCount)
|
|
column = row == 1 and i or self.ColumnCount
|
|
else
|
|
column = ceil(i / self.RowCount)
|
|
row = column == 1 and i or self.RowCount
|
|
end
|
|
|
|
if self.KeepColumnSize then
|
|
column = self.ColumnCount
|
|
if row == 0 then row = 1 end
|
|
end
|
|
if self.KeepRowSize then
|
|
row = self.RowCount
|
|
if column == 0 then column = 1 end
|
|
end
|
|
|
|
if row > 0 and column > 0 then
|
|
if not self.FixedWidth then
|
|
self:SetWidth(column * self.ElementWidth + (column - 1) * self.HSpacing + self.MarginLeft + self.MarginRight)
|
|
end
|
|
|
|
if not self.FixedHeight then
|
|
self:SetHeight(row * self.ElementHeight + (row - 1) * self.VSpacing + self.MarginTop + self.MarginBottom)
|
|
end
|
|
else
|
|
if not self.FixedWidth then
|
|
self:SetWidth(1)
|
|
end
|
|
|
|
if not self.FixedHeight then
|
|
self:SetHeight(1)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function reduce(self, index)
|
|
index = index or self.RowCount * self.ColumnCount
|
|
|
|
if index < self.Count then
|
|
for i = self.Count, index + 1, -1 do
|
|
local ele = self[i]
|
|
|
|
-- still keep them to be re-use
|
|
ele:ClearAllPoints()
|
|
ele:Hide()
|
|
|
|
OnElementRemove(self, ele)
|
|
|
|
self.__ElementPanel_Count = i - 1
|
|
end
|
|
|
|
if self.FixedHeight or self.FixedWidth then
|
|
return self:Refresh()
|
|
else
|
|
return adjustPanel(self)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function generate(self, index)
|
|
if self.ElementType and index > self.Count then
|
|
local ele
|
|
|
|
for i = self.Count + 1, index do
|
|
local ele = self[i]
|
|
|
|
if not ele then
|
|
ele = self.ElementType(self.ElementPrefix .. i, self)
|
|
self[i] = ele
|
|
ele:SetID(i)
|
|
|
|
OnElementCreated(self, ele)
|
|
end
|
|
|
|
ele:Show()
|
|
|
|
adjustElement(ele, self)
|
|
OnElementAdd(self, ele)
|
|
|
|
self.__ElementPanel_Count = i
|
|
end
|
|
|
|
if self.FixedHeight or self.FixedWidth then
|
|
return self:Refresh()
|
|
else
|
|
return adjustPanel(self)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function nextItem(self, index)
|
|
index = index + 1
|
|
local ele = self[index]
|
|
if ele then return index, ele end
|
|
end
|
|
|
|
------------------------------------------------------
|
|
-- Event
|
|
------------------------------------------------------
|
|
-- Fired when an element is added
|
|
event "OnElementAdd"
|
|
|
|
-- Fired when an element is removed
|
|
event "OnElementRemove"
|
|
|
|
-- Fired when an element is created
|
|
event "OnElementCreated"
|
|
|
|
------------------------------------------------------
|
|
-- Method
|
|
------------------------------------------------------
|
|
function GetIterator(self, key)
|
|
return nextItem, self, tonumber(key) or 0
|
|
end
|
|
|
|
__AsyncSingle__()
|
|
function Refresh(self)
|
|
Next()
|
|
|
|
reduce(self)
|
|
|
|
if self.FixedHeight or self.FixedWidth then
|
|
self:SetAttribute("__ElementPanel_FixedSize", true)
|
|
self.OnSizeChanged = self.OnSizeChanged + onSizeChanged
|
|
|
|
local row
|
|
local column
|
|
|
|
if self.Orientation == Orientation.HORIZONTAL then
|
|
row = ceil(self.Count / self.ColumnCount)
|
|
column = row == 1 and self.Count or self.ColumnCount
|
|
else
|
|
column = ceil(self.Count / self.RowCount)
|
|
row = column == 1 and self.Count or self.RowCount
|
|
end
|
|
|
|
if self.FixedWidth then
|
|
local total = self:GetWidth()
|
|
self.ElementWidth = max(1, (total - self.MarginLeft - self.MarginRight - (column - 1) * self.HSpacing) / column)
|
|
end
|
|
|
|
if self.FixedHeight then
|
|
local total = self:GetHeight()
|
|
self.ElementHeight = max(1, (total - self.MarginTop - self.MarginBottom - (row - 1) * self.VSpacing) / row)
|
|
end
|
|
elseif self:GetAttribute("__ElementPanel_FixedSize") then
|
|
self:SetAttribute("__ElementPanel_FixedSize", nil)
|
|
self.OnSizeChanged = self.OnSizeChanged - onSizeChanged
|
|
end
|
|
|
|
self:Each(adjustElement, self)
|
|
|
|
adjustPanel(self)
|
|
end
|
|
|
|
------------------------------------------------------
|
|
-- Property
|
|
------------------------------------------------------
|
|
-- The Element accessor, used like obj.Element[i].
|
|
__Indexer__(NaturalNumber)
|
|
property "Elements" {
|
|
get = function(self, index)
|
|
if index >= 1 and index <= self.ColumnCount * self.RowCount then
|
|
if self[index] then return self[index] end
|
|
|
|
if self.ElementType then
|
|
generate(self, index)
|
|
|
|
return self[index]
|
|
else
|
|
return nil
|
|
end
|
|
end
|
|
end,
|
|
}
|
|
|
|
-- The columns's count
|
|
property "ColumnCount" { type = PositiveNumber, default = 8, handler = Refresh }
|
|
|
|
-- The row's count
|
|
property "RowCount" { type = PositiveNumber, default = 8, handler = Refresh }
|
|
|
|
-- The elements's max count
|
|
property "MaxCount" { Get = function(self) return self.ColumnCount * self.RowCount end }
|
|
|
|
-- The element's width
|
|
property "ElementWidth" { type = PositiveNumber, default = 16, handler = Refresh }
|
|
|
|
-- The element's height
|
|
property "ElementHeight" { type = PositiveNumber, default = 16, handler = Refresh }
|
|
|
|
-- The element's count
|
|
property "Count" {
|
|
type = NaturalNumber,
|
|
field = "__ElementPanel_Count",
|
|
set = function(self, cnt)
|
|
if cnt > self.RowCount * self.ColumnCount then
|
|
error("Count can't be more than "..self.RowCount * self.ColumnCount, 2)
|
|
end
|
|
|
|
if cnt > self.Count then
|
|
if self.ElementType then
|
|
generate(self, cnt)
|
|
else
|
|
error("ElementType not set.", 2)
|
|
end
|
|
elseif cnt < self.Count then
|
|
reduce(self, cnt)
|
|
end
|
|
end,
|
|
default = 0,
|
|
}
|
|
|
|
-- The orientation for elements
|
|
property "Orientation" { type = Orientation, default = Orientation.HORIZONTAL, handler = Refresh }
|
|
|
|
-- Whether the elements start from left to right
|
|
property "LeftToRight" { type = Boolean, default = true, handler = Refresh }
|
|
|
|
-- Whether the elements start from top to bottom
|
|
property "TopToBottom" { type = Boolean, default = true, handler = Refresh }
|
|
|
|
-- The element's type
|
|
property "ElementType" { type = ClassType }
|
|
|
|
-- The horizontal spacing
|
|
property "HSpacing" { type = Number, default = 0, handler = Refresh }
|
|
|
|
-- The vertical spacing
|
|
property "VSpacing" { type = Number, default = 0, handler = Refresh }
|
|
|
|
-- The top margin
|
|
property "MarginTop" { type = Number, default = 0, handler = Refresh }
|
|
|
|
-- The bottom margin
|
|
property "MarginBottom" { type = Number, default = 0, handler = Refresh }
|
|
|
|
-- The left margin
|
|
property "MarginLeft" { type = Number, default = 0, handler = Refresh }
|
|
|
|
-- The right margin
|
|
property "MarginRight" { type = Number, default = 0, handler = Refresh }
|
|
|
|
-- The prefix for the element's name
|
|
property "ElementPrefix" { type = String, default = "Element" }
|
|
|
|
-- Whether the elementPanel should keep it's max size
|
|
property "KeepMaxSize" { type = Boolean, handler = Refresh }
|
|
|
|
-- Whether keep the max size for columns
|
|
property "KeepColumnSize" { type = Boolean, handler = Refresh }
|
|
|
|
-- Whether keep the max size for rows
|
|
property "KeepRowSize" { type = Boolean, handler = Refresh }
|
|
|
|
-- Whether the panel's height is fixed, so the element height will be modified
|
|
property "FixedHeight" { type = Boolean, handler = Refresh }
|
|
|
|
-- Whether the panel's width is fixed, so the element width will be modified
|
|
property "FixedWidth" { type = Boolean, handler = Refresh }
|
|
end)
|
|
|