Docs Update

This commit is contained in:
Robert Jelic
2025-02-18 09:46:32 +01:00
parent d821bfd6a6
commit 8b6eaccd18
33 changed files with 1477 additions and 418 deletions

View File

@@ -31,6 +31,7 @@ BaseElement.defineProperty(BaseElement, "name", {default = "", type = "string"})
BaseElement.defineProperty(BaseElement, "eventCallbacks", {default = {}, type = "table"})
--- Registers an event that this class can listen to
--- @shortDescription Registers an event that this class can listen to
--- @param class table The class to add the event to
--- @param eventName string The name of the event to register
--- @usage BaseElement.listenTo(MyClass, "mouse_click")
@@ -42,16 +43,18 @@ function BaseElement.listenTo(class, eventName)
end
--- Creates a new BaseElement instance
--- @shortDescription Creates a new BaseElement instance
--- @param props table The properties to initialize the element with
--- @param basalt table The basalt instance
--- @return table The newly created BaseElement instance
--- @usage local element = BaseElement.new("myId", basalt)
--- @usage local element = BaseElement.new()
function BaseElement.new()
local self = setmetatable({}, BaseElement):__init()
return self
end
--- Initializes the BaseElement instance
--- @shortDescription Initializes the BaseElement instance
--- @param props table The properties to initialize the element with
--- @param basalt table The basalt instance
--- @return table self The initialized instance
@@ -76,6 +79,7 @@ function BaseElement:init(props, basalt)
end
--- Post initialization
--- @shortDescription Post initialization
--- @return table self The BaseElement instance
function BaseElement:postInit()
if(self._props)then
@@ -88,6 +92,7 @@ function BaseElement:postInit()
end
--- Checks if the element is a specific type
--- @shortDescription Checks if the element is a specific type
--- @param type string The type to check for
--- @return boolean Whether the element is of the specified type
function BaseElement:isType(type)
@@ -100,6 +105,7 @@ function BaseElement:isType(type)
end
--- Enables or disables event listening for a specific event
--- @shortDescription Enables or disables event listening for a specific event
--- @param eventName string The name of the event to listen for
--- @param enable? boolean Whether to enable or disable the event (default: true)
--- @return table self The BaseElement instance
@@ -123,6 +129,7 @@ function BaseElement:listenEvent(eventName, enable)
end
--- Registers a callback function for an event
--- @shortDescription Registers a callback function
--- @param event string The event to register the callback for
--- @param callback function The callback function to register
--- @return table self The BaseElement instance
@@ -141,6 +148,7 @@ function BaseElement:registerCallback(event, callback)
end
--- Triggers an event and calls all registered callbacks
--- @shortDescription Triggers an event and calls all registered callbacks
--- @param event string The event to fire
--- @param ... any Additional arguments to pass to the callbacks
--- @return table self The BaseElement instance
@@ -156,6 +164,7 @@ function BaseElement:fireEvent(event, ...)
end
--- Handles all events
--- @shortDescription Handles all events
--- @param event string The event to handle
--- @vararg any The arguments for the event
--- @return boolean? handled Whether the event was handled
@@ -167,6 +176,7 @@ function BaseElement:dispatchEvent(event, ...)
end
--- The default event handler for all events
--- @shortDescription The default event handler for all events
--- @param event string The event to handle
--- @vararg any The arguments for the event
--- @return boolean? handled Whether the event was handled
@@ -175,6 +185,7 @@ function BaseElement:handleEvent(event, ...)
end
--- Returns the base frame of the element
--- @shortDescription Returns the base frame of the element
--- @return table BaseFrame The base frame of the element
function BaseElement:getBaseFrame()
if self.parent then
@@ -184,6 +195,7 @@ function BaseElement:getBaseFrame()
end
--- Destroys the element and cleans up all references
--- @shortDescription Destroys the element and cleans up all references
--- @usage element:destroy()
function BaseElement:destroy()
-- Remove from parent if exists
@@ -206,6 +218,7 @@ function BaseElement:destroy()
end
--- Requests a render update for this element
--- @shortDescription Requests a render update for this element
--- @usage element:updateRender()
function BaseElement:updateRender()
if(self.parent) then

View File

