deploy: 1c90c6cf04
This commit is contained in:
@@ -1,198 +1,159 @@
|
||||
local PropertySystem = require("propertySystem")
|
||||
local uuid = require("/libraries/utils").uuid
|
||||
# BaseElement : PropertySystem
|
||||
The base class for all UI elements in Basalt
|
||||
|
||||
--- The base class for all UI elements in Basalt
|
||||
--- @class BaseElement : PropertySystem
|
||||
local BaseElement = setmetatable({}, PropertySystem)
|
||||
BaseElement.__index = BaseElement
|
||||
BaseElement._events = {}
|
||||
## Properties
|
||||
|
||||
--- @property type string BaseElement The type identifier of the element
|
||||
BaseElement.defineProperty(BaseElement, "type", {default = {"BaseElement"}, type = "string", setter=function(self, value)
|
||||
if type(value) == "string" then
|
||||
table.insert(self._values.type, 1, value)
|
||||
return self._values.type
|
||||
end
|
||||
return value
|
||||
end, getter = function(self, _, index)
|
||||
if index~= nil and index < 1 then
|
||||
return self._values.type
|
||||
end
|
||||
return self._values.type[index or 1]
|
||||
end})
|
||||
|Property|Type|Default|Description|
|
||||
|---|---|---|---|
|
||||
|type|string|BaseElement|The type identifier of the element
|
||||
|id|string|BaseElement|The unique identifier for the element
|
||||
|name|string|BaseElement|The name of the element
|
||||
|eventCallbacks|table|{}|Table containing all registered event callbacks
|
||||
|
||||
--- @property id string BaseElement The unique identifier for the element
|
||||
BaseElement.defineProperty(BaseElement, "id", {default = "", type = "string", readonly = true})
|
||||
## Functions
|
||||
|
||||
--- @property name string BaseElement The name of the element
|
||||
BaseElement.defineProperty(BaseElement, "name", {default = "", type = "string"})
|
||||
|Method|Returns|Description|
|
||||
|---|---|---|
|
||||
|[BaseElement.listenTo](#BaseElement.listenTo)|-|
|
||||
|[BaseElement.new](#BaseElement.new)|table|
|
||||
|[BaseElement:destroy](#BaseElement:destroy)|-|
|
||||
|[BaseElement:dispatchEvent](#BaseElement:dispatchEvent)|boolean?|
|
||||
|[BaseElement:fireEvent](#BaseElement:fireEvent)|table|
|
||||
|[BaseElement:getBaseFrame](#BaseElement:getBaseFrame)|-|
|
||||
|[BaseElement:handleEvent](#BaseElement:handleEvent)|boolean?|
|
||||
|[BaseElement:init](#BaseElement:init)|table|
|
||||
|[BaseElement:isType](#BaseElement:isType)|boolean|
|
||||
|[BaseElement:listenEvent](#BaseElement:listenEvent)|table|
|
||||
|[BaseElement:postInit](#BaseElement:postInit)|table|
|
||||
|[BaseElement:registerCallback](#BaseElement:registerCallback)|table|
|
||||
|[BaseElement:updateRender](#BaseElement:updateRender)|-|
|
||||
|
||||
--- @property eventCallbacks table {} Table containing all registered event callbacks
|
||||
BaseElement.defineProperty(BaseElement, "eventCallbacks", {default = {}, type = "table"})
|
||||
## BaseElement.listenTo(class, eventName)
|
||||
Registers an event that this class can listen to
|
||||
|
||||
--- 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")
|
||||
function BaseElement.listenTo(class, eventName)
|
||||
if not class._events then
|
||||
class._events = {}
|
||||
end
|
||||
class._events[eventName] = true
|
||||
end
|
||||
### Parameters
|
||||
* `class` `table` The class to add the event to
|
||||
* `eventName` `string` The name of the event to register
|
||||
|
||||
--- 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)
|
||||
function BaseElement.new()
|
||||
local self = setmetatable({}, BaseElement):__init()
|
||||
return self
|
||||
end
|
||||
### Usage
|
||||
```lua
|
||||
BaseElement.listenTo(MyClass, "mouse_click")
|
||||
```
|
||||
|
||||
--- 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
|
||||
function BaseElement:init(props, basalt)
|
||||
self._props = props
|
||||
self._values.id = uuid()
|
||||
self.basalt = basalt
|
||||
self._registeredEvents = {}
|
||||
if BaseElement._events then
|
||||
for event in pairs(BaseElement._events) do
|
||||
self._registeredEvents[event] = true
|
||||
local handlerName = "on" .. event:gsub("_(%l)", function(c)
|
||||
return c:upper()
|
||||
end):gsub("^%l", string.upper)
|
||||
self[handlerName] = function(self, ...)
|
||||
self:registerCallback(event, ...)
|
||||
return self
|
||||
end
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
## BaseElement.new(props, basalt)
|
||||
Creates a new BaseElement instance
|
||||
|
||||
--- Post initialization hook
|
||||
--- @return table self The BaseElement instance
|
||||
function BaseElement:postInit()
|
||||
if(self._props)then
|
||||
for k,v in pairs(self._props)do
|
||||
self.set(k, v)
|
||||
end
|
||||
end
|
||||
self._props = nil
|
||||
return self
|
||||
end
|
||||
### Parameters
|
||||
* `props` `table` The properties to initialize the element with
|
||||
* `basalt` `table` The basalt instance
|
||||
|
||||
--- 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)
|
||||
for _, t in ipairs(self._values.type) do
|
||||
if t == type then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
### Returns
|
||||
* `table` `The` newly created BaseElement instance
|
||||
|
||||
--- 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
|
||||
--- @usage element:listenEvent("mouse_click", true)
|
||||
function BaseElement:listenEvent(eventName, enable)
|
||||
enable = enable ~= false
|
||||
if enable ~= (self._registeredEvents[eventName] or false) then
|
||||
if enable then
|
||||
self._registeredEvents[eventName] = true
|
||||
if self.parent then
|
||||
self.parent:registerChildEvent(self, eventName)
|
||||
end
|
||||
else
|
||||
self._registeredEvents[eventName] = nil
|
||||
if self.parent then
|
||||
self.parent:unregisterChildEvent(self, eventName)
|
||||
end
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
### Usage
|
||||
```lua
|
||||
local element = BaseElement.new("myId", basalt)
|
||||
```
|
||||
|
||||
--- Registers a callback function for an event
|
||||
--- @param event string The event to register the callback for
|
||||
--- @param callback function The callback function to register
|
||||
--- @return table self The BaseElement instance
|
||||
--- @usage element:registerCallback("mouse_click", function(self, ...) end)
|
||||
function BaseElement:registerCallback(event, callback)
|
||||
if not self._registeredEvents[event] then
|
||||
self:listenEvent(event, true)
|
||||
end
|
||||
## BaseElement:destroy()
|
||||
|
||||
if not self._values.eventCallbacks[event] then
|
||||
self._values.eventCallbacks[event] = {}
|
||||
end
|
||||
## BaseElement:dispatchEvent(event)
|
||||
Handles all events
|
||||
@vararg any The arguments for the event
|
||||
|
||||
table.insert(self._values.eventCallbacks[event], callback)
|
||||
return self
|
||||
end
|
||||
### Parameters
|
||||
* `event` `string` The event to handle
|
||||
|
||||
--- 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
|
||||
--- @usage element:fireEvent("mouse_click", 1, 2)
|
||||
function BaseElement:fireEvent(event, ...)
|
||||
if self._values.eventCallbacks[event] then
|
||||
for _, callback in ipairs(self._values.eventCallbacks[event]) do
|
||||
local result = callback(self, ...)
|
||||
return result
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
### Returns
|
||||
* `boolean?` `handled` Whether the event was handled
|
||||
|
||||
--- 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
|
||||
function BaseElement:dispatchEvent(event, ...)
|
||||
if self[event] then
|
||||
return self[event](self, ...)
|
||||
end
|
||||
return self:handleEvent(event, ...)
|
||||
end
|
||||
## BaseElement:fireEvent(event, ...)
|
||||
Triggers an event and calls all registered callbacks
|
||||
|
||||
--- 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
|
||||
function BaseElement:handleEvent(event, ...)
|
||||
return false
|
||||
end
|
||||
### Parameters
|
||||
* `event` `string` The event to fire
|
||||
* `...` `any` Additional arguments to pass to the callbacks
|
||||
|
||||
function BaseElement:getBaseFrame()
|
||||
if self.parent then
|
||||
return self.parent:getBaseFrame()
|
||||
end
|
||||
return self
|
||||
end
|
||||
### Returns
|
||||
* `table` `self` The BaseElement instance
|
||||
|
||||
function BaseElement:destroy()
|
||||
### Usage
|
||||
```lua
|
||||
element:fireEvent("mouse_click", 1, 2)
|
||||
```
|
||||
|
||||
end
|
||||
## BaseElement:getBaseFrame()
|
||||
|
||||
--- Requests a render update for this element
|
||||
--- @usage element:updateRender()
|
||||
function BaseElement:updateRender()
|
||||
if(self.parent) then
|
||||
self.parent:updateRender()
|
||||
else
|
||||
self._renderUpdate = true
|
||||
end
|
||||
end
|
||||
## BaseElement:handleEvent(event)
|
||||
The default event handler for all events
|
||||
@vararg any The arguments for the event
|
||||
|
||||
### Parameters
|
||||
* `event` `string` The event to handle
|
||||
|
||||
### Returns
|
||||
* `boolean?` `handled` Whether the event was handled
|
||||
|
||||
## BaseElement:init(props, basalt)
|
||||
Initializes the BaseElement instance
|
||||
|
||||
### Parameters
|
||||
* `props` `table` The properties to initialize the element with
|
||||
* `basalt` `table` The basalt instance
|
||||
|
||||
### Returns
|
||||
* `table` `self` The initialized instance
|
||||
|
||||
## BaseElement:isType(type)
|
||||
Checks if the element is a specific type
|
||||
|
||||
### Parameters
|
||||
* `type` `string` The type to check for
|
||||
|
||||
### Returns
|
||||
* `boolean` `Whether` the element is of the specified type
|
||||
|
||||
## BaseElement:listenEvent(eventName, enable?)
|
||||
Enables or disables event listening for a specific event
|
||||
|
||||
### Parameters
|
||||
* `eventName` `string` The name of the event to listen for
|
||||
* `enable` *(optional)* `boolean` Whether to enable or disable the event (default: true)
|
||||
|
||||
### Returns
|
||||
* `table` `self` The BaseElement instance
|
||||
|
||||
### Usage
|
||||
```lua
|
||||
element:listenEvent("mouse_click", true)
|
||||
```
|
||||
|
||||
## BaseElement:postInit()
|
||||
Post initialization hook
|
||||
|
||||
### Returns
|
||||
* `table` `self` The BaseElement instance
|
||||
|
||||
## BaseElement:registerCallback(event, callback)
|
||||
Registers a callback function for an event
|
||||
|
||||
### Parameters
|
||||
* `event` `string` The event to register the callback for
|
||||
* `callback` `function` The callback function to register
|
||||
|
||||
### Returns
|
||||
* `table` `self` The BaseElement instance
|
||||
|
||||
### Usage
|
||||
```lua
|
||||
element:registerCallback("mouse_click", function(self, ...) end)
|
||||
```
|
||||
|
||||
## BaseElement:updateRender()
|
||||
Requests a render update for this element
|
||||
|
||||
### Usage
|
||||
```lua
|
||||
element:updateRender()
|
||||
```
|
||||
|
||||
return BaseElement
|
||||
@@ -1,65 +1,37 @@
|
||||
local elementManager = require("elementManager")
|
||||
local Container = elementManager.getElement("Container")
|
||||
local Render = require("render")
|
||||
# BaseFrame : Container
|
||||
|
||||
---@class BaseFrame : Container
|
||||
local BaseFrame = setmetatable({}, Container)
|
||||
BaseFrame.__index = BaseFrame
|
||||
## Properties
|
||||
|
||||
---@property text term term nil text
|
||||
BaseFrame.defineProperty(BaseFrame, "term", {default = nil, type = "table", setter = function(self, value)
|
||||
if value == nil or value.setCursorPos == nil then
|
||||
return value
|
||||
end
|
||||
self._render = Render.new(value)
|
||||
self._renderUpdate = true
|
||||
local width, height = value.getSize()
|
||||
self.set("width", width)
|
||||
self.set("height", height)
|
||||
return value
|
||||
end})
|
||||
|Property|Type|Default|Description|
|
||||
|---|---|---|---|
|
||||
|text|term|term|nil text
|
||||
|
||||
function BaseFrame.new()
|
||||
local self = setmetatable({}, BaseFrame):__init()
|
||||
self.set("term", term.current())
|
||||
self.set("background", colors.lightGray)
|
||||
return self
|
||||
end
|
||||
## Functions
|
||||
|
||||
function BaseFrame:init(props, basalt)
|
||||
Container.init(self, props, basalt)
|
||||
self.set("type", "BaseFrame")
|
||||
end
|
||||
|Method|Returns|Description|
|
||||
|---|---|---|
|
||||
|[BaseFrame.new](#BaseFrame.new)|-|
|
||||
|[BaseFrame:blit](#BaseFrame:blit)|-|
|
||||
|[BaseFrame:init](#BaseFrame:init)|-|
|
||||
|[BaseFrame:multiBlit](#BaseFrame:multiBlit)|-|
|
||||
|[BaseFrame:render](#BaseFrame:render)|-|
|
||||
|[BaseFrame:setCursor](#BaseFrame:setCursor)|-|
|
||||
|[BaseFrame:textBg](#BaseFrame:textBg)|-|
|
||||
|[BaseFrame:textFg](#BaseFrame:textFg)|-|
|
||||
|
||||
function BaseFrame:multiBlit(x, y, width, height, text, fg, bg)
|
||||
self._render:multiBlit(x, y, width, height, text, fg, bg)
|
||||
end
|
||||
## BaseFrame.new()
|
||||
|
||||
function BaseFrame:textFg(x, y, text, fg)
|
||||
self._render:textFg(x, y, text, fg)
|
||||
end
|
||||
## BaseFrame:blit()
|
||||
|
||||
function BaseFrame:textBg(x, y, text, bg)
|
||||
self._render:textBg(x, y, text, bg)
|
||||
end
|
||||
## BaseFrame:init()
|
||||
|
||||
function BaseFrame:blit(x, y, text, fg, bg)
|
||||
self._render:blit(x, y, text, fg, bg)
|
||||
end
|
||||
## BaseFrame:multiBlit()
|
||||
|
||||
function BaseFrame:setCursor(x, y, blink)
|
||||
local term = self.get("term")
|
||||
self._render:setCursor(x, y, blink)
|
||||
end
|
||||
## BaseFrame:render()
|
||||
|
||||
function BaseFrame:render()
|
||||
if(self._renderUpdate) then
|
||||
if self._render ~= nil then
|
||||
Container.render(self)
|
||||
self._render:render()
|
||||
self._renderUpdate = false
|
||||
end
|
||||
end
|
||||
end
|
||||
## BaseFrame:setCursor()
|
||||
|
||||
## BaseFrame:textBg()
|
||||
|
||||
## BaseFrame:textFg()
|
||||
|
||||
return BaseFrame
|
||||
@@ -1,42 +1,33 @@
|
||||
local elementManager = require("elementManager")
|
||||
local VisualElement = elementManager.getElement("VisualElement")
|
||||
local getCenteredPosition = require("libraries/utils").getCenteredPosition
|
||||
# Button : VisualElement
|
||||
|
||||
---@class Button : VisualElement
|
||||
local Button = setmetatable({}, VisualElement)
|
||||
Button.__index = Button
|
||||
## Properties
|
||||
|
||||
---@property text string Button Button text
|
||||
Button.defineProperty(Button, "text", {default = "Button", type = "string", canTriggerRender = true})
|
||||
|Property|Type|Default|Description|
|
||||
|---|---|---|---|
|
||||
|text|string|Button|Button text
|
||||
|
||||
---@event mouse_click The event that is triggered when the button is clicked
|
||||
Button.listenTo(Button, "mouse_click")
|
||||
Button.listenTo(Button, "mouse_up")
|
||||
## Functions
|
||||
|
||||
--- Creates a new Button instance
|
||||
--- @return table self The created instance
|
||||
function Button.new()
|
||||
local self = setmetatable({}, Button):__init()
|
||||
self.set("width", 10)
|
||||
self.set("height", 3)
|
||||
self.set("z", 5)
|
||||
return self
|
||||
end
|
||||
|Method|Returns|Description|
|
||||
|---|---|---|
|
||||
|[Button.new](#Button.new)|table|
|
||||
|[Button:init](#Button:init)|-|
|
||||
|[Button:render](#Button:render)|-|
|
||||
|
||||
--- 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)
|
||||
VisualElement.init(self, props, basalt)
|
||||
self.set("type", "Button")
|
||||
end
|
||||
@event mouse_click The event that is triggered when the button is clicked
|
||||
## Button.new()
|
||||
Creates a new Button instance
|
||||
|
||||
--- Renders the Button
|
||||
function Button:render()
|
||||
VisualElement.render(self)
|
||||
local text = self.get("text")
|
||||
local xO, yO = getCenteredPosition(text, self.get("width"), self.get("height"))
|
||||
self:textFg(xO, yO, text, self.get("foreground"))
|
||||
end
|
||||
### Returns
|
||||
* `table` `self` The created instance
|
||||
|
||||
## Button:init(props, basalt)
|
||||
Initializes the Button instance
|
||||
|
||||
### Parameters
|
||||
* `props` `table` The properties to initialize the element with
|
||||
* `basalt` `table` The basalt instance
|
||||
|
||||
## Button:render()
|
||||
Renders the Button
|
||||
|
||||
return Button
|
||||
@@ -1,60 +1,46 @@
|
||||
local VisualElement = require("elements/VisualElement")
|
||||
# Checkbox : VisualElement
|
||||
|
||||
---@class Checkbox : VisualElement
|
||||
local Checkbox = setmetatable({}, VisualElement)
|
||||
Checkbox.__index = Checkbox
|
||||
## Properties
|
||||
|
||||
---@property checked boolean Whether checkbox is checked
|
||||
Checkbox.defineProperty(Checkbox, "checked", {default = false, type = "boolean", canTriggerRender = true})
|
||||
---@property text string Label text
|
||||
Checkbox.defineProperty(Checkbox, "text", {default = "", type = "string", canTriggerRender = true})
|
||||
---@property symbol string Check symbol
|
||||
Checkbox.defineProperty(Checkbox, "symbol", {default = "x", type = "string"})
|
||||
|Property|Type|Default|Description|
|
||||
|---|---|---|---|
|
||||
|checked|boolean|Whether|checkbox is checked
|
||||
|text|string|Label|text
|
||||
|symbol|string|Check|symbol
|
||||
|
||||
Checkbox.listenTo(Checkbox, "mouse_click")
|
||||
## Functions
|
||||
|
||||
--- Creates a new Checkbox instance
|
||||
--- @return Checkbox self The created instance
|
||||
function Checkbox.new()
|
||||
local self = setmetatable({}, Checkbox):__init()
|
||||
self.set("width", 1)
|
||||
self.set("height", 1)
|
||||
return self
|
||||
end
|
||||
|Method|Returns|Description|
|
||||
|---|---|---|
|
||||
|[Checkbox.new](#Checkbox.new)|Checkbox|
|
||||
|[Checkbox:init](#Checkbox:init)|-|
|
||||
|[Checkbox:mouse_click](#Checkbox:mouse_click)|boolean|
|
||||
|[Checkbox:render](#Checkbox:render)|-|
|
||||
|
||||
--- 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)
|
||||
VisualElement.init(self, props, basalt)
|
||||
self.set("type", "Checkbox")
|
||||
end
|
||||
## Checkbox.new()
|
||||
Creates a new Checkbox instance
|
||||
|
||||
--- 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
|
||||
function Checkbox:mouse_click(button, x, y)
|
||||
if VisualElement.mouse_click(self, button, x, y) then
|
||||
self.set("checked", not self.get("checked"))
|
||||
self:fireEvent("change", self.get("checked"))
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
### Returns
|
||||
* `Checkbox` `self` The created instance
|
||||
|
||||
--- Renders the Checkbox
|
||||
function Checkbox:render()
|
||||
VisualElement.render(self)
|
||||
## Checkbox:init(props, basalt)
|
||||
Initializes the Checkbox instance
|
||||
|
||||
local text = self.get("checked") and self.get("symbol") or " "
|
||||
self:textFg(1, 1, "["..text.."]", self.get("foreground"))
|
||||
### Parameters
|
||||
* `props` `table` The properties to initialize the element with
|
||||
* `basalt` `table` The basalt instance
|
||||
|
||||
local label = self.get("text")
|
||||
if #label > 0 then
|
||||
self:textFg(4, 1, label, self.get("foreground"))
|
||||
end
|
||||
end
|
||||
## Checkbox:mouse_click(button, x, y)
|
||||
Handles mouse click events
|
||||
|
||||
### Parameters
|
||||
* `button` `number` The button that was clicked
|
||||
* `x` `number` The x position of the click
|
||||
* `y` `number` The y position of the click
|
||||
|
||||
### Returns
|
||||
* `boolean` `Whether` the event was handled
|
||||
|
||||
## Checkbox:render()
|
||||
Renders the Checkbox
|
||||
|
||||
return Checkbox
|
||||
@@ -1,467 +1,253 @@
|
||||
local elementManager = require("elementManager")
|
||||
local VisualElement = elementManager.getElement("VisualElement")
|
||||
local expect = require("libraries/expect")
|
||||
local split = require("libraries/utils").split
|
||||
# Container : VisualElement
|
||||
|
||||
local max = math.max
|
||||
## Properties
|
||||
|
||||
---@class Container : VisualElement
|
||||
local Container = setmetatable({}, VisualElement)
|
||||
Container.__index = Container
|
||||
|Property|Type|Default|Description|
|
||||
|---|---|---|---|
|
||||
|children|table|{}|The children of the container
|
||||
|childrenSorted|boolean|true|Whether the children are sorted
|
||||
|childrenEventsSorted|boolean|true|Whether the children events are sorted
|
||||
|childrenEvents|table|{}|The children events of the container
|
||||
|eventListenerCount|table|{}|The event listener count of the container
|
||||
|focusedChild|table|nil|The focused child of the container
|
||||
|visibleChildren|table|{}|The visible children of the container
|
||||
|visibleChildrenEvents|table|{}|The visible children events of the container
|
||||
|
||||
---@property children table {} The children of the container
|
||||
Container.defineProperty(Container, "children", {default = {}, type = "table"})
|
||||
---@property childrenSorted boolean true Whether the children are sorted
|
||||
Container.defineProperty(Container, "childrenSorted", {default = true, type = "boolean"})
|
||||
---@property childrenEventsSorted boolean true Whether the children events are sorted
|
||||
Container.defineProperty(Container, "childrenEventsSorted", {default = true, type = "boolean"})
|
||||
---@property childrenEvents table {} The children events of the container
|
||||
Container.defineProperty(Container, "childrenEvents", {default = {}, type = "table"})
|
||||
---@property eventListenerCount table {} The event listener count of the container
|
||||
Container.defineProperty(Container, "eventListenerCount", {default = {}, type = "table"})
|
||||
---@property focusedChild table nil The focused child of the container
|
||||
Container.defineProperty(Container, "focusedChild", {default = nil, type = "table", setter = function(self, value, internal)
|
||||
local oldChild = self._values.focusedChild
|
||||
## Functions
|
||||
|
||||
if value == oldChild then return value end
|
||||
|Method|Returns|Description|
|
||||
|---|---|---|
|
||||
|[Container.new](#Container.new)|Container|
|
||||
|[Container:addChild](#Container:addChild)|Container|
|
||||
|[Container:blit](#Container:blit)|Container|
|
||||
|[Container:char](#Container:char)|boolean|
|
||||
|[Container:clear](#Container:clear)|Container|
|
||||
|[Container:getChild](#Container:getChild)|Container?|
|
||||
|[Container:handleEvent](#Container:handleEvent)|boolean|
|
||||
|[Container:init](#Container:init)|-|
|
||||
|[Container:isChildVisible](#Container:isChildVisible)|boolean|
|
||||
|[Container:key](#Container:key)|boolean|
|
||||
|[Container:key_up](#Container:key_up)|boolean|
|
||||
|[Container:mouse_click](#Container:mouse_click)|boolean|
|
||||
|[Container:mouse_up](#Container:mouse_up)|boolean|
|
||||
|[Container:multiBlit](#Container:multiBlit)|Container|
|
||||
|[Container:registerChildEvent](#Container:registerChildEvent)|Container|
|
||||
|[Container:registerChildrenEvents](#Container:registerChildrenEvents)|Container|
|
||||
|[Container:removeChild](#Container:removeChild)|Container|
|
||||
|[Container:removeChildrenEvents](#Container:removeChildrenEvents)|Container|
|
||||
|[Container:render](#Container:render)|-|
|
||||
|[Container:sortChildren](#Container:sortChildren)|Container|
|
||||
|[Container:sortChildrenEvents](#Container:sortChildrenEvents)|Container|
|
||||
|[Container:textFg](#Container:textFg)|Container|
|
||||
|[Container:unregisterChildEvent](#Container:unregisterChildEvent)|Container|
|
||||
|
||||
if oldChild then
|
||||
if oldChild:isType("Container") then
|
||||
oldChild.set("focusedChild", nil, true)
|
||||
end
|
||||
oldChild.set("focused", false, true)
|
||||
end
|
||||
## Container.new()
|
||||
Creates a new Container instance
|
||||
|
||||
if value and not internal then
|
||||
value.set("focused", true, true)
|
||||
if self.parent then
|
||||
self.parent:setFocusedChild(self)
|
||||
end
|
||||
end
|
||||
### Returns
|
||||
* `Container` `self` The new container instance
|
||||
|
||||
return value
|
||||
end})
|
||||
## Container:addChild(child)
|
||||
Adds a child to the container
|
||||
|
||||
---@property visibleChildren table {} The visible children of the container
|
||||
Container.defineProperty(Container, "visibleChildren", {default = {}, type = "table"})
|
||||
---@property visibleChildrenEvents table {} The visible children events of the container
|
||||
Container.defineProperty(Container, "visibleChildrenEvents", {default = {}, type = "table"})
|
||||
### Parameters
|
||||
* `child` `table` The child to add
|
||||
|
||||
for k, _ in pairs(elementManager:getElementList()) do
|
||||
local capitalizedName = k:sub(1,1):upper() .. k:sub(2)
|
||||
if capitalizedName ~= "BaseFrame" then
|
||||
Container["add"..capitalizedName] = function(self, ...)
|
||||
expect(1, self, "table")
|
||||
local element = self.basalt.create(k, ...)
|
||||
self:addChild(element)
|
||||
element:postInit()
|
||||
return element
|
||||
end
|
||||
Container["addDelayed"..capitalizedName] = function(self, prop)
|
||||
expect(1, self, "table")
|
||||
local element = self.basalt.create(k, prop, true, self)
|
||||
return element
|
||||
end
|
||||
end
|
||||
end
|
||||
### Returns
|
||||
* `Container` `self` The container instance
|
||||
|
||||
--- Creates a new Container instance
|
||||
--- @return Container self The new container instance
|
||||
function Container.new()
|
||||
local self = setmetatable({}, Container):__init()
|
||||
return self
|
||||
end
|
||||
## Container:blit(x, y, text, fg, bg)
|
||||
Draws a line of text and fg and bg as colors, it is usually used in the render loop
|
||||
|
||||
--- 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)
|
||||
VisualElement.init(self, props, basalt)
|
||||
self.set("type", "Container")
|
||||
end
|
||||
### Parameters
|
||||
* `x` `number` The x position to draw the text
|
||||
* `y` `number` The y position to draw the text
|
||||
* `text` `string` The text to draw
|
||||
* `fg` `string` The foreground color of the text
|
||||
* `bg` `string` The background color of the text
|
||||
|
||||
--- Returns whether a child is visible
|
||||
--- @param child table The child to check
|
||||
--- @return boolean boolean the child is visible
|
||||
function Container:isChildVisible(child)
|
||||
local childX, childY = child.get("x"), child.get("y")
|
||||
local childW, childH = child.get("width"), child.get("height")
|
||||
local containerW, containerH = self.get("width"), self.get("height")
|
||||
### Returns
|
||||
* `Container` `self` The container instance
|
||||
|
||||
return childX <= containerW and
|
||||
childY <= containerH and
|
||||
childX + childW > 0 and
|
||||
childY + childH > 0
|
||||
end
|
||||
## Container:char(char)
|
||||
Handles char events
|
||||
|
||||
--- Adds a child to the container
|
||||
--- @param child table The child to add
|
||||
--- @return Container self The container instance
|
||||
function Container:addChild(child)
|
||||
if child == self then
|
||||
error("Cannot add container to itself")
|
||||
end
|
||||
### Parameters
|
||||
* `char` `string` The character that was pressed
|
||||
|
||||
table.insert(self._values.children, child)
|
||||
child.parent = self
|
||||
self.set("childrenSorted", false)
|
||||
self:registerChildrenEvents(child)
|
||||
return self
|
||||
end
|
||||
### Returns
|
||||
* `boolean` `Whether` the event was handled
|
||||
|
||||
local function sortAndFilterChildren(self, children)
|
||||
local visibleChildren = {}
|
||||
|
||||
for _, child in ipairs(children) do
|
||||
if self:isChildVisible(child) and child.get("visible") then
|
||||
table.insert(visibleChildren, child)
|
||||
end
|
||||
end
|
||||
## Container:clear()
|
||||
Clears the container
|
||||
|
||||
for i = 2, #visibleChildren do
|
||||
local current = visibleChildren[i]
|
||||
local currentZ = current.get("z")
|
||||
local j = i - 1
|
||||
while j > 0 do
|
||||
local compare = visibleChildren[j].get("z")
|
||||
if compare > currentZ then
|
||||
visibleChildren[j + 1] = visibleChildren[j]
|
||||
j = j - 1
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
visibleChildren[j + 1] = current
|
||||
end
|
||||
### Returns
|
||||
* `Container` `self` The container instance
|
||||
|
||||
return visibleChildren
|
||||
end
|
||||
## Container:getChild(path)
|
||||
Removes a child from the container
|
||||
|
||||
--- Clears the container
|
||||
--- @return Container self The container instance
|
||||
function Container:clear()
|
||||
self.set("children", {})
|
||||
self.set("childrenEvents", {})
|
||||
self.set("visibleChildren", {})
|
||||
self.set("visibleChildrenEvents", {})
|
||||
self.set("childrenSorted", true)
|
||||
self.set("childrenEventsSorted", true)
|
||||
return self
|
||||
end
|
||||
### Parameters
|
||||
* `path` `string` The path to the child to remove
|
||||
|
||||
--- Sorts the children of the container
|
||||
--- @return Container self The container instance
|
||||
function Container:sortChildren()
|
||||
self.set("visibleChildren", sortAndFilterChildren(self, self._values.children))
|
||||
self.set("childrenSorted", true)
|
||||
return self
|
||||
end
|
||||
### Returns
|
||||
* `Container?` `self` The container instance
|
||||
|
||||
--- 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)
|
||||
if self._values.childrenEvents[eventName] then
|
||||
self._values.visibleChildrenEvents[eventName] = sortAndFilterChildren(self, self._values.childrenEvents[eventName])
|
||||
end
|
||||
self.set("childrenEventsSorted", true)
|
||||
return self
|
||||
end
|
||||
## Container:handleEvent(event)
|
||||
Default handler for events
|
||||
@vararg any The event arguments
|
||||
|
||||
--- 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)
|
||||
if(child._registeredEvents == nil)then return end
|
||||
for event in pairs(child._registeredEvents) do
|
||||
self:registerChildEvent(child, event)
|
||||
end
|
||||
return self
|
||||
end
|
||||
### Parameters
|
||||
* `event` `string` The event to handle
|
||||
|
||||
--- 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
|
||||
function Container:registerChildEvent(child, eventName)
|
||||
if not self._values.childrenEvents[eventName] then
|
||||
self._values.childrenEvents[eventName] = {}
|
||||
self._values.eventListenerCount[eventName] = 0
|
||||
### Returns
|
||||
* `boolean` `Whether` the event was handled
|
||||
|
||||
if self.parent then
|
||||
self.parent:registerChildEvent(self, eventName)
|
||||
end
|
||||
end
|
||||
## Container:init(props, basalt)
|
||||
Initializes the Container instance
|
||||
|
||||
for _, registeredChild in ipairs(self._values.childrenEvents[eventName]) do
|
||||
if registeredChild == child then
|
||||
return self
|
||||
end
|
||||
end
|
||||
### Parameters
|
||||
* `props` `table` The properties to initialize the element with
|
||||
* `basalt` `table` The basalt instance
|
||||
|
||||
self.set("childrenEventsSorted", false)
|
||||
table.insert(self._values.childrenEvents[eventName], child)
|
||||
self._values.eventListenerCount[eventName] = self._values.eventListenerCount[eventName] + 1
|
||||
return self
|
||||
end
|
||||
## Container:isChildVisible(child)
|
||||
Returns whether a child is visible
|
||||
|
||||
--- 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)
|
||||
if(child._registeredEvents == nil)then return self end
|
||||
for event in pairs(child._registeredEvents) do
|
||||
self:unregisterChildEvent(child, event)
|
||||
end
|
||||
return self
|
||||
end
|
||||
### Parameters
|
||||
* `child` `table` The child to check
|
||||
|
||||
--- 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
|
||||
function Container:unregisterChildEvent(child, eventName)
|
||||
if self._values.childrenEvents[eventName] then
|
||||
for i, listener in ipairs(self._values.childrenEvents[eventName]) do
|
||||
if listener == child then
|
||||
table.remove(self._values.childrenEvents[eventName], i)
|
||||
self._values.eventListenerCount[eventName] = self._values.eventListenerCount[eventName] - 1
|
||||
### Returns
|
||||
* `boolean` `boolean` the child is visible
|
||||
|
||||
if self._values.eventListenerCount[eventName] <= 0 then
|
||||
self._values.childrenEvents[eventName] = nil
|
||||
self._values.eventListenerCount[eventName] = nil
|
||||
## Container:key(key)
|
||||
Handles key events
|
||||
|
||||
if self.parent then
|
||||
self.parent:unregisterChildEvent(self, eventName)
|
||||
end
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
### Parameters
|
||||
* `key` `number` The key that was pressed
|
||||
|
||||
--- Removes a child from the container
|
||||
--- @param child table The child to remove
|
||||
--- @return Container self The container instance
|
||||
function Container:removeChild(child)
|
||||
for i,v in ipairs(self._values.children) do
|
||||
if v == child then
|
||||
table.remove(self._values.children, i)
|
||||
child.parent = nil
|
||||
break
|
||||
end
|
||||
end
|
||||
self:removeChildrenEvents(child)
|
||||
return self
|
||||
end
|
||||
### Returns
|
||||
* `boolean` `Whether` the event was handled
|
||||
|
||||
--- 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)
|
||||
if type(path) == "string" then
|
||||
local parts = split(path, "/")
|
||||
for _,v in pairs(self._values.children) do
|
||||
if v.get("name") == parts[1] then
|
||||
if #parts == 1 then
|
||||
return v
|
||||
else
|
||||
if(v:isType("Container"))then
|
||||
return v:find(table.concat(parts, "/", 2))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
## Container:key_up(key)
|
||||
Handles key up events
|
||||
|
||||
local function convertMousePosition(self, event, ...)
|
||||
local args = {...}
|
||||
if event:find("mouse_") then
|
||||
local button, absX, absY = ...
|
||||
local relX, relY = self:getRelativePosition(absX, absY)
|
||||
args = {button, relX, relY}
|
||||
end
|
||||
return args
|
||||
end
|
||||
### Parameters
|
||||
* `key` `number` The key that was released
|
||||
|
||||
local function callChildrenEvents(self, visibleOnly, event, ...)
|
||||
local children = visibleOnly and self.get("visibleChildrenEvents") or self.get("childrenEvents")
|
||||
if children[event] then
|
||||
local events = children[event]
|
||||
for i = #events, 1, -1 do
|
||||
local child = events[i]
|
||||
if(child:dispatchEvent(event, ...))then
|
||||
return true, child
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
### Returns
|
||||
* `boolean` `Whether` the event was handled
|
||||
|
||||
--- Default handler for events
|
||||
--- @param event string The event to handle
|
||||
--- @vararg any The event arguments
|
||||
--- @return boolean Whether the event was handled
|
||||
function Container:handleEvent(event, ...)
|
||||
VisualElement.handleEvent(self, event, ...)
|
||||
local args = convertMousePosition(self, event, ...)
|
||||
return callChildrenEvents(self, false, event, table.unpack(args))
|
||||
end
|
||||
## Container:mouse_click(button, x, y)
|
||||
Handles mouse click events
|
||||
|
||||
--- 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
|
||||
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)
|
||||
local success, child = callChildrenEvents(self, true, "mouse_click", table.unpack(args))
|
||||
if(success)then
|
||||
self.set("focusedChild", child)
|
||||
return true
|
||||
end
|
||||
self.set("focusedChild", nil)
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
### Parameters
|
||||
* `button` `number` The button that was clicked
|
||||
* `x` `number` The x position of the click
|
||||
* `y` `number` The y position of the click
|
||||
|
||||
--- 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
|
||||
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)
|
||||
local success, child = callChildrenEvents(self, true, "mouse_up", table.unpack(args))
|
||||
if(success)then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
### Returns
|
||||
* `boolean` `Whether` the event was handled
|
||||
|
||||
--- Handles key events
|
||||
--- @param key number The key that was pressed
|
||||
--- @return boolean Whether the event was handled
|
||||
function Container:key(key)
|
||||
if self.get("focusedChild") then
|
||||
return self.get("focusedChild"):dispatchEvent("key", key)
|
||||
end
|
||||
return true
|
||||
end
|
||||
## Container:mouse_up(button, x, y)
|
||||
Handles mouse up events
|
||||
|
||||
--- Handles char events
|
||||
--- @param char string The character that was pressed
|
||||
--- @return boolean Whether the event was handled
|
||||
function Container:char(char)
|
||||
if self.get("focusedChild") then
|
||||
return self.get("focusedChild"):dispatchEvent("char", char)
|
||||
end
|
||||
return true
|
||||
end
|
||||
### Parameters
|
||||
* `button` `number` The button that was clicked
|
||||
* `x` `number` The x position of the click
|
||||
* `y` `number` The y position of the click
|
||||
|
||||
--- Handles key up events
|
||||
--- @param key number The key that was released
|
||||
--- @return boolean Whether the event was handled
|
||||
function Container:key_up(key)
|
||||
if self.get("focusedChild") then
|
||||
return self.get("focusedChild"):dispatchEvent("key_up", key)
|
||||
end
|
||||
return true
|
||||
end
|
||||
### Returns
|
||||
* `boolean` `Whether` the event was handled
|
||||
|
||||
--- Draws multiple lines of text, fg and bg strings, it is usually used in the render loop
|
||||
--- @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
|
||||
--- @param height number The height of the text
|
||||
--- @param text string The text to draw
|
||||
--- @param fg string The foreground color of the text
|
||||
--- @param bg string The background color of the text
|
||||
--- @return Container self The container instance
|
||||
function Container:multiBlit(x, y, width, height, text, fg, bg)
|
||||
local w, h = self.get("width"), self.get("height")
|
||||
## Container:multiBlit(x, y, width, height, text, fg, bg)
|
||||
Draws multiple lines of text, fg and bg strings, it is usually used in the render loop
|
||||
|
||||
width = x < 1 and math.min(width + x - 1, w) or math.min(width, math.max(0, w - x + 1))
|
||||
height = y < 1 and math.min(height + y - 1, h) or math.min(height, math.max(0, h - y + 1))
|
||||
### Parameters
|
||||
* `x` `number` The x position to draw the text
|
||||
* `y` `number` The y position to draw the text
|
||||
* `width` `number` The width of the text
|
||||
* `height` `number` The height of the text
|
||||
* `text` `string` The text to draw
|
||||
* `fg` `string` The foreground color of the text
|
||||
* `bg` `string` The background color of the text
|
||||
|
||||
if width <= 0 or height <= 0 then return self end
|
||||
### Returns
|
||||
* `Container` `self` The container instance
|
||||
|
||||
VisualElement.multiBlit(self, math.max(1, x), math.max(1, y), width, height, text, fg, bg)
|
||||
return self
|
||||
end
|
||||
## Container:registerChildEvent(child, eventName)
|
||||
Registers the children events of the container
|
||||
|
||||
--- Draws a line of text and fg as color, it is usually used in the render loop
|
||||
--- @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
|
||||
--- @param fg color The foreground color of the text
|
||||
--- @return Container self The container instance
|
||||
function Container:textFg(x, y, text, fg)
|
||||
local w, h = self.get("width"), self.get("height")
|
||||
### Parameters
|
||||
* `child` `table` The child to register events for
|
||||
* `eventName` `string` The event name to register
|
||||
|
||||
if y < 1 or y > h then return self end
|
||||
### Returns
|
||||
* `Container` `self` The container instance
|
||||
|
||||
local textStart = x < 1 and (2 - x) or 1
|
||||
local textLen = math.min(#text - textStart + 1, w - math.max(1, x) + 1)
|
||||
## Container:registerChildrenEvents(child)
|
||||
Registers the children events of the container
|
||||
|
||||
if textLen <= 0 then return self end
|
||||
### Parameters
|
||||
* `child` `table` The child to register events for
|
||||
|
||||
VisualElement.textFg(self, math.max(1, x), math.max(1, y), text:sub(textStart, textStart + textLen - 1), fg)
|
||||
end
|
||||
### Returns
|
||||
* `Container` `self` The container instance
|
||||
|
||||
--- Draws a line of text and fg and bg as colors, it is usually used in the render loop
|
||||
--- @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
|
||||
--- @param fg string The foreground color of the text
|
||||
--- @param bg string The background color of the text
|
||||
--- @return Container self The container instance
|
||||
function Container:blit(x, y, text, fg, bg)
|
||||
local w, h = self.get("width"), self.get("height")
|
||||
## Container:removeChild(child)
|
||||
Removes a child from the container
|
||||
|
||||
if y < 1 or y > h then return self end
|
||||
### Parameters
|
||||
* `child` `table` The child to remove
|
||||
|
||||
local textStart = x < 1 and (2 - x) or 1
|
||||
local textLen = math.min(#text - textStart + 1, w - math.max(1, x) + 1)
|
||||
local fgLen = math.min(#fg - textStart + 1, w - math.max(1, x) + 1)
|
||||
local bgLen = math.min(#bg - textStart + 1, w - math.max(1, x) + 1)
|
||||
### Returns
|
||||
* `Container` `self` The container instance
|
||||
|
||||
if textLen <= 0 then return self end
|
||||
## Container:removeChildrenEvents(child)
|
||||
Unregisters the children events of the container
|
||||
|
||||
local finalText = text:sub(textStart, textStart + textLen - 1)
|
||||
local finalFg = fg:sub(textStart, textStart + fgLen - 1)
|
||||
local finalBg = bg:sub(textStart, textStart + bgLen - 1)
|
||||
### Parameters
|
||||
* `child` `table` The child to unregister events for
|
||||
|
||||
VisualElement.blit(self, math.max(1, x), math.max(1, y), finalText, finalFg, finalBg)
|
||||
return self
|
||||
end
|
||||
### Returns
|
||||
* `Container` `self` The container instance
|
||||
|
||||
--- Renders the container
|
||||
function Container:render()
|
||||
VisualElement.render(self)
|
||||
if not self.get("childrenSorted")then
|
||||
self:sortChildren()
|
||||
end
|
||||
if not self.get("childrenEventsSorted")then
|
||||
for event in pairs(self._values.childrenEvents) do
|
||||
self:sortChildrenEvents(event)
|
||||
end
|
||||
end
|
||||
for _, child in ipairs(self.get("visibleChildren")) do
|
||||
if child == self then
|
||||
self.basalt.LOGGER.error("CIRCULAR REFERENCE DETECTED!")
|
||||
return
|
||||
end
|
||||
child:render()
|
||||
end
|
||||
end
|
||||
## Container:render()
|
||||
Renders the container
|
||||
|
||||
## Container:sortChildren()
|
||||
Sorts the children of the container
|
||||
|
||||
### Returns
|
||||
* `Container` `self` The container instance
|
||||
|
||||
## Container:sortChildrenEvents(eventName)
|
||||
Sorts the children events of the container
|
||||
|
||||
### Parameters
|
||||
* `eventName` `string` The event name to sort
|
||||
|
||||
### Returns
|
||||
* `Container` `self` The container instance
|
||||
|
||||
## Container:textFg(x, y, text, fg)
|
||||
Draws a line of text and fg as color, it is usually used in the render loop
|
||||
|
||||
### Parameters
|
||||
* `x` `number` The x position to draw the text
|
||||
* `y` `number` The y position to draw the text
|
||||
* `text` `string` The text to draw
|
||||
* `fg` `color` The foreground color of the text
|
||||
|
||||
### Returns
|
||||
* `Container` `self` The container instance
|
||||
|
||||
## Container:unregisterChildEvent(child, eventName)
|
||||
Unregisters the children events of the container
|
||||
|
||||
### Parameters
|
||||
* `child` `table` The child to unregister events for
|
||||
* `eventName` `string` The event name to unregister
|
||||
|
||||
### Returns
|
||||
* `Container` `self` The container instance
|
||||
|
||||
return Container
|
||||
@@ -1,121 +1,19 @@
|
||||
local VisualElement = require("elements/VisualElement")
|
||||
local List = require("elements/List")
|
||||
local tHex = require("libraries/colorHex")
|
||||
# Dropdown : List
|
||||
|
||||
---@class Dropdown : List
|
||||
local Dropdown = setmetatable({}, List)
|
||||
Dropdown.__index = Dropdown
|
||||
## Functions
|
||||
|
||||
Dropdown.defineProperty(Dropdown, "isOpen", {default = false, type = "boolean", canTriggerRender = true})
|
||||
Dropdown.defineProperty(Dropdown, "dropdownHeight", {default = 5, type = "number"})
|
||||
Dropdown.defineProperty(Dropdown, "selectedText", {default = "", type = "string"})
|
||||
Dropdown.defineProperty(Dropdown, "dropSymbol", {default = "\31", type = "string"}) -- ▼ Symbol
|
||||
|Method|Returns|Description|
|
||||
|---|---|---|
|
||||
|[Dropdown.new](#Dropdown.new)|-|
|
||||
|[Dropdown:init](#Dropdown:init)|-|
|
||||
|[Dropdown:mouse_click](#Dropdown:mouse_click)|-|
|
||||
|[Dropdown:render](#Dropdown:render)|-|
|
||||
|
||||
function Dropdown.new()
|
||||
local self = setmetatable({}, Dropdown):__init()
|
||||
self.set("width", 16)
|
||||
self.set("height", 1)
|
||||
self.set("z", 8)
|
||||
return self
|
||||
end
|
||||
## Dropdown.new()
|
||||
|
||||
function Dropdown:init(props, basalt)
|
||||
List.init(self, props, basalt)
|
||||
self.set("type", "Dropdown")
|
||||
end
|
||||
## Dropdown:init()
|
||||
|
||||
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
|
||||
self.set("isOpen", not self.get("isOpen"))
|
||||
if not self.get("isOpen") then
|
||||
self.set("height", 1)
|
||||
else
|
||||
self.set("height", 1 + math.min(self.get("dropdownHeight"), #self.get("items")))
|
||||
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")
|
||||
## Dropdown:mouse_click()
|
||||
|
||||
if index <= #items then
|
||||
local item = items[index]
|
||||
if type(item) == "table" and item.separator then
|
||||
return false
|
||||
end
|
||||
|
||||
self.set("selectedIndex", index)
|
||||
self.set("isOpen", false)
|
||||
self.set("height", 1)
|
||||
## Dropdown:render()
|
||||
|
||||
if type(item) == "table" and item.callback then
|
||||
item.callback(self)
|
||||
end
|
||||
|
||||
self:fireEvent("select", index, item)
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
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 item = items[itemIndex]
|
||||
|
||||
if item then
|
||||
if type(item) == "table" and item.separator then
|
||||
local separatorChar = (item.text or "-"):sub(1,1)
|
||||
local separatorText = string.rep(separatorChar, width)
|
||||
local fg = item.foreground or self.get("foreground")
|
||||
local bg = item.background or self.get("background")
|
||||
|
||||
self:textBg(1, i, string.rep(" ", width), bg)
|
||||
self:textFg(1, i, separatorText, fg)
|
||||
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"))
|
||||
|
||||
local fg = isSelected and
|
||||
(item.selectedForeground or colors.white) or
|
||||
(item.foreground or self.get("foreground"))
|
||||
|
||||
self:textBg(1, i, string.rep(" ", width), bg)
|
||||
self:textFg(1, i, itemText, fg)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return Dropdown
|
||||
|
||||
@@ -1,294 +1,41 @@
|
||||
local elementManager = require("elementManager")
|
||||
local Container = elementManager.getElement("Container")
|
||||
# Flexbox : Container
|
||||
|
||||
---@class Flexbox : Container
|
||||
local Flexbox = setmetatable({}, Container)
|
||||
Flexbox.__index = Flexbox
|
||||
## Functions
|
||||
|
||||
Flexbox.defineProperty(Flexbox, "flexDirection", {default = "row", type = "string"})
|
||||
Flexbox.defineProperty(Flexbox, "flexSpacing", {default = 1, type = "number"})
|
||||
Flexbox.defineProperty(Flexbox, "flexJustifyContent", {
|
||||
default = "flex-start",
|
||||
type = "string",
|
||||
setter = function(self, value)
|
||||
if not value:match("^flex%-") then
|
||||
value = "flex-" .. value
|
||||
end
|
||||
return value
|
||||
end
|
||||
})
|
||||
Flexbox.defineProperty(Flexbox, "flexWrap", {default = false, type = "boolean"})
|
||||
Flexbox.defineProperty(Flexbox, "flexUpdateLayout", {default = false, type = "boolean"})
|
||||
|Method|Returns|Description|
|
||||
|---|---|---|
|
||||
|[Flexbox.new](#Flexbox.new)|Flexbox|
|
||||
|[Flexbox:addChild](#Flexbox:addChild)|-|
|
||||
|[Flexbox:addLineBreak](#Flexbox:addLineBreak)|Flexbox|
|
||||
|[Flexbox:init](#Flexbox:init)|-|
|
||||
|[Flexbox:removeChild](#Flexbox:removeChild)|-|
|
||||
|[Flexbox:render](#Flexbox:render)|-|
|
||||
|
||||
local lineBreakElement = {
|
||||
getHeight = function(self) return 0 end,
|
||||
getWidth = function(self) return 0 end,
|
||||
getZ = function(self) return 1 end,
|
||||
getPosition = function(self) return 0, 0 end,
|
||||
getSize = function(self) return 0, 0 end,
|
||||
isType = function(self) return false end,
|
||||
getType = function(self) return "lineBreak" end,
|
||||
getName = function(self) return "lineBreak" end,
|
||||
setPosition = function(self) end,
|
||||
setParent = function(self) end,
|
||||
setSize = function(self) end,
|
||||
getFlexGrow = function(self) return 0 end,
|
||||
getFlexShrink = function(self) return 0 end,
|
||||
getFlexBasis = function(self) return 0 end,
|
||||
init = function(self) end,
|
||||
getVisible = function(self) return true end,
|
||||
}
|
||||
## Flexbox.new()
|
||||
Creates a new Flexbox instance
|
||||
|
||||
### Returns
|
||||
* `Flexbox` `object` The newly created Flexbox instance
|
||||
|
||||
local function sortElements(self, direction, spacing, wrap)
|
||||
local elements = self.get("children")
|
||||
local sortedElements = {}
|
||||
if not(wrap)then
|
||||
local index = 1
|
||||
local lineSize = 1
|
||||
local lineOffset = 1
|
||||
for _,v in pairs(elements)do
|
||||
if(sortedElements[index]==nil)then sortedElements[index]={offset=1} end
|
||||
### Usage
|
||||
```lua
|
||||
local element = Flexbox.new("myId", basalt)
|
||||
```
|
||||
|
||||
local childHeight = direction == "row" and v.get("height") or v.get("width")
|
||||
if childHeight > lineSize then
|
||||
lineSize = childHeight
|
||||
end
|
||||
if(v == lineBreakElement)then
|
||||
lineOffset = lineOffset + lineSize + spacing
|
||||
lineSize = 1
|
||||
index = index + 1
|
||||
sortedElements[index] = {offset=lineOffset}
|
||||
else
|
||||
table.insert(sortedElements[index], v)
|
||||
end
|
||||
end
|
||||
elseif(wrap)then
|
||||
local lineSize = 1
|
||||
local lineOffset = 1
|
||||
## Flexbox:addChild()
|
||||
|
||||
local maxSize = direction == "row" and self.get("width") or self.get("height")
|
||||
local usedSize = 0
|
||||
local index = 1
|
||||
## Flexbox:addLineBreak(self)
|
||||
Adds a new line break to the flexbox.
|
||||
|
||||
for _,v in pairs(elements) do
|
||||
if(sortedElements[index]==nil) then sortedElements[index]={offset=1} end
|
||||
### Parameters
|
||||
* `self` `Flexbox` The element itself
|
||||
|
||||
if v:getType() == "lineBreak" then
|
||||
lineOffset = lineOffset + lineSize + spacing
|
||||
usedSize = 0
|
||||
lineSize = 1
|
||||
index = index + 1
|
||||
sortedElements[index] = {offset=lineOffset}
|
||||
else
|
||||
local objSize = direction == "row" and v.get("width") or v.get("height")
|
||||
if(objSize+usedSize<=maxSize) then
|
||||
table.insert(sortedElements[index], v)
|
||||
usedSize = usedSize + objSize + spacing
|
||||
else
|
||||
lineOffset = lineOffset + lineSize + spacing
|
||||
lineSize = direction == "row" and v.get("height") or v.get("width")
|
||||
index = index + 1
|
||||
usedSize = objSize + spacing
|
||||
sortedElements[index] = {offset=lineOffset, v}
|
||||
end
|
||||
### Returns
|
||||
* `nil` `nil` nil
|
||||
|
||||
local childHeight = direction == "row" and v.get("height") or v.get("width")
|
||||
if childHeight > lineSize then
|
||||
lineSize = childHeight
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return sortedElements
|
||||
end
|
||||
## Flexbox:init()
|
||||
|
||||
local function calculateRow(self, children, spacing, justifyContent)
|
||||
local containerWidth = self.get("width")
|
||||
## Flexbox:removeChild()
|
||||
|
||||
local usedSpace = spacing * (#children - 1)
|
||||
local totalFlexGrow = 0
|
||||
## Flexbox:render()
|
||||
|
||||
for _, child in ipairs(children) do
|
||||
if child ~= lineBreakElement then
|
||||
usedSpace = usedSpace + child.get("width")
|
||||
totalFlexGrow = totalFlexGrow + child.get("flexGrow")
|
||||
end
|
||||
end
|
||||
|
||||
local remainingSpace = containerWidth - usedSpace
|
||||
local extraSpacePerUnit = totalFlexGrow > 0 and (remainingSpace / totalFlexGrow) or 0
|
||||
local distributedSpace = 0
|
||||
|
||||
local currentX = 1
|
||||
for i, child in ipairs(children) do
|
||||
if child ~= lineBreakElement then
|
||||
local childWidth = child.get("width")
|
||||
|
||||
if child.get("flexGrow") > 0 then
|
||||
|
||||
if i == #children then
|
||||
local extraSpace = remainingSpace - distributedSpace
|
||||
childWidth = childWidth + extraSpace
|
||||
else
|
||||
local extraSpace = math.floor(extraSpacePerUnit * child.get("flexGrow"))
|
||||
childWidth = childWidth + extraSpace
|
||||
distributedSpace = distributedSpace + extraSpace
|
||||
end
|
||||
end
|
||||
|
||||
child.set("x", currentX)
|
||||
child.set("y", children.offset or 1)
|
||||
child.set("width", childWidth)
|
||||
currentX = currentX + childWidth + spacing
|
||||
end
|
||||
end
|
||||
|
||||
if justifyContent == "flex-end" then
|
||||
local offset = containerWidth - (currentX - spacing - 1)
|
||||
for _, child in ipairs(children) do
|
||||
child.set("x", child.get("x") + offset)
|
||||
end
|
||||
elseif justifyContent == "flex-center" or justifyContent == "center" then -- Akzeptiere beide Formate
|
||||
local offset = math.floor((containerWidth - (currentX - spacing - 1)) / 2)
|
||||
for _, child in ipairs(children) do
|
||||
child.set("x", child.get("x") + offset)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function calculateColumn(self, children, spacing, justifyContent)
|
||||
local containerHeight = self.get("height")
|
||||
|
||||
local usedSpace = spacing * (#children - 1)
|
||||
local totalFlexGrow = 0
|
||||
|
||||
for _, child in ipairs(children) do
|
||||
if child ~= lineBreakElement then
|
||||
usedSpace = usedSpace + child.get("height")
|
||||
totalFlexGrow = totalFlexGrow + child.get("flexGrow")
|
||||
end
|
||||
end
|
||||
|
||||
local remainingSpace = containerHeight - usedSpace
|
||||
local extraSpacePerUnit = totalFlexGrow > 0 and (remainingSpace / totalFlexGrow) or 0
|
||||
local distributedSpace = 0
|
||||
|
||||
local currentY = 1
|
||||
for i, child in ipairs(children) do
|
||||
if child ~= lineBreakElement then
|
||||
local childHeight = child.get("height")
|
||||
|
||||
if child.get("flexGrow") > 0 then
|
||||
|
||||
if i == #children then
|
||||
local extraSpace = remainingSpace - distributedSpace
|
||||
childHeight = childHeight + extraSpace
|
||||
else
|
||||
local extraSpace = math.floor(extraSpacePerUnit * child.get("flexGrow"))
|
||||
childHeight = childHeight + extraSpace
|
||||
distributedSpace = distributedSpace + extraSpace
|
||||
end
|
||||
end
|
||||
|
||||
child.set("x", children.offset or 1)
|
||||
child.set("y", currentY)
|
||||
child.set("height", childHeight)
|
||||
currentY = currentY + childHeight + spacing
|
||||
end
|
||||
end
|
||||
|
||||
if justifyContent == "flex-end" then
|
||||
local offset = containerHeight - (currentY - spacing - 1)
|
||||
for _, child in ipairs(children) do
|
||||
child.set("y", child.get("y") + offset)
|
||||
end
|
||||
elseif justifyContent == "flex-center" or justifyContent == "center" then -- Akzeptiere beide Formate
|
||||
local offset = math.floor((containerHeight - (currentY - spacing - 1)) / 2)
|
||||
for _, child in ipairs(children) do
|
||||
child.set("y", child.get("y") + offset)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function updateLayout(self, direction, spacing, justifyContent, wrap)
|
||||
local elements = sortElements(self, direction, spacing, wrap)
|
||||
if direction == "row" then
|
||||
for _,v in pairs(elements)do
|
||||
calculateRow(self, v, spacing, justifyContent)
|
||||
end
|
||||
else
|
||||
for _,v in pairs(elements)do
|
||||
calculateColumn(self, v, spacing, justifyContent)
|
||||
end
|
||||
end
|
||||
self.set("flexUpdateLayout", false)
|
||||
end
|
||||
|
||||
--- Creates a new Flexbox instance
|
||||
--- @return Flexbox object The newly created Flexbox instance
|
||||
--- @usage local element = Flexbox.new("myId", basalt)
|
||||
function Flexbox.new()
|
||||
local self = setmetatable({}, Flexbox):__init()
|
||||
self.set("width", 12)
|
||||
self.set("height", 6)
|
||||
self.set("background", colors.blue)
|
||||
self.set("z", 10)
|
||||
self:observe("width", function() self.set("flexUpdateLayout", true) end)
|
||||
self:observe("height", function() self.set("flexUpdateLayout", true) end)
|
||||
return self
|
||||
end
|
||||
|
||||
function Flexbox:init(props, basalt)
|
||||
Container.init(self, props, basalt)
|
||||
self.set("type", "Flexbox")
|
||||
end
|
||||
|
||||
function Flexbox:addChild(element)
|
||||
Container.addChild(self, element)
|
||||
|
||||
if(element~=lineBreakElement)then
|
||||
element:instanceProperty("flexGrow", {default = 0, type = "number"})
|
||||
element:instanceProperty("flexShrink", {default = 0, type = "number"})
|
||||
element:instanceProperty("flexBasis", {default = 0, type = "number"})
|
||||
end
|
||||
|
||||
self.set("flexUpdateLayout", true)
|
||||
return self
|
||||
end
|
||||
|
||||
function Flexbox:removeChild(element)
|
||||
Container.removeChild(self, element)
|
||||
|
||||
if(element~=lineBreakElement)then
|
||||
element.setFlexGrow = nil
|
||||
element.setFlexShrink = nil
|
||||
element.setFlexBasis = nil
|
||||
element.getFlexGrow = nil
|
||||
element.getFlexShrink = nil
|
||||
element.getFlexBasis = nil
|
||||
element.set("flexGrow", nil)
|
||||
element.set("flexShrink", nil)
|
||||
element.set("flexBasis", nil)
|
||||
end
|
||||
|
||||
self.set("flexUpdateLayout", true)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Adds a new line break to the flexbox.
|
||||
---@param self Flexbox The element itself
|
||||
---@return Flexbox
|
||||
function Flexbox:addLineBreak()
|
||||
self:addChild(lineBreakElement)
|
||||
return self
|
||||
end
|
||||
|
||||
function Flexbox:render()
|
||||
if(self.get("flexUpdateLayout"))then
|
||||
updateLayout(self, self.get("flexDirection"), self.get("flexSpacing"), self.get("flexJustifyContent"), self.get("flexWrap"))
|
||||
end
|
||||
Container.render(self)
|
||||
end
|
||||
|
||||
return Flexbox
|
||||
@@ -1,25 +1,23 @@
|
||||
local elementManager = require("elementManager")
|
||||
local Container = elementManager.getElement("Container")
|
||||
# Frame : Container
|
||||
|
||||
---@class Frame : Container
|
||||
local Frame = setmetatable({}, Container)
|
||||
Frame.__index = Frame
|
||||
## Functions
|
||||
|
||||
--- Creates a new Frame instance
|
||||
--- @return Frame object The newly created Frame instance
|
||||
--- @usage local element = Frame.new("myId", basalt)
|
||||
function Frame.new()
|
||||
local self = setmetatable({}, Frame):__init()
|
||||
self.set("width", 12)
|
||||
self.set("height", 6)
|
||||
self.set("background", colors.gray)
|
||||
self.set("z", 10)
|
||||
return self
|
||||
end
|
||||
|Method|Returns|Description|
|
||||
|---|---|---|
|
||||
|[Frame.new](#Frame.new)|Frame|
|
||||
|[Frame:init](#Frame:init)|-|
|
||||
|
||||
function Frame:init(props, basalt)
|
||||
Container.init(self, props, basalt)
|
||||
self.set("type", "Frame")
|
||||
end
|
||||
## Frame.new()
|
||||
Creates a new Frame instance
|
||||
|
||||
### Returns
|
||||
* `Frame` `object` The newly created Frame instance
|
||||
|
||||
### Usage
|
||||
```lua
|
||||
local element = Frame.new("myId", basalt)
|
||||
```
|
||||
|
||||
## Frame:init()
|
||||
Initializes the Frame instance
|
||||
|
||||
return Frame
|
||||
@@ -1,150 +1,52 @@
|
||||
local VisualElement = require("elements/VisualElement")
|
||||
local tHex = require("libraries/colorHex")
|
||||
# Input : VisualElement
|
||||
|
||||
---@class Input : VisualElement
|
||||
local Input = setmetatable({}, VisualElement)
|
||||
Input.__index = Input
|
||||
## Properties
|
||||
|
||||
---@property text string Input - text to be displayed
|
||||
Input.defineProperty(Input, "text", {default = "", type = "string", canTriggerRender = true})
|
||||
|Property|Type|Default|Description|
|
||||
|---|---|---|---|
|
||||
|text|string|Input|- text to be displayed
|
||||
|cursorPos|number|Input|- current cursor position
|
||||
|viewOffset|number|Input|- offset of view
|
||||
|
||||
---@property cursorPos number Input - current cursor position
|
||||
Input.defineProperty(Input, "cursorPos", {default = 1, type = "number"})
|
||||
## Functions
|
||||
|
||||
---@property viewOffset number Input - offset of view
|
||||
Input.defineProperty(Input, "viewOffset", {default = 0, type = "number", canTriggerRender = true})
|
||||
|Method|Returns|Description|
|
||||
|---|---|---|
|
||||
|[Input.new](#Input.new)|Input|
|
||||
|[Input:blur](#Input:blur)|-|
|
||||
|[Input:char](#Input:char)|-|
|
||||
|[Input:focus](#Input:focus)|-|
|
||||
|[Input:init](#Input:init)|-|
|
||||
|[Input:key](#Input:key)|-|
|
||||
|[Input:mouse_click](#Input:mouse_click)|-|
|
||||
|[Input:render](#Input:render)|-|
|
||||
|[Input:updateViewport](#Input:updateViewport)|-|
|
||||
|
||||
-- Neue Properties
|
||||
Input.defineProperty(Input, "maxLength", {default = nil, type = "number"})
|
||||
Input.defineProperty(Input, "placeholder", {default = "asd", type = "string"})
|
||||
Input.defineProperty(Input, "placeholderColor", {default = colors.gray, type = "number"})
|
||||
Input.defineProperty(Input, "focusedColor", {default = colors.blue, type = "number"})
|
||||
Input.defineProperty(Input, "pattern", {default = nil, type = "string"})
|
||||
Neue Properties
|
||||
## Input.new()
|
||||
Creates a new Input instance
|
||||
|
||||
Input.listenTo(Input, "mouse_click")
|
||||
Input.listenTo(Input, "key")
|
||||
Input.listenTo(Input, "char")
|
||||
### Returns
|
||||
* `Input` `object` The newly created Input instance
|
||||
|
||||
--- Creates a new Input instance
|
||||
--- @return Input object The newly created Input instance
|
||||
--- @usage local element = Input.new("myId", basalt)
|
||||
function Input.new()
|
||||
local self = setmetatable({}, Input):__init()
|
||||
self.set("width", 8)
|
||||
self.set("z", 3)
|
||||
return self
|
||||
end
|
||||
### Usage
|
||||
```lua
|
||||
local element = Input.new("myId", basalt)
|
||||
```
|
||||
|
||||
function Input:init(props, basalt)
|
||||
VisualElement.init(self, props, basalt)
|
||||
self.set("type", "Input")
|
||||
end
|
||||
## Input:blur()
|
||||
|
||||
function Input:char(char)
|
||||
if not self.get("focused") then return end
|
||||
local text = self.get("text")
|
||||
local pos = self.get("cursorPos")
|
||||
local maxLength = self.get("maxLength")
|
||||
local pattern = self.get("pattern")
|
||||
## Input:char()
|
||||
|
||||
if maxLength and #text >= maxLength then return end
|
||||
if pattern and not char:match(pattern) then return end
|
||||
## Input:focus()
|
||||
|
||||
self.set("text", text:sub(1, pos-1) .. char .. text:sub(pos))
|
||||
self.set("cursorPos", pos + 1)
|
||||
self:updateRender()
|
||||
self:updateViewport()
|
||||
end
|
||||
## Input:init()
|
||||
|
||||
function Input:key(key)
|
||||
if not self.get("focused") then return end
|
||||
local pos = self.get("cursorPos")
|
||||
local text = self.get("text")
|
||||
local viewOffset = self.get("viewOffset")
|
||||
local width = self.get("width")
|
||||
## Input:key()
|
||||
|
||||
if key == keys.left then
|
||||
if pos > 1 then
|
||||
self.set("cursorPos", pos - 1)
|
||||
if pos - 1 <= viewOffset then
|
||||
self.set("viewOffset", math.max(0, pos - 2))
|
||||
end
|
||||
end
|
||||
elseif key == keys.right then
|
||||
if pos <= #text then
|
||||
self.set("cursorPos", pos + 1)
|
||||
if pos - viewOffset >= width then
|
||||
self.set("viewOffset", pos - width + 1)
|
||||
end
|
||||
end
|
||||
elseif key == keys.backspace then
|
||||
if pos > 1 then
|
||||
self.set("text", text:sub(1, pos-2) .. text:sub(pos))
|
||||
self.set("cursorPos", pos - 1)
|
||||
self:updateRender()
|
||||
self:updateViewport()
|
||||
end
|
||||
end
|
||||
## Input:mouse_click()
|
||||
|
||||
local relativePos = self.get("cursorPos") - self.get("viewOffset")
|
||||
self:setCursor(relativePos, 1, true)
|
||||
end
|
||||
## Input:render()
|
||||
|
||||
function Input:focus()
|
||||
VisualElement.focus(self)
|
||||
self:updateRender()
|
||||
end
|
||||
## Input:updateViewport()
|
||||
|
||||
function Input:blur()
|
||||
VisualElement.blur(self)
|
||||
self:updateRender()
|
||||
end
|
||||
|
||||
function Input:mouse_click(button, x, y)
|
||||
if VisualElement.mouse_click(self, button, x, y) then
|
||||
local relX, relY = self:getRelativePosition(x, y)
|
||||
local text = self.get("text")
|
||||
self:setCursor(math.min(relX, #text + 1), relY, true)
|
||||
self:set("cursorPos", relX + self.get("viewOffset"))
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function Input:updateViewport()
|
||||
local width = self.get("width")
|
||||
local cursorPos = self.get("cursorPos")
|
||||
local viewOffset = self.get("viewOffset")
|
||||
local textLength = #self.get("text")
|
||||
|
||||
if cursorPos - viewOffset > width then
|
||||
self.set("viewOffset", cursorPos - width)
|
||||
elseif cursorPos <= viewOffset then
|
||||
|
||||
self.set("viewOffset", math.max(0, cursorPos - 1))
|
||||
end
|
||||
|
||||
if viewOffset > textLength - width then
|
||||
self.set("viewOffset", math.max(0, textLength - width))
|
||||
end
|
||||
end
|
||||
|
||||
function Input:render()
|
||||
local text = self.get("text")
|
||||
local viewOffset = self.get("viewOffset")
|
||||
local width = self.get("width")
|
||||
local placeholder = self.get("placeholder")
|
||||
local focusedColor = self.get("focusedColor")
|
||||
local focused = self.get("focused")
|
||||
local width, height = self.get("width"), self.get("height")
|
||||
self:multiBlit(1, 1, width, height, " ", tHex[self.get("foreground")], tHex[focused and focusedColor or self.get("background")])
|
||||
|
||||
if #text == 0 and #placeholder ~= 0 and self.get("focused") == false then
|
||||
self:textFg(1, 1, placeholder:sub(1, width), self.get("placeholderColor"))
|
||||
return
|
||||
end
|
||||
|
||||
local visibleText = text:sub(viewOffset + 1, viewOffset + width)
|
||||
self:textFg(1, 1, visibleText, self.get("foreground"))
|
||||
end
|
||||
|
||||
return Input
|
||||
@@ -1,37 +1,31 @@
|
||||
local elementManager = require("elementManager")
|
||||
local VisualElement = elementManager.getElement("VisualElement")
|
||||
# Label : VisualElement
|
||||
|
||||
---@class Label : VisualElement
|
||||
local Label = setmetatable({}, VisualElement)
|
||||
Label.__index = Label
|
||||
## Properties
|
||||
|
||||
---@property text string Label Label text to be displayed
|
||||
Label.defineProperty(Label, "text", {default = "Label", type = "string", setter = function(self, value)
|
||||
if(type(value)=="function")then value = value() end
|
||||
self.set("width", #value)
|
||||
return value
|
||||
end})
|
||||
|Property|Type|Default|Description|
|
||||
|---|---|---|---|
|
||||
|text|string|Label|Label text to be displayed
|
||||
|
||||
--- Creates a new Label instance
|
||||
--- @return Label object The newly created Label instance
|
||||
--- @usage local element = Label.new("myId", basalt)
|
||||
function Label.new()
|
||||
local self = setmetatable({}, Label):__init()
|
||||
self.set("z", 3)
|
||||
self.set("foreground", colors.black)
|
||||
self.set("backgroundEnabled", false)
|
||||
return self
|
||||
end
|
||||
## Functions
|
||||
|
||||
function Label:init(props, basalt)
|
||||
VisualElement.init(self, props, basalt)
|
||||
self.set("type", "Label")
|
||||
end
|
||||
|Method|Returns|Description|
|
||||
|---|---|---|
|
||||
|[Label.new](#Label.new)|Label|
|
||||
|[Label:init](#Label:init)|-|
|
||||
|[Label:render](#Label:render)|-|
|
||||
|
||||
function Label:render()
|
||||
VisualElement.render(self)
|
||||
local text = self.get("text")
|
||||
self:textFg(1, 1, text, self.get("foreground"))
|
||||
end
|
||||
## Label.new()
|
||||
Creates a new Label instance
|
||||
|
||||
### Returns
|
||||
* `Label` `object` The newly created Label instance
|
||||
|
||||
### Usage
|
||||
```lua
|
||||
local element = Label.new("myId", basalt)
|
||||
```
|
||||
|
||||
## Label:init()
|
||||
|
||||
## Label:render()
|
||||
|
||||
return Label
|
||||
@@ -1,135 +1,44 @@
|
||||
local VisualElement = require("elements/VisualElement")
|
||||
# List : VisualElement
|
||||
|
||||
---@class List : VisualElement
|
||||
local List = setmetatable({}, VisualElement)
|
||||
List.__index = List
|
||||
## Properties
|
||||
|
||||
---@property items table List of items to display
|
||||
List.defineProperty(List, "items", {default = {}, type = "table", canTriggerRender = true})
|
||||
---@property selectedIndex number Currently selected item index
|
||||
List.defineProperty(List, "selectedIndex", {default = 0, type = "number", canTriggerRender = true})
|
||||
---@property selectable boolean Whether items can be selected
|
||||
List.defineProperty(List, "selectable", {default = true, type = "boolean"})
|
||||
---@property offset number Scrolling offset
|
||||
List.defineProperty(List, "offset", {default = 0, type = "number", canTriggerRender = true})
|
||||
---@property selectedColor color Color for selected item
|
||||
List.defineProperty(List, "selectedColor", {default = colors.blue, type = "number"})
|
||||
|Property|Type|Default|Description|
|
||||
|---|---|---|---|
|
||||
|items|table|List|of items to display
|
||||
|selectedIndex|number|Currently|selected item index
|
||||
|selectable|boolean|Whether|items can be selected
|
||||
|offset|number|Scrolling|offset
|
||||
|selectedColor|color|Color|for selected item
|
||||
|
||||
List.listenTo(List, "mouse_click")
|
||||
List.listenTo(List, "mouse_scroll")
|
||||
## Functions
|
||||
|
||||
function List.new()
|
||||
local self = setmetatable({}, List):__init()
|
||||
self.set("width", 16)
|
||||
self.set("height", 8)
|
||||
self.set("background", colors.gray)
|
||||
return self
|
||||
end
|
||||
|Method|Returns|Description|
|
||||
|---|---|---|
|
||||
|[List.new](#List.new)|-|
|
||||
|[List:addItem](#List:addItem)|-|
|
||||
|[List:clear](#List:clear)|-|
|
||||
|[List:init](#List:init)|-|
|
||||
|[List:mouse_click](#List:mouse_click)|-|
|
||||
|[List:mouse_scroll](#List:mouse_scroll)|-|
|
||||
|[List:onSelect](#List:onSelect)|-|
|
||||
|[List:removeItem](#List:removeItem)|-|
|
||||
|[List:render](#List:render)|-|
|
||||
|
||||
function List:init(props, basalt)
|
||||
VisualElement.init(self, props, basalt)
|
||||
self.set("type", "List")
|
||||
end
|
||||
## List.new()
|
||||
|
||||
function List:addItem(text)
|
||||
local items = self.get("items")
|
||||
table.insert(items, text)
|
||||
self:updateRender()
|
||||
return self
|
||||
end
|
||||
## List:addItem()
|
||||
|
||||
function List:removeItem(index)
|
||||
local items = self.get("items")
|
||||
table.remove(items, index)
|
||||
self:updateRender()
|
||||
return self
|
||||
end
|
||||
## List:clear()
|
||||
|
||||
function List:clear()
|
||||
self.set("items", {})
|
||||
self.set("selectedIndex", 0)
|
||||
self:updateRender()
|
||||
return self
|
||||
end
|
||||
## List:init()
|
||||
|
||||
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)
|
||||
|
||||
local adjustedIndex = index + self.get("offset")
|
||||
local items = self.get("items")
|
||||
## List:mouse_click()
|
||||
|
||||
if adjustedIndex <= #items then
|
||||
local item = items[adjustedIndex]
|
||||
self.set("selectedIndex", adjustedIndex)
|
||||
## List:mouse_scroll()
|
||||
|
||||
if type(item) == "table" and item.callback then
|
||||
item.callback(self)
|
||||
end
|
||||
## List:onSelect()
|
||||
|
||||
self:fireEvent("select", adjustedIndex, item)
|
||||
self:updateRender()
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
## List:removeItem()
|
||||
|
||||
function List:mouse_scroll(direction, x, y)
|
||||
if self:isInBounds(x, y) then
|
||||
local offset = self.get("offset")
|
||||
local maxOffset = math.max(0, #self.get("items") - self.get("height"))
|
||||
|
||||
offset = math.min(maxOffset, math.max(0, offset + direction))
|
||||
self.set("offset", offset)
|
||||
return true
|
||||
end
|
||||
end
|
||||
## List:render()
|
||||
|
||||
function List:onSelect(callback)
|
||||
self:registerCallback("select", callback)
|
||||
return self
|
||||
end
|
||||
|
||||
function List:render()
|
||||
VisualElement.render(self)
|
||||
|
||||
local items = self.get("items")
|
||||
local height = self.get("height")
|
||||
local offset = self.get("offset")
|
||||
local selected = self.get("selectedIndex")
|
||||
local width = self.get("width")
|
||||
|
||||
for i = 1, height do
|
||||
local itemIndex = i + offset
|
||||
local item = items[itemIndex]
|
||||
|
||||
if item then
|
||||
if type(item) == "table" and item.separator then
|
||||
local separatorChar = (item.text or "-"):sub(1,1)
|
||||
local separatorText = string.rep(separatorChar, width)
|
||||
local fg = item.foreground or self.get("foreground")
|
||||
local bg = item.background or self.get("background")
|
||||
|
||||
self:textBg(1, i, string.rep(" ", width), bg)
|
||||
self:textFg(1, i, separatorText, fg)
|
||||
else
|
||||
local text = type(item) == "table" and item.text or item
|
||||
local isSelected = itemIndex == selected
|
||||
|
||||
local bg = isSelected and
|
||||
(item.selectedBackground or self.get("selectedColor")) or
|
||||
(item.background or self.get("background"))
|
||||
|
||||
local fg = isSelected and
|
||||
(item.selectedForeground or colors.white) or
|
||||
(item.foreground or self.get("foreground"))
|
||||
|
||||
self:textBg(1, i, string.rep(" ", width), bg)
|
||||
self:textFg(1, i, text, fg)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return List
|
||||
|
||||
@@ -1,90 +1,22 @@
|
||||
local VisualElement = require("elements/VisualElement")
|
||||
local List = require("elements/List")
|
||||
local tHex = require("libraries/colorHex")
|
||||
# Menu : List
|
||||
|
||||
---@class Menu : List
|
||||
local Menu = setmetatable({}, List)
|
||||
Menu.__index = Menu
|
||||
## Functions
|
||||
|
||||
Menu.defineProperty(Menu, "separatorColor", {default = colors.gray, type = "number"})
|
||||
|Method|Returns|Description|
|
||||
|---|---|---|
|
||||
|[Menu.new](#Menu.new)|-|
|
||||
|[Menu:init](#Menu:init)|-|
|
||||
|[Menu:mouse_click](#Menu:mouse_click)|-|
|
||||
|[Menu:render](#Menu:render)|-|
|
||||
|[Menu:setItems](#Menu:setItems)|-|
|
||||
|
||||
function Menu.new()
|
||||
local self = setmetatable({}, Menu):__init()
|
||||
self.set("width", 30)
|
||||
self.set("height", 1)
|
||||
self.set("background", colors.gray)
|
||||
return self
|
||||
end
|
||||
## Menu.new()
|
||||
|
||||
function Menu:init(props, basalt)
|
||||
List.init(self, props, basalt)
|
||||
self.set("type", "Menu")
|
||||
return self
|
||||
end
|
||||
## Menu:init()
|
||||
|
||||
function Menu:setItems(items)
|
||||
local listItems = {}
|
||||
local totalWidth = 0
|
||||
for _, item in ipairs(items) do
|
||||
if item.separator then
|
||||
table.insert(listItems, {text = item.text or "|", selectable = false})
|
||||
totalWidth = totalWidth + 1
|
||||
else
|
||||
local text = " " .. item.text .. " "
|
||||
item.text = text
|
||||
table.insert(listItems, item)
|
||||
totalWidth = totalWidth + #text
|
||||
end
|
||||
end
|
||||
self.set("width", totalWidth)
|
||||
return List.setItems(self, listItems)
|
||||
end
|
||||
## Menu:mouse_click()
|
||||
|
||||
function Menu:render()
|
||||
VisualElement.render(self)
|
||||
local currentX = 1
|
||||
## Menu:render()
|
||||
|
||||
for i, item in ipairs(self.get("items")) do
|
||||
local isSelected = i == self.get("selectedIndex")
|
||||
## Menu:setItems()
|
||||
|
||||
local fg = item.selectable == false and self.get("separatorColor") or
|
||||
(isSelected and (item.selectedForeground or self.get("foreground")) or
|
||||
(item.foreground or self.get("foreground")))
|
||||
|
||||
local bg = isSelected and
|
||||
(item.selectedBackground or self.get("selectedColor")) or
|
||||
(item.background or self.get("background"))
|
||||
|
||||
self:blit(currentX, 1, item.text,
|
||||
string.rep(tHex[fg], #item.text),
|
||||
string.rep(tHex[bg], #item.text))
|
||||
|
||||
currentX = currentX + #item.text
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
local relX = select(1, self:getRelativePosition(x, y))
|
||||
local currentX = 1
|
||||
|
||||
for i, item in ipairs(self.get("items")) do
|
||||
if relX >= currentX and relX < currentX + #item.text then
|
||||
if item.selectable ~= false then
|
||||
self.set("selectedIndex", i)
|
||||
if type(item) == "table" then
|
||||
if item.callback then
|
||||
item.callback(self)
|
||||
end
|
||||
end
|
||||
self:fireEvent("select", i, item)
|
||||
end
|
||||
return true
|
||||
end
|
||||
currentX = currentX + #item.text
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
return Menu
|
||||
|
||||
@@ -1,176 +1,52 @@
|
||||
local elementManager = require("elementManager")
|
||||
local VisualElement = elementManager.getElement("VisualElement")
|
||||
local errorManager = require("errorManager")
|
||||
Rendering optimization (only render when screen changed)
|
||||
Eventsystem improvement
|
||||
Cursor is sometimes not visible on time
|
||||
# Program : VisualElement
|
||||
|
||||
--TODO:
|
||||
-- Rendering optimization (only render when screen changed)
|
||||
-- Eventsystem improvement
|
||||
-- Cursor is sometimes not visible on time
|
||||
## Functions
|
||||
|
||||
---@class Program : VisualElement
|
||||
local Program = setmetatable({}, VisualElement)
|
||||
Program.__index = Program
|
||||
|Method|Returns|Description|
|
||||
|---|---|---|
|
||||
|[BasaltProgram.new](#BasaltProgram.new)|-|
|
||||
|[BasaltProgram:resize](#BasaltProgram:resize)|-|
|
||||
|[BasaltProgram:resume](#BasaltProgram:resume)|-|
|
||||
|[BasaltProgram:run](#BasaltProgram:run)|-|
|
||||
|[BasaltProgram:stop](#BasaltProgram:stop)|-|
|
||||
|[Program.new](#Program.new)|Program|
|
||||
|[Program:dispatchEvent](#Program:dispatchEvent)|-|
|
||||
|[Program:execute](#Program:execute)|-|
|
||||
|[Program:focus](#Program:focus)|-|
|
||||
|[Program:init](#Program:init)|-|
|
||||
|[Program:render](#Program:render)|-|
|
||||
|
||||
Program.defineProperty(Program, "program", {default = nil, type = "table"})
|
||||
Program.defineProperty(Program, "path", {default = "", type = "string"})
|
||||
Program.defineProperty(Program, "running", {default = false, type = "boolean"})
|
||||
## BasaltProgram.new()
|
||||
|
||||
Program.listenTo(Program, "key")
|
||||
Program.listenTo(Program, "char")
|
||||
Program.listenTo(Program, "key_up")
|
||||
Program.listenTo(Program, "paste")
|
||||
Program.listenTo(Program, "mouse_click")
|
||||
Program.listenTo(Program, "mouse_drag")
|
||||
Program.listenTo(Program, "mouse_scroll")
|
||||
Program.listenTo(Program, "mouse_up")
|
||||
## BasaltProgram:resize()
|
||||
|
||||
local BasaltProgram = {}
|
||||
BasaltProgram.__index = BasaltProgram
|
||||
local newPackage = dofile("rom/modules/main/cc/require.lua").make
|
||||
## BasaltProgram:resume()
|
||||
|
||||
function BasaltProgram.new()
|
||||
local self = setmetatable({}, BasaltProgram)
|
||||
self.env = {}
|
||||
self.args = {}
|
||||
return self
|
||||
end
|
||||
## BasaltProgram:run()
|
||||
|
||||
function BasaltProgram:run(path, width, height)
|
||||
self.window = window.create(term.current(), 1, 1, width, height, false)
|
||||
local pPath = shell.resolveProgram(path)
|
||||
if(pPath~=nil)then
|
||||
if(fs.exists(pPath)) then
|
||||
local file = fs.open(pPath, "r")
|
||||
local content = file.readAll()
|
||||
file.close()
|
||||
local env = setmetatable(self.env, {__index=_ENV})
|
||||
env.shell = shell
|
||||
env.term = self.window
|
||||
env.require, env.package = newPackage(env, fs.getDir(pPath))
|
||||
env.term.current = term.current
|
||||
env.term.redirect = term.redirect
|
||||
env.term.native = term.native
|
||||
## BasaltProgram:stop()
|
||||
|
||||
self.coroutine = coroutine.create(function()
|
||||
local program = load(content, path, "bt", env)
|
||||
if program then
|
||||
local current = term.current()
|
||||
term.redirect(self.window)
|
||||
local result = program(path, table.unpack(self.args))
|
||||
term.redirect(current)
|
||||
return result
|
||||
end
|
||||
end)
|
||||
local current = term.current()
|
||||
term.redirect(self.window)
|
||||
local ok, result = coroutine.resume(self.coroutine)
|
||||
term.redirect(current)
|
||||
if not ok then
|
||||
errorManager.header = "Basalt Program Error ".. path
|
||||
errorManager.error(result)
|
||||
end
|
||||
else
|
||||
errorManager.header = "Basalt Program Error ".. path
|
||||
errorManager.error("File not found")
|
||||
end
|
||||
else
|
||||
errorManager.header = "Basalt Program Error"
|
||||
errorManager.error("Program "..path.." not found")
|
||||
end
|
||||
end
|
||||
## Program.new()
|
||||
Creates a new Program instance
|
||||
|
||||
function BasaltProgram:resize(width, height)
|
||||
self.window.reposition(1, 1, width, height)
|
||||
end
|
||||
### Returns
|
||||
* `Program` `object` The newly created Program instance
|
||||
|
||||
function BasaltProgram:resume(event, ...)
|
||||
if self.coroutine==nil or coroutine.status(self.coroutine)=="dead" then return end
|
||||
if(self.filter~=nil)then
|
||||
if(event~=self.filter)then return end
|
||||
self.filter=nil
|
||||
end
|
||||
local current = term.current()
|
||||
term.redirect(self.window)
|
||||
local ok, result = coroutine.resume(self.coroutine, event, ...)
|
||||
term.redirect(current)
|
||||
### Usage
|
||||
```lua
|
||||
local element = Program.new("myId", basalt)
|
||||
```
|
||||
|
||||
if ok then
|
||||
self.filter = result
|
||||
else
|
||||
errorManager.header = "Basalt Program Error"
|
||||
errorManager.error(result)
|
||||
end
|
||||
return ok, result
|
||||
end
|
||||
## Program:dispatchEvent()
|
||||
|
||||
function BasaltProgram:stop()
|
||||
## Program:execute()
|
||||
|
||||
end
|
||||
## Program:focus()
|
||||
|
||||
--- Creates a new Program instance
|
||||
--- @return Program object The newly created Program instance
|
||||
--- @usage local element = Program.new("myId", basalt)
|
||||
function Program.new()
|
||||
local self = setmetatable({}, Program):__init()
|
||||
self.set("z", 5)
|
||||
self.set("width", 30)
|
||||
self.set("height", 12)
|
||||
return self
|
||||
end
|
||||
## Program:init()
|
||||
|
||||
function Program:init(props, basalt)
|
||||
VisualElement.init(self, props, basalt)
|
||||
self.set("type", "Program")
|
||||
end
|
||||
## Program:render()
|
||||
|
||||
function Program:execute(path)
|
||||
self.set("path", path)
|
||||
self.set("running", true)
|
||||
local program = BasaltProgram.new()
|
||||
self.set("program", program)
|
||||
program:run(path, self.get("width"), self.get("height"))
|
||||
self:updateRender()
|
||||
return self
|
||||
end
|
||||
|
||||
function Program:dispatchEvent(event, ...)
|
||||
local program = self.get("program")
|
||||
local result = VisualElement.dispatchEvent(self, event, ...)
|
||||
if program then
|
||||
program:resume(event, ...)
|
||||
if(self.get("focused"))then
|
||||
local cursorBlink = program.window.getCursorBlink()
|
||||
local cursorX, cursorY = program.window.getCursorPos()
|
||||
self:setCursor(cursorX, cursorY, cursorBlink)
|
||||
end
|
||||
self:updateRender()
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
function Program:focus()
|
||||
if(VisualElement.focus(self))then
|
||||
local program = self.get("program")
|
||||
if program then
|
||||
local cursorBlink = program.window.getCursorBlink()
|
||||
local cursorX, cursorY = program.window.getCursorPos()
|
||||
self:setCursor(cursorX, cursorY, cursorBlink)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Program:render()
|
||||
VisualElement.render(self)
|
||||
local program = self.get("program")
|
||||
if program then
|
||||
local _, height = program.window.getSize()
|
||||
for y = 1, height do
|
||||
local text, fg, bg = program.window.getLine(y)
|
||||
if text then
|
||||
self:blit(1, y, text, fg, bg)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return Program
|
||||
@@ -1,41 +1,24 @@
|
||||
local VisualElement = require("elements/VisualElement")
|
||||
# ProgressBar : VisualElement
|
||||
|
||||
---@class ProgressBar : VisualElement
|
||||
local ProgressBar = setmetatable({}, VisualElement)
|
||||
ProgressBar.__index = ProgressBar
|
||||
## Properties
|
||||
|
||||
---@property progress number Current progress (0-100)
|
||||
ProgressBar.defineProperty(ProgressBar, "progress", {default = 0, type = "number", canTriggerRender = true})
|
||||
---@property showPercentage boolean Show percentage text
|
||||
ProgressBar.defineProperty(ProgressBar, "showPercentage", {default = false, type = "boolean"})
|
||||
---@property progressColor color Progress bar color
|
||||
ProgressBar.defineProperty(ProgressBar, "progressColor", {default = colors.lime, type = "number"})
|
||||
|Property|Type|Default|Description|
|
||||
|---|---|---|---|
|
||||
|progress|number|Current|progress (0-100)
|
||||
|showPercentage|boolean|Show|percentage text
|
||||
|progressColor|color|Progress|bar color
|
||||
|
||||
function ProgressBar.new()
|
||||
local self = setmetatable({}, ProgressBar):__init()
|
||||
self.set("width", 10)
|
||||
self.set("height", 1)
|
||||
return self
|
||||
end
|
||||
## Functions
|
||||
|
||||
function ProgressBar:init(props, basalt)
|
||||
VisualElement.init(self, props, basalt)
|
||||
self.set("type", "ProgressBar")
|
||||
end
|
||||
|Method|Returns|Description|
|
||||
|---|---|---|
|
||||
|[ProgressBar.new](#ProgressBar.new)|-|
|
||||
|[ProgressBar:init](#ProgressBar:init)|-|
|
||||
|[ProgressBar:render](#ProgressBar:render)|-|
|
||||
|
||||
function ProgressBar:render()
|
||||
VisualElement.render(self)
|
||||
local width = self.get("width")
|
||||
local progress = math.min(100, math.max(0, self.get("progress")))
|
||||
local fillWidth = math.floor((width * progress) / 100)
|
||||
## ProgressBar.new()
|
||||
|
||||
self:textBg(1, 1, string.rep(" ", fillWidth), self.get("progressColor"))
|
||||
## ProgressBar:init()
|
||||
|
||||
if self.get("showPercentage") then
|
||||
local text = tostring(progress).."%"
|
||||
local x = math.floor((width - #text) / 2) + 1
|
||||
self:textFg(x, 1, text, self.get("foreground"))
|
||||
end
|
||||
end
|
||||
## ProgressBar:render()
|
||||
|
||||
return ProgressBar
|
||||
@@ -1,86 +1,35 @@
|
||||
local VisualElement = require("elements/VisualElement")
|
||||
# Slider : VisualElement
|
||||
|
||||
---@class Slider : VisualElement
|
||||
local Slider = setmetatable({}, VisualElement)
|
||||
Slider.__index = Slider
|
||||
## Properties
|
||||
|
||||
---@property step number 1 Current step position (1 to width/height)
|
||||
Slider.defineProperty(Slider, "step", {default = 1, type = "number", canTriggerRender = true})
|
||||
---@property max number 100 Maximum value for value conversion
|
||||
Slider.defineProperty(Slider, "max", {default = 100, type = "number"})
|
||||
---@property horizontal boolean true Whether the slider is horizontal
|
||||
Slider.defineProperty(Slider, "horizontal", {default = true, type = "boolean", canTriggerRender = true})
|
||||
---@property barColor color color Colors for the slider bar
|
||||
Slider.defineProperty(Slider, "barColor", {default = colors.gray, type = "number", canTriggerRender = true})
|
||||
---@property sliderColor color The color of the slider handle
|
||||
Slider.defineProperty(Slider, "sliderColor", {default = colors.blue, type = "number", canTriggerRender = true})
|
||||
|Property|Type|Default|Description|
|
||||
|---|---|---|---|
|
||||
|step|number|1|Current step position (1 to width/height)
|
||||
|max|number|100|Maximum value for value conversion
|
||||
|horizontal|boolean|true|Whether the slider is horizontal
|
||||
|barColor|color|color|Colors for the slider bar
|
||||
|sliderColor|color|The|color of the slider handle
|
||||
|
||||
Slider.listenTo(Slider, "mouse_click")
|
||||
Slider.listenTo(Slider, "mouse_drag")
|
||||
Slider.listenTo(Slider, "mouse_up")
|
||||
## Functions
|
||||
|
||||
function Slider.new()
|
||||
local self = setmetatable({}, Slider):__init()
|
||||
self.set("width", 8)
|
||||
self.set("height", 1)
|
||||
self.set("backgroundEnabled", false)
|
||||
return self
|
||||
end
|
||||
|Method|Returns|Description|
|
||||
|---|---|---|
|
||||
|[Slider.new](#Slider.new)|-|
|
||||
|[Slider:getValue](#Slider:getValue)|-|
|
||||
|[Slider:init](#Slider:init)|-|
|
||||
|[Slider:mouse_click](#Slider:mouse_click)|-|
|
||||
|[Slider:mouse_scroll](#Slider:mouse_scroll)|-|
|
||||
|[Slider:render](#Slider:render)|-|
|
||||
|
||||
function Slider:init(props, basalt)
|
||||
VisualElement.init(self, props, basalt)
|
||||
self.set("type", "Slider")
|
||||
end
|
||||
## Slider.new()
|
||||
|
||||
function Slider:getValue()
|
||||
local step = self.get("step")
|
||||
local max = self.get("max")
|
||||
local maxSteps = self.get("horizontal") and self.get("width") or self.get("height")
|
||||
return math.floor((step - 1) * (max / (maxSteps - 1)))
|
||||
end
|
||||
## Slider:getValue()
|
||||
|
||||
function Slider:mouse_click(button, x, y)
|
||||
if button == 1 and self:isInBounds(x, y) then
|
||||
local relX, relY = self:getRelativePosition(x, y)
|
||||
local pos = self.get("horizontal") and relX or relY
|
||||
local maxSteps = self.get("horizontal") and self.get("width") or self.get("height")
|
||||
## Slider:init()
|
||||
|
||||
self.set("step", math.min(maxSteps, math.max(1, pos)))
|
||||
self:updateRender()
|
||||
return true
|
||||
end
|
||||
end
|
||||
Slider.mouse_drag = Slider.mouse_click
|
||||
## Slider:mouse_click()
|
||||
|
||||
function Slider:mouse_scroll(direction, x, y)
|
||||
if self:isInBounds(x, y) then
|
||||
local step = self.get("step")
|
||||
local maxSteps = self.get("horizontal") and self.get("width") or self.get("height")
|
||||
self.set("step", math.min(maxSteps, math.max(1, step + direction)))
|
||||
self:updateRender()
|
||||
return true
|
||||
end
|
||||
end
|
||||
## Slider:mouse_scroll()
|
||||
|
||||
function Slider:render()
|
||||
VisualElement.render(self)
|
||||
local width = self.get("width")
|
||||
local height = self.get("height")
|
||||
local horizontal = self.get("horizontal")
|
||||
local step = self.get("step")
|
||||
## Slider:render()
|
||||
|
||||
local barChar = horizontal and "\140" or "│"
|
||||
local text = string.rep(barChar, horizontal and width or height)
|
||||
|
||||
if horizontal then
|
||||
self:textFg(1, 1, text, self.get("barColor"))
|
||||
self:textBg(step, 1, " ", self.get("sliderColor"))
|
||||
else
|
||||
for y = 1, height do
|
||||
self:textFg(1, y, barChar, self.get("barColor"))
|
||||
end
|
||||
self:textFg(1, step, "\140", self.get("sliderColor"))
|
||||
end
|
||||
end
|
||||
|
||||
return Slider
|
||||
@@ -1,181 +1,31 @@
|
||||
local VisualElement = require("elements/VisualElement")
|
||||
local tHex = require("libraries/colorHex")
|
||||
# Table : VisualElement
|
||||
|
||||
---@class Table : VisualElement
|
||||
local Table = setmetatable({}, VisualElement)
|
||||
Table.__index = Table
|
||||
## Functions
|
||||
|
||||
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})
|
||||
|Method|Returns|Description|
|
||||
|---|---|---|
|
||||
|[Table.new](#Table.new)|-|
|
||||
|[Table:init](#Table:init)|-|
|
||||
|[Table:mouse_click](#Table:mouse_click)|-|
|
||||
|[Table:mouse_scroll](#Table:mouse_scroll)|-|
|
||||
|[Table:render](#Table:render)|-|
|
||||
|[Table:setColumns](#Table:setColumns)|-|
|
||||
|[Table:setData](#Table:setData)|-|
|
||||
|[Table:sortData](#Table:sortData)|-|
|
||||
|
||||
Table.listenTo(Table, "mouse_click")
|
||||
Table.listenTo(Table, "mouse_scroll")
|
||||
## Table.new()
|
||||
|
||||
function Table.new()
|
||||
local self = setmetatable({}, Table):__init()
|
||||
self.set("width", 30)
|
||||
self.set("height", 10)
|
||||
self.set("z", 5)
|
||||
return self
|
||||
end
|
||||
## Table:init()
|
||||
|
||||
function Table:init(props, basalt)
|
||||
VisualElement.init(self, props, basalt)
|
||||
self.set("type", "Table")
|
||||
return self
|
||||
end
|
||||
## Table:mouse_click()
|
||||
|
||||
function Table:setColumns(columns)
|
||||
-- Columns Format: {{name="ID", width=4}, {name="Name", width=10}}
|
||||
self.set("columns", columns)
|
||||
return self
|
||||
end
|
||||
## Table:mouse_scroll()
|
||||
|
||||
function Table:setData(data)
|
||||
-- Data Format: {{"1", "Item One"}, {"2", "Item Two"}}
|
||||
self.set("data", data)
|
||||
return self
|
||||
end
|
||||
## Table:render()
|
||||
|
||||
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
|
||||
## Table:setColumns()
|
||||
|
||||
function Table:mouse_click(button, x, y)
|
||||
if not VisualElement.mouse_click(self, button, x, y) then return false end
|
||||
## Table:setData()
|
||||
|
||||
local relX, relY = self:getRelativePosition(x, y)
|
||||
## Table:sortData()
|
||||
|
||||
-- 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
|
||||
@@ -1,146 +1,37 @@
|
||||
local VisualElement = require("elements/VisualElement")
|
||||
local tHex = require("libraries/colorHex")
|
||||
# Tree : VisualElement
|
||||
|
||||
---@class Tree : VisualElement
|
||||
local Tree = setmetatable({}, VisualElement)
|
||||
Tree.__index = Tree
|
||||
## Functions
|
||||
|
||||
Tree.defineProperty(Tree, "nodes", {default = {}, type = "table", canTriggerRender = true})
|
||||
Tree.defineProperty(Tree, "selectedNode", {default = nil, type = "table", canTriggerRender = true})
|
||||
Tree.defineProperty(Tree, "expandedNodes", {default = {}, type = "table", canTriggerRender = true})
|
||||
Tree.defineProperty(Tree, "scrollOffset", {default = 0, type = "number", canTriggerRender = true})
|
||||
Tree.defineProperty(Tree, "nodeColor", {default = colors.white, type = "number"})
|
||||
Tree.defineProperty(Tree, "selectedColor", {default = colors.lightBlue, type = "number"})
|
||||
|Method|Returns|Description|
|
||||
|---|---|---|
|
||||
|[Tree.new](#Tree.new)|-|
|
||||
|[Tree:collapseNode](#Tree:collapseNode)|-|
|
||||
|[Tree:expandNode](#Tree:expandNode)|-|
|
||||
|[Tree:init](#Tree:init)|-|
|
||||
|[Tree:mouse_click](#Tree:mouse_click)|-|
|
||||
|[Tree:mouse_scroll](#Tree:mouse_scroll)|-|
|
||||
|[Tree:onSelect](#Tree:onSelect)|-|
|
||||
|[Tree:render](#Tree:render)|-|
|
||||
|[Tree:setNodes](#Tree:setNodes)|-|
|
||||
|[Tree:toggleNode](#Tree:toggleNode)|-|
|
||||
|
||||
Tree.listenTo(Tree, "mouse_click")
|
||||
Tree.listenTo(Tree, "mouse_scroll")
|
||||
## Tree.new()
|
||||
|
||||
function Tree.new()
|
||||
local self = setmetatable({}, Tree):__init()
|
||||
self.set("width", 30)
|
||||
self.set("height", 10)
|
||||
self.set("z", 5)
|
||||
return self
|
||||
end
|
||||
## Tree:collapseNode()
|
||||
|
||||
function Tree:init(props, basalt)
|
||||
VisualElement.init(self, props, basalt)
|
||||
self.set("type", "Tree")
|
||||
return self
|
||||
end
|
||||
## Tree:expandNode()
|
||||
|
||||
function Tree:setNodes(nodes)
|
||||
self.set("nodes", nodes)
|
||||
if #nodes > 0 then
|
||||
self.get("expandedNodes")[nodes[1]] = true
|
||||
end
|
||||
return self
|
||||
end
|
||||
## Tree:init()
|
||||
|
||||
function Tree:expandNode(node)
|
||||
self.get("expandedNodes")[node] = true
|
||||
self:updateRender()
|
||||
return self
|
||||
end
|
||||
## Tree:mouse_click()
|
||||
|
||||
function Tree:collapseNode(node)
|
||||
self.get("expandedNodes")[node] = nil
|
||||
self:updateRender()
|
||||
return self
|
||||
end
|
||||
## Tree:mouse_scroll()
|
||||
|
||||
function Tree:toggleNode(node)
|
||||
if self.get("expandedNodes")[node] then
|
||||
self:collapseNode(node)
|
||||
else
|
||||
self:expandNode(node)
|
||||
end
|
||||
return self
|
||||
end
|
||||
## Tree:onSelect()
|
||||
|
||||
local function flattenTree(nodes, expandedNodes, level, result)
|
||||
result = result or {}
|
||||
level = level or 0
|
||||
## Tree:render()
|
||||
|
||||
for _, node in ipairs(nodes) do
|
||||
table.insert(result, {node = node, level = level})
|
||||
if expandedNodes[node] and node.children then
|
||||
flattenTree(node.children, expandedNodes, level + 1, result)
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
## Tree:setNodes()
|
||||
|
||||
function Tree:mouse_click(button, x, y)
|
||||
if not VisualElement.mouse_click(self, button, x, y) then return false end
|
||||
## Tree:toggleNode()
|
||||
|
||||
local relX, relY = self:getRelativePosition(x, y)
|
||||
local flatNodes = flattenTree(self.get("nodes"), self.get("expandedNodes"))
|
||||
local visibleIndex = relY + self.get("scrollOffset")
|
||||
|
||||
if flatNodes[visibleIndex] then
|
||||
local nodeInfo = flatNodes[visibleIndex]
|
||||
local node = nodeInfo.node
|
||||
|
||||
if relX <= nodeInfo.level * 2 + 2 then
|
||||
self:toggleNode(node)
|
||||
end
|
||||
|
||||
self.set("selectedNode", node)
|
||||
self:fireEvent("node_select", node)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function Tree:onSelect(callback)
|
||||
self:registerCallback("node_select", callback)
|
||||
return self
|
||||
end
|
||||
|
||||
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")
|
||||
local expandedNodes = self.get("expandedNodes")
|
||||
local scrollOffset = self.get("scrollOffset")
|
||||
|
||||
for y = 1, height do
|
||||
local nodeInfo = flatNodes[y + scrollOffset]
|
||||
if nodeInfo then
|
||||
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"
|
||||
end
|
||||
|
||||
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")))
|
||||
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")))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return Tree
|
||||
|
||||
@@ -1,272 +1,183 @@
|
||||
local elementManager = require("elementManager")
|
||||
local BaseElement = elementManager.getElement("BaseElement")
|
||||
local tHex = require("libraries/colorHex")
|
||||
@alias color number
|
||||
# VisualElement : BaseElement
|
||||
|
||||
---@alias color number
|
||||
## Properties
|
||||
|
||||
---@class VisualElement : BaseElement
|
||||
local VisualElement = setmetatable({}, BaseElement)
|
||||
VisualElement.__index = VisualElement
|
||||
|Property|Type|Default|Description|
|
||||
|---|---|---|---|
|
||||
|x|number|1|x position of the element
|
||||
|y|number|1|y position of the element
|
||||
|z|number|1|z position of the element
|
||||
|width|number|1|width of the element
|
||||
|height|number|1|height of the element
|
||||
|background|color|black|background color of the element
|
||||
|foreground|color|white|foreground color of the element
|
||||
|clicked|boolean|an|false element is currently clicked
|
||||
|backgroundEnabled|boolean|true|whether the background is enabled
|
||||
|focused|boolean|false|whether the element is focused
|
||||
|visible|boolean|true|whether the element is visible
|
||||
|
||||
---@property x number 1 x position of the element
|
||||
VisualElement.defineProperty(VisualElement, "x", {default = 1, type = "number", canTriggerRender = true})
|
||||
---@property y number 1 y position of the element
|
||||
VisualElement.defineProperty(VisualElement, "y", {default = 1, type = "number", canTriggerRender = true})
|
||||
---@property z number 1 z position of the element
|
||||
VisualElement.defineProperty(VisualElement, "z", {default = 1, type = "number", canTriggerRender = true, setter = function(self, value)
|
||||
if self.parent then
|
||||
self.parent:sortChildren()
|
||||
end
|
||||
return value
|
||||
end})
|
||||
## Functions
|
||||
|
||||
---@property width number 1 width of the element
|
||||
VisualElement.defineProperty(VisualElement, "width", {default = 1, type = "number", canTriggerRender = true})
|
||||
---@property height number 1 height of the element
|
||||
VisualElement.defineProperty(VisualElement, "height", {default = 1, type = "number", canTriggerRender = true})
|
||||
---@property background color black background color of the element
|
||||
VisualElement.defineProperty(VisualElement, "background", {default = colors.black, type = "number", canTriggerRender = true})
|
||||
---@property foreground color white foreground color of the element
|
||||
VisualElement.defineProperty(VisualElement, "foreground", {default = colors.white, type = "number", canTriggerRender = true})
|
||||
---@property clicked boolean an false element is currently clicked
|
||||
VisualElement.defineProperty(VisualElement, "clicked", {default = false, type = "boolean"})
|
||||
---@property backgroundEnabled boolean true whether the background is enabled
|
||||
VisualElement.defineProperty(VisualElement, "backgroundEnabled", {default = true, type = "boolean", canTriggerRender = true})
|
||||
---@property focused boolean false whether the element is focused
|
||||
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
|
||||
|Method|Returns|Description|
|
||||
|---|---|---|
|
||||
|[VisualElement.new](#VisualElement.new)|VisualElement|
|
||||
|[VisualElement:blit](#VisualElement:blit)|-|
|
||||
|[VisualElement:blur](#VisualElement:blur)|-|
|
||||
|[VisualElement:focus](#VisualElement:focus)|-|
|
||||
|[VisualElement:getAbsolutePosition](#VisualElement:getAbsolutePosition)|-|
|
||||
|[VisualElement:getRelativePosition](#VisualElement:getRelativePosition)|number,|
|
||||
|[VisualElement:init](#VisualElement:init)|-|
|
||||
|[VisualElement:isInBounds](#VisualElement:isInBounds)|boolean|
|
||||
|[VisualElement:mouse_click](#VisualElement:mouse_click)|boolean|
|
||||
|[VisualElement:mouse_release](#VisualElement:mouse_release)|boolean|
|
||||
|[VisualElement:mouse_up](#VisualElement:mouse_up)|boolean|
|
||||
|[VisualElement:multiBlit](#VisualElement:multiBlit)|-|
|
||||
|[VisualElement:render](#VisualElement:render)|-|
|
||||
|[VisualElement:setCursor](#VisualElement:setCursor)|-|
|
||||
|[VisualElement:textBg](#VisualElement:textBg)|-|
|
||||
|[VisualElement:textFg](#VisualElement:textFg)|-|
|
||||
|
||||
if value then
|
||||
self:focus()
|
||||
else
|
||||
self:blur()
|
||||
end
|
||||
@combinedProperty position x y
|
||||
@combinedProperty size width height
|
||||
@combinedProperty color foreground background
|
||||
## VisualElement.new(props, basalt)
|
||||
Creates a new VisualElement instance
|
||||
|
||||
if not internal and self.parent then
|
||||
if value then
|
||||
self.parent:setFocusedChild(self)
|
||||
else
|
||||
self.parent:setFocusedChild(nil)
|
||||
end
|
||||
end
|
||||
return value
|
||||
end})
|
||||
### Parameters
|
||||
* `props` `table` The properties to initialize the element with
|
||||
* `basalt` `table` The basalt instance
|
||||
|
||||
---@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)
|
||||
self.parent.set("childrenEventsSorted", false)
|
||||
end
|
||||
return value
|
||||
end})
|
||||
### Returns
|
||||
* `VisualElement` `object` The newly created VisualElement instance
|
||||
|
||||
---@combinedProperty position x y
|
||||
VisualElement.combineProperties(VisualElement, "position", "x", "y")
|
||||
---@combinedProperty size width height
|
||||
VisualElement.combineProperties(VisualElement, "size", "width", "height")
|
||||
---@combinedProperty color foreground background
|
||||
VisualElement.combineProperties(VisualElement, "color", "foreground", "background")
|
||||
### Usage
|
||||
```lua
|
||||
local element = VisualElement.new("myId", basalt)
|
||||
```
|
||||
|
||||
VisualElement.listenTo(VisualElement, "focus")
|
||||
VisualElement.listenTo(VisualElement, "blur")
|
||||
## VisualElement:blit(x, y, text, fg, bg)
|
||||
Draws a text character with a foreground and background at the specified position, used in the rendering system
|
||||
|
||||
local max, min = math.max, math.min
|
||||
### Parameters
|
||||
* `x` `number` The x position to draw
|
||||
* `y` `number` The y position to draw
|
||||
* `text` `string` The text char to draw
|
||||
* `fg` `string` The foreground color
|
||||
* `bg` `string` The background color
|
||||
|
||||
--- Creates a new VisualElement instance
|
||||
--- @param props table The properties to initialize the element with
|
||||
--- @param basalt table The basalt instance
|
||||
--- @return VisualElement object The newly created VisualElement instance
|
||||
--- @usage local element = VisualElement.new("myId", basalt)
|
||||
function VisualElement.new()
|
||||
local self = setmetatable({}, VisualElement):__init()
|
||||
return self
|
||||
end
|
||||
## VisualElement:blur()
|
||||
Handles a blur event
|
||||
|
||||
--- Initializes the VisualElement instance
|
||||
function VisualElement:init(props, basalt)
|
||||
BaseElement.init(self, props, basalt)
|
||||
self.set("type", "VisualElement")
|
||||
end
|
||||
## VisualElement:focus()
|
||||
Handles a focus event
|
||||
|
||||
--- Draws a text character/fg/bg at the specified position with a certain size, used in the rendering system
|
||||
--- @param x number The x position to draw
|
||||
--- @param y number The y position to draw
|
||||
--- @param width number The width of the element
|
||||
--- @param height number The height of the element
|
||||
--- @param text string The text char to draw
|
||||
--- @param fg color The foreground color
|
||||
--- @param bg color The background color
|
||||
function VisualElement:multiBlit(x, y, width, height, text, fg, bg)
|
||||
x = x + self.get("x") - 1
|
||||
y = y + self.get("y") - 1
|
||||
self.parent:multiBlit(x, y, width, height, text, fg, bg)
|
||||
end
|
||||
## VisualElement:getAbsolutePosition(x?, y?)
|
||||
Returns the absolute position of the element or the given coordinates.
|
||||
|
||||
--- Draws a text character at the specified position, used in the rendering system
|
||||
--- @param x number The x position to draw
|
||||
--- @param y number The y position to draw
|
||||
--- @param text string The text char to draw
|
||||
--- @param fg color The foreground color
|
||||
function VisualElement:textFg(x, y, text, fg)
|
||||
x = x + self.get("x") - 1
|
||||
y = y + self.get("y") - 1
|
||||
self.parent:textFg(x, y, text, fg)
|
||||
end
|
||||
### Parameters
|
||||
* `x` *(optional)* `number` x position
|
||||
* `y` *(optional)* `number` y position
|
||||
|
||||
--- Draws a text character with a background at the specified position, used in the rendering system
|
||||
--- @param x number The x position to draw
|
||||
--- @param y number The y position to draw
|
||||
--- @param text string The text char to draw
|
||||
--- @param bg color The background color
|
||||
function VisualElement:textBg(x, y, text, bg)
|
||||
x = x + self.get("x") - 1
|
||||
y = y + self.get("y") - 1
|
||||
self.parent:textBg(x, y, text, bg)
|
||||
end
|
||||
## VisualElement:getRelativePosition(x?, y?)
|
||||
Returns the relative position of the element or the given coordinates.
|
||||
|
||||
--- Draws a text character with a foreground and background at the specified position, used in the rendering system
|
||||
--- @param x number The x position to draw
|
||||
--- @param y number The y position to draw
|
||||
--- @param text string The text char to draw
|
||||
--- @param fg string The foreground color
|
||||
--- @param bg string The background color
|
||||
function VisualElement:blit(x, y, text, fg, bg)
|
||||
x = x + self.get("x") - 1
|
||||
y = y + self.get("y") - 1
|
||||
self.parent:blit(x, y, text, fg, bg)
|
||||
end
|
||||
### Parameters
|
||||
* `x` *(optional)* `number` x position
|
||||
* `y` *(optional)* `number` y position
|
||||
|
||||
--- Checks if the specified coordinates are within the bounds of the element
|
||||
--- @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
|
||||
function VisualElement:isInBounds(x, y)
|
||||
local xPos, yPos = self.get("x"), self.get("y")
|
||||
local width, height = self.get("width"), self.get("height")
|
||||
### Returns
|
||||
* `nil` `nil` nil
|
||||
|
||||
return x >= xPos and x <= xPos + width - 1 and
|
||||
y >= yPos and y <= yPos + height - 1
|
||||
end
|
||||
## VisualElement:init()
|
||||
Initializes the VisualElement instance
|
||||
|
||||
--- 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
|
||||
--- @return boolean clicked Whether the element was clicked
|
||||
function VisualElement:mouse_click(button, x, y)
|
||||
if self:isInBounds(x, y) then
|
||||
self.set("clicked", true)
|
||||
self:fireEvent("mouse_click", button, self:getRelativePosition(x, y))
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
## VisualElement:isInBounds(x, y)
|
||||
Checks if the specified coordinates are within the bounds of the element
|
||||
|
||||
--- 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
|
||||
--- @return boolean release Whether the element was released on the element
|
||||
function VisualElement:mouse_up(button, x, y)
|
||||
self.set("clicked", false)
|
||||
if self:isInBounds(x, y) then
|
||||
self:fireEvent("mouse_up", button, x, y)
|
||||
return true
|
||||
end
|
||||
self:fireEvent("mouse_release", button, self:getRelativePosition(x, y))
|
||||
end
|
||||
### Parameters
|
||||
* `x` `number` The x position to check
|
||||
* `y` `number` The y position to check
|
||||
|
||||
--- 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
|
||||
--- @return boolean release Whether the element was released on the element
|
||||
function VisualElement:mouse_release(button, x, y)
|
||||
if self.get("clicked") then
|
||||
self:fireEvent("mouse_release", button, self:getRelativePosition(x, y))
|
||||
self.set("clicked", false)
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
### Returns
|
||||
* `boolean` `isInBounds` Whether the coordinates are within the bounds of the element
|
||||
|
||||
--- Handles a focus event
|
||||
function VisualElement:focus()
|
||||
self:fireEvent("focus")
|
||||
end
|
||||
## VisualElement:mouse_click(button, x, y)
|
||||
Handles a mouse click event
|
||||
|
||||
--- Handles a blur event
|
||||
function VisualElement:blur()
|
||||
self:fireEvent("blur")
|
||||
self:setCursor(1,1, false)
|
||||
end
|
||||
### Parameters
|
||||
* `button` `number` The button that was clicked
|
||||
* `x` `number` The x position of the click
|
||||
* `y` `number` The y position of the click
|
||||
|
||||
--- Returns the absolute position of the element or the given coordinates.
|
||||
---@param x? number x position
|
||||
---@param y? number y position
|
||||
function VisualElement:getAbsolutePosition(x, y)
|
||||
local xPos, yPos = self.get("x"), self.get("y")
|
||||
if(x ~= nil) then
|
||||
xPos = xPos + x - 1
|
||||
end
|
||||
if(y ~= nil) then
|
||||
yPos = yPos + y - 1
|
||||
end
|
||||
### Returns
|
||||
* `boolean` `clicked` Whether the element was clicked
|
||||
|
||||
local parent = self.parent
|
||||
while parent do
|
||||
local px, py = parent.get("x"), parent.get("y")
|
||||
xPos = xPos + px - 1
|
||||
yPos = yPos + py - 1
|
||||
parent = parent.parent
|
||||
end
|
||||
## VisualElement:mouse_release(button, x, y)
|
||||
Handles a mouse release event
|
||||
|
||||
return xPos, yPos
|
||||
end
|
||||
### Parameters
|
||||
* `button` `number` The button that was released
|
||||
* `x` `number` The x position of the release
|
||||
* `y` `number` The y position of the release
|
||||
|
||||
--- Returns the relative position of the element or the given coordinates.
|
||||
---@param x? number x position
|
||||
---@param y? number y position
|
||||
---@return number, number
|
||||
function VisualElement:getRelativePosition(x, y)
|
||||
if (x == nil) or (y == nil) then
|
||||
x, y = self.get("x"), self.get("y")
|
||||
end
|
||||
### Returns
|
||||
* `boolean` `release` Whether the element was released on the element
|
||||
|
||||
local parentX, parentY = 1, 1
|
||||
if self.parent then
|
||||
parentX, parentY = self.parent:getRelativePosition()
|
||||
end
|
||||
## VisualElement:mouse_up(button, x, y)
|
||||
Handles a mouse up event
|
||||
|
||||
local elementX = self.get("x")
|
||||
local elementY = self.get("y")
|
||||
### Parameters
|
||||
* `button` `number` The button that was released
|
||||
* `x` `number` The x position of the release
|
||||
* `y` `number` The y position of the release
|
||||
|
||||
return x - (elementX - 1) - (parentX - 1),
|
||||
y - (elementY - 1) - (parentY - 1)
|
||||
end
|
||||
### Returns
|
||||
* `boolean` `release` Whether the element was released on the element
|
||||
|
||||
--- 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
|
||||
function VisualElement:setCursor(x, y, blink)
|
||||
if self.parent then
|
||||
local absX, absY = self:getAbsolutePosition(x, y)
|
||||
absX = max(self.get("x"), min(absX, self.get("width") + self.get("x") - 1))
|
||||
return self.parent:setCursor(absX, absY, blink)
|
||||
end
|
||||
end
|
||||
## VisualElement:multiBlit(x, y, width, height, text, fg, bg)
|
||||
Draws a text character/fg/bg at the specified position with a certain size, used in the rendering system
|
||||
|
||||
--- Renders the element
|
||||
--- @usage element:render()
|
||||
function VisualElement:render()
|
||||
if(not self.get("backgroundEnabled"))then
|
||||
return
|
||||
end
|
||||
local width, height = self.get("width"), self.get("height")
|
||||
self:multiBlit(1, 1, width, height, " ", tHex[self.get("foreground")], tHex[self.get("background")])
|
||||
end
|
||||
### Parameters
|
||||
* `x` `number` The x position to draw
|
||||
* `y` `number` The y position to draw
|
||||
* `width` `number` The width of the element
|
||||
* `height` `number` The height of the element
|
||||
* `text` `string` The text char to draw
|
||||
* `fg` `color` The foreground color
|
||||
* `bg` `color` The background color
|
||||
|
||||
## VisualElement:render()
|
||||
Renders the element
|
||||
|
||||
### Usage
|
||||
```lua
|
||||
element:render()
|
||||
```
|
||||
|
||||
## VisualElement:setCursor(x, y, blink)
|
||||
Sets the cursor position
|
||||
|
||||
### Parameters
|
||||
* `x` `number` The x position of the cursor
|
||||
* `y` `number` The y position of the cursor
|
||||
* `blink` `boolean` Whether the cursor should blink
|
||||
|
||||
## VisualElement:textBg(x, y, text, bg)
|
||||
Draws a text character with a background at the specified position, used in the rendering system
|
||||
|
||||
### Parameters
|
||||
* `x` `number` The x position to draw
|
||||
* `y` `number` The y position to draw
|
||||
* `text` `string` The text char to draw
|
||||
* `bg` `color` The background color
|
||||
|
||||
## VisualElement:textFg(x, y, text, fg)
|
||||
Draws a text character at the specified position, used in the rendering system
|
||||
|
||||
### Parameters
|
||||
* `x` `number` The x position to draw
|
||||
* `y` `number` The y position to draw
|
||||
* `text` `string` The text char to draw
|
||||
* `fg` `color` The foreground color
|
||||
|
||||
return VisualElement
|
||||
Reference in New Issue
Block a user