- Added monitor support
- Fixed states example - Small changes to Display element
This commit is contained in:
@@ -1,111 +1,54 @@
|
||||
local basalt = require("basalt")
|
||||
|
||||
-- Create main frame
|
||||
local main = basalt.getMainFrame()
|
||||
-- Initialize form states
|
||||
:initializeState("username", "", true) -- make them persistent
|
||||
:initializeState("password", "", true) -- make them persistent
|
||||
:initializeState("confirmPassword", "", true) -- make them persistent
|
||||
|
||||
-- Create a complex form using states for managing its data
|
||||
local form = main:addFrame()
|
||||
:setSize("{parent.width - 4}", "{parent.height - 4}")
|
||||
:setPosition(3, 3)
|
||||
:setBackground(colors.lightGray)
|
||||
-- Initialize form states with default values
|
||||
:initializeState("username", "", true) -- true = triggers render update
|
||||
:initializeState("email", "")
|
||||
:initializeState("age", 0)
|
||||
:initializeState("submitted", false)
|
||||
-- Add computed state for form validation
|
||||
:computed("isValid", function(self)
|
||||
local username = self:getState("username")
|
||||
local email = self:getState("email")
|
||||
local age = self:getState("age")
|
||||
return #username > 0 and email:match(".+@.+") and age > 0
|
||||
end)
|
||||
|
||||
-- Create form title
|
||||
form:addLabel()
|
||||
:setText("Registration Form")
|
||||
:setPosition(2, 2)
|
||||
:setForeground(colors.black)
|
||||
-- Add computed validation state
|
||||
form:computed("isValid", function(self)
|
||||
local username = self:getState("username")
|
||||
local password = self:getState("password")
|
||||
local confirmPass = self:getState("confirmPassword")
|
||||
return #username >= 3 and #password >= 6 and password == confirmPass
|
||||
end)
|
||||
|
||||
-- Username input with state binding
|
||||
local usernameInput = form:addInput()
|
||||
:setPosition(2, 4)
|
||||
:setSize(20, 1)
|
||||
:setBackground(colors.white)
|
||||
:setForeground(colors.black)
|
||||
-- Update username state when input changes
|
||||
:onChange(function(self, value)
|
||||
form:setState("username", value)
|
||||
end)
|
||||
-- Create labels
|
||||
form:addLabel({text="Username:", x = 2, y = 2, foreground = colors.lightGray})
|
||||
form:addLabel({text="Password:", x = 2, y = 4, foreground = colors.lightGray})
|
||||
form:addLabel({text="Confirm:", x = 2, y = 6, foreground = colors.lightGray})
|
||||
|
||||
form:addLabel()
|
||||
:setText("Username:")
|
||||
:setPosition(2, 3)
|
||||
:setForeground(colors.black)
|
||||
local userInput = form:addInput({x = 11, y = 2, width = 20, height = 1}):bind("text", "username")
|
||||
local passwordInput = form:addInput({x = 11, y = 4, width = 20, height = 1}):bind("text", "password")
|
||||
local confirmInput = form:addInput({x = 11, y = 6, width = 20, height = 1}):bind("text", "confirmPassword")
|
||||
|
||||
-- Email input
|
||||
local emailInput = form:addInput()
|
||||
:setPosition(2, 6)
|
||||
:setSize(20, 1)
|
||||
:setBackground(colors.white)
|
||||
:setForeground(colors.black)
|
||||
:onChange(function(self, value)
|
||||
form:setState("email", value)
|
||||
end)
|
||||
|
||||
form:addLabel()
|
||||
:setText("Email:")
|
||||
:setPosition(2, 5)
|
||||
:setForeground(colors.black)
|
||||
|
||||
-- Age input
|
||||
local ageInput = form:addInput()
|
||||
:setPosition(2, 8)
|
||||
:setSize(5, 1)
|
||||
:setBackground(colors.white)
|
||||
:setForeground(colors.black)
|
||||
:onChange(function(self, value)
|
||||
-- Convert to number and update state
|
||||
form:setState("age", tonumber(value) or 0)
|
||||
end)
|
||||
|
||||
form:addLabel()
|
||||
:setText("Age:")
|
||||
:setPosition(2, 7)
|
||||
:setForeground(colors.black)
|
||||
|
||||
-- Submit button that reacts to form validity
|
||||
-- Submit button
|
||||
local submitBtn = form:addButton()
|
||||
:setPosition(2, 10)
|
||||
:setSize(10, 1)
|
||||
:setText("Submit")
|
||||
-- Button color changes based on form validity
|
||||
:onStateChange("isValid", function(self, isValid)
|
||||
submitBtn:setBackground(isValid and colors.lime or colors.gray)
|
||||
end)
|
||||
:onMouseClick(function()
|
||||
if form.computedStates.isValid() then
|
||||
form:setState("submitted", true)
|
||||
end
|
||||
end)
|
||||
:setPosition(2, 8)
|
||||
:setSize(29, 1)
|
||||
|
||||
-- Status message that updates when form is submitted
|
||||
-- Status label
|
||||
local statusLabel = form:addLabel()
|
||||
:setPosition(2, 12)
|
||||
:setForeground(colors.black)
|
||||
:setPosition(2, 10)
|
||||
:setSize(29, 1)
|
||||
|
||||
-- Listen for form submission
|
||||
form:onStateChange("submitted", function(self, submitted)
|
||||
if submitted then
|
||||
local username = self:getState("username")
|
||||
local email = self:getState("email")
|
||||
local age = self:getState("age")
|
||||
statusLabel:setText(string.format(
|
||||
"Submitted: %s (%s) - Age: %d",
|
||||
username, email, age
|
||||
))
|
||||
|
||||
form:onStateChange("isValid", function(self, isValid)
|
||||
if isValid then
|
||||
statusLabel:setText("Form is valid!")
|
||||
:setForeground(colors.green)
|
||||
submitBtn:setBackground(colors.green)
|
||||
else
|
||||
statusLabel:setText("Please fill all fields correctly")
|
||||
:setForeground(colors.red)
|
||||
submitBtn:setBackground(colors.red)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Run basalt
|
||||
basalt.run()
|
||||
basalt.run()
|
||||
@@ -1,5 +1,6 @@
|
||||
local elementManager = require("elementManager")
|
||||
local Container = elementManager.getElement("Container")
|
||||
local errorManager = require("errorManager")
|
||||
local Render = require("render")
|
||||
---@configDescription This is the base frame class. It is the root element of all elements and the only element without a parent.
|
||||
|
||||
@@ -8,14 +9,39 @@ local Render = require("render")
|
||||
---@class BaseFrame : Container
|
||||
---@field _render Render The render object
|
||||
---@field _renderUpdate boolean Whether the render object needs to be updated
|
||||
---@field _peripheralName string The name of a peripheral
|
||||
local BaseFrame = setmetatable({}, Container)
|
||||
BaseFrame.__index = BaseFrame
|
||||
|
||||
---@property text term nil The terminal object to render to
|
||||
local function isPeripheral(t)
|
||||
local ok, result = pcall(function()
|
||||
return peripheral.getType(t)
|
||||
end)
|
||||
if ok then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
---@property term term|peripheral term.current() The terminal or (monitor) peripheral object to render to
|
||||
BaseFrame.defineProperty(BaseFrame, "term", {default = nil, type = "table", setter = function(self, value)
|
||||
self._peripheralName = nil
|
||||
if self.basalt.getActiveFrame(self._values.term)==self then
|
||||
self.basalt.setActiveFrame(self, false)
|
||||
end
|
||||
if value == nil or value.setCursorPos == nil then
|
||||
return value
|
||||
end
|
||||
|
||||
if(isPeripheral(value)) then
|
||||
self._peripheralName = peripheral.getName(value)
|
||||
end
|
||||
|
||||
self._values.term = value
|
||||
if self.basalt.getActiveFrame(value) == nil then
|
||||
self.basalt.setActiveFrame(self)
|
||||
end
|
||||
|
||||
self._render = Render.new(value)
|
||||
self._renderUpdate = true
|
||||
local width, height = value.getSize()
|
||||
@@ -105,10 +131,39 @@ end
|
||||
--- @param y number The y position to set the cursor to
|
||||
--- @param blink boolean Whether the cursor should blink
|
||||
function BaseFrame:setCursor(x, y, blink, color)
|
||||
local term = self.get("term")
|
||||
local _term = self.get("term")
|
||||
self._render:setCursor(x, y, blink, color)
|
||||
end
|
||||
|
||||
--- @shortDescription Handles monitor touch events
|
||||
--- @param name string The name of the monitor that was touched
|
||||
--- @param x number The x position of the mouse
|
||||
--- @param y number The y position of the mouse
|
||||
--- @protected
|
||||
function BaseFrame:monitor_touch(name, x, y)
|
||||
local _term = self.get("term")
|
||||
if _term == nil then return end
|
||||
if(isPeripheral(_term))then
|
||||
if self._peripheralName == name then
|
||||
self:mouse_click(0, x, y)
|
||||
self.basalt.schedule(function()
|
||||
sleep(0.1)
|
||||
self:mouse_up(0, x, y)
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- @shortDescription Handles mouse click events
|
||||
--- @param button number The button that was clicked
|
||||
--- @param x number The x position of the mouse
|
||||
--- @param y number The y position of the mouse
|
||||
--- @protected
|
||||
function BaseFrame:mouse_click(button, x, y)
|
||||
Container.mouse_click(self, button, x, y)
|
||||
self.basalt.setFocus(self)
|
||||
end
|
||||
|
||||
--- @shortDescription Handles mouse up events
|
||||
--- @param button number The button that was released
|
||||
--- @param x number The x position of the mouse
|
||||
@@ -156,6 +211,17 @@ function BaseFrame:char(char)
|
||||
Container.char(self, char)
|
||||
end
|
||||
|
||||
function BaseFrame:dispatchEvent(event, ...)
|
||||
local _term = self.get("term")
|
||||
if _term == nil then return end
|
||||
if(isPeripheral(_term))then
|
||||
if event == "mouse_click" then
|
||||
return
|
||||
end
|
||||
end
|
||||
Container.dispatchEvent(self, event, ...)
|
||||
end
|
||||
|
||||
--- @shortDescription Renders the Frame
|
||||
--- @protected
|
||||
function BaseFrame:render()
|
||||
|
||||
@@ -2,7 +2,8 @@ local elementManager = require("elementManager")
|
||||
local VisualElement = elementManager.getElement("VisualElement")
|
||||
local getCenteredPosition = require("libraries/utils").getCenteredPosition
|
||||
local deepcopy = require("libraries/utils").deepcopy
|
||||
---@cofnigDescription The Display is a special element which uses the cc window API which you can use.
|
||||
local colorHex = require("libraries/colorHex")
|
||||
---@configDescription The Display is a special element which uses the cc window API which you can use.
|
||||
---@configDefault false
|
||||
|
||||
--- The Display is a special element where you can use the window (term) API to draw on a element, useful when you need to use external APIs.
|
||||
@@ -30,6 +31,8 @@ function Display:init(props, basalt)
|
||||
self.set("type", "Display")
|
||||
self._window = window.create(basalt.getActiveFrame():getTerm(), 1, 1, self.get("width"), self.get("height"), false)
|
||||
local reposition = self._window.reposition
|
||||
local blit = self._window.blit
|
||||
local write = self._window.write
|
||||
self._window.reposition = function(self, x, y, width, height)
|
||||
self.set("x", x)
|
||||
self.set("y", y)
|
||||
@@ -49,6 +52,14 @@ function Display:init(props, basalt)
|
||||
self._window.isVisible = function(self)
|
||||
return self.get("visible")
|
||||
end
|
||||
self._window.blit = function(self, x, y, text, fg, bg)
|
||||
blit(self, x, y, text, fg, bg)
|
||||
self:updateRender()
|
||||
end
|
||||
self._window.write = function(self, x, y, text)
|
||||
write(self, x, y, text)
|
||||
self:updateRender()
|
||||
end
|
||||
|
||||
self:observe("width", function(self, width)
|
||||
local window = self._window
|
||||
@@ -71,6 +82,30 @@ function Display:getWindow()
|
||||
return self._window
|
||||
end
|
||||
|
||||
--- Writes text to the display at the given position with the given foreground and background colors
|
||||
--- @shortDescription Writes text to the display
|
||||
--- @param x number The x position to write to
|
||||
--- @param y number The y position to write to
|
||||
--- @param text string The text to write
|
||||
--- @param fg? colors The foreground color (optional)
|
||||
--- @param bg? colors The background color (optional)
|
||||
--- @return Display self The display instance
|
||||
function Display:write(x, y, text, fg, bg)
|
||||
local window = self._window
|
||||
if window then
|
||||
if fg then
|
||||
window.setTextColor(fg)
|
||||
end
|
||||
if bg then
|
||||
window.setBackgroundColor(bg)
|
||||
end
|
||||
window.setCursorPos(x, y)
|
||||
window.write(text)
|
||||
end
|
||||
self:updateRender()
|
||||
return self
|
||||
end
|
||||
|
||||
--- @shortDescription Renders the Display
|
||||
--- @protected
|
||||
function Display:render()
|
||||
|
||||
105
src/main.lua
105
src/main.lua
@@ -29,8 +29,9 @@ else
|
||||
basalt.path = fs.getDir(select(2, ...))
|
||||
end
|
||||
|
||||
local mainFrame = nil
|
||||
local activeFrame = nil
|
||||
local main = nil
|
||||
local focusedFrame = nil
|
||||
local activeFrames = {}
|
||||
local _type = type
|
||||
|
||||
local lazyElements = {}
|
||||
@@ -94,52 +95,79 @@ end
|
||||
--- Creates and returns a new BaseFrame
|
||||
--- @shortDescription Creates a new BaseFrame
|
||||
--- @return BaseFrame BaseFrame The created frame instance
|
||||
--- @usage local mainFrame = basalt.createFrame()
|
||||
function basalt.createFrame()
|
||||
local frame = basalt.create("BaseFrame")
|
||||
frame:postInit()
|
||||
if(mainFrame==nil)then
|
||||
mainFrame = frame
|
||||
activeFrame = frame
|
||||
end
|
||||
return frame
|
||||
end
|
||||
|
||||
--- Returns the element manager instance
|
||||
--- @shortDescription Returns the element manager
|
||||
--- @return table ElementManager The element manager
|
||||
--- @usage local manager = basalt.getElementManager()
|
||||
function basalt.getElementManager()
|
||||
return elementManager
|
||||
end
|
||||
|
||||
--- Returns the error manager instance
|
||||
--- @shortDescription Returns the error manager
|
||||
--- @return table ErrorManager The error manager
|
||||
function basalt.getErrorManager()
|
||||
return errorManager
|
||||
end
|
||||
|
||||
--- Gets or creates the main frame
|
||||
--- @shortDescription Gets or creates the main frame
|
||||
--- @return BaseFrame BaseFrame The main frame instance
|
||||
--- @usage local frame = basalt.getMainFrame()
|
||||
function basalt.getMainFrame()
|
||||
if(mainFrame == nil)then
|
||||
mainFrame = basalt.createFrame()
|
||||
activeFrame = mainFrame
|
||||
local _main = tostring(term.current())
|
||||
if(activeFrames[_main] == nil)then
|
||||
main = _main
|
||||
basalt.createFrame()
|
||||
end
|
||||
return mainFrame
|
||||
return activeFrames[_main]
|
||||
end
|
||||
|
||||
--- Sets the active frame
|
||||
--- @shortDescription Sets the active frame
|
||||
--- @param frame BaseFrame The frame to set as active
|
||||
--- @usage basalt.setActiveFrame(myFrame)
|
||||
function basalt.setActiveFrame(frame)
|
||||
activeFrame = frame
|
||||
activeFrame:updateRender()
|
||||
--- @param setActive? boolean Whether to set the frame as active (default: true)
|
||||
function basalt.setActiveFrame(frame, setActive)
|
||||
local t = frame:getTerm()
|
||||
if(setActive==nil)then setActive = true end
|
||||
if(t~=nil)then
|
||||
activeFrames[tostring(t)] = setActive and frame or nil
|
||||
frame:updateRender()
|
||||
end
|
||||
end
|
||||
|
||||
--- Returns the active frame
|
||||
--- @shortDescription Returns the active frame
|
||||
--- @param t? term The term to get the active frame for (default: current term)
|
||||
--- @return BaseFrame? BaseFrame The frame to set as active
|
||||
--- @usage local frame = basalt.getActiveFrame()
|
||||
function basalt.getActiveFrame()
|
||||
return activeFrame
|
||||
function basalt.getActiveFrame(t)
|
||||
if(t==nil)then t = term.current() end
|
||||
return activeFrames[tostring(t)]
|
||||
end
|
||||
|
||||
--- Sets a frame as focused
|
||||
--- @shortDescription Sets a frame as focused
|
||||
--- @param frame BaseFrame The frame to set as focused
|
||||
function basalt.setFocus(frame)
|
||||
if(focusedFrame==frame)then return end
|
||||
if(focusedFrame~=nil)then
|
||||
focusedFrame:dispatchEvent("blur")
|
||||
end
|
||||
focusedFrame = frame
|
||||
if(focusedFrame~=nil)then
|
||||
focusedFrame:dispatchEvent("focus")
|
||||
end
|
||||
end
|
||||
|
||||
--- Returns the focused frame
|
||||
--- @shortDescription Returns the focused frame
|
||||
--- @return BaseFrame? BaseFrame The focused frame
|
||||
function basalt.getFocus()
|
||||
return focusedFrame
|
||||
end
|
||||
|
||||
--- Schedules a function to run in a coroutine
|
||||
@@ -147,7 +175,6 @@ end
|
||||
--- @function scheduleUpdate
|
||||
--- @param func function The function to schedule
|
||||
--- @return thread func The scheduled function
|
||||
--- @usage local id = basalt.scheduleUpdate(myFunction)
|
||||
function basalt.schedule(func)
|
||||
expect(1, func, "function")
|
||||
|
||||
@@ -167,7 +194,6 @@ end
|
||||
--- @function removeSchedule
|
||||
--- @param func thread The scheduled function to remove
|
||||
--- @return boolean success Whether the scheduled function was removed
|
||||
--- @usage basalt.removeSchedule(scheduleId)
|
||||
function basalt.removeSchedule(func)
|
||||
for i, v in ipairs(basalt._schedule) do
|
||||
if(v.coroutine==func)then
|
||||
@@ -178,15 +204,36 @@ function basalt.removeSchedule(func)
|
||||
return false
|
||||
end
|
||||
|
||||
local mouseEvents = {
|
||||
mouse_click = true,
|
||||
mouse_up = true,
|
||||
mouse_scroll = true,
|
||||
mouse_drag = true,
|
||||
}
|
||||
|
||||
local keyEvents = {
|
||||
key = true,
|
||||
key_up = true,
|
||||
char = true,
|
||||
}
|
||||
|
||||
local function updateEvent(event, ...)
|
||||
if(event=="terminate")then basalt.stop() end
|
||||
if lazyElementsEventHandler(event, ...) then return end
|
||||
|
||||
if(activeFrame)then
|
||||
activeFrame:dispatchEvent(event, ...)
|
||||
if(mouseEvents[event])then
|
||||
activeFrames[main]:dispatchEvent(event, ...)
|
||||
elseif(keyEvents[event])then
|
||||
if(focusedFrame~=nil)then
|
||||
focusedFrame:dispatchEvent(event, ...)
|
||||
end
|
||||
else
|
||||
for _, frame in pairs(activeFrames) do
|
||||
frame:dispatchEvent(event, ...)
|
||||
end
|
||||
end
|
||||
|
||||
for k, func in ipairs(basalt._schedule) do
|
||||
for _, func in ipairs(basalt._schedule) do
|
||||
if(event==func.filter)or(func.filter==nil)then
|
||||
local ok, result = coroutine.resume(func.coroutine, event, ...)
|
||||
if(not ok)then
|
||||
@@ -208,15 +255,14 @@ local function updateEvent(event, ...)
|
||||
end
|
||||
|
||||
local function renderFrames()
|
||||
if(activeFrame)then
|
||||
activeFrame:render()
|
||||
for _, frame in pairs(activeFrames)do
|
||||
frame:render()
|
||||
end
|
||||
end
|
||||
|
||||
--- Runs basalt once, can be used to update the UI manually, but you have to feed it the events
|
||||
--- @shortDescription Runs basalt once
|
||||
--- @vararg any The event to run with
|
||||
--- @usage basalt.update()
|
||||
function basalt.update(...)
|
||||
local f = function(...)
|
||||
updateEvent(...)
|
||||
@@ -231,7 +277,6 @@ end
|
||||
|
||||
--- Stops the Basalt runtime
|
||||
--- @shortDescription Stops the Basalt runtime
|
||||
--- @usage basalt.stop()
|
||||
function basalt.stop()
|
||||
basalt.isRunning = false
|
||||
term.clear()
|
||||
@@ -241,8 +286,6 @@ end
|
||||
--- Starts the Basalt runtime
|
||||
--- @shortDescription Starts the Basalt runtime
|
||||
--- @param isActive? boolean Whether to start active (default: true)
|
||||
--- @usage basalt.run()
|
||||
--- @usage basalt.run(false)
|
||||
function basalt.run(isActive)
|
||||
if(basalt.isRunning)then errorManager.error("Basalt is already running") end
|
||||
if(isActive==nil)then
|
||||
|
||||
Reference in New Issue
Block a user