local utils = require("utils") local tHex = require("tHex") return function(name, basalt) local base = basalt.getObject("ChangeableObject")(name, basalt) base:setType("Treeview") base:addProperty("Nodes", "table", {}) base:addProperty("SelectionBackground", "color", colors.black) base:addProperty("SelectionForeground", "color", colors.lightGray) base:combineProperty("SelectionColor", "SelectionBackground", "SelectionForeground") base:addProperty("XOffset", "number", 0) base:addProperty("YOffset", "number", 0) base:combineProperty("Offset", "XOffset", "YOffset") base:addProperty("Scrollable", "boolean", true) base:addProperty("TextAlign", {"left", "center", "right"}, "left") base:addProperty("ExpandableSymbol", "char", "\7") base:addProperty("ExpandableSymbolForeground", "color", colors.lightGray) base:addProperty("ExpandableSymbolBackground", "color", colors.black) base:combineProperty("ExpandableSymbolColor", "ExpandableSymbolForeground", "ExpandableSymbolBackground") base:addProperty("ExpandedSymbol", "char", "\8") base:addProperty("ExpandedSymbolForeground", "color", colors.lightGray) base:addProperty("ExpandedSymbolBackground", "color", colors.black) base:combineProperty("ExpandedSymbolColor", "ExpandedSymbolForeground", "ExpandedSymbolBackground") base:addProperty("ExpandableSymbolSpacing", "number", 1) base:addProperty("selectionColorActive", "boolean", true) base:setSize(16, 8) base:setZ(5) local function newNode(text, expandable) text = text or "" expandable = expandable or false local expanded = false local parent = nil local children = {} local node = {} local onSelect node = { getChildren = function(self) return children end, setParent = function(self, p) if(parent~=nil)then parent.removeChild(parent.findChildrenByText(node.getText())) end parent = p base:updateDraw() return node end, getParent = function(self) return parent end, addChild = function(self, text, expandable) local childNode = newNode(text, expandable) childNode.setParent(node) table.insert(children, childNode) base:updateDraw() return childNode end, setExpanded = function(self, exp) if(expandable)then expanded = exp end base:updateDraw() return node end, isExpanded = function(self) return expanded end, onSelect = function(self, ...) for _,v in pairs(table.pack(...))do if(type(v)=="function")then onSelect = v end end return node end, callOnSelect = function(self) if(onSelect~=nil)then onSelect(node) end end, setExpandable = function(self, _expandable) expandable = _expandable base:updateDraw() return node end, isExpandable = function(self) return expandable end, removeChild = function(self, index) if(type(index)=="table")then for k,v in pairs(index)do if(v==index)then index = k break end end end table.remove(children, index) base:updateDraw() return node end, findChildrenByText = function(self, searchText) local foundNodes = {} for _, child in ipairs(children) do if string.find(child.getText(), searchText) then table.insert(foundNodes, child) end end return foundNodes end, getText = function(self) return text end, setText = function(self, t) text = t base:updateDraw() return node end } return node end local root = newNode("Root", true) base:addProperty("Root", "table", root, false, function(self, value) value.setParent(nil) end) root:setExpanded(true) local object = { init = function(self) local parent = self:getParent() self:listenEvent("mouse_click") self:listenEvent("mouse_scroll") return base.init(self) end, getBase = function(self) return base end, onSelect = function(self, ...) for _,v in pairs(table.pack(...))do if(type(v)=="function")then self:registerEvent("treeview_select", v) end end return self end, selectionHandler = function(self, node) node.callOnSelect(node) self:sendEvent("treeview_select", node) return self end, mouseHandler = function(self, button, x, y) if base.mouseHandler(self, button, x, y) then local currentLine = 1 - self:getYOffset() local obx, oby = self:getAbsolutePosition() local w, h = self:getSize() local function checkNodeClick(node, level) if y == oby+currentLine-1 then if x >= obx and x < obx + w then node:setExpanded(not node:isExpanded()) self:selectionHandler(node) self:setValue(node) self:updateDraw() return true end end currentLine = currentLine + 1 if node:isExpanded() then for _, child in ipairs(node:getChildren()) do if checkNodeClick(child, level + 1) then return true end end end return false end for _, item in ipairs(root:getChildren()) do if checkNodeClick(item, 1) then return true end end end end, scrollHandler = function(self, dir, x, y) if base.scrollHandler(self, dir, x, y) then local scrollable = self:getScrollable() local yOffset = self:getYOffset() if scrollable then local _, h = self:getSize() yOffset = yOffset + dir if yOffset < 0 then yOffset = 0 end if dir >= 1 then local visibleLines = 0 local function countVisibleLines(node, level) visibleLines = visibleLines + 1 if node:isExpanded() then for _, child in ipairs(node:getChildren()) do countVisibleLines(child, level + 1) end end end for _, item in ipairs(root:getChildren()) do countVisibleLines(item, 1) end if visibleLines > h then if yOffset > visibleLines - h then yOffset = visibleLines - h end else yOffset = yOffset - 1 end end self:setYOffset(yOffset) self:updateDraw() end return true end return false end, draw = function(self) base.draw(self) self:addDraw("treeview", function() local xOffset, yOffset = self:getOffset() local itemSelectedBG self:getSelectionBackground() local itemSelectedFG self:getSelectionForeground() local currentLine = 1 - yOffset local lastClickedNode = self:getValue() local function drawNode(node, level) local w, h = self:getSize() if currentLine >= 1 and currentLine <= h then local bg = (node == lastClickedNode) and itemSelectedBG or self:getBackground() local fg = (node == lastClickedNode) and itemSelectedFG or self:getForeground() local text = node.getText() self:addBlit(1 + level + xOffset, currentLine, text, tHex[fg]:rep(#text), tHex[bg]:rep(#text)) end currentLine = currentLine + 1 if node:isExpanded() then for _, child in ipairs(node:getChildren()) do drawNode(child, level + 1) end end end for _, item in ipairs(root:getChildren()) do drawNode(item, 1) end end) end, } object.__index = object return setmetatable(object, base) end