Files
Basalt2/docs/references/elements/Table.md
2025-02-16 14:12:49 +00:00

6.3 KiB

local VisualElement = require("elements/VisualElement") local tHex = require("libraries/colorHex")

---@class Table : VisualElement local Table = setmetatable({}, VisualElement) Table.__index = Table

Table.defineProperty(Table, "columns", {default = {}, type = "table"}) Table.defineProperty(Table, "data", {default = {}, type = "table", canTriggerRender = true}) Table.defineProperty(Table, "selectedRow", {default = nil, type = "number", canTriggerRender = true}) Table.defineProperty(Table, "headerColor", {default = colors.blue, type = "number"}) Table.defineProperty(Table, "selectedColor", {default = colors.lightBlue, type = "number"}) Table.defineProperty(Table, "gridColor", {default = colors.gray, type = "number"}) Table.defineProperty(Table, "sortColumn", {default = nil, type = "number"}) Table.defineProperty(Table, "sortDirection", {default = "asc", type = "string"}) Table.defineProperty(Table, "scrollOffset", {default = 0, type = "number", canTriggerRender = true})

Table.listenTo(Table, "mouse_click") Table.listenTo(Table, "mouse_scroll")

function Table.new() local self = setmetatable({}, Table):__init() self.set("width", 30) self.set("height", 10) self.set("z", 5) return self end

function Table:init(props, basalt) VisualElement.init(self, props, basalt) self.set("type", "Table") return self end

function Table:setColumns(columns) -- Columns Format: {{name="ID", width=4}, {name="Name", width=10}} self.set("columns", columns) return self end

function Table:setData(data) -- Data Format: {{"1", "Item One"}, {"2", "Item Two"}} self.set("data", data) return self end

function Table:sortData(columnIndex) local data = self.get("data") local direction = self.get("sortDirection")

table.sort(data, function(a, b)
    if direction == "asc" then
        return a[columnIndex] < b[columnIndex]
    else
        return a[columnIndex] > b[columnIndex]
    end
end)

self.set("data", data)
return self

end

function Table:mouse_click(button, x, y) if not VisualElement.mouse_click(self, button, x, y) then return false end

local relX, relY = self:getRelativePosition(x, y)

-- Header-Click für Sorting
if relY == 1 then
    local currentX = 1
    for i, col in ipairs(self.get("columns")) do
        if relX >= currentX and relX < currentX + col.width then
            if self.get("sortColumn") == i then
                self.set("sortDirection", self.get("sortDirection") == "asc" and "desc" or "asc")
            else
                self.set("sortColumn", i)
                self.set("sortDirection", "asc")
            end
            self:sortData(i)
            break
        end
        currentX = currentX + col.width
    end
end

-- Row-Selection (berücksichtigt Scroll-Offset)
if relY > 1 then
    local rowIndex = relY - 2 + self.get("scrollOffset")
    if rowIndex >= 0 and rowIndex < #self.get("data") then
        self.set("selectedRow", rowIndex + 1)
    end
end

return true

end

function Table:mouse_scroll(direction, x, y) local data = self.get("data") local height = self.get("height") local visibleRows = height - 2 local maxScroll = math.max(0, #data - visibleRows + 1) -- +1 korrigiert den Scroll-Bereich local newOffset = math.min(maxScroll, math.max(0, self.get("scrollOffset") + direction))

self.set("scrollOffset", newOffset)
return true

end

function Table:render() VisualElement.render(self)

local columns = self.get("columns")
local data = self.get("data")
local selected = self.get("selectedRow")
local sortCol = self.get("sortColumn")
local scrollOffset = self.get("scrollOffset")
local height = self.get("height")

local currentX = 1
for i, col in ipairs(columns) do
    local text = col.name
    if i == sortCol then
        text = text .. (self.get("sortDirection") == "asc" and "\30" or "\31")
    end
    self:textFg(currentX, 1, text:sub(1, col.width), self.get("headerColor"))
    currentX = currentX + col.width
end

-- Angepasste Berechnung der sichtbaren Zeilen
local visibleRows = height - 2  -- Verfügbare Zeilen (minus Header)
for y = 2, height do
    local rowIndex = y - 2 + scrollOffset
    local rowData = data[rowIndex + 1]

    -- Zeile nur rendern wenn es auch Daten dafür gibt
    if rowData and (rowIndex + 1) <= #data then  -- Korrigierte Bedingung
        currentX = 1
        local bg = (rowIndex + 1) == selected and self.get("selectedColor") or self.get("background")

        for i, col in ipairs(columns) do
            local cellText = rowData[i] or ""
            local paddedText = cellText .. string.rep(" ", col.width - #cellText)
            self:blit(currentX, y, paddedText,
                string.rep(tHex[self.get("foreground")], col.width),
                string.rep(tHex[bg], col.width))
            currentX = currentX + col.width
        end
    else
        -- Leere Zeile füllen
        self:blit(1, y, string.rep(" ", self.get("width")),
            string.rep(tHex[self.get("foreground")], self.get("width")),
            string.rep(tHex[self.get("background")], self.get("width")))
    end
end

-- Scrollbar Berechnung überarbeitet
if #data > height - 2 then
    local scrollbarHeight = height - 2
    local thumbSize = math.max(1, math.floor(scrollbarHeight * (height - 2) / #data))
    
    -- Thumb Position korrigiert
    local maxScroll = #data - (height - 2) + 1  -- +1 für korrekte End-Position
    local scrollPercent = scrollOffset / maxScroll
    local thumbPos = 2 + math.floor(scrollPercent * (scrollbarHeight - thumbSize))
    
    if scrollOffset >= maxScroll then
        thumbPos = height - thumbSize  -- Exakt am Ende
    end

    -- Scrollbar Background
    for y = 2, height do
        self:blit(self.get("width"), y, "\127", tHex[colors.gray], tHex[colors.gray])
    end

    -- Thumb zeichnen
    for y = thumbPos, math.min(height, thumbPos + thumbSize - 1) do
        self:blit(self.get("width"), y, "\127", tHex[colors.white], tHex[colors.white])
    end
end

end

return Table