Lot of bug fixxes
This commit is contained in:
@@ -1,39 +0,0 @@
|
|||||||
local markdown = require("tools/markdown")
|
|
||||||
local log = require("src/log")
|
|
||||||
|
|
||||||
if not fs.exists("docs/references") then
|
|
||||||
fs.makeDir("docs/references")
|
|
||||||
end
|
|
||||||
|
|
||||||
local function processFile(inputFile)
|
|
||||||
local parsed = markdown.parseFile(inputFile)
|
|
||||||
local md = markdown.makeMarkdown(parsed)
|
|
||||||
|
|
||||||
local relativePath = inputFile:match("Basalt2/src/(.+)")
|
|
||||||
if not relativePath then return end
|
|
||||||
|
|
||||||
local outputFile = "docs/references/" .. relativePath:gsub("%.lua$", "")
|
|
||||||
|
|
||||||
local dir = fs.getDir(outputFile)
|
|
||||||
if not fs.exists(dir) then
|
|
||||||
fs.makeDir(dir)
|
|
||||||
end
|
|
||||||
|
|
||||||
--print(string.format("Processing: %s -> %s", inputFile, outputFile))
|
|
||||||
|
|
||||||
markdown.saveToFile(outputFile, md)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function processDirectory(path)
|
|
||||||
for _, file in ipairs(fs.list(path)) do
|
|
||||||
local fullPath = fs.combine(path, file)
|
|
||||||
if fs.isDir(fullPath) then
|
|
||||||
processDirectory(fullPath)
|
|
||||||
elseif file:match("%.lua$") and not file:match("LuaLS%.lua$") then
|
|
||||||
processFile(fullPath)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
processDirectory("Basalt2/src")
|
|
||||||
@@ -34,12 +34,13 @@ BaseElement.defineProperty(BaseElement, "eventCallbacks", {default = {}, type =
|
|||||||
--- @shortDescription Registers an event that this class can listen to
|
--- @shortDescription Registers an event that this class can listen to
|
||||||
--- @param class table The class to add the event to
|
--- @param class table The class to add the event to
|
||||||
--- @param eventName string The name of the event to register
|
--- @param eventName string The name of the event to register
|
||||||
|
--- @param event? string The event to handle
|
||||||
--- @usage BaseElement.listenTo(MyClass, "mouse_click")
|
--- @usage BaseElement.listenTo(MyClass, "mouse_click")
|
||||||
function BaseElement.listenTo(class, eventName)
|
function BaseElement.listenTo(class, eventName, event)
|
||||||
if not class._events then
|
if not class._events then
|
||||||
class._events = {}
|
class._events = {}
|
||||||
end
|
end
|
||||||
class._events[eventName] = true
|
class._events[eventName] = {enabled=true, name=eventName, event=event}
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Creates a new BaseElement instance
|
--- Creates a new BaseElement instance
|
||||||
@@ -64,13 +65,13 @@ function BaseElement:init(props, basalt)
|
|||||||
self.basalt = basalt
|
self.basalt = basalt
|
||||||
self._registeredEvents = {}
|
self._registeredEvents = {}
|
||||||
if BaseElement._events then
|
if BaseElement._events then
|
||||||
for event in pairs(BaseElement._events) do
|
for key,event in pairs(BaseElement._events) do
|
||||||
self._registeredEvents[event] = true
|
self._registeredEvents[event.event or event.name] = true
|
||||||
local handlerName = "on" .. event:gsub("_(%l)", function(c)
|
local handlerName = "on" .. event.name:gsub("_(%l)", function(c)
|
||||||
return c:upper()
|
return c:upper()
|
||||||
end):gsub("^%l", string.upper)
|
end):gsub("^%l", string.upper)
|
||||||
self[handlerName] = function(self, ...)
|
self[handlerName] = function(self, ...)
|
||||||
self:registerCallback(event, ...)
|
self:registerCallback(event.name, ...)
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -93,9 +93,14 @@ end
|
|||||||
--- @param x number The x position to set the cursor to
|
--- @param x number The x position to set the cursor to
|
||||||
--- @param y number The y position to set the cursor to
|
--- @param y number The y position to set the cursor to
|
||||||
--- @param blink boolean Whether the cursor should blink
|
--- @param blink boolean Whether the cursor should blink
|
||||||
function BaseFrame:setCursor(x, y, blink)
|
function BaseFrame:setCursor(x, y, blink, color)
|
||||||
local term = self.get("term")
|
local term = self.get("term")
|
||||||
self._render:setCursor(x, y, blink)
|
self._render:setCursor(x, y, blink, color)
|
||||||
|
end
|
||||||
|
|
||||||
|
function BaseFrame:mouse_up(button, x, y)
|
||||||
|
Container.mouse_up(self, button, x, y)
|
||||||
|
Container.mouse_release(self, button, x, y)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Renders the Frame
|
--- Renders the Frame
|
||||||
@@ -110,4 +115,15 @@ function BaseFrame:render()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function BaseFrame:term_resize()
|
||||||
|
local width, height = self.get("term").getSize()
|
||||||
|
if(width == self.get("width") and height == self.get("height")) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
self.set("width", width)
|
||||||
|
self.set("height", height)
|
||||||
|
self._render:setSize(width, height)
|
||||||
|
self._renderUpdate = true
|
||||||
|
end
|
||||||
|
|
||||||
return BaseFrame
|
return BaseFrame
|
||||||
@@ -3,8 +3,6 @@ local VisualElement = elementManager.getElement("VisualElement")
|
|||||||
local expect = require("libraries/expect")
|
local expect = require("libraries/expect")
|
||||||
local split = require("libraries/utils").split
|
local split = require("libraries/utils").split
|
||||||
|
|
||||||
local max = math.max
|
|
||||||
|
|
||||||
--- The container class. It is a visual element that can contain other elements. It is the base class for all containers,
|
--- The container class. It is a visual element that can contain other elements. It is the base class for all containers,
|
||||||
--- like Frames, BaseFrames, and more.
|
--- like Frames, BaseFrames, and more.
|
||||||
---@class Container : VisualElement
|
---@class Container : VisualElement
|
||||||
@@ -303,7 +301,7 @@ local function convertMousePosition(self, event, ...)
|
|||||||
return args
|
return args
|
||||||
end
|
end
|
||||||
|
|
||||||
local function callChildrenEvents(self, visibleOnly, event, ...)
|
function Container:callChildrenEvents(visibleOnly, event, ...)
|
||||||
local children = visibleOnly and self.get("visibleChildrenEvents") or self.get("childrenEvents")
|
local children = visibleOnly and self.get("visibleChildrenEvents") or self.get("childrenEvents")
|
||||||
if children[event] then
|
if children[event] then
|
||||||
local events = children[event]
|
local events = children[event]
|
||||||
@@ -325,7 +323,7 @@ end
|
|||||||
function Container:handleEvent(event, ...)
|
function Container:handleEvent(event, ...)
|
||||||
VisualElement.handleEvent(self, event, ...)
|
VisualElement.handleEvent(self, event, ...)
|
||||||
local args = convertMousePosition(self, event, ...)
|
local args = convertMousePosition(self, event, ...)
|
||||||
return callChildrenEvents(self, false, event, table.unpack(args))
|
return self:callChildrenEvents(false, event, table.unpack(args))
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Handles mouse click events
|
--- Handles mouse click events
|
||||||
@@ -337,7 +335,7 @@ end
|
|||||||
function Container:mouse_click(button, x, y)
|
function Container:mouse_click(button, x, y)
|
||||||
if VisualElement.mouse_click(self, button, x, y) then
|
if VisualElement.mouse_click(self, button, x, y) then
|
||||||
local args = convertMousePosition(self, "mouse_click", button, x, y)
|
local args = convertMousePosition(self, "mouse_click", button, x, y)
|
||||||
local success, child = callChildrenEvents(self, true, "mouse_click", table.unpack(args))
|
local success, child = self:callChildrenEvents(true, "mouse_click", table.unpack(args))
|
||||||
if(success)then
|
if(success)then
|
||||||
self.set("focusedChild", child)
|
self.set("focusedChild", child)
|
||||||
return true
|
return true
|
||||||
@@ -357,7 +355,7 @@ end
|
|||||||
function Container:mouse_up(button, x, y)
|
function Container:mouse_up(button, x, y)
|
||||||
if VisualElement.mouse_up(self, button, x, y) then
|
if VisualElement.mouse_up(self, button, x, y) then
|
||||||
local args = convertMousePosition(self, "mouse_up", button, x, y)
|
local args = convertMousePosition(self, "mouse_up", button, x, y)
|
||||||
local success, child = callChildrenEvents(self, true, "mouse_up", table.unpack(args))
|
local success, child = self:callChildrenEvents(true, "mouse_up", table.unpack(args))
|
||||||
if(success)then
|
if(success)then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@@ -365,6 +363,45 @@ function Container:mouse_up(button, x, y)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Container:mouse_release(button, x, y)
|
||||||
|
VisualElement.mouse_release(self, button, x, y)
|
||||||
|
local args = convertMousePosition(self, "mouse_release", button, x, y)
|
||||||
|
self:callChildrenEvents(false, "mouse_release", table.unpack(args))
|
||||||
|
end
|
||||||
|
|
||||||
|
function Container:mouse_move(_, x, y)
|
||||||
|
if VisualElement.mouse_move(self, _, x, y) then
|
||||||
|
local args = convertMousePosition(self, "mouse_move", _, x, y)
|
||||||
|
local success, child = self:callChildrenEvents(true, "mouse_move", table.unpack(args))
|
||||||
|
if(success)then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function Container:mouse_drag(button, x, y)
|
||||||
|
if VisualElement.mouse_drag(self, button, x, y) then
|
||||||
|
local args = convertMousePosition(self, "mouse_drag", button, x, y)
|
||||||
|
local success, child = self:callChildrenEvents(true, "mouse_drag", table.unpack(args))
|
||||||
|
if(success)then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function Container:mouse_scroll(direction, x, y)
|
||||||
|
if VisualElement.mouse_scroll(self, direction, x, y) then
|
||||||
|
local args = convertMousePosition(self, "mouse_scroll", direction, x, y)
|
||||||
|
local success, child = self:callChildrenEvents(true, "mouse_scroll", table.unpack(args))
|
||||||
|
if(success)then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
--- Handles key events
|
--- Handles key events
|
||||||
--- @shortDescription Handles key events
|
--- @shortDescription Handles key events
|
||||||
--- @param key number The key that was pressed
|
--- @param key number The key that was pressed
|
||||||
@@ -438,6 +475,7 @@ function Container:textFg(x, y, text, fg)
|
|||||||
if textLen <= 0 then return self end
|
if textLen <= 0 then return self end
|
||||||
|
|
||||||
VisualElement.textFg(self, math.max(1, x), math.max(1, y), text:sub(textStart, textStart + textLen - 1), fg)
|
VisualElement.textFg(self, math.max(1, x), math.max(1, y), text:sub(textStart, textStart + textLen - 1), fg)
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Draws a line of text and fg and bg as colors, it is usually used in the render loop
|
--- Draws a line of text and fg and bg as colors, it is usually used in the render loop
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ Input.defineProperty(Input, "placeholderColor", {default = colors.gray, type = "
|
|||||||
Input.defineProperty(Input, "focusedColor", {default = colors.blue, type = "number"})
|
Input.defineProperty(Input, "focusedColor", {default = colors.blue, type = "number"})
|
||||||
---@property pattern string? nil Regular expression pattern for input validation
|
---@property pattern string? nil Regular expression pattern for input validation
|
||||||
Input.defineProperty(Input, "pattern", {default = nil, type = "string"})
|
Input.defineProperty(Input, "pattern", {default = nil, type = "string"})
|
||||||
|
---@property cursorColor number nil Color of the cursor
|
||||||
|
Input.defineProperty(Input, "cursorColor", {default = nil, type = "number"})
|
||||||
|
|
||||||
Input.listenTo(Input, "mouse_click")
|
Input.listenTo(Input, "mouse_click")
|
||||||
Input.listenTo(Input, "key")
|
Input.listenTo(Input, "key")
|
||||||
@@ -106,7 +108,7 @@ function Input:key(key)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local relativePos = self.get("cursorPos") - self.get("viewOffset")
|
local relativePos = self.get("cursorPos") - self.get("viewOffset")
|
||||||
self:setCursor(relativePos, 1, true)
|
self:setCursor(relativePos, 1, true, self.get("cursorColor") or self.get("foreground"))
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -134,7 +136,7 @@ function Input:mouse_click(button, x, y)
|
|||||||
if VisualElement.mouse_click(self, button, x, y) then
|
if VisualElement.mouse_click(self, button, x, y) then
|
||||||
local relX, relY = self:getRelativePosition(x, y)
|
local relX, relY = self:getRelativePosition(x, y)
|
||||||
local text = self.get("text")
|
local text = self.get("text")
|
||||||
self:setCursor(math.min(relX, #text + 1), relY, true)
|
self:setCursor(math.min(relX, #text + 1), relY, true, self.get("cursorColor") or self.get("foreground"))
|
||||||
self:set("cursorPos", relX + self.get("viewOffset"))
|
self:set("cursorPos", relX + self.get("viewOffset"))
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|||||||
180
src/elements/Scrollbar.lua
Normal file
180
src/elements/Scrollbar.lua
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
local VisualElement = require("elements/VisualElement")
|
||||||
|
local tHex = require("libraries/colorHex")
|
||||||
|
|
||||||
|
---A scrollbar element that can be attached to other elements to control their scroll properties
|
||||||
|
---@class Scrollbar : VisualElement
|
||||||
|
local Scrollbar = setmetatable({}, VisualElement)
|
||||||
|
Scrollbar.__index = Scrollbar
|
||||||
|
|
||||||
|
---@property value number 0 Current scroll value
|
||||||
|
Scrollbar.defineProperty(Scrollbar, "value", {default = 0, type = "number", canTriggerRender = true})
|
||||||
|
---@property min number 0 Minimum scroll value
|
||||||
|
Scrollbar.defineProperty(Scrollbar, "min", {default = 0, type = "number", canTriggerRender = true})
|
||||||
|
---@property max number 100 Maximum scroll value
|
||||||
|
Scrollbar.defineProperty(Scrollbar, "max", {default = 100, type = "number", canTriggerRender = true})
|
||||||
|
---@property step number 1 Step size for scroll operations
|
||||||
|
Scrollbar.defineProperty(Scrollbar, "step", {default = 10, type = "number"})
|
||||||
|
---@property dragMultiplier number 1 How fast the scrollbar moves when dragging
|
||||||
|
Scrollbar.defineProperty(Scrollbar, "dragMultiplier", {default = 1, type = "number"})
|
||||||
|
---@property symbol string " " Symbol used for the scrollbar handle
|
||||||
|
Scrollbar.defineProperty(Scrollbar, "symbol", {default = " ", type = "string", canTriggerRender = true})
|
||||||
|
---@property backgroundSymbol string "\127" Symbol used for the scrollbar background
|
||||||
|
Scrollbar.defineProperty(Scrollbar, "symbolColor", {default = colors.gray, type = "number", canTriggerRender = true})
|
||||||
|
---@property symbolBackgroundColor color black Background color of the scrollbar handle
|
||||||
|
Scrollbar.defineProperty(Scrollbar, "symbolBackgroundColor", {default = colors.black, type = "number", canTriggerRender = true})
|
||||||
|
---@property backgroundSymbol string "\127" Symbol used for the scrollbar background
|
||||||
|
Scrollbar.defineProperty(Scrollbar, "backgroundSymbol", {default = "\127", type = "string", canTriggerRender = true})
|
||||||
|
---@property attachedElement table? nil The element this scrollbar is attached to
|
||||||
|
Scrollbar.defineProperty(Scrollbar, "attachedElement", {default = nil, type = "table"})
|
||||||
|
---@property attachedProperty string? nil The property being controlled
|
||||||
|
Scrollbar.defineProperty(Scrollbar, "attachedProperty", {default = nil, type = "string"})
|
||||||
|
---@property minValue number|function 0 Minimum value or function that returns it
|
||||||
|
Scrollbar.defineProperty(Scrollbar, "minValue", {default = 0, type = "number"})
|
||||||
|
---@property maxValue number|function 100 Maximum value or function that returns it
|
||||||
|
Scrollbar.defineProperty(Scrollbar, "maxValue", {default = 100, type = "number"})
|
||||||
|
---@property orientation string vertical Orientation of the scrollbar ("vertical" or "horizontal")
|
||||||
|
Scrollbar.defineProperty(Scrollbar, "orientation", {default = "vertical", type = "string", canTriggerRender = true})
|
||||||
|
|
||||||
|
---@property handleSize number 2 Size of the scrollbar handle in characters
|
||||||
|
Scrollbar.defineProperty(Scrollbar, "handleSize", {default = 2, type = "number", canTriggerRender = true})
|
||||||
|
|
||||||
|
Scrollbar.listenTo(Scrollbar, "mouse_click")
|
||||||
|
Scrollbar.listenTo(Scrollbar, "mouse_release")
|
||||||
|
Scrollbar.listenTo(Scrollbar, "mouse_drag")
|
||||||
|
Scrollbar.listenTo(Scrollbar, "mouse_scroll")
|
||||||
|
|
||||||
|
--- Creates a new Scrollbar instance
|
||||||
|
--- @shortDescription Creates a new Scrollbar instance
|
||||||
|
--- @return Scrollbar self The newly created Scrollbar instance
|
||||||
|
--- @usage local scrollbar = Scrollbar.new()
|
||||||
|
function Scrollbar.new()
|
||||||
|
local self = setmetatable({}, Scrollbar):__init()
|
||||||
|
self.set("width", 1)
|
||||||
|
self.set("height", 10)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function Scrollbar:init(props, basalt)
|
||||||
|
VisualElement.init(self, props, basalt)
|
||||||
|
self.set("type", "Scrollbar")
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Attaches the scrollbar to an element's property
|
||||||
|
--- @param element BaseElement The element to attach to
|
||||||
|
--- @param config table Configuration {property = "propertyName", min = number|function, max = number|function}
|
||||||
|
--- @return Scrollbar self The scrollbar instance
|
||||||
|
function Scrollbar:attach(element, config)
|
||||||
|
self.set("attachedElement", element)
|
||||||
|
self.set("attachedProperty", config.property)
|
||||||
|
self.set("minValue", config.min or 0)
|
||||||
|
self.set("maxValue", config.max or 100)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function Scrollbar:updateAttachedElement()
|
||||||
|
local element = self.get("attachedElement")
|
||||||
|
if not element then return end
|
||||||
|
|
||||||
|
local value = self.get("value")
|
||||||
|
local min = self.get("minValue")
|
||||||
|
local max = self.get("maxValue")
|
||||||
|
|
||||||
|
if type(min) == "function" then min = min() end
|
||||||
|
if type(max) == "function" then max = max() end
|
||||||
|
|
||||||
|
local mappedValue = min + (value / 100) * (max - min)
|
||||||
|
element.set(self.get("attachedProperty"), math.floor(mappedValue + 0.5))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function getScrollbarSize(self)
|
||||||
|
return self.get("orientation") == "vertical" and self.get("height") or self.get("width")
|
||||||
|
end
|
||||||
|
|
||||||
|
local function getRelativeScrollPosition(self, x, y)
|
||||||
|
local relX, relY = self:getRelativePosition(x, y)
|
||||||
|
return self.get("orientation") == "vertical" and relY or relX
|
||||||
|
end
|
||||||
|
|
||||||
|
function Scrollbar:mouse_click(button, x, y)
|
||||||
|
if VisualElement.mouse_click(self, button, x, y) then
|
||||||
|
local size = getScrollbarSize(self)
|
||||||
|
local value = self.get("value")
|
||||||
|
local handleSize = self.get("handleSize")
|
||||||
|
|
||||||
|
local handlePos = math.floor((value / 100) * (size - handleSize)) + 1
|
||||||
|
local relPos = getRelativeScrollPosition(self, x, y)
|
||||||
|
|
||||||
|
if relPos >= handlePos and relPos < handlePos + handleSize then
|
||||||
|
self.dragOffset = relPos - handlePos
|
||||||
|
else
|
||||||
|
local newValue = ((relPos - 1) / (size - handleSize)) * 100
|
||||||
|
self.set("value", math.min(100, math.max(0, newValue)))
|
||||||
|
self:updateAttachedElement()
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Scrollbar:mouse_drag(button, x, y)
|
||||||
|
if(VisualElement.mouse_drag(self, button, x, y))then
|
||||||
|
local size = getScrollbarSize(self)
|
||||||
|
local handleSize = self.get("handleSize")
|
||||||
|
local dragMultiplier = self.get("dragMultiplier")
|
||||||
|
local relPos = getRelativeScrollPosition(self, x, y)
|
||||||
|
|
||||||
|
relPos = math.max(1, math.min(size, relPos))
|
||||||
|
|
||||||
|
local newPos = relPos - (self.dragOffset or 0)
|
||||||
|
local newValue = (newPos - 1) / (size - handleSize) * 100 * dragMultiplier
|
||||||
|
|
||||||
|
self.set("value", math.min(100, math.max(0, newValue)))
|
||||||
|
self:updateAttachedElement()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Scrollbar:mouse_scroll(direction, x, y)
|
||||||
|
if not self:isInBounds(x, y) then return false end
|
||||||
|
direction = direction > 0 and -1 or 1
|
||||||
|
local step = self.get("step")
|
||||||
|
local currentValue = self.get("value")
|
||||||
|
local newValue = currentValue - direction * step
|
||||||
|
|
||||||
|
self.set("value", math.min(100, math.max(0, newValue)))
|
||||||
|
self:updateAttachedElement()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function Scrollbar:render()
|
||||||
|
VisualElement.render(self)
|
||||||
|
|
||||||
|
local size = getScrollbarSize(self)
|
||||||
|
local value = self.get("value")
|
||||||
|
local handleSize = self.get("handleSize")
|
||||||
|
local symbol = self.get("symbol")
|
||||||
|
local symbolColor = self.get("symbolColor")
|
||||||
|
local symbolBackgroundColor = self.get("symbolBackgroundColor")
|
||||||
|
local bgSymbol = self.get("backgroundSymbol")
|
||||||
|
local isVertical = self.get("orientation") == "vertical"
|
||||||
|
|
||||||
|
local handlePos = math.floor((value / 100) * (size - handleSize)) + 1
|
||||||
|
|
||||||
|
for i = 1, size do
|
||||||
|
if isVertical then
|
||||||
|
self:blit(1, i, bgSymbol, tHex[self.get("foreground")], tHex[self.get("background")])
|
||||||
|
else
|
||||||
|
self:blit(i, 1, bgSymbol, tHex[self.get("foreground")], tHex[self.get("background")])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = handlePos, handlePos + handleSize - 1 do
|
||||||
|
if isVertical then
|
||||||
|
self:blit(1, i, symbol, tHex[symbolColor], tHex[symbolBackgroundColor])
|
||||||
|
else
|
||||||
|
self:blit(i, 1, symbol, tHex[symbolColor], tHex[symbolBackgroundColor])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return Scrollbar
|
||||||
@@ -20,6 +20,8 @@ TextBox.defineProperty(TextBox, "scrollY", {default = 0, type = "number", canTri
|
|||||||
TextBox.defineProperty(TextBox, "editable", {default = true, type = "boolean"})
|
TextBox.defineProperty(TextBox, "editable", {default = true, type = "boolean"})
|
||||||
---@property syntaxPatterns table {} Syntax highlighting patterns
|
---@property syntaxPatterns table {} Syntax highlighting patterns
|
||||||
TextBox.defineProperty(TextBox, "syntaxPatterns", {default = {}, type = "table"})
|
TextBox.defineProperty(TextBox, "syntaxPatterns", {default = {}, type = "table"})
|
||||||
|
---@property cursorColor number nil Color of the cursor
|
||||||
|
TextBox.defineProperty(TextBox, "cursorColor", {default = nil, type = "number"})
|
||||||
|
|
||||||
TextBox.listenTo(TextBox, "mouse_click")
|
TextBox.listenTo(TextBox, "mouse_click")
|
||||||
TextBox.listenTo(TextBox, "key")
|
TextBox.listenTo(TextBox, "key")
|
||||||
@@ -41,7 +43,7 @@ end
|
|||||||
|
|
||||||
--- Adds a new syntax highlighting pattern
|
--- Adds a new syntax highlighting pattern
|
||||||
--- @param pattern string The regex pattern to match
|
--- @param pattern string The regex pattern to match
|
||||||
--- @param color color The color to apply
|
--- @param color colors The color to apply
|
||||||
function TextBox:addSyntaxPattern(pattern, color)
|
function TextBox:addSyntaxPattern(pattern, color)
|
||||||
table.insert(self.get("syntaxPatterns"), {pattern = pattern, color = color})
|
table.insert(self.get("syntaxPatterns"), {pattern = pattern, color = color})
|
||||||
return self
|
return self
|
||||||
@@ -162,7 +164,14 @@ end
|
|||||||
function TextBox:mouse_scroll(direction, x, y)
|
function TextBox:mouse_scroll(direction, x, y)
|
||||||
if self:isInBounds(x, y) then
|
if self:isInBounds(x, y) then
|
||||||
local scrollY = self.get("scrollY")
|
local scrollY = self.get("scrollY")
|
||||||
self.set("scrollY", math.max(0, scrollY + direction))
|
local height = self.get("height")
|
||||||
|
local lines = self.get("lines")
|
||||||
|
|
||||||
|
local maxScroll = math.max(0, #lines - height + 2)
|
||||||
|
|
||||||
|
local newScroll = math.max(0, math.min(maxScroll, scrollY + direction))
|
||||||
|
|
||||||
|
self.set("scrollY", newScroll)
|
||||||
self:updateRender()
|
self:updateRender()
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@@ -182,16 +191,22 @@ function TextBox:mouse_click(button, x, y)
|
|||||||
self.set("cursorY", targetY)
|
self.set("cursorY", targetY)
|
||||||
self.set("cursorX", math.min(relX + scrollX, #lines[targetY] + 1))
|
self.set("cursorX", math.min(relX + scrollX, #lines[targetY] + 1))
|
||||||
end
|
end
|
||||||
|
self:updateRender()
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
function TextBox:setText(text)
|
function TextBox:setText(text)
|
||||||
self.set("lines", {""})
|
local lines = {}
|
||||||
for line in text:gmatch("[^\n]+") do
|
if text == "" then
|
||||||
table.insert(self.get("lines"), line)
|
lines = {""}
|
||||||
|
else
|
||||||
|
for line in (text.."\n"):gmatch("([^\n]*)\n") do
|
||||||
|
table.insert(lines, line)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
self.set("lines", lines)
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -244,7 +259,7 @@ function TextBox:render()
|
|||||||
local relativeX = self.get("cursorX") - scrollX
|
local relativeX = self.get("cursorX") - scrollX
|
||||||
local relativeY = self.get("cursorY") - scrollY
|
local relativeY = self.get("cursorY") - scrollY
|
||||||
if relativeX >= 1 and relativeX <= width and relativeY >= 1 and relativeY <= height then
|
if relativeX >= 1 and relativeX <= width and relativeY >= 1 and relativeY <= height then
|
||||||
self:setCursor(relativeX, relativeY, true)
|
self:setCursor(relativeX, relativeY, true, self.get("cursorColor") or self.get("foreground"))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -13,8 +13,10 @@ Tree.defineProperty(Tree, "nodes", {default = {}, type = "table", canTriggerRend
|
|||||||
Tree.defineProperty(Tree, "selectedNode", {default = nil, type = "table", canTriggerRender = true})
|
Tree.defineProperty(Tree, "selectedNode", {default = nil, type = "table", canTriggerRender = true})
|
||||||
---@property expandedNodes table {} Table of nodes that are currently expanded
|
---@property expandedNodes table {} Table of nodes that are currently expanded
|
||||||
Tree.defineProperty(Tree, "expandedNodes", {default = {}, type = "table", canTriggerRender = true})
|
Tree.defineProperty(Tree, "expandedNodes", {default = {}, type = "table", canTriggerRender = true})
|
||||||
---@property scrollOffset number 0 Current scroll position
|
---@property scrollOffset number 0 Current vertical scroll position
|
||||||
Tree.defineProperty(Tree, "scrollOffset", {default = 0, type = "number", canTriggerRender = true})
|
Tree.defineProperty(Tree, "scrollOffset", {default = 0, type = "number", canTriggerRender = true})
|
||||||
|
---@property horizontalOffset number 0 Current horizontal scroll position
|
||||||
|
Tree.defineProperty(Tree, "horizontalOffset", {default = 0, type = "number", canTriggerRender = true})
|
||||||
---@property nodeColor color white Color of unselected nodes
|
---@property nodeColor color white Color of unselected nodes
|
||||||
Tree.defineProperty(Tree, "nodeColor", {default = colors.white, type = "number"})
|
Tree.defineProperty(Tree, "nodeColor", {default = colors.white, type = "number"})
|
||||||
---@property selectedColor color lightBlue Background color of selected node
|
---@property selectedColor color lightBlue Background color of selected node
|
||||||
@@ -106,24 +108,24 @@ local function flattenTree(nodes, expandedNodes, level, result)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Tree:mouse_click(button, x, y)
|
function Tree:mouse_click(button, x, y)
|
||||||
if not VisualElement.mouse_click(self, button, x, y) then return false end
|
if VisualElement.mouse_click(self, button, x, y) then
|
||||||
|
local relX, relY = self:getRelativePosition(x, y)
|
||||||
|
local flatNodes = flattenTree(self.get("nodes"), self.get("expandedNodes"))
|
||||||
|
local visibleIndex = relY + self.get("scrollOffset")
|
||||||
|
|
||||||
local relX, relY = self:getRelativePosition(x, y)
|
if flatNodes[visibleIndex] then
|
||||||
local flatNodes = flattenTree(self.get("nodes"), self.get("expandedNodes"))
|
local nodeInfo = flatNodes[visibleIndex]
|
||||||
local visibleIndex = relY + self.get("scrollOffset")
|
local node = nodeInfo.node
|
||||||
|
|
||||||
if flatNodes[visibleIndex] then
|
if relX <= nodeInfo.level * 2 + 2 then
|
||||||
local nodeInfo = flatNodes[visibleIndex]
|
self:toggleNode(node)
|
||||||
local node = nodeInfo.node
|
end
|
||||||
|
|
||||||
if relX <= nodeInfo.level * 2 + 2 then
|
self.set("selectedNode", node)
|
||||||
self:toggleNode(node)
|
self:fireEvent("node_select", node)
|
||||||
end
|
end
|
||||||
|
return true
|
||||||
self.set("selectedNode", node)
|
|
||||||
self:fireEvent("node_select", node)
|
|
||||||
end
|
end
|
||||||
return true
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function Tree:onSelect(callback)
|
function Tree:onSelect(callback)
|
||||||
@@ -131,13 +133,25 @@ function Tree:onSelect(callback)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
function Tree:mouse_scroll(direction)
|
function Tree:mouse_scroll(direction, x, y)
|
||||||
local flatNodes = flattenTree(self.get("nodes"), self.get("expandedNodes"))
|
if VisualElement.mouse_scroll(self, direction, x, y) then
|
||||||
local maxScroll = math.max(0, #flatNodes - self.get("height"))
|
local flatNodes = flattenTree(self.get("nodes"), self.get("expandedNodes"))
|
||||||
local newScroll = math.min(maxScroll, math.max(0, self.get("scrollOffset") + direction))
|
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)
|
self.set("scrollOffset", newScroll)
|
||||||
return true
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Tree:getNodeSize()
|
||||||
|
local width, height = 0, 0
|
||||||
|
local flatNodes = flattenTree(self.get("nodes"), self.get("expandedNodes"))
|
||||||
|
for _, nodeInfo in ipairs(flatNodes) do
|
||||||
|
width = math.max(width, nodeInfo.level + #nodeInfo.node.text)
|
||||||
|
end
|
||||||
|
height = #flatNodes
|
||||||
|
return width, height
|
||||||
end
|
end
|
||||||
|
|
||||||
function Tree:render()
|
function Tree:render()
|
||||||
@@ -148,6 +162,7 @@ function Tree:render()
|
|||||||
local selectedNode = self.get("selectedNode")
|
local selectedNode = self.get("selectedNode")
|
||||||
local expandedNodes = self.get("expandedNodes")
|
local expandedNodes = self.get("expandedNodes")
|
||||||
local scrollOffset = self.get("scrollOffset")
|
local scrollOffset = self.get("scrollOffset")
|
||||||
|
local horizontalOffset = self.get("horizontalOffset")
|
||||||
|
|
||||||
for y = 1, height do
|
for y = 1, height do
|
||||||
local nodeInfo = flatNodes[y + scrollOffset]
|
local nodeInfo = flatNodes[y + scrollOffset]
|
||||||
@@ -162,11 +177,10 @@ function Tree:render()
|
|||||||
end
|
end
|
||||||
|
|
||||||
local bg = node == selectedNode and self.get("selectedColor") or self.get("background")
|
local bg = node == selectedNode and self.get("selectedColor") or self.get("background")
|
||||||
local text = indent .. symbol .." " .. (node.text or "Node")
|
local fullText = indent .. symbol .." " .. (node.text or "Node")
|
||||||
text = sub(text, 1, self.get("width"))
|
local text = sub(fullText, horizontalOffset + 1, horizontalOffset + self.get("width"))
|
||||||
|
|
||||||
self:textFg(1, y, text .. string.rep(" ", self.get("width") - #text), self.get("foreground"))
|
self:textFg(1, y, text .. string.rep(" ", self.get("width") - #text), self.get("foreground"))
|
||||||
|
|
||||||
else
|
else
|
||||||
self:textFg(1, y, string.rep(" ", self.get("width")), self.get("foreground"), self.get("background"))
|
self:textFg(1, y, string.rep(" ", self.get("width")), self.get("foreground"), self.get("background"))
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ VisualElement.defineProperty(VisualElement, "background", {default = colors.blac
|
|||||||
VisualElement.defineProperty(VisualElement, "foreground", {default = colors.white, type = "number", canTriggerRender = true})
|
VisualElement.defineProperty(VisualElement, "foreground", {default = colors.white, type = "number", canTriggerRender = true})
|
||||||
---@property clicked boolean false Whether the element is currently clicked
|
---@property clicked boolean false Whether the element is currently clicked
|
||||||
VisualElement.defineProperty(VisualElement, "clicked", {default = false, type = "boolean"})
|
VisualElement.defineProperty(VisualElement, "clicked", {default = false, type = "boolean"})
|
||||||
|
---@property hover boolean false Whether the mouse is currently hover over the element (Craftos-PC only)
|
||||||
|
VisualElement.defineProperty(VisualElement, "hover", {default = false, type = "boolean"})
|
||||||
---@property backgroundEnabled boolean true Whether to render the background
|
---@property backgroundEnabled boolean true Whether to render the background
|
||||||
VisualElement.defineProperty(VisualElement, "backgroundEnabled", {default = true, type = "boolean", canTriggerRender = true})
|
VisualElement.defineProperty(VisualElement, "backgroundEnabled", {default = true, type = "boolean", canTriggerRender = true})
|
||||||
---@property focused boolean false Whether the element has input focus
|
---@property focused boolean false Whether the element has input focus
|
||||||
@@ -81,6 +83,8 @@ VisualElement.combineProperties(VisualElement, "color", "foreground", "backgroun
|
|||||||
|
|
||||||
VisualElement.listenTo(VisualElement, "focus")
|
VisualElement.listenTo(VisualElement, "focus")
|
||||||
VisualElement.listenTo(VisualElement, "blur")
|
VisualElement.listenTo(VisualElement, "blur")
|
||||||
|
VisualElement.listenTo(VisualElement, "mouse_enter", "mouse_move")
|
||||||
|
VisualElement.listenTo(VisualElement, "mouse_leave", "mouse_move")
|
||||||
|
|
||||||
local max, min = math.max, math.min
|
local max, min = math.max, math.min
|
||||||
|
|
||||||
@@ -185,12 +189,12 @@ end
|
|||||||
--- @param y number The y position of the release
|
--- @param y number The y position of the release
|
||||||
--- @return boolean release Whether the element was released on the element
|
--- @return boolean release Whether the element was released on the element
|
||||||
function VisualElement:mouse_up(button, x, y)
|
function VisualElement:mouse_up(button, x, y)
|
||||||
self.set("clicked", false)
|
|
||||||
if self:isInBounds(x, y) then
|
if self:isInBounds(x, y) then
|
||||||
self:fireEvent("mouse_up", button, x, y)
|
self.set("clicked", false)
|
||||||
|
self:fireEvent("mouse_up", button, self:getRelativePosition(x, y))
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
self:fireEvent("mouse_release", button, self:getRelativePosition(x, y))
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Handles a mouse release event
|
--- Handles a mouse release event
|
||||||
@@ -198,11 +202,42 @@ end
|
|||||||
--- @param button number The button that was released
|
--- @param button number The button that was released
|
||||||
--- @param x number The x position of the release
|
--- @param x number The x position of the release
|
||||||
--- @param y number The y 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)
|
function VisualElement:mouse_release(button, x, y)
|
||||||
if self.get("clicked") then
|
self:fireEvent("mouse_release", button, self:getRelativePosition(x, y))
|
||||||
self:fireEvent("mouse_release", button, self:getRelativePosition(x, y))
|
self.set("clicked", false)
|
||||||
self.set("clicked", false)
|
end
|
||||||
|
|
||||||
|
function VisualElement:mouse_move(_, x, y)
|
||||||
|
if(x==nil)or(y==nil)then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local hover = self.get("hover")
|
||||||
|
if(self:isInBounds(x, y))then
|
||||||
|
if(not hover)then
|
||||||
|
self.set("hover", true)
|
||||||
|
self:fireEvent("mouse_enter", self:getRelativePosition(x, y))
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
if(hover)then
|
||||||
|
self.set("hover", false)
|
||||||
|
self:fireEvent("mouse_leave", self:getRelativePosition(x, y))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function VisualElement:mouse_scroll(direction, x, y)
|
||||||
|
if(self:isInBounds(x, y))then
|
||||||
|
self:fireEvent("mouse_scroll", direction, self:getRelativePosition(x, y))
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function VisualElement:mouse_drag(button, x, y)
|
||||||
|
if(self.get("clicked"))then
|
||||||
|
self:fireEvent("mouse_drag", button, self:getRelativePosition(x, y))
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
@@ -272,11 +307,11 @@ end
|
|||||||
--- @param x number The x position of the cursor
|
--- @param x number The x position of the cursor
|
||||||
--- @param y number The y position of the cursor
|
--- @param y number The y position of the cursor
|
||||||
--- @param blink boolean Whether the cursor should blink
|
--- @param blink boolean Whether the cursor should blink
|
||||||
function VisualElement:setCursor(x, y, blink)
|
function VisualElement:setCursor(x, y, blink, color)
|
||||||
if self.parent then
|
if self.parent then
|
||||||
local absX, absY = self:getAbsolutePosition(x, y)
|
local absX, absY = self:getAbsolutePosition(x, y)
|
||||||
absX = max(self.get("x"), min(absX, self.get("width") + self.get("x") - 1))
|
absX = max(self.get("x"), min(absX, self.get("width") + self.get("x") - 1))
|
||||||
return self.parent:setCursor(absX, absY, blink)
|
return self.parent:setCursor(absX, absY, blink, color)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
64
src/main.lua
64
src/main.lua
@@ -1,6 +1,7 @@
|
|||||||
local elementManager = require("elementManager")
|
local elementManager = require("elementManager")
|
||||||
local errorManager = require("errorManager")
|
local errorManager = require("errorManager")
|
||||||
local propertySystem = require("propertySystem")
|
local propertySystem = require("propertySystem")
|
||||||
|
local expect = require("libraries/expect")
|
||||||
|
|
||||||
|
|
||||||
--- This is the UI Manager and the starting point for your project. The following functions allow you to influence the default behavior of Basalt.
|
--- This is the UI Manager and the starting point for your project. The following functions allow you to influence the default behavior of Basalt.
|
||||||
@@ -123,27 +124,42 @@ function basalt.setActiveFrame(frame)
|
|||||||
mainFrame = frame
|
mainFrame = frame
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Schedules a function to be updated
|
--- Schedules a function to run in a coroutine
|
||||||
--- @shortDescription Schedules a function to be updated
|
--- @shortDescription Schedules a function to run in a coroutine
|
||||||
--- @function scheduleUpdate
|
--- @function scheduleUpdate
|
||||||
--- @param func function The function to schedule
|
--- @param func function The function to schedule
|
||||||
--- @return number Id The schedule ID
|
--- @return thread func The scheduled function
|
||||||
--- @usage local id = basalt.scheduleUpdate(myFunction)
|
--- @usage local id = basalt.scheduleUpdate(myFunction)
|
||||||
function basalt.scheduleUpdate(func)
|
function basalt.schedule(func)
|
||||||
table.insert(basalt._schedule, func)
|
expect(1, func, "function")
|
||||||
return #basalt._schedule
|
|
||||||
|
local co = coroutine.create(func)
|
||||||
|
local ok, result = coroutine.resume(co)
|
||||||
|
if(ok)then
|
||||||
|
table.insert(basalt._schedule, {coroutine=co, filter=result})
|
||||||
|
else
|
||||||
|
errorManager.header = "Basalt Schedule Error"
|
||||||
|
errorManager.error(result)
|
||||||
|
end
|
||||||
|
return co
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Removes a scheduled update
|
--- Removes a scheduled update
|
||||||
--- @shortDescription Removes a scheduled update
|
--- @shortDescription Removes a scheduled update
|
||||||
--- @function removeSchedule
|
--- @function removeSchedule
|
||||||
--- @param id number The schedule ID to remove
|
--- @param func thread The scheduled function to remove
|
||||||
|
--- @return boolean success Whether the scheduled function was removed
|
||||||
--- @usage basalt.removeSchedule(scheduleId)
|
--- @usage basalt.removeSchedule(scheduleId)
|
||||||
function basalt.removeSchedule(id)
|
function basalt.removeSchedule(func)
|
||||||
basalt._schedule[id] = nil
|
for i, v in ipairs(basalt._schedule) do
|
||||||
|
if(v.coroutine==func)then
|
||||||
|
table.remove(basalt._schedule, i)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
---@private
|
|
||||||
local function updateEvent(event, ...)
|
local function updateEvent(event, ...)
|
||||||
if(event=="terminate")then basalt.stop() end
|
if(event=="terminate")then basalt.stop() end
|
||||||
if lazyElementsEventHandler(event, ...) then return end
|
if lazyElementsEventHandler(event, ...) then return end
|
||||||
@@ -154,6 +170,19 @@ local function updateEvent(event, ...)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
for _, func in ipairs(basalt._schedule) do
|
||||||
|
if(event==func.filter)then
|
||||||
|
local ok, result = coroutine.resume(func.coroutine, event, ...)
|
||||||
|
if(not ok)then
|
||||||
|
errorManager.header = "Basalt Schedule Error"
|
||||||
|
errorManager.error(result)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if(coroutine.status(func.coroutine)=="dead")then
|
||||||
|
basalt.removeSchedule(func.coroutine)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if basalt._events[event] then
|
if basalt._events[event] then
|
||||||
for _, callback in ipairs(basalt._events[event]) do
|
for _, callback in ipairs(basalt._events[event]) do
|
||||||
callback(...)
|
callback(...)
|
||||||
@@ -161,22 +190,19 @@ local function updateEvent(event, ...)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---@private
|
|
||||||
local function renderFrames()
|
local function renderFrames()
|
||||||
if(mainFrame)then
|
if(mainFrame)then
|
||||||
mainFrame:render()
|
mainFrame:render()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Updates all scheduled functions
|
--- Runs basalt once
|
||||||
--- @shortDescription Updates all scheduled functions
|
--- @shortDescription Runs basalt once
|
||||||
|
--- @vararg any The event to run with
|
||||||
--- @usage basalt.update()
|
--- @usage basalt.update()
|
||||||
function basalt.update()
|
function basalt.update(...)
|
||||||
for k,v in pairs(basalt._schedule) do
|
updateEvent(...)
|
||||||
if type(v)=="function" then
|
renderFrames()
|
||||||
v()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Stops the Basalt runtime
|
--- Stops the Basalt runtime
|
||||||
|
|||||||
@@ -194,6 +194,9 @@ PropertySystem.addSetterHook(function(element, propertyName, value, config)
|
|||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
--- This module provides reactive functionality for elements, it adds no new functionality for elements.
|
||||||
|
--- It is used to evaluate expressions in property values and update the element when the expression changes.
|
||||||
|
---@class Reactive
|
||||||
local BaseElement = {}
|
local BaseElement = {}
|
||||||
|
|
||||||
BaseElement.hooks = {
|
BaseElement.hooks = {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
local colorChars = require("libraries/colorHex")
|
local colorChars = require("libraries/colorHex")
|
||||||
|
local log = require("log")
|
||||||
|
|
||||||
--- This is the render module for Basalt. It tries to mimic the functionality of the `term` API. but with additional
|
--- This is the render module for Basalt. It tries to mimic the functionality of the `term` API. but with additional
|
||||||
--- functionality. It also has a buffer system to reduce the number of calls
|
--- functionality. It also has a buffer system to reduce the number of calls
|
||||||
@@ -13,6 +14,8 @@ local colorChars = require("libraries/colorHex")
|
|||||||
local Render = {}
|
local Render = {}
|
||||||
Render.__index = Render
|
Render.__index = Render
|
||||||
|
|
||||||
|
local sub = string.sub
|
||||||
|
|
||||||
--- Creates a new Render object
|
--- Creates a new Render object
|
||||||
--- @param terminal table The terminal object to render to
|
--- @param terminal table The terminal object to render to
|
||||||
--- @return Render
|
--- @return Render
|
||||||
@@ -66,9 +69,9 @@ function Render:blit(x, y, text, fg, bg)
|
|||||||
error("Text, fg, and bg must be the same length")
|
error("Text, fg, and bg must be the same length")
|
||||||
end
|
end
|
||||||
|
|
||||||
self.buffer.text[y] = self.buffer.text[y]:sub(1,x-1) .. text .. self.buffer.text[y]:sub(x+#text)
|
self.buffer.text[y] = sub(self.buffer.text[y]:sub(1,x-1) .. text .. self.buffer.text[y]:sub(x+#text), 1, self.width)
|
||||||
self.buffer.fg[y] = self.buffer.fg[y]:sub(1,x-1) .. fg .. self.buffer.fg[y]:sub(x+#fg)
|
self.buffer.fg[y] = sub(self.buffer.fg[y]:sub(1,x-1) .. fg .. self.buffer.fg[y]:sub(x+#fg), 1, self.width)
|
||||||
self.buffer.bg[y] = self.buffer.bg[y]:sub(1,x-1) .. bg .. self.buffer.bg[y]:sub(x+#bg)
|
self.buffer.bg[y] = sub(self.buffer.bg[y]:sub(1,x-1) .. bg .. self.buffer.bg[y]:sub(x+#bg), 1, self.width)
|
||||||
self:addDirtyRect(x, y, #text, 1)
|
self:addDirtyRect(x, y, #text, 1)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@@ -96,9 +99,9 @@ function Render:multiBlit(x, y, width, height, text, fg, bg)
|
|||||||
for dy=0, height-1 do
|
for dy=0, height-1 do
|
||||||
local cy = y + dy
|
local cy = y + dy
|
||||||
if cy >= 1 and cy <= self.height then
|
if cy >= 1 and cy <= self.height then
|
||||||
self.buffer.text[cy] = self.buffer.text[cy]:sub(1,x-1) .. text .. self.buffer.text[cy]:sub(x+#text)
|
self.buffer.text[cy] = sub(self.buffer.text[cy]:sub(1,x-1) .. text .. self.buffer.text[cy]:sub(x+#text), 1, self.width)
|
||||||
self.buffer.fg[cy] = self.buffer.fg[cy]:sub(1,x-1) .. fg .. self.buffer.fg[cy]:sub(x+#fg)
|
self.buffer.fg[cy] = sub(self.buffer.fg[cy]:sub(1,x-1) .. fg .. self.buffer.fg[cy]:sub(x+#fg), 1, self.width)
|
||||||
self.buffer.bg[cy] = self.buffer.bg[cy]:sub(1,x-1) .. bg .. self.buffer.bg[cy]:sub(x+#bg)
|
self.buffer.bg[cy] = sub(self.buffer.bg[cy]:sub(1,x-1) .. bg .. self.buffer.bg[cy]:sub(x+#bg), 1, self.width)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -115,9 +118,10 @@ end
|
|||||||
function Render:textFg(x, y, text, fg)
|
function Render:textFg(x, y, text, fg)
|
||||||
if y < 1 or y > self.height then return self end
|
if y < 1 or y > self.height then return self end
|
||||||
fg = colorChars[fg] or "0"
|
fg = colorChars[fg] or "0"
|
||||||
|
fg = fg:rep(#text)
|
||||||
|
|
||||||
self.buffer.text[y] = self.buffer.text[y]:sub(1,x-1) .. text .. self.buffer.text[y]:sub(x+#text)
|
self.buffer.text[y] = sub(self.buffer.text[y]:sub(1,x-1) .. text .. self.buffer.text[y]:sub(x+#text), 1, self.width)
|
||||||
self.buffer.fg[y] = self.buffer.fg[y]:sub(1,x-1) .. fg:rep(#text) .. self.buffer.fg[y]:sub(x+#text)
|
self.buffer.fg[y] = sub(self.buffer.fg[y]:sub(1,x-1) .. fg .. self.buffer.fg[y]:sub(x+#fg), 1, self.width)
|
||||||
self:addDirtyRect(x, y, #text, 1)
|
self:addDirtyRect(x, y, #text, 1)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@@ -133,8 +137,8 @@ function Render:textBg(x, y, text, bg)
|
|||||||
if y < 1 or y > self.height then return self end
|
if y < 1 or y > self.height then return self end
|
||||||
bg = colorChars[bg] or "f"
|
bg = colorChars[bg] or "f"
|
||||||
|
|
||||||
self.buffer.text[y] = self.buffer.text[y]:sub(1,x-1) .. text .. self.buffer.text[y]:sub(x+#text)
|
self.buffer.text[y] = sub(self.buffer.text[y]:sub(1,x-1) .. text .. self.buffer.text[y]:sub(x+#text), 1, self.width)
|
||||||
self.buffer.bg[y] = self.buffer.bg[y]:sub(1,x-1) .. bg:rep(#text) .. self.buffer.bg[y]:sub(x+#text)
|
self.buffer.bg[y] = sub(self.buffer.bg[y]:sub(1,x-1) .. bg:rep(#text) .. self.buffer.bg[y]:sub(x+#text), 1, self.width)
|
||||||
self:addDirtyRect(x, y, #text, 1)
|
self:addDirtyRect(x, y, #text, 1)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@@ -148,7 +152,7 @@ end
|
|||||||
function Render:text(x, y, text)
|
function Render:text(x, y, text)
|
||||||
if y < 1 or y > self.height then return self end
|
if y < 1 or y > self.height then return self end
|
||||||
|
|
||||||
self.buffer.text[y] = self.buffer.text[y]:sub(1,x-1) .. text .. self.buffer.text[y]:sub(x+#text)
|
self.buffer.text[y] = sub(self.buffer.text[y]:sub(1,x-1) .. text .. self.buffer.text[y]:sub(x+#text), 1, self.width)
|
||||||
self:addDirtyRect(x, y, #text, 1)
|
self:addDirtyRect(x, y, #text, 1)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@@ -162,7 +166,7 @@ end
|
|||||||
function Render:fg(x, y, fg)
|
function Render:fg(x, y, fg)
|
||||||
if y < 1 or y > self.height then return self end
|
if y < 1 or y > self.height then return self end
|
||||||
|
|
||||||
self.buffer.fg[y] = self.buffer.fg[y]:sub(1,x-1) .. fg .. self.buffer.fg[y]:sub(x+#fg)
|
self.buffer.fg[y] = sub(self.buffer.fg[y]:sub(1,x-1) .. fg .. self.buffer.fg[y]:sub(x+#fg), 1, self.width)
|
||||||
self:addDirtyRect(x, y, #fg, 1)
|
self:addDirtyRect(x, y, #fg, 1)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@@ -176,7 +180,7 @@ end
|
|||||||
function Render:bg(x, y, bg)
|
function Render:bg(x, y, bg)
|
||||||
if y < 1 or y > self.height then return self end
|
if y < 1 or y > self.height then return self end
|
||||||
|
|
||||||
self.buffer.bg[y] = self.buffer.bg[y]:sub(1,x-1) .. bg .. self.buffer.bg[y]:sub(x+#bg)
|
self.buffer.bg[y] = sub(self.buffer.bg[y]:sub(1,x-1) .. bg .. self.buffer.bg[y]:sub(x+#bg), 1, self.width)
|
||||||
self:addDirtyRect(x, y, #bg, 1)
|
self:addDirtyRect(x, y, #bg, 1)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@@ -230,6 +234,7 @@ function Render:render()
|
|||||||
self.buffer.dirtyRects = {}
|
self.buffer.dirtyRects = {}
|
||||||
|
|
||||||
if self.blink then
|
if self.blink then
|
||||||
|
self.terminal.setTextColor(self.cursorColor)
|
||||||
self.terminal.setCursorPos(self.xCursor, self.yCursor)
|
self.terminal.setCursorPos(self.xCursor, self.yCursor)
|
||||||
self.terminal.setCursorBlink(true)
|
self.terminal.setCursorBlink(true)
|
||||||
else
|
else
|
||||||
@@ -271,12 +276,14 @@ end
|
|||||||
--- @param y number The y position of the cursor
|
--- @param y number The y position of the cursor
|
||||||
--- @param blink boolean Whether the cursor should blink
|
--- @param blink boolean Whether the cursor should blink
|
||||||
--- @return Render
|
--- @return Render
|
||||||
function Render:setCursor(x, y, blink)
|
function Render:setCursor(x, y, blink, color)
|
||||||
|
if color ~= nil then self.terminal.setTextColor(color) end
|
||||||
self.terminal.setCursorPos(x, y)
|
self.terminal.setCursorPos(x, y)
|
||||||
self.terminal.setCursorBlink(blink)
|
self.terminal.setCursorBlink(blink)
|
||||||
self.xCursor = x
|
self.xCursor = x
|
||||||
self.yCursor = y
|
self.yCursor = y
|
||||||
self.blink = blink
|
self.blink = blink
|
||||||
|
self.cursorColor = color
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -306,4 +313,15 @@ function Render:getSize()
|
|||||||
return self.width, self.height
|
return self.width, self.height
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Render:setSize(width, height)
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
for y=1, self.height do
|
||||||
|
self.buffer.text[y] = string.rep(" ", self.width)
|
||||||
|
self.buffer.fg[y] = string.rep("0", self.width)
|
||||||
|
self.buffer.bg[y] = string.rep("f", self.width)
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
return Render
|
return Render
|
||||||
Reference in New Issue
Block a user