@@ -2,11 +2,14 @@ local elementManager = require("elementManager")
local Container = elementManager.getElement("Container")
local Render = require("render")
--- This is the base frame class. It is the root element of all elements and the only element without a parent.
---@class BaseFrame : Container
---@field _render Render The render object
---@field _renderUpdate boolean Whether the render object needs to be updated
local BaseFrame = setmetatable({}, Container)
BaseFrame.__index = BaseFrame
---@property text term term nil text
---@property text term nil The terminal object to render to
BaseFrame.defineProperty(BaseFrame, "term", {default = nil, type = "table", setter = function(self, value)
if value == nil or value.setCursorPos == nil then
return value
@@ -19,6 +22,10 @@ BaseFrame.defineProperty(BaseFrame, "term", {default = nil, type = "table", sett
return value
end})
--- Creates a new Frame instance
--- @shortDescription Creates a new Frame instance
--- @return BaseFrame object The newly created Frame instance
--- @usage local element = BaseFrame.new()
function BaseFrame.new()
local self = setmetatable({}, BaseFrame):__init()
self.set("term", term.current())
@@ -26,32 +33,73 @@ function BaseFrame.new()
return self
end
--- Initializes the Frame instance
--- @shortDescription Initializes the Frame instance
--- @param props table The properties to initialize the element with
--- @param basalt table The basalt instance
--- @return table self The initialized instance
function BaseFrame:init(props, basalt)
Container.init(self, props, basalt)
self.set("type", "BaseFrame")
return self
end
--- Renders a multiBlit to the render Object
--- @shortDescription Renders a multiBlit to the render Object
--- @param x number The x position to render to
--- @param y number The y position to render to
--- @param width number The width of the text
--- @param height number The height of the text
--- @param text string The text to render
--- @param fg string The foreground color
--- @param bg string The background color
function BaseFrame:multiBlit(x, y, width, height, text, fg, bg)
self._render:multiBlit(x, y, width, height, text, fg, bg)
end
--- Renders a text with a foreground color to the render Object
--- @shortDescription Renders a text with a foreground color to the render Object
--- @param x number The x position to render to
--- @param y number The y position to render to
--- @param text string The text to render
--- @param fg colors The foreground color
function BaseFrame:textFg(x, y, text, fg)
self._render:textFg(x, y, text, fg)
end
--- Renders a text with a background color to the render Object
--- @shortDescription Renders a text with a background color to the render Object
--- @param x number The x position to render to
--- @param y number The y position to render to
--- @param text string The text to render
--- @param bg colors The background color
function BaseFrame:textBg(x, y, text, bg)
self._render:textBg(x, y, text, bg)
end
--- Renders a text with a foreground and background color to the render Object
--- @shortDescription Renders a text with a foreground and background color to the render Object
--- @param x number The x position to render to
--- @param y number The y position to render to
--- @param text string The text to render
--- @param fg string The foreground color
--- @param bg string The background color
function BaseFrame:blit(x, y, text, fg, bg)
self._render:blit(x, y, text, fg, bg)
end
--- Sets the cursor position
--- @shortDescription Sets the cursor position
--- @param x number The x position to set the cursor to
--- @param y number The y position to set the cursor to
--- @param blink boolean Whether the cursor should blink
function BaseFrame:setCursor(x, y, blink)
local term = self.get("term")
self._render:setCursor(x, y, blink)
end
--- Renders the Frame
--- @shortDescription Renders the Frame
function BaseFrame:render()
if(self._renderUpdate) then
if self._render ~= nil then

View File

@@ -2,6 +2,7 @@ local elementManager = require("elementManager")
local VisualElement = elementManager.getElement("VisualElement")
local getCenteredPosition = require("libraries/utils").getCenteredPosition
--- This is the button class. It is a visual element that can be clicked.
---@class Button : VisualElement
local Button = setmetatable({}, VisualElement)
Button.__index = Button
@@ -14,6 +15,7 @@ Button.listenTo(Button, "mouse_click")
Button.listenTo(Button, "mouse_up")
--- Creates a new Button instance
--- @shortDescription Creates a new Button instance
--- @return table self The created instance
function Button.new()
local self = setmetatable({}, Button):__init()
@@ -24,6 +26,7 @@ function Button.new()
end
--- Initializes the Button instance
--- @shortDescription Initializes the Button instance
--- @param props table The properties to initialize the element with
--- @param basalt table The basalt instance
function Button:init(props, basalt)
@@ -32,6 +35,7 @@ function Button:init(props, basalt)
end
--- Renders the Button
--- @shortDescription Renders the Button
function Button:render()
VisualElement.render(self)
local text = self.get("text")

View File

@@ -1,5 +1,6 @@
local VisualElement = require("elements/VisualElement")
--- This is the checkbox class. It is a visual element that can be checked.
---@class Checkbox : VisualElement
local Checkbox = setmetatable({}, VisualElement)
Checkbox.__index = Checkbox
@@ -14,6 +15,7 @@ Checkbox.defineProperty(Checkbox, "symbol", {default = "x", type = "string"})
Checkbox.listenTo(Checkbox, "mouse_click")
--- Creates a new Checkbox instance
--- @shortDescription Creates a new Checkbox instance
--- @return Checkbox self The created instance
function Checkbox.new()
local self = setmetatable({}, Checkbox):__init()
@@ -23,6 +25,7 @@ function Checkbox.new()
end
--- Initializes the Checkbox instance
--- @shortDescription Initializes the Checkbox instance
--- @param props table The properties to initialize the element with
--- @param basalt table The basalt instance
function Checkbox:init(props, basalt)
@@ -31,10 +34,11 @@ function Checkbox:init(props, basalt)
end
--- Handles mouse click events
--- @shortDescription Handles mouse click events
--- @param button number The button that was clicked
--- @param x number The x position of the click
--- @param y number The y position of the click
--- @return boolean Whether the event was handled
--- @return boolean Clicked Whether the event was handled
function Checkbox:mouse_click(button, x, y)
if VisualElement.mouse_click(self, button, x, y) then
self.set("checked", not self.get("checked"))
@@ -45,6 +49,7 @@ function Checkbox:mouse_click(button, x, y)
end
--- Renders the Checkbox
--- @shortDescription Renders the Checkbox
function Checkbox:render()
VisualElement.render(self)

View File

@@ -5,6 +5,8 @@ local split = require("libraries/utils").split
local max = math.max
--- The container class. It is a visual element that can contain other elements. It is the base class for all containers,
--- like Frames, BaseFrames, and more.
---@class Container : VisualElement
local Container = setmetatable({}, VisualElement)
Container.__index = Container
@@ -66,6 +68,7 @@ for k, _ in pairs(elementManager:getElementList()) do
end
--- Creates a new Container instance
--- @shortDescription Creates a new Container instance
--- @return Container self The new container instance
function Container.new()
local self = setmetatable({}, Container):__init()
@@ -73,6 +76,7 @@ function Container.new()
end
--- Initializes the Container instance
--- @shortDescription Initializes the Container instance
--- @param props table The properties to initialize the element with
--- @param basalt table The basalt instance
function Container:init(props, basalt)
@@ -81,6 +85,7 @@ function Container:init(props, basalt)
end
--- Returns whether a child is visible
--- @shortDescription Returns whether a child is visible
--- @param child table The child to check
--- @return boolean boolean the child is visible
function Container:isChildVisible(child)
@@ -95,6 +100,7 @@ function Container:isChildVisible(child)
end
--- Adds a child to the container
--- @shortDescription Adds a child to the container
--- @param child table The child to add
--- @return Container self The container instance
function Container:addChild(child)
@@ -138,6 +144,7 @@ local function sortAndFilterChildren(self, children)
end
--- Clears the container
--- @shortDescription Clears the container
--- @return Container self The container instance
function Container:clear()
self.set("children", {})
@@ -150,6 +157,7 @@ function Container:clear()
end
--- Sorts the children of the container
--- @shortDescription Sorts the children of the container
--- @return Container self The container instance
function Container:sortChildren()
self.set("visibleChildren", sortAndFilterChildren(self, self._values.children))
@@ -158,6 +166,7 @@ function Container:sortChildren()
end
--- Sorts the children events of the container
--- @shortDescription Sorts the children events of the container
--- @param eventName string The event name to sort
--- @return Container self The container instance
function Container:sortChildrenEvents(eventName)
@@ -169,6 +178,7 @@ function Container:sortChildrenEvents(eventName)
end
--- Registers the children events of the container
--- @shortDescription Registers the children events of the container
--- @param child table The child to register events for
--- @return Container self The container instance
function Container:registerChildrenEvents(child)
@@ -180,6 +190,7 @@ function Container:registerChildrenEvents(child)
end
--- Registers the children events of the container
--- @shortDescription Registers the children events of the container
--- @param child table The child to register events for
--- @param eventName string The event name to register
--- @return Container self The container instance
@@ -206,6 +217,7 @@ function Container:registerChildEvent(child, eventName)
end
--- Unregisters the children events of the container
--- @shortDescription Unregisters the children events of the container
--- @param child table The child to unregister events for
--- @return Container self The container instance
function Container:removeChildrenEvents(child)
@@ -217,6 +229,7 @@ function Container:removeChildrenEvents(child)
end
--- Unregisters the children events of the container
--- @shortDescription Unregisters the children events of the container
--- @param child table The child to unregister events for
--- @param eventName string The event name to unregister
--- @return Container self The container instance
@@ -243,6 +256,7 @@ function Container:unregisterChildEvent(child, eventName)
end
--- Removes a child from the container
--- @shortDescription Removes a child from the container
--- @param child table The child to remove
--- @return Container self The container instance
function Container:removeChild(child)
@@ -258,6 +272,7 @@ function Container:removeChild(child)
end
--- Removes a child from the container
--- @shortDescription Removes a child from the container
--- @param path string The path to the child to remove
--- @return Container? self The container instance
function Container:getChild(path)
@@ -303,9 +318,10 @@ local function callChildrenEvents(self, visibleOnly, event, ...)
end
--- Default handler for events
--- @shortDescription Default handler for events
--- @param event string The event to handle
--- @vararg any The event arguments
--- @return boolean Whether the event was handled
--- @return boolean handled Whether the event was handled
function Container:handleEvent(event, ...)
VisualElement.handleEvent(self, event, ...)
local args = convertMousePosition(self, event, ...)
@@ -313,10 +329,11 @@ function Container:handleEvent(event, ...)
end
--- Handles mouse click events
--- @shortDescription Handles mouse click events
--- @param button number The button that was clicked
--- @param x number The x position of the click
--- @param y number The y position of the click
--- @return boolean Whether the event was handled
--- @return boolean handled Whether the event was handled
function Container:mouse_click(button, x, y)
if VisualElement.mouse_click(self, button, x, y) then
local args = convertMousePosition(self, "mouse_click", button, x, y)
@@ -332,10 +349,11 @@ function Container:mouse_click(button, x, y)
end
--- Handles mouse up events
--- @shortDescription Handles mouse up events
--- @param button number The button that was clicked
--- @param x number The x position of the click
--- @param y number The y position of the click
--- @return boolean Whether the event was handled
--- @return boolean handled Whether the event was handled
function Container:mouse_up(button, x, y)
if VisualElement.mouse_up(self, button, x, y) then
local args = convertMousePosition(self, "mouse_up", button, x, y)
@@ -348,8 +366,9 @@ function Container:mouse_up(button, x, y)
end
--- Handles key events
--- @shortDescription Handles key events
--- @param key number The key that was pressed
--- @return boolean Whether the event was handled
--- @return boolean handled Whether the event was handled
function Container:key(key)
if self.get("focusedChild") then
return self.get("focusedChild"):dispatchEvent("key", key)
@@ -358,8 +377,9 @@ function Container:key(key)
end
--- Handles char events
--- @shortDescription Handles char events
--- @param char string The character that was pressed
--- @return boolean Whether the event was handled
--- @return boolean handled Whether the event was handled
function Container:char(char)
if self.get("focusedChild") then
return self.get("focusedChild"):dispatchEvent("char", char)
@@ -368,8 +388,9 @@ function Container:char(char)
end
--- Handles key up events
--- @shortDescription Handles key up events
--- @param key number The key that was released
--- @return boolean Whether the event was handled
--- @return boolean handled Whether the event was handled
function Container:key_up(key)
if self.get("focusedChild") then
return self.get("focusedChild"):dispatchEvent("key_up", key)
@@ -378,6 +399,7 @@ function Container:key_up(key)
end
--- Draws multiple lines of text, fg and bg strings, it is usually used in the render loop
--- @shortDescription Draws multiple lines of text, fg and bg strings
--- @param x number The x position to draw the text
--- @param y number The y position to draw the text
--- @param width number The width of the text
@@ -399,6 +421,7 @@ function Container:multiBlit(x, y, width, height, text, fg, bg)
end
--- Draws a line of text and fg as color, it is usually used in the render loop
--- @shortDescription Draws a line of text and fg as color
--- @param x number The x position to draw the text
--- @param y number The y position to draw the text
--- @param text string The text to draw
@@ -418,6 +441,7 @@ function Container:textFg(x, y, text, fg)
end
--- Draws a line of text and fg and bg as colors, it is usually used in the render loop
--- @shortDescription Draws a line of text and fg and bg as colors
--- @param x number The x position to draw the text
--- @param y number The y position to draw the text
--- @param text string The text to draw
@@ -445,6 +469,7 @@ function Container:blit(x, y, text, fg, bg)
end
--- Renders the container
--- @shortDescription Renders the container
function Container:render()
VisualElement.render(self)
if not self.get("childrenSorted")then
@@ -464,6 +489,9 @@ function Container:render()
end
end
--- Destroys the container and its children
--- @shortDescription Destroys the container and its children
--- @return Container self The container instance
function Container:destroy()
for _, child in ipairs(self._values.children) do
child:destroy()

View File

@@ -2,15 +2,24 @@ local VisualElement = require("elements/VisualElement")
local List = require("elements/List")
local tHex = require("libraries/colorHex")
--- This is the dropdown class. It is a visual element that can show a list of selectable items in a dropdown menu.
---@class Dropdown : List
local Dropdown = setmetatable({}, List)
Dropdown.__index = Dropdown
---@property isOpen boolean false Whether the dropdown menu is currently open
Dropdown.defineProperty(Dropdown, "isOpen", {default = false, type = "boolean", canTriggerRender = true})
---@property dropdownHeight number 5 Maximum height of the dropdown menu when open
Dropdown.defineProperty(Dropdown, "dropdownHeight", {default = 5, type = "number"})
---@property selectedText string "" The text to show when no item is selected
Dropdown.defineProperty(Dropdown, "selectedText", {default = "", type = "string"})
Dropdown.defineProperty(Dropdown, "dropSymbol", {default = "\31", type = "string"}) -- ▼ Symbol
---@property dropSymbol string "\31" The symbol to show for dropdown indication
Dropdown.defineProperty(Dropdown, "dropSymbol", {default = "\31", type = "string"})
--- Creates a new Dropdown instance
--- @shortDescription Creates a new Dropdown instance
--- @return Dropdown self The newly created Dropdown instance
--- @usage local dropdown = Dropdown.new()
function Dropdown.new()
local self = setmetatable({}, Dropdown):__init()
self.set("width", 16)
@@ -19,17 +28,29 @@ function Dropdown.new()
return self
end
--- Initializes the Dropdown instance
--- @shortDescription Initializes the Dropdown instance
--- @param props table The properties to initialize the element with
--- @param basalt table The basalt instance
--- @return Dropdown self The initialized instance
function Dropdown:init(props, basalt)
List.init(self, props, basalt)
self.set("type", "Dropdown")
return self
end
--- Handles mouse click events
--- @shortDescription Handles mouse click events
--- @param button number The button that was clicked
--- @param x number The x position of the click
--- @param y number The y position of the click
--- @return boolean handled Whether the event was handled
function Dropdown: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)
if relY == 1 then -- Klick auf Header
if relY == 1 then
self.set("isOpen", not self.get("isOpen"))
if not self.get("isOpen") then
self.set("height", 1)
@@ -38,7 +59,6 @@ function Dropdown:mouse_click(button, x, y)
end
return true
elseif self.get("isOpen") and relY > 1 then
-- Offset für die Liste korrigieren (relY - 1 wegen Header)
local index = relY - 1 + self.get("offset")
local items = self.get("items")
@@ -47,7 +67,7 @@ function Dropdown:mouse_click(button, x, y)
if type(item) == "table" and item.separator then
return false
end
self.set("selectedIndex", index)
self.set("isOpen", false)
self.set("height", 1)
@@ -63,30 +83,29 @@ function Dropdown:mouse_click(button, x, y)
return false
end
--- Renders the Dropdown
--- @shortDescription Renders the Dropdown
function Dropdown:render()
VisualElement.render(self)
-- Header rendern
local text = self.get("selectedText")
if #text == 0 and self.get("selectedIndex") > 0 then
local item = self.get("items")[self.get("selectedIndex")]
text = type(item) == "table" and item.text or tostring(item)
end
self:blit(1, 1, text .. string.rep(" ", self.get("width") - #text - 1) .. (self.get("isOpen") and "\31" or "\17"),
string.rep(tHex[self.get("foreground")], self.get("width")),
string.rep(tHex[self.get("background")], self.get("width")))
-- Items nur rendern wenn offen
if self.get("isOpen") then
local items = self.get("items")
local offset = self.get("offset")
local selected = self.get("selectedIndex")
local width = self.get("width")
-- Liste ab Zeile 2 rendern (unterhalb des Headers)
for i = 2, self.get("height") do
local itemIndex = i - 1 + offset -- -1 wegen Header
local itemIndex = i - 1 + offset
local item = items[itemIndex]
if item then
@@ -101,7 +120,7 @@ function Dropdown:render()
else
local itemText = type(item) == "table" and item.text or tostring(item)
local isSelected = itemIndex == selected
local bg = isSelected and
(item.selectedBackground or self.get("selectedColor")) or
(item.background or self.get("background"))

View File

@@ -1,13 +1,18 @@
local elementManager = require("elementManager")
local Container = elementManager.getElement("Container")
--- This is the frame class. It serves as a grouping container for other elements.
---@class Frame : Container
local Frame = setmetatable({}, Container)
Frame.__index = Frame
---@event onResize {width number, height number} Fired when the frame is resized
Frame.listenTo(Frame, "resize")
--- Creates a new Frame instance
--- @return Frame object The newly created Frame instance
--- @usage local element = Frame.new("myId", basalt)
--- @shortDescription Creates a new Frame instance
--- @return Frame self The newly created Frame instance
--- @usage local frame = Frame.new()
function Frame.new()
local self = setmetatable({}, Frame):__init()
self.set("width", 12)
@@ -18,9 +23,14 @@ function Frame.new()
end
--- Initializes the Frame instance
--- @shortDescription Initializes the Frame instance
--- @param props table The properties to initialize the element with
--- @param basalt table The basalt instance
--- @return Frame self The initialized instance
function Frame:init(props, basalt)
Container.init(self, props, basalt)
self.set("type", "Frame")
return self
end
return Frame

View File

@@ -1,24 +1,27 @@
local VisualElement = require("elements/VisualElement")
local tHex = require("libraries/colorHex")
--- This is the input class. It provides a text input field that can handle user input with various features like
--- cursor movement, text manipulation, placeholder text, and input validation.
---@class Input : VisualElement
local Input = setmetatable({}, VisualElement)
Input.__index = Input
---@property text string Input - text to be displayed
---@property text string - The current text content of the input
Input.defineProperty(Input, "text", {default = "", type = "string", canTriggerRender = true})
---@property cursorPos number Input - current cursor position
---@property cursorPos number 1 The current cursor position in the text
Input.defineProperty(Input, "cursorPos", {default = 1, type = "number"})
---@property viewOffset number Input - offset of view
---@property viewOffset number 0 The horizontal scroll offset for viewing long text
Input.defineProperty(Input, "viewOffset", {default = 0, type = "number", canTriggerRender = true})
-- Neue Properties
---@property maxLength number? nil Maximum length of input text (optional)
Input.defineProperty(Input, "maxLength", {default = nil, type = "number"})
Input.defineProperty(Input, "placeholder", {default = "asd", type = "string"})
---@property placeholder string ... Text to display when input is empty
Input.defineProperty(Input, "placeholder", {default = "...", type = "string"})
---@property placeholderColor color gray Color of the placeholder text
Input.defineProperty(Input, "placeholderColor", {default = colors.gray, type = "number"})
---@property focusedColor color blue Background color when input is focused
Input.defineProperty(Input, "focusedColor", {default = colors.blue, type = "number"})
---@property pattern string? nil Regular expression pattern for input validation
Input.defineProperty(Input, "pattern", {default = nil, type = "string"})
Input.listenTo(Input, "mouse_click")
@@ -26,6 +29,7 @@ Input.listenTo(Input, "key")
Input.listenTo(Input, "char")
--- Creates a new Input instance
--- @shortDescription Creates a new Input instance
--- @return Input object The newly created Input instance
--- @usage local element = Input.new("myId", basalt)
function Input.new()
@@ -35,13 +39,23 @@ function Input.new()
return self
end
--- Initializes the Input instance
--- @shortDescription Initializes the Input instance
--- @param props table The properties to initialize the element with
--- @param basalt table The basalt instance
--- @return Input self The initialized instance
function Input:init(props, basalt)
VisualElement.init(self, props, basalt)
self.set("type", "Input")
return self
end
--- Handles char events
--- @shortDescription Handles char events
--- @param char string The character that was typed
--- @return boolean handled Whether the event was handled
function Input:char(char)
if not self.get("focused") then return end
if not self.get("focused") then return false end
local text = self.get("text")
local pos = self.get("cursorPos")
local maxLength = self.get("maxLength")
@@ -54,10 +68,15 @@ function Input:char(char)
self.set("cursorPos", pos + 1)
self:updateRender()
self:updateViewport()
return true
end
--- Handles key events
--- @shortDescription Handles key events
--- @param key number The key that was pressed
--- @return boolean handled Whether the event was handled
function Input:key(key)
if not self.get("focused") then return end
if not self.get("focused") then return false end
local pos = self.get("cursorPos")
local text = self.get("text")
local viewOffset = self.get("viewOffset")
@@ -88,18 +107,29 @@ function Input:key(key)
local relativePos = self.get("cursorPos") - self.get("viewOffset")
self:setCursor(relativePos, 1, true)
return true
end
--- Handles focus events
--- @shortDescription Handles focus events
function Input:focus()
VisualElement.focus(self)
self:updateRender()
end
--- Handles blur events
--- @shortDescription Handles blur events
function Input:blur()
VisualElement.blur(self)
self:updateRender()
end
--- Handles mouse click events
--- @shortDescription Handles mouse click events
--- @param button number The button that was clicked
--- @param x number The x position of the click
--- @param y number The y position of the click
--- @return boolean handled Whether the event was handled
function Input:mouse_click(button, x, y)
if VisualElement.mouse_click(self, button, x, y) then
local relX, relY = self:getRelativePosition(x, y)
@@ -110,6 +140,8 @@ function Input:mouse_click(button, x, y)
end
end
--- Updates the input's viewport
--- @shortDescription Updates the input's viewport
function Input:updateViewport()
local width = self.get("width")
local cursorPos = self.get("cursorPos")
@@ -128,6 +160,8 @@ function Input:updateViewport()
end
end
--- Renders the input element
--- @shortDescription Renders the input element
function Input:render()
local text = self.get("text")
local viewOffset = self.get("viewOffset")

View File

@@ -1,11 +1,13 @@
local elementManager = require("elementManager")
local VisualElement = elementManager.getElement("VisualElement")
--- This is the label class. It provides a simple text display element that automatically
--- resizes its width based on the text content.
---@class Label : VisualElement
local Label = setmetatable({}, VisualElement)
Label.__index = Label
---@property text string Label Label text to be displayed
---@property text string Label The text content to display. Can be a string or a function that returns a string
Label.defineProperty(Label, "text", {default = "Label", type = "string", setter = function(self, value)
if(type(value)=="function")then value = value() end
self.set("width", #value)
@@ -13,8 +15,9 @@ Label.defineProperty(Label, "text", {default = "Label", type = "string", setter
end})
--- Creates a new Label instance
--- @return Label object The newly created Label instance
--- @usage local element = Label.new("myId", basalt)
--- @shortDescription Creates a new Label instance
--- @return Label self The newly created Label instance
--- @usage local label = Label.new()
function Label.new()
local self = setmetatable({}, Label):__init()
self.set("z", 3)
@@ -23,11 +26,19 @@ function Label.new()
return self
end
--- Initializes the Label instance
--- @shortDescription Initializes the Label instance
--- @param props table The properties to initialize the element with
--- @param basalt table The basalt instance
--- @return Label self The initialized instance
function Label:init(props, basalt)
VisualElement.init(self, props, basalt)
self.set("type", "Label")
return self
end
--- Renders the Label
--- @shortDescription Renders the Label by drawing its text content
function Label:render()
VisualElement.render(self)
local text = self.get("text")

View File

@@ -1,23 +1,30 @@
local VisualElement = require("elements/VisualElement")
--- This is the list class. It provides a scrollable list of selectable items with support for
--- custom item rendering, separators, and selection handling.
---@class List : VisualElement
local List = setmetatable({}, VisualElement)
List.__index = List
---@property items table List of items to display
---@property items table {} List of items to display. Items can be strings or tables with properties
List.defineProperty(List, "items", {default = {}, type = "table", canTriggerRender = true})
---@property selectedIndex number Currently selected item index
---@property selectedIndex number 0 Index of the currently selected item (0 means no selection)
List.defineProperty(List, "selectedIndex", {default = 0, type = "number", canTriggerRender = true})
---@property selectable boolean Whether items can be selected
---@property selectable boolean true Whether items in the list can be selected
List.defineProperty(List, "selectable", {default = true, type = "boolean"})
---@property offset number Scrolling offset
---@property offset number 0 Current scroll offset for viewing long lists
List.defineProperty(List, "offset", {default = 0, type = "number", canTriggerRender = true})
---@property selectedColor color Color for selected item
---@property selectedColor color blue Background color for the selected item
List.defineProperty(List, "selectedColor", {default = colors.blue, type = "number"})
---@event onSelect {index number, item any} Fired when an item is selected
List.listenTo(List, "mouse_click")
List.listenTo(List, "mouse_scroll")
--- Creates a new List instance
--- @shortDescription Creates a new List instance
--- @return List self The newly created List instance
--- @usage local list = List.new()
function List.new()
local self = setmetatable({}, List):__init()
self.set("width", 16)
@@ -26,11 +33,22 @@ function List.new()
return self
end
--- Initializes the List instance
--- @shortDescription Initializes the List instance
--- @param props table The properties to initialize the element with
--- @param basalt table The basalt instance
--- @return List self The initialized instance
function List:init(props, basalt)
VisualElement.init(self, props, basalt)
self.set("type", "List")
end
--- Adds an item to the list
--- @shortDescription Adds an item to the list
--- @param text string|table The item to add (string or item table)
--- @return List self The List instance
--- @usage list:addItem("New Item")
--- @usage list:addItem({text="Item", callback=function() end})
function List:addItem(text)
local items = self.get("items")
table.insert(items, text)
@@ -38,6 +56,11 @@ function List:addItem(text)
return self
end
--- Removes an item from the list
--- @shortDescription Removes an item from the list
--- @param index number The index of the item to remove
--- @return List self The List instance
--- @usage list:removeItem(1)
function List:removeItem(index)
local items = self.get("items")
table.remove(items, index)
@@ -45,6 +68,10 @@ function List:removeItem(index)
return self
end
--- Clears all items from the list
--- @shortDescription Clears all items from the list
--- @return List self The List instance
--- @usage list:clear()
function List:clear()
self.set("items", {})
self.set("selectedIndex", 0)
@@ -52,6 +79,12 @@ function List:clear()
return self
end
--- Handles mouse click events
--- @shortDescription Handles mouse click events
--- @param button number The mouse button that was clicked
--- @param x number The x-coordinate of the click
--- @param y number The y-coordinate of the click
--- @return boolean Whether the event was handled
function List:mouse_click(button, x, y)
if button == 1 and self:isInBounds(x, y) and self.get("selectable") then
local _, index = self:getRelativePosition(x, y)
@@ -75,6 +108,12 @@ function List:mouse_click(button, x, y)
return false
end
--- Handles mouse scroll events
--- @shortDescription Handles mouse scroll events
--- @param direction number The direction of the scroll (1 for down, -1 for up)
--- @param x number The x-coordinate of the scroll
--- @param y number The y-coordinate of the scroll
--- @return boolean Whether the event was handled
function List:mouse_scroll(direction, x, y)
if self:isInBounds(x, y) then
local offset = self.get("offset")
@@ -84,13 +123,21 @@ function List:mouse_scroll(direction, x, y)
self.set("offset", offset)
return true
end
return false
end
--- Registers a callback for the select event
--- @shortDescription Registers a callback for the select event
--- @param callback function The callback function to register
--- @return List self The List instance
--- @usage list:onSelect(function(index, item) print("Selected item:", index, item) end)
function List:onSelect(callback)
self:registerCallback("select", callback)
return self
end
--- Renders the list
--- @shortDescription Renders the list
function List:render()
VisualElement.render(self)

View File

@@ -2,12 +2,19 @@ local VisualElement = require("elements/VisualElement")
local List = require("elements/List")
local tHex = require("libraries/colorHex")
--- This is the menu class. It provides a horizontal menu bar with selectable items and separators.
--- Menu items are displayed in a single row and can have custom colors and callbacks.
---@class Menu : List
local Menu = setmetatable({}, List)
Menu.__index = Menu
---@property separatorColor color gray The color used for separator items in the menu
Menu.defineProperty(Menu, "separatorColor", {default = colors.gray, type = "number"})
--- Creates a new Menu instance
--- @shortDescription Creates a new Menu instance
--- @return Menu self The newly created Menu instance
--- @usage local menu = Menu.new()
function Menu.new()
local self = setmetatable({}, Menu):__init()
self.set("width", 30)
@@ -16,12 +23,22 @@ function Menu.new()
return self
end
--- Initializes the Menu instance
--- @shortDescription Initializes the Menu instance
--- @param props table The properties to initialize the element with
--- @param basalt table The basalt instance
--- @return Menu self The initialized instance
function Menu:init(props, basalt)
List.init(self, props, basalt)
self.set("type", "Menu")
return self
end
--- Sets the menu items
--- @shortDescription Sets the menu items and calculates total width
--- @param items table[] List of items with {text, separator, callback, foreground, background} properties
--- @return Menu self The Menu instance
--- @usage menu:setItems({{text="File"}, {separator=true}, {text="Edit"}})
function Menu:setItems(items)
local listItems = {}
local totalWidth = 0
@@ -40,6 +57,8 @@ function Menu:setItems(items)
return List.setItems(self, listItems)
end
--- Renders the menu
--- @shortDescription Renders the menu horizontally with proper spacing and colors
function Menu:render()
VisualElement.render(self)
local currentX = 1
@@ -63,6 +82,12 @@ function Menu:render()
end
end
--- Handles mouse click events
--- @shortDescription Handles mouse click events and item selection
--- @param button number The button that was clicked
--- @param x number The x position of the click
--- @param y number The y position of the click
--- @return boolean Whether the event was handled
function Menu:mouse_click(button, x, y)
if not VisualElement.mouse_click(self, button, x, y) then return false end
if(self.get("selectable") == false) then return false end

View File

@@ -1,16 +1,22 @@
local VisualElement = require("elements/VisualElement")
--- This is the progress bar class. It provides a visual representation of progress
--- with optional percentage display and customizable colors.
---@class ProgressBar : VisualElement
local ProgressBar = setmetatable({}, VisualElement)
ProgressBar.__index = ProgressBar
---@property progress number Current progress (0-100)
---@property progress number 0 Current progress value (0-100)
ProgressBar.defineProperty(ProgressBar, "progress", {default = 0, type = "number", canTriggerRender = true})
---@property showPercentage boolean Show percentage text
---@property showPercentage boolean false Whether to show the percentage text in the center
ProgressBar.defineProperty(ProgressBar, "showPercentage", {default = false, type = "boolean"})
---@property progressColor color Progress bar color
---@property progressColor color lime The color used for the filled portion of the progress bar
ProgressBar.defineProperty(ProgressBar, "progressColor", {default = colors.lime, type = "number"})
--- Creates a new ProgressBar instance
--- @shortDescription Creates a new ProgressBar instance
--- @return ProgressBar self The newly created ProgressBar instance
--- @usage local progressBar = ProgressBar.new()
function ProgressBar.new()
local self = setmetatable({}, ProgressBar):__init()
self.set("width", 10)
@@ -18,11 +24,18 @@ function ProgressBar.new()
return self
end
--- Initializes the ProgressBar instance
--- @shortDescription Initializes the ProgressBar instance
--- @param props table The properties to initialize the element with
--- @param basalt table The basalt instance
--- @return ProgressBar self The initialized instance
function ProgressBar:init(props, basalt)
VisualElement.init(self, props, basalt)
self.set("type", "ProgressBar")
end
--- Renders the ProgressBar
--- @shortDescription Renders the progress bar with filled portion and optional percentage text
function ProgressBar:render()
VisualElement.render(self)
local width = self.get("width")

View File

@@ -1,24 +1,31 @@
local VisualElement = require("elements/VisualElement")
--- This is the slider class. It provides a draggable slider control that can be either horizontal or vertical,
--- with customizable colors and value ranges.
---@class Slider : VisualElement
local Slider = setmetatable({}, VisualElement)
Slider.__index = Slider
---@property step number 1 Current step position (1 to width/height)
---@property step number 1 Current position of the slider handle (1 to width/height)
Slider.defineProperty(Slider, "step", {default = 1, type = "number", canTriggerRender = true})
---@property max number 100 Maximum value for value conversion
---@property max number 100 Maximum value for value conversion (maps slider position to this range)
Slider.defineProperty(Slider, "max", {default = 100, type = "number"})
---@property horizontal boolean true Whether the slider is horizontal
---@property horizontal boolean true Whether the slider is horizontal (false for vertical)
Slider.defineProperty(Slider, "horizontal", {default = true, type = "boolean", canTriggerRender = true})
---@property barColor color color Colors for the slider bar
---@property barColor color gray Color of the slider track
Slider.defineProperty(Slider, "barColor", {default = colors.gray, type = "number", canTriggerRender = true})
---@property sliderColor color The color of the slider handle
---@property sliderColor color blue Color of the slider handle
Slider.defineProperty(Slider, "sliderColor", {default = colors.blue, type = "number", canTriggerRender = true})
---@event onChange {value number} Fired when the slider value changes
Slider.listenTo(Slider, "mouse_click")
Slider.listenTo(Slider, "mouse_drag")
Slider.listenTo(Slider, "mouse_up")
--- Creates a new Slider instance
--- @shortDescription Creates a new Slider instance
--- @return Slider self The newly created Slider instance
--- @usage local slider = Slider.new()
function Slider.new()
local self = setmetatable({}, Slider):__init()
self.set("width", 8)
@@ -27,11 +34,20 @@ function Slider.new()
return self
end
--- Initializes the Slider instance
--- @shortDescription Initializes the Slider instance
--- @param props table The properties to initialize the element with
--- @param basalt table The basalt instance
--- @return Slider self The initialized instance
function Slider:init(props, basalt)
VisualElement.init(self, props, basalt)
self.set("type", "Slider")
end
--- Gets the current value of the slider
--- @shortDescription Gets the current value mapped to the max range
--- @return number value The current value (0 to max)
--- @usage local value = slider:getValue()
function Slider:getValue()
local step = self.get("step")
local max = self.get("max")
@@ -39,6 +55,12 @@ function Slider:getValue()
return math.floor((step - 1) * (max / (maxSteps - 1)))
end
--- Handles mouse click events
--- @shortDescription Updates slider position on mouse click
--- @param button number The mouse button that was clicked
--- @param x number The x position of the click
--- @param y number The y position of the click
--- @return boolean handled Whether the event was handled
function Slider:mouse_click(button, x, y)
if button == 1 and self:isInBounds(x, y) then
local relX, relY = self:getRelativePosition(x, y)
@@ -62,6 +84,8 @@ function Slider:mouse_scroll(direction, x, y)
end
end
--- Renders the slider
--- @shortDescription Renders the slider with track and handle
function Slider:render()
VisualElement.render(self)
local width = self.get("width")

View File

@@ -1,23 +1,38 @@
local VisualElement = require("elements/VisualElement")
local tHex = require("libraries/colorHex")
--- This is the table class. It provides a sortable data grid with customizable columns,
--- row selection, and scrolling capabilities.
---@class Table : VisualElement
local Table = setmetatable({}, VisualElement)
Table.__index = Table
---@property columns table {} List of column definitions with {name, width} properties
Table.defineProperty(Table, "columns", {default = {}, type = "table"})
---@property data table {} The table data as array of row arrays
Table.defineProperty(Table, "data", {default = {}, type = "table", canTriggerRender = true})
---@property selectedRow number? nil Currently selected row index
Table.defineProperty(Table, "selectedRow", {default = nil, type = "number", canTriggerRender = true})
---@property headerColor color blue Color of the column headers
Table.defineProperty(Table, "headerColor", {default = colors.blue, type = "number"})
---@property selectedColor color lightBlue Background color of selected row
Table.defineProperty(Table, "selectedColor", {default = colors.lightBlue, type = "number"})
---@property gridColor color gray Color of grid lines
Table.defineProperty(Table, "gridColor", {default = colors.gray, type = "number"})
---@property sortColumn number? nil Currently sorted column index
Table.defineProperty(Table, "sortColumn", {default = nil, type = "number"})
---@property sortDirection string "asc" Sort direction ("asc" or "desc")
Table.defineProperty(Table, "sortDirection", {default = "asc", type = "string"})
---@property scrollOffset number 0 Current scroll position
Table.defineProperty(Table, "scrollOffset", {default = 0, type = "number", canTriggerRender = true})
Table.listenTo(Table, "mouse_click")
Table.listenTo(Table, "mouse_scroll")
--- Creates a new Table instance
--- @shortDescription Creates a new Table instance
--- @return Table self The newly created Table instance
--- @usage local table = Table.new()
function Table.new()
local self = setmetatable({}, Table):__init()
self.set("width", 30)
@@ -26,24 +41,43 @@ function Table.new()
return self
end
--- Initializes the Table instance
--- @shortDescription Initializes the Table instance
--- @param props table The properties to initialize the element with
--- @param basalt table The basalt instance
--- @return Table self The initialized instance
function Table:init(props, basalt)
VisualElement.init(self, props, basalt)
self.set("type", "Table")
return self
end
--- Sets the table columns
--- @shortDescription Sets the table columns configuration
--- @param columns table[] Array of column definitions {name="Name", width=10}
--- @return Table self The Table instance
--- @usage table:setColumns({{name="ID", width=4}, {name="Name", width=10}})
function Table:setColumns(columns)
-- Columns Format: {{name="ID", width=4}, {name="Name", width=10}}
self.set("columns", columns)
return self
end
--- Sets the table data
--- @shortDescription Sets the table data
--- @param data table[] Array of row data arrays
--- @return Table self The Table instance
--- @usage table:setData({{"1", "Item One"}, {"2", "Item Two"}})
function Table:setData(data)
-- Data Format: {{"1", "Item One"}, {"2", "Item Two"}}
self.set("data", data)
return self
end
--- Sorts the table data by column
--- @shortDescription Sorts the table data by the specified column
--- @param columnIndex number The index of the column to sort by
--- @return Table self The Table instance
function Table:sortData(columnIndex)
local data = self.get("data")
local direction = self.get("sortDirection")
@@ -60,6 +94,12 @@ function Table:sortData(columnIndex)
return self
end
--- Handles mouse click events
--- @shortDescription Handles header clicks for sorting and row selection
--- @param button number The button that was clicked
--- @param x number The x position of the click
--- @param y number The y position of the click
--- @return boolean handled Whether the event was handled
function Table:mouse_click(button, x, y)
if not VisualElement.mouse_click(self, button, x, y) then return false end
@@ -92,6 +132,12 @@ function Table:mouse_click(button, x, y)
return true
end
--- Handles mouse scroll events
--- @shortDescription Handles scrolling through the table data
--- @param direction number The scroll direction (-1 up, 1 down)
--- @param x number The x position of the scroll
--- @param y number The y position of the scroll
--- @return boolean handled Whether the event was handled
function Table:mouse_scroll(direction, x, y)
local data = self.get("data")
local height = self.get("height")
@@ -103,6 +149,8 @@ function Table:mouse_scroll(direction, x, y)
return true
end
--- Renders the table
--- @shortDescription Renders the table with headers, data and scrollbar
function Table:render()
VisualElement.render(self)

252
src/elements/TextBox.lua Normal file
View File

@@ -0,0 +1,252 @@
local VisualElement = require("elements/VisualElement")
local tHex = require("libraries/colorHex")
---A multi-line text editor component with cursor support and text manipulation features
---@class TextBox : VisualElement
local TextBox = setmetatable({}, VisualElement)
TextBox.__index = TextBox
---@property lines table {} Array of text lines
TextBox.defineProperty(TextBox, "lines", {default = {""}, type = "table", canTriggerRender = true})
---@property cursorX number 1 Cursor X position
TextBox.defineProperty(TextBox, "cursorX", {default = 1, type = "number"})
---@property cursorY number 1 Cursor Y position (line number)
TextBox.defineProperty(TextBox, "cursorY", {default = 1, type = "number"})
---@property scrollX number 0 Horizontal scroll offset
TextBox.defineProperty(TextBox, "scrollX", {default = 0, type = "number", canTriggerRender = true})
---@property scrollY number 0 Vertical scroll offset
TextBox.defineProperty(TextBox, "scrollY", {default = 0, type = "number", canTriggerRender = true})
---@property editable boolean true Whether text can be edited
TextBox.defineProperty(TextBox, "editable", {default = true, type = "boolean"})
---@property syntaxPatterns table {} Syntax highlighting patterns
TextBox.defineProperty(TextBox, "syntaxPatterns", {default = {}, type = "table"})
TextBox.listenTo(TextBox, "mouse_click")
TextBox.listenTo(TextBox, "key")
TextBox.listenTo(TextBox, "char")
TextBox.listenTo(TextBox, "mouse_scroll")
function TextBox.new()
local self = setmetatable({}, TextBox):__init()
self.set("width", 20)
self.set("height", 10)
return self
end
function TextBox:init(props, basalt)
VisualElement.init(self, props, basalt)
self.set("type", "TextBox")
return self
end
--- Adds a new syntax highlighting pattern
--- @param pattern string The regex pattern to match
--- @param color color The color to apply
function TextBox:addSyntaxPattern(pattern, color)
table.insert(self.get("syntaxPatterns"), {pattern = pattern, color = color})
return self
end
local function insertChar(self, char)
local lines = self.get("lines")
local cursorX = self.get("cursorX")
local cursorY = self.get("cursorY")
local currentLine = lines[cursorY]
lines[cursorY] = currentLine:sub(1, cursorX-1) .. char .. currentLine:sub(cursorX)
self.set("cursorX", cursorX + 1)
self:updateViewport()
self:updateRender()
end
local function newLine(self)
local lines = self.get("lines")
local cursorX = self.get("cursorX")
local cursorY = self.get("cursorY")
local currentLine = lines[cursorY]
local restOfLine = currentLine:sub(cursorX)
lines[cursorY] = currentLine:sub(1, cursorX-1)
table.insert(lines, cursorY + 1, restOfLine)
self.set("cursorX", 1)
self.set("cursorY", cursorY + 1)
self:updateViewport()
self:updateRender()
end
local function backspace(self)
local lines = self.get("lines")
local cursorX = self.get("cursorX")
local cursorY = self.get("cursorY")
local currentLine = lines[cursorY]
if cursorX > 1 then
lines[cursorY] = currentLine:sub(1, cursorX-2) .. currentLine:sub(cursorX)
self.set("cursorX", cursorX - 1)
elseif cursorY > 1 then
local previousLine = lines[cursorY-1]
self.set("cursorX", #previousLine + 1)
self.set("cursorY", cursorY - 1)
lines[cursorY-1] = previousLine .. currentLine
table.remove(lines, cursorY)
end
self:updateViewport()
self:updateRender()
end
function TextBox:updateViewport()
local cursorX = self.get("cursorX")
local cursorY = self.get("cursorY")
local scrollX = self.get("scrollX")
local scrollY = self.get("scrollY")
local width = self.get("width")
local height = self.get("height")
-- Horizontal scrolling
if cursorX - scrollX > width then
self.set("scrollX", cursorX - width)
elseif cursorX - scrollX < 1 then
self.set("scrollX", cursorX - 1)
end
-- Vertical scrolling
if cursorY - scrollY > height then
self.set("scrollY", cursorY - height)
elseif cursorY - scrollY < 1 then
self.set("scrollY", cursorY - 1)
end
end
function TextBox:char(char)
if not self.get("editable") or not self.get("focused") then return false end
insertChar(self, char)
return true
end
function TextBox:key(key)
if not self.get("editable") or not self.get("focused") then return false end
local lines = self.get("lines")
local cursorX = self.get("cursorX")
local cursorY = self.get("cursorY")
if key == keys.enter then
newLine(self)
elseif key == keys.backspace then
backspace(self)
elseif key == keys.left then
if cursorX > 1 then
self.set("cursorX", cursorX - 1)
elseif cursorY > 1 then
self.set("cursorY", cursorY - 1)
self.set("cursorX", #lines[cursorY-1] + 1)
end
elseif key == keys.right then
if cursorX <= #lines[cursorY] then
self.set("cursorX", cursorX + 1)
elseif cursorY < #lines then
self.set("cursorY", cursorY + 1)
self.set("cursorX", 1)
end
elseif key == keys.up and cursorY > 1 then
self.set("cursorY", cursorY - 1)
self.set("cursorX", math.min(cursorX, #lines[cursorY-1] + 1))
elseif key == keys.down and cursorY < #lines then
self.set("cursorY", cursorY + 1)
self.set("cursorX", math.min(cursorX, #lines[cursorY+1] + 1))
end
self:updateRender()
self:updateViewport()
return true
end
function TextBox:mouse_scroll(direction, x, y)
if self:isInBounds(x, y) then
local scrollY = self.get("scrollY")
self.set("scrollY", math.max(0, scrollY + direction))
self:updateRender()
return true
end
return false
end
function TextBox:mouse_click(button, x, y)
if VisualElement.mouse_click(self, button, x, y) then
local relX, relY = self:getRelativePosition(x, y)
local scrollX = self.get("scrollX")
local scrollY = self.get("scrollY")
local targetY = relY + scrollY
local lines = self.get("lines")
if targetY <= #lines then
self.set("cursorY", targetY)
self.set("cursorX", math.min(relX + scrollX, #lines[targetY] + 1))
end
return true
end
return false
end
function TextBox:setText(text)
self.set("lines", {""})
for line in text:gmatch("[^\n]+") do
table.insert(self.get("lines"), line)
end
return self
end
function TextBox:getText()
return table.concat(self.get("lines"), "\n")
end
local function applySyntaxHighlighting(self, line)
local text = line
local colors = string.rep(tHex[self.get("foreground")], #text)
local patterns = self.get("syntaxPatterns")
for _, syntax in ipairs(patterns) do
local start = 1
while true do
local s, e = text:find(syntax.pattern, start)
if not s then break end
colors = colors:sub(1, s-1) .. string.rep(tHex[syntax.color], e-s+1) .. colors:sub(e+1)
start = e + 1
end
end
return text, colors
end
function TextBox:render()
VisualElement.render(self)
local lines = self.get("lines")
local scrollX = self.get("scrollX")
local scrollY = self.get("scrollY")
local width = self.get("width")
local height = self.get("height")
local fg = tHex[self.get("foreground")]
local bg = tHex[self.get("background")]
for y = 1, height do
local lineNum = y + scrollY
local line = lines[lineNum] or ""
local visibleText = line:sub(scrollX + 1, scrollX + width)
if #visibleText < width then
visibleText = visibleText .. string.rep(" ", width - #visibleText)
end
local text, colors = applySyntaxHighlighting(self, visibleText)
self:blit(1, y, text, colors, string.rep(bg, #visibleText))
end
if self.get("focused") then
local relativeX = self.get("cursorX") - scrollX
local relativeY = self.get("cursorY") - scrollY
if relativeX >= 1 and relativeX <= width and relativeY >= 1 and relativeY <= height then
self:setCursor(relativeX, relativeY, true)
end
end
end
return TextBox

View File

@@ -1,20 +1,32 @@
local VisualElement = require("elements/VisualElement")
local tHex = require("libraries/colorHex")
local sub = string.sub
--- This is the tree class. It provides a hierarchical view of nodes that can be expanded and collapsed,
--- with support for selection and scrolling.
---@class Tree : VisualElement
local Tree = setmetatable({}, VisualElement)
Tree.__index = Tree
---@property nodes table {} The tree structure containing node objects with {text, children} properties
Tree.defineProperty(Tree, "nodes", {default = {}, type = "table", canTriggerRender = true})
---@property selectedNode table? nil Currently selected node
Tree.defineProperty(Tree, "selectedNode", {default = nil, type = "table", canTriggerRender = true})
---@property expandedNodes table {} Table of nodes that are currently expanded
Tree.defineProperty(Tree, "expandedNodes", {default = {}, type = "table", canTriggerRender = true})
---@property scrollOffset number 0 Current scroll position
Tree.defineProperty(Tree, "scrollOffset", {default = 0, type = "number", canTriggerRender = true})
---@property nodeColor color white Color of unselected nodes
Tree.defineProperty(Tree, "nodeColor", {default = colors.white, type = "number"})
---@property selectedColor color lightBlue Background color of selected node
Tree.defineProperty(Tree, "selectedColor", {default = colors.lightBlue, type = "number"})
Tree.listenTo(Tree, "mouse_click")
Tree.listenTo(Tree, "mouse_scroll")
--- Creates a new Tree instance
--- @shortDescription Creates a new Tree instance
--- @return Tree self The newly created Tree instance
--- @usage local tree = Tree.new()
function Tree.new()
local self = setmetatable({}, Tree):__init()
self.set("width", 30)
@@ -23,12 +35,22 @@ function Tree.new()
return self
end
--- Initializes the Tree instance
--- @shortDescription Initializes the Tree instance
--- @param props table The properties to initialize the element with
--- @param basalt table The basalt instance
--- @return Tree self The initialized instance
function Tree:init(props, basalt)
VisualElement.init(self, props, basalt)
self.set("type", "Tree")
return self
end
--- Sets the tree nodes
--- @shortDescription Sets the tree nodes and expands the root node
--- @param nodes table[] Array of node objects
--- @return Tree self The Tree instance
--- @usage tree:setNodes({{text="Root", children={{text="Child"}}}})
function Tree:setNodes(nodes)
self.set("nodes", nodes)
if #nodes > 0 then
@@ -37,18 +59,30 @@ function Tree:setNodes(nodes)
return self
end
--- Expands a node
--- @shortDescription Expands a node to show its children
--- @param node table The node to expand
--- @return Tree self The Tree instance
function Tree:expandNode(node)
self.get("expandedNodes")[node] = true
self:updateRender()
return self
end
--- Collapses a node
--- @shortDescription Collapses a node to hide its children
--- @param node table The node to collapse
--- @return Tree self The Tree instance
function Tree:collapseNode(node)
self.get("expandedNodes")[node] = nil
self:updateRender()
return self
end
--- Toggles a node's expanded state
--- @shortDescription Toggles between expanded and collapsed state
--- @param node table The node to toggle
--- @return Tree self The Tree instance
function Tree:toggleNode(node)
if self.get("expandedNodes")[node] then
self:collapseNode(node)
@@ -101,14 +135,14 @@ function Tree:mouse_scroll(direction)
local flatNodes = flattenTree(self.get("nodes"), self.get("expandedNodes"))
local maxScroll = math.max(0, #flatNodes - self.get("height"))
local newScroll = math.min(maxScroll, math.max(0, self.get("scrollOffset") + direction))
self.set("scrollOffset", newScroll)
return true
end
function Tree:render()
VisualElement.render(self)
local flatNodes = flattenTree(self.get("nodes"), self.get("expandedNodes"))
local height = self.get("height")
local selectedNode = self.get("selectedNode")
@@ -121,8 +155,7 @@ function Tree:render()
local node = nodeInfo.node
local level = nodeInfo.level
local indent = string.rep(" ", level)
-- Expand/Collapse Symbol
local symbol = " "
if node.children and #node.children > 0 then
symbol = expandedNodes[node] and "\31" or "\16"
@@ -130,15 +163,12 @@ function Tree:render()
local bg = node == selectedNode and self.get("selectedColor") or self.get("background")
local text = indent .. symbol .." " .. (node.text or "Node")
self:blit(1, y, text .. string.rep(" ", self.get("width") - #text),
string.rep(tHex[self.get("nodeColor")], self.get("width")),
string.rep(tHex[bg], self.get("width")))
text = sub(text, 1, self.get("width"))
self:textFg(1, y, text .. string.rep(" ", self.get("width") - #text), self.get("foreground"))
else
-- Leere Zeile
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")))
self:textFg(1, y, string.rep(" ", self.get("width")), self.get("foreground"), self.get("background"))
end
end
end

View File

@@ -2,15 +2,17 @@ local elementManager = require("elementManager")
local BaseElement = elementManager.getElement("BaseElement")
local tHex = require("libraries/colorHex")
--- This is the visual element class. It serves as the base class for all visual UI elements
--- and provides core functionality for positioning, sizing, colors, and rendering.
---@class VisualElement : BaseElement
local VisualElement = setmetatable({}, BaseElement)
VisualElement.__index = VisualElement
---@property x number 1 x position of the element
---@property x number 1 The horizontal position relative to parent
VisualElement.defineProperty(VisualElement, "x", {default = 1, type = "number", canTriggerRender = true})
---@property y number 1 y position of the element
---@property y number 1 The vertical position relative to parent
VisualElement.defineProperty(VisualElement, "y", {default = 1, type = "number", canTriggerRender = true})
---@property z number 1 z position of the element
---@property z number 1 The z-index for layering elements
VisualElement.defineProperty(VisualElement, "z", {default = 1, type = "number", canTriggerRender = true, setter = function(self, value)
if self.parent then
self.parent:sortChildren()
@@ -18,19 +20,19 @@ VisualElement.defineProperty(VisualElement, "z", {default = 1, type = "number",
return value
end})
---@property width number 1 width of the element
---@property width number 1 The width of the element
VisualElement.defineProperty(VisualElement, "width", {default = 1, type = "number", canTriggerRender = true})
---@property height number 1 height of the element
---@property height number 1 The height of the element
VisualElement.defineProperty(VisualElement, "height", {default = 1, type = "number", canTriggerRender = true})
---@property background color black background color of the element
---@property background color black The background color
VisualElement.defineProperty(VisualElement, "background", {default = colors.black, type = "number", canTriggerRender = true})
---@property foreground color white foreground color of the element
---@property foreground color white The text/foreground color
VisualElement.defineProperty(VisualElement, "foreground", {default = colors.white, type = "number", canTriggerRender = true})
---@property clicked boolean an false element is currently clicked
---@property clicked boolean false Whether the element is currently clicked
VisualElement.defineProperty(VisualElement, "clicked", {default = false, type = "boolean"})
---@property backgroundEnabled boolean true whether the background is enabled
---@property backgroundEnabled boolean true Whether to render the background
VisualElement.defineProperty(VisualElement, "backgroundEnabled", {default = true, type = "boolean", canTriggerRender = true})
---@property focused boolean false whether the element is focused
---@property focused boolean false Whether the element has input focus
VisualElement.defineProperty(VisualElement, "focused", {default = false, type = "boolean", setter = function(self, value, internal)
local curValue = self.get("focused")
if value == curValue then return value end
@@ -51,7 +53,7 @@ VisualElement.defineProperty(VisualElement, "focused", {default = false, type =
return value
end})
---@property visible boolean true whether the element is visible
---@property visible boolean true Whether the element is visible
VisualElement.defineProperty(VisualElement, "visible", {default = true, type = "boolean", canTriggerRender = true, setter=function(self, value)
if(self.parent~=nil)then
self.parent.set("childrenSorted", false)
@@ -60,23 +62,22 @@ VisualElement.defineProperty(VisualElement, "visible", {default = true, type = "
return value
end})
---@combinedProperty position {x y} Position of the element
---@combinedProperty position {x y} Combined x, y position
VisualElement.combineProperties(VisualElement, "position", "x", "y")
---@combinedProperty size {width height} Size of the element
---@combinedProperty size {width height} Combined width, height
VisualElement.combineProperties(VisualElement, "size", "width", "height")
---@combinedProperty color {foreground background} Color of the element
---@combinedProperty color {foreground background} Combined foreground, background colors
VisualElement.combineProperties(VisualElement, "color", "foreground", "background")
---@event onMouseClick {button number, x number, y number} Fired when the element is clicked
---@event onMouseUp {button number, x number, y number} Fired when the mouse is released
---@event onMouseRelease {button number, x number, y number} Fired when the mouse is released
---@event onMouseDrag {button number, x number, y number} Fired when the mouse is dragged
---@event onFocus {-} Fired when the element is focused
---@event onBlur {-} Fired when the element is blurred
---@event onKey {key number, code number, isRepeat boolean} Fired when a key is pressed
---@event onKeyUp {key number, code number} Fired when a key is released
---@event onChar {char string} Fired when a key is pressed
---@event onMouseClick {button number, x number, y number} Fired on mouse click
---@event onMouseUp {button number, x number, y number} Fired on mouse button release
---@event onMouseRelease {button number, x number, y number} Fired when mouse leaves while clicked
---@event onMouseDrag {button number, x number, y number} Fired when mouse moves while clicked
---@event onFocus {-} Fired when element receives focus
---@event onBlur {-} Fired when element loses focus
---@event onKey {key number, code number, isRepeat boolean} Fired on key press
---@event onKeyUp {key number, code number} Fired on key release
---@event onChar {char string} Fired on character input
VisualElement.listenTo(VisualElement, "focus")
VisualElement.listenTo(VisualElement, "blur")
@@ -84,6 +85,7 @@ VisualElement.listenTo(VisualElement, "blur")
local max, min = math.max, math.min
--- Creates a new VisualElement instance
--- @shortDescription Creates a new visual element
--- @param props table The properties to initialize the element with
--- @param basalt table The basalt instance
--- @return VisualElement object The newly created VisualElement instance
@@ -94,11 +96,16 @@ function VisualElement.new()
end
--- Initializes the VisualElement instance
--- @shortDescription Initializes a new visual element with properties
--- @param props table The properties to initialize the element with
--- @param basalt table The basalt instance
function VisualElement:init(props, basalt)
BaseElement.init(self, props, basalt)
self.set("type", "VisualElement")
end
--- Draws multiple characters at once with colors
--- @shortDescription Multi-character drawing with colors
---@protected
function VisualElement:multiBlit(x, y, width, height, text, fg, bg)
x = x + self.get("x") - 1
@@ -106,7 +113,8 @@ function VisualElement:multiBlit(x, y, width, height, text, fg, bg)
self.parent:multiBlit(x, y, width, height, text, fg, bg)
end
--- Draws a text character at the specified position, used in the rendering system
--- Draws text with foreground color
--- @shortDescription Draws text with foreground color
--- @param x number The x position to draw
--- @param y number The y position to draw
--- @param text string The text char to draw
@@ -117,7 +125,8 @@ function VisualElement:textFg(x, y, text, fg)
self.parent:textFg(x, y, text, fg)
end
--- Draws a text character with a background at the specified position, used in the rendering system
--- Draws text with background color
--- @shortDescription Draws text with background color
--- @param x number The x position to draw
--- @param y number The y position to draw
--- @param text string The text char to draw
@@ -128,7 +137,8 @@ function VisualElement:textBg(x, y, text, bg)
self.parent:textBg(x, y, text, bg)
end
--- Draws a text character with a foreground and background at the specified position, used in the rendering system
--- Draws text with both foreground and background colors
--- @shortDescription Draws text with both colors
--- @param x number The x position to draw
--- @param y number The y position to draw
--- @param text string The text char to draw
@@ -141,6 +151,7 @@ function VisualElement:blit(x, y, text, fg, bg)
end
--- Checks if the specified coordinates are within the bounds of the element
--- @shortDescription Checks if point is within bounds
--- @param x number The x position to check
--- @param y number The y position to check
--- @return boolean isInBounds Whether the coordinates are within the bounds of the element
@@ -153,6 +164,7 @@ function VisualElement:isInBounds(x, y)
end
--- Handles a mouse click event
--- @shortDescription Handles a mouse click event
--- @param button number The button that was clicked
--- @param x number The x position of the click
--- @param y number The y position of the click
@@ -167,6 +179,7 @@ function VisualElement:mouse_click(button, x, y)
end
--- Handles a mouse up event
--- @shortDescription Handles a mouse up event
--- @param button number The button that was released
--- @param x number The x position of the release
--- @param y number The y position of the release
@@ -181,6 +194,7 @@ function VisualElement:mouse_up(button, x, y)
end
--- Handles a mouse release event
--- @shortDescription Handles a mouse release event
--- @param button number The button that was released
--- @param x number The x position of the release
--- @param y number The y position of the release
@@ -195,17 +209,20 @@ function VisualElement:mouse_release(button, x, y)
end
--- Handles a focus event
--- @shortDescription Handles a focus event
function VisualElement:focus()
self:fireEvent("focus")
end
--- Handles a blur event
--- @shortDescription Handles a blur event
function VisualElement:blur()
self:fireEvent("blur")
self:setCursor(1,1, false)
end
--- Returns the absolute position of the element or the given coordinates.
--- @shortDescription Returns the absolute position of the element
---@param x? number x position
---@param y? number y position
function VisualElement:getAbsolutePosition(x, y)
@@ -229,6 +246,7 @@ function VisualElement:getAbsolutePosition(x, y)
end
--- Returns the relative position of the element or the given coordinates.
--- @shortDescription Returns the relative position of the element
---@param x? number x position
---@param y? number y position
---@return number, number
@@ -250,6 +268,7 @@ function VisualElement:getRelativePosition(x, y)
end
--- Sets the cursor position
--- @shortDescription Sets the cursor position
--- @param x number The x position of the cursor
--- @param y number The y position of the cursor
--- @param blink boolean Whether the cursor should blink
@@ -262,6 +281,7 @@ function VisualElement:setCursor(x, y, blink)
end
--- Renders the element
--- @shortDescription Renders the element
function VisualElement:render()
if(not self.get("backgroundEnabled"))then
return