added search feature for dropdown lists

Closes LocalIdentity/PathOfBuilding#130

(cherry picked from commit 66ca0ee1e4)
This commit is contained in:
Fish013
2020-01-21 17:39:45 +01:00
parent e1e6cfe10f
commit cd840de094
4 changed files with 307 additions and 24 deletions

View File

@@ -8,11 +8,21 @@ local m_min = math.min
local m_max = math.max
local m_floor = math.floor
local DropDownClass = newClass("DropDownControl", "Control", "ControlHost", "TooltipHost", function(self, anchor, x, y, width, height, list, selFunc, tooltipText)
local DropDownClass = newClass("DropDownControl", "Control", "ControlHost", "TooltipHost", "SearchHost", function(self, anchor, x, y, width, height, list, selFunc, tooltipText)
self.Control(anchor, x, y, width, height)
self.ControlHost()
self.TooltipHost(tooltipText)
self.controls.scrollBar = new("ScrollBarControl", {"TOPRIGHT",self,"TOPRIGHT"}, -1, 0, 18, 0, (height - 4) * 4)
self.SearchHost(
-- list to filter
function()
return self.list
end,
-- value mapping function
function(listVal)
return StripEscapes(type(listVal) == "table" and listVal.label or listVal)
end
)
self.controls.scrollBar = new("ScrollBarControl", { "TOPRIGHT", self, "TOPRIGHT" }, -1, 0, 18, 0, (height - 4) * 4)
self.controls.scrollBar.height = function()
return self.dropHeight + 2
end
@@ -25,6 +35,67 @@ local DropDownClass = newClass("DropDownControl", "Control", "ControlHost", "Too
self.selFunc = selFunc
end)
function DropDownClass:DropIndexToListIndex(dropIndex, default)
if dropIndex and dropIndex > 0 and self:IsSearchActive() and self.searchInfos then
for listIndex, info in ipairs(self.searchInfos) do
if info and info.matches then
dropIndex = dropIndex - 1
if (dropIndex <= 0) then
return listIndex
end
end
end
return default -- if drop index > self:GetDropCount()
end
return dropIndex
end
function DropDownClass:ListIndexToDropIndex(listIndex, default)
if listIndex and self:IsSearchActive() and self.searchInfos then
local dropIndex = 0
for listIndexLoop, info in ipairs(self.searchInfos) do
if info and info.matches then
dropIndex = dropIndex + 1
if (listIndex == listIndexLoop) then
return dropIndex
end
end
end
return default -- it is possible that for a given listIndex there is no dropIndex (when it is currently filtered out)
end
return listIndex
end
function DropDownClass:GetDropCount()
if self:IsSearchActive() then
return self:GetMatchCount()
else
return #self.list
end
end
function DropDownClass:DrawSearchHighlights(label, searchInfo, x, y, width, height)
if searchInfo and searchInfo.matches then
local startX = 0
local endX = 0
local last = 0
SetDrawColor(1, 1, 0, 0.2)
for idx, range in ipairs(searchInfo.ranges) do
if range.from - last - 1 > 0 then
startX = DrawStringWidth(height, "VAR", label:sub(last + 1, range.from - 1)) + x + endX
else
startX = endX
end
endX = DrawStringWidth(height, "VAR", label:sub(range.from, range.to)) + x + startX
last = range.to
DrawImage(nil, startX, y, endX - startX, height)
end
SetDrawColor(1, 1, 1)
end
end
function DropDownClass:SelByValue(value, key)
for index, listVal in ipairs(self.list) do
if type(listVal) == "table" then
@@ -46,8 +117,9 @@ function DropDownClass:GetSelValue(key)
end
function DropDownClass:SetSel(newSel)
newSel = m_max(1, m_min(#self.list, newSel))
if newSel ~= self.selIndex then
newSel = m_max(1, m_min(self:GetDropCount(), newSel))
newSel = self:DropIndexToListIndex(newSel)
if newSel and newSel ~= self.selIndex then
self.selIndex = newSel
if self.selFunc then
self.selFunc(newSel, self.list[newSel])
@@ -58,8 +130,8 @@ end
function DropDownClass:ScrollSelIntoView()
local width, height = self:GetSize()
local scrollBar = self.controls.scrollBar
scrollBar:SetContentDimension((height - 4) * #self.list, self.dropHeight)
scrollBar:ScrollIntoView((self.selIndex - 2) * (height - 4), 3 * (height - 4))
scrollBar:SetContentDimension((height - 4) * self:GetDropCount(), self.dropHeight)
scrollBar:ScrollIntoView((self:ListIndexToDropIndex(self.selIndex, 1) - 2) * (height - 4), 3 * (height - 4))
end
function DropDownClass:IsMouseOver()
@@ -120,8 +192,14 @@ function DropDownClass:Draw(viewPort)
end
end
if self.dropped then
self:DrawSearch(viewPort, self.dropUp and "BOTTOM" or "TOP")
else
self:ResetSearch()
end
local dropExtra = self.dropHeight + 4
scrollBar:SetContentDimension(lineHeight * #self.list, self.dropHeight)
scrollBar:SetContentDimension(lineHeight * self:GetDropCount(), self.dropHeight)
local dropY = self.dropUp and y - dropExtra or y + height
if not enabled then
SetDrawColor(0.33, 0.33, 0.33)
@@ -189,43 +267,63 @@ function DropDownClass:Draw(viewPort)
SetDrawLayer(nil, 5)
self:DrawControls(viewPort)
local cursorX, cursorY = GetCursorPos()
self.hoverSel = mOver and not scrollBar:IsMouseOver() and math.floor((cursorY - dropY + scrollBar.offset) / lineHeight) + 1
self.hoverSelDrop = mOver and not scrollBar:IsMouseOver() and math.floor((cursorY - dropY + scrollBar.offset) / lineHeight) + 1
self.hoverSel = self:DropIndexToListIndex(self.hoverSelDrop)
if self.hoverSel and not self.list[self.hoverSel] then
self.hoverSel = nil
end
if self.hoverSel then
SetDrawLayer(nil, 100)
self:DrawTooltip(
x, dropY + 2 + (self.hoverSel - 1) * lineHeight - scrollBar.offset,
x, dropY + 2 + (self.hoverSelDrop - 1) * lineHeight - scrollBar.offset,
width, lineHeight,
viewPort,
"HOVER", self.hoverSel, self.list[self.hoverSel])
SetDrawLayer(nil, 5)
end
SetViewport(x + 2, dropY + 2, scrollBar.enabled and width - 22 or width - 4, self.dropHeight)
local dropIndex = 0
for index, listVal in ipairs(self.list) do
local y = (index - 1) * lineHeight - scrollBar.offset
if index == self.hoverSel then
SetDrawColor(0.5, 0.4, 0.3)
DrawImage(nil, 0, y, width - 4, lineHeight)
local searchInfo = self.searchInfos[index]
if not self:IsSearchActive() or searchInfo and searchInfo.matches then
dropIndex = dropIndex + 1
local y = (dropIndex - 1) * lineHeight - scrollBar.offset
if index == self.hoverSel then
SetDrawColor(0.5, 0.4, 0.3)
DrawImage(nil, 0, y, width - 4, lineHeight)
end
if index == self.hoverSel or index == self.selIndex then
SetDrawColor(1, 1, 1)
else
SetDrawColor(0.66, 0.66, 0.66)
end
local label = StripEscapes(type(listVal) == "table" and listVal.label or listVal)
DrawString(0, y, "LEFT", lineHeight, "VAR", label)
self:DrawSearchHighlights(label, searchInfo, 0, y, width - 4, lineHeight)
end
if index == self.hoverSel or index == self.selIndex then
SetDrawColor(1, 1, 1)
else
SetDrawColor(0.66, 0.66, 0.66)
end
local label = type(listVal) == "table" and listVal.label or listVal
DrawString(0, y, "LEFT", lineHeight, "VAR", StripEscapes(label))
end
SetViewport()
SetDrawColor(1, 1, 1)
SetDrawLayer(nil, 0)
end
end
function DropDownClass:OnChar(key)
if not self:IsShown() or not self:IsEnabled() or not self.dropped then
return
end
return self:OnSearchChar(key)
end
function DropDownClass:OnKeyDown(key)
if not self:IsShown() or not self:IsEnabled() then
return
end
if self.dropped then
if self:OnSearchKeyDown(key) then
return self
end
end
local mOverControl = self:GetMouseOverControl()
if mOverControl and mOverControl.OnKeyDown then
self.selControl = mOverControl
@@ -279,22 +377,22 @@ function DropDownClass:OnKeyUp(key)
if self.dropped and self.controls.scrollBar.enabled then
self.controls.scrollBar:Scroll(1)
else
self:SetSel(self.selIndex + 1)
self:SetSel(self:ListIndexToDropIndex(self.selIndex, 0) + 1)
end
return self
elseif key == "DOWN" then
self:SetSel(self.selIndex + 1)
self:SetSel(self:ListIndexToDropIndex(self.selIndex, 0) + 1)
self:ScrollSelIntoView()
return self
elseif key == "WHEELUP" then
if self.dropped and self.controls.scrollBar.enabled then
self.controls.scrollBar:Scroll(-1)
else
self:SetSel(self.selIndex - 1)
self:SetSel(self:ListIndexToDropIndex(self.selIndex, 0) - 1)
end
return self
elseif key == "UP" then
self:SetSel(self.selIndex - 1)
self:SetSel(self:ListIndexToDropIndex(self.selIndex, 0) - 1)
self:ScrollSelIntoView()
return self
end