Files
PathOfBuilding/Classes/ListControl.lua
Openarl 2806cbac3d 1.4.118 Initial commit
- Added/updated skill gems and bases
- Fixed curse stats with wrong sign
- Fixed wrong sources on quality mods
2018-12-09 16:29:54 +13:00

403 lines
14 KiB
Lua

-- Path of Building
--
-- Class: List Control
-- Basic list control.
--
-- This is an abstract base class; derived classes can supply these properties and methods to configure the list control:
-- .label [Adds a label above the top left corner]
-- .dragTargetList [List of controls that can receive drag events from this list control]
-- .showRowSeparators [Shows separators between rows]
-- :GetRowValue(column, index, value) [Required; called to retrieve the text for the given column of the given list value]
-- :AddValueTooltip(index, value) [Called to add the tooltip for the given list value]
-- :GetDragValue(index, value) [Called to retrieve the drag type and object for the given list value]
-- :CanReceiveDrag(type, value) [Called on drag target to determine if it can receive this value]
-- :ReceiveDrag(type, value, source) [Called on the drag target when a drag completes]
-- :OnDragSend(index, value, target) [Called after a drag event]
-- :OnOrderChange() [Called after list order is changed through dragging]
-- :OnSelect(index, value) [Called when a list value is selected]
-- :OnSelClick(index, value, doubleClick) [Called when a list value is clicked]
-- :OnSelCopy(index, value) [Called when Ctrl+C is pressed while a list value is selected]
-- :OnSelDelete(index, value) [Called when backspace or delete is pressed while a list value is selected]
-- :OnSelKeyDown(index, value) [Called when any other key is pressed while a list value is selected]
--
local ipairs = ipairs
local t_insert = table.insert
local t_remove = table.remove
local m_min = math.min
local m_max = math.max
local m_floor = math.floor
local ListClass = newClass("ListControl", "Control", "ControlHost", function(self, anchor, x, y, width, height, rowHeight, scrollH, isMutable, list)
self.Control(anchor, x, y, width, height)
self.ControlHost()
self.rowHeight = rowHeight
self.scrollH = scrollH
self.isMutable = isMutable
self.list = list or { }
self.colList = { { } }
self.tooltip = new("Tooltip")
self.font = "VAR"
self.controls.scrollBarH = new("ScrollBarControl", {"BOTTOM",self,"BOTTOM"}, -8, -1, 0, 16, rowHeight * 2, "HORIZONTAL") {
shown = function()
return self.scrollH
end,
width = function()
local width, height = self:GetSize()
return width - 18
end
}
self.controls.scrollBarV = new("ScrollBarControl", {"RIGHT",self,"RIGHT"}, -1, 0, 16, 0, rowHeight * 2) {
y = function()
return (self.scrollH and -8 or 0)
end,
height = function()
local width, height = self:GetSize()
return height - 2 - (self.scrollH and 16 or 0)
end
}
end)
function ListClass:SelectIndex(index)
self.selValue = self.list[index]
if self.selValue then
self.selIndex = index
local width, height = self:GetSize()
self.controls.scrollBarV:SetContentDimension(#self.list * self.rowHeight, height - 4)
self.controls.scrollBarV:ScrollIntoView((index - 2) * self.rowHeight, self.rowHeight * 3)
if self.OnSelect then
self:OnSelect(self.selIndex, self.selValue)
end
end
end
function ListClass:GetColumnProperty(column, property)
if type(column[property]) == "function" then
return column[property](self, column)
else
return column[property]
end
end
function ListClass:IsMouseOver()
if not self:IsShown() then
return
end
return self:IsMouseInBounds() or self:GetMouseOverControl()
end
function ListClass:GetRowRegion()
local width, height = self:GetSize()
return {
x = 2,
y = self.colLabels and 20 or 2,
width = width - 20,
height = height - 4 - (self.scrollH and 16 or 0) - (self.colLabels and 18 or 0),
}
end
function ListClass:Draw(viewPort)
local x, y = self:GetPos()
local width, height = self:GetSize()
local rowHeight = self.rowHeight
local list = self.list
local colOffset = 0
for index, column in ipairs(self.colList) do
column._offset = colOffset
column._width = self:GetColumnProperty(column, "width") or (index == #self.colList and width - 20 - colOffset) or 0
colOffset = colOffset + column._width
end
local scrollBarV = self.controls.scrollBarV
local rowRegion = self:GetRowRegion()
scrollBarV:SetContentDimension(#list * rowHeight, rowRegion.height)
local scrollOffsetV = scrollBarV.offset
local scrollBarH = self.controls.scrollBarH
local lastCol = self.colList[#self.colList]
scrollBarH:SetContentDimension(lastCol._offset + lastCol._width, rowRegion.width)
local scrollOffsetH = scrollBarH.offset
local cursorX, cursorY = GetCursorPos()
if self.selValue and self.selDragging and not self.selDragActive and (cursorX-self.selCX)*(cursorX-self.selCX)+(cursorY-self.selCY)*(cursorY-self.selCY) > 100 then
self.selDragActive = true
if self.dragTargetList then
self.dragType, self.dragValue = self:GetDragValue(self.selIndex, self.selValue)
for _, target in ipairs(self.dragTargetList) do
if not target.CanReceiveDrag or target:CanReceiveDrag(self.dragType, self.dragValue) then
target.otherDragSource = self
end
end
end
end
self.selDragIndex = nil
if (self.selDragActive or self.otherDragSource) and self.isMutable then
if cursorX >= x + 2 and cursorY >= y + 2 and cursorX < x + width - 20 and cursorY < y + height - 2 then
local index = math.floor((cursorY - y - 2 + scrollOffsetV) / rowHeight + 0.5) + 1
if not self.selIndex or index < self.selIndex or index > self.selIndex + 1 then
self.selDragIndex = m_min(index, #list + 1)
end
end
end
if self.selDragActive and self.dragTargetList then
self.dragTarget = nil
for _, target in ipairs(self.dragTargetList) do
if not self.dragTarget and target.otherDragSource == self and target:IsMouseOver() then
self.dragTarget = target
target.otherDragTargeting = true
else
target.otherDragTargeting = false
end
end
end
local label = self:GetProperty("label")
if label then
DrawString(x, y - 20, "LEFT", 16, self.font, label)
end
if self.otherDragSource and not self.CanDragToValue then
SetDrawColor(0.2, 0.6, 0.2)
elseif self.hasFocus then
SetDrawColor(1, 1, 1)
else
SetDrawColor(0.5, 0.5, 0.5)
end
DrawImage(nil, x, y, width, height)
if self.otherDragSource and not self.CanDragToValue then
SetDrawColor(0, 0.05, 0)
else
SetDrawColor(0, 0, 0)
end
DrawImage(nil, x + 1, y + 1, width - 2, height - 2)
self:DrawControls(viewPort)
SetViewport(x + 2, y + 2, width - 20, height - 4 - (self.scrollH and 16 or 0))
local textOffsetY = self.showRowSeparators and 2 or 0
local textHeight = rowHeight - textOffsetY * 2
local ttIndex, ttValue, ttX, ttY, ttWidth
local minIndex = m_floor(scrollOffsetV / rowHeight + 1)
local maxIndex = m_min(m_floor((scrollOffsetV + height) / rowHeight + 1), #list)
for colIndex, column in ipairs(self.colList) do
local colFont = self:GetColumnProperty(column, "font") or "VAR"
local elipWidth = DrawStringWidth(textHeight, colFont, "...")
local colOffset = column._offset - scrollOffsetH
local colWidth = column._width
for index = minIndex, maxIndex do
local lineY = rowHeight * (index - 1) - scrollOffsetV + (self.colLabels and 18 or 0)
local value = list[index]
local text = self:GetRowValue(colIndex, index, value)
local textWidth = DrawStringWidth(textHeight, colFont, text)
if textWidth > colWidth - 2 then
local clipIndex = DrawStringCursorIndex(textHeight, colFont, text, colWidth - elipWidth - 2, 0)
text = text:sub(1, clipIndex - 1) .. "..."
textWidth = DrawStringWidth(textHeight, colFont, text)
end
if not scrollBarV.dragging and (not self.selDragActive or (self.CanDragToValue and self:CanDragToValue(index, value, self.otherDragSource))) then
local cursorX, cursorY = GetCursorPos()
local relX = cursorX - (x + 2)
local relY = cursorY - (y + 2)
if relX >= colOffset and relX < width - 20 and relY >= 0 and relY >= lineY and relY < height - 2 - (self.scrollH and 18 or 0) and relY < lineY + rowHeight then
ttIndex = index
ttValue = value
ttX = x + 2 + colOffset
ttY = lineY + y + 2
ttWidth = m_max(textWidth + 8, relX - colOffset)
end
end
if self.showRowSeparators then
if self.hasFocus and value == self.selValue then
SetDrawColor(1, 1, 1)
elseif value == ttValue then
SetDrawColor(0.8, 0.8, 0.8)
else
SetDrawColor(0.5, 0.5, 0.5)
end
DrawImage(nil, colOffset, lineY, colWidth, rowHeight)
if (value == self.selValue or value == ttValue) then
SetDrawColor(0.33, 0.33, 0.33)
elseif self.otherDragSource and self.CanDragToValue and self:CanDragToValue(index, value, self.otherDragSource) then
SetDrawColor(0, 0.2, 0)
elseif index % 2 == 0 then
SetDrawColor(0.05, 0.05, 0.05)
else
SetDrawColor(0, 0, 0)
end
DrawImage(nil, colOffset, lineY + 1, colWidth, rowHeight - 2)
elseif value == self.selValue or value == ttValue then
if self.hasFocus and value == self.selValue then
SetDrawColor(1, 1, 1)
elseif value == ttValue then
SetDrawColor(0.8, 0.8, 0.8)
else
SetDrawColor(0.5, 0.5, 0.5)
end
DrawImage(nil, colOffset, lineY, colWidth, rowHeight)
if self.otherDragSource and self.CanDragToValue and self:CanDragToValue(index, value, self.otherDragSource) then
SetDrawColor(0, 0.2, 0)
else
SetDrawColor(0.15, 0.15, 0.15)
end
DrawImage(nil, colOffset, lineY + 1, colWidth, rowHeight - 2)
end
SetDrawColor(1, 1, 1)
DrawString(colOffset, lineY + textOffsetY, "LEFT", textHeight, colFont, text)
end
if self.colLabels then
local cursorX, cursorY = GetCursorPos()
local relX = cursorX - (x + 2)
local relY = cursorY - (y + 2)
SetDrawColor(0.5, 0.5, 0.5)
DrawImage(nil, colOffset, 1, colWidth, 18)
SetDrawColor(0.15, 0.15, 0.15)
DrawImage(nil, colOffset + 1, 2, colWidth - 2, 16)
local label = self:GetColumnProperty(column, "label")
if label and #label > 0 then
SetDrawColor(1, 1, 1)
DrawString(colOffset + colWidth/2, 4, "CENTER_X", 12, "VAR", label)
end
end
end
if #self.list == 0 and self.defaultText then
SetDrawColor(1, 1, 1)
DrawString(2, 2, "LEFT", 14, self.font, self.defaultText)
end
if self.selDragIndex then
local lineY = rowHeight * (self.selDragIndex - 1) - scrollOffsetV
SetDrawColor(1, 1, 1)
DrawImage(nil, 0, lineY - 1, width - 20, 3)
SetDrawColor(0, 0, 0)
DrawImage(nil, 0, lineY, width - 20, 1)
end
SetViewport()
if self.selDragActive and self.dragTargetList and (not self.isMutable or not self:IsMouseOver()) then
main.showDragText = self:GetRowValue(1, self.selIndex, self.selValue)
end
self.hoverIndex = ttIndex
self.hoverValue = ttValue
if ttIndex and self.AddValueTooltip then
SetDrawLayer(nil, 100)
self:AddValueTooltip(self.tooltip, ttIndex, ttValue)
self.tooltip:Draw(ttX, ttY, ttWidth, rowHeight, viewPort)
SetDrawLayer(nil, 0)
end
end
function ListClass:OnKeyDown(key, doubleClick)
if not self:IsShown() or not self:IsEnabled() then
return
end
local mOverControl = self:GetMouseOverControl()
if mOverControl and mOverControl.OnKeyDown then
return mOverControl:OnKeyDown(key)
end
if not self:IsMouseOver() and key:match("BUTTON") then
return
end
if key == "LEFTBUTTON" then
self.selValue = nil
self.selIndex = nil
local x, y = self:GetPos()
local cursorX, cursorY = GetCursorPos()
local rowRegion = self:GetRowRegion()
if cursorX >= x + rowRegion.x and cursorY >= y + rowRegion.y and cursorX < x + rowRegion.x + rowRegion.width and cursorY < y + rowRegion.y + rowRegion.height then
local index = math.floor((cursorY - y - rowRegion.y + self.controls.scrollBarV.offset) / self.rowHeight) + 1
self.selValue = self.list[index]
if self.selValue then
self.selIndex = index
if (self.isMutable or self.dragTargetList) and self:IsShown() then
self.selCX = cursorX
self.selCY = cursorY
self.selDragging = true
self.selDragActive = false
end
if self.OnSelect then
self:OnSelect(self.selIndex, self.selValue)
end
if self.OnSelClick then
self:OnSelClick(self.selIndex, self.selValue, doubleClick)
end
end
end
elseif #self.list > 0 and not self.selDragActive then
if key == "UP" then
self:SelectIndex(((self.selIndex or 1) - 2) % #self.list + 1)
elseif key == "DOWN" then
self:SelectIndex((self.selIndex or #self.list) % #self.list + 1)
elseif key == "HOME" then
self:SelectIndex(1)
elseif key == "END" then
self:SelectIndex(#self.list)
elseif self.selValue then
if key == "c" and IsKeyDown("CTRL") then
if self.OnSelCopy then
self:OnSelCopy(self.selIndex, self.selValue)
end
elseif key == "x" and IsKeyDown("CTRL") then
if self.OnSelCut then
self:OnSelCut(self.selIndex, self.selValue)
end
elseif key == "BACK" or key == "DELETE" then
if self.OnSelDelete then
self:OnSelDelete(self.selIndex, self.selValue)
end
elseif self.OnSelKeyDown then
self:OnSelKeyDown(self.selIndex, self.selValue, key)
end
end
end
return self
end
function ListClass:OnKeyUp(key)
if not self:IsShown() or not self:IsEnabled() then
return
end
if key == "WHEELDOWN" then
if self.scrollH and IsKeyDown("SHIFT") then
self.controls.scrollBarH:Scroll(1)
else
self.controls.scrollBarV:Scroll(1)
end
elseif key == "WHEELUP" then
if self.scrollH and IsKeyDown("SHIFT") then
self.controls.scrollBarH:Scroll(-1)
else
self.controls.scrollBarV:Scroll(-1)
end
elseif self.selValue then
if key == "LEFTBUTTON" then
self.selDragging = false
if self.selDragActive then
self.selDragActive = false
if self.selDragIndex and self.selDragIndex ~= self.selIndex then
t_remove(self.list, self.selIndex)
if self.selDragIndex > self.selIndex then
self.selDragIndex = self.selDragIndex - 1
end
t_insert(self.list, self.selDragIndex, self.selValue)
if self.OnOrderChange then
self:OnOrderChange()
end
self.selValue = nil
elseif self.dragTarget then
self.dragTarget:ReceiveDrag(self.dragType, self.dragValue, self)
if self.OnDragSend then
self:OnDragSend(self.selIndex, self.selValue, self.dragTarget)
end
self.selValue = nil
end
if self.dragTargetList then
for _, target in ipairs(self.dragTargetList) do
target.otherDragSource = nil
target.otherDragTargeting = false
end
end
self.dragType = nil
self.dragValue = nil
self.dragTarget = nil
end
end
end
return self
end