Lot of bug fixxes
This commit is contained in:
@@ -34,12 +34,13 @@ BaseElement.defineProperty(BaseElement, "eventCallbacks", {default = {}, type =
|
||||
--- @shortDescription Registers an event that this class can listen to
|
||||
--- @param class table The class to add the event to
|
||||
--- @param eventName string The name of the event to register
|
||||
--- @param event? string The event to handle
|
||||
--- @usage BaseElement.listenTo(MyClass, "mouse_click")
|
||||
function BaseElement.listenTo(class, eventName)
|
||||
function BaseElement.listenTo(class, eventName, event)
|
||||
if not class._events then
|
||||
class._events = {}
|
||||
end
|
||||
class._events[eventName] = true
|
||||
class._events[eventName] = {enabled=true, name=eventName, event=event}
|
||||
end
|
||||
|
||||
--- Creates a new BaseElement instance
|
||||
@@ -64,13 +65,13 @@ function BaseElement:init(props, basalt)
|
||||
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)
|
||||
for key,event in pairs(BaseElement._events) do
|
||||
self._registeredEvents[event.event or event.name] = true
|
||||
local handlerName = "on" .. event.name:gsub("_(%l)", function(c)
|
||||
return c:upper()
|
||||
end):gsub("^%l", string.upper)
|
||||
self[handlerName] = function(self, ...)
|
||||
self:registerCallback(event, ...)
|
||||
self:registerCallback(event.name, ...)
|
||||
return self
|
||||
end
|
||||
end
|
||||
|
||||
@@ -93,9 +93,14 @@ end
|
||||
--- @param x number The x position to set the cursor to
|
||||
--- @param y number The y position to set the cursor to
|
||||
--- @param blink boolean Whether the cursor should blink
|
||||
function BaseFrame:setCursor(x, y, blink)
|
||||
function BaseFrame:setCursor(x, y, blink, color)
|
||||
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
|
||||
|
||||
--- Renders the Frame
|
||||
@@ -110,4 +115,15 @@ function BaseFrame:render()
|
||||
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
|
||||
@@ -3,8 +3,6 @@ local VisualElement = elementManager.getElement("VisualElement")
|
||||
local expect = require("libraries/expect")
|
||||
local split = require("libraries/utils").split
|
||||
|
||||
local max = math.max
|
||||
|
||||
--- The container class. It is a visual element that can contain other elements. It is the base class for all containers,
|
||||
--- like Frames, BaseFrames, and more.
|
||||
---@class Container : VisualElement
|
||||
@@ -303,7 +301,7 @@ local function convertMousePosition(self, event, ...)
|
||||
return args
|
||||
end
|
||||
|
||||
local function callChildrenEvents(self, visibleOnly, event, ...)
|
||||
function Container:callChildrenEvents(visibleOnly, event, ...)
|
||||
local children = visibleOnly and self.get("visibleChildrenEvents") or self.get("childrenEvents")
|
||||
if children[event] then
|
||||
local events = children[event]
|
||||
@@ -325,7 +323,7 @@ end
|
||||
function Container:handleEvent(event, ...)
|
||||
VisualElement.handleEvent(self, event, ...)
|
||||
local args = convertMousePosition(self, event, ...)
|
||||
return callChildrenEvents(self, false, event, table.unpack(args))
|
||||
return self:callChildrenEvents(false, event, table.unpack(args))
|
||||
end
|
||||
|
||||
--- Handles mouse click events
|
||||
@@ -337,7 +335,7 @@ end
|
||||
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))
|
||||
local success, child = self:callChildrenEvents(true, "mouse_click", table.unpack(args))
|
||||
if(success)then
|
||||
self.set("focusedChild", child)
|
||||
return true
|
||||
@@ -357,7 +355,7 @@ end
|
||||
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))
|
||||
local success, child = self:callChildrenEvents(true, "mouse_up", table.unpack(args))
|
||||
if(success)then
|
||||
return true
|
||||
end
|
||||
@@ -365,6 +363,45 @@ function Container:mouse_up(button, x, y)
|
||||
return false
|
||||
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
|
||||
--- @shortDescription Handles key events
|
||||
--- @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
|
||||
|
||||
VisualElement.textFg(self, math.max(1, x), math.max(1, y), text:sub(textStart, textStart + textLen - 1), fg)
|
||||
return self
|
||||
end
|
||||
|
||||
--- 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"})
|
||||
---@property pattern string? nil Regular expression pattern for input validation
|
||||
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, "key")
|
||||
@@ -106,7 +108,7 @@ function Input:key(key)
|
||||
end
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
@@ -134,7 +136,7 @@ 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:setCursor(math.min(relX, #text + 1), relY, true, self.get("cursorColor") or self.get("foreground"))
|
||||
self:set("cursorPos", relX + self.get("viewOffset"))
|
||||
return true
|
||||
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"})
|
||||
---@property syntaxPatterns table {} Syntax highlighting patterns
|
||||
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, "key")
|
||||
@@ -41,7 +43,7 @@ end
|
||||
|
||||
--- Adds a new syntax highlighting pattern
|
||||
--- @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)
|
||||
table.insert(self.get("syntaxPatterns"), {pattern = pattern, color = color})
|
||||
return self
|
||||
@@ -162,7 +164,14 @@ end
|
||||
function TextBox:mouse_scroll(direction, x, y)
|
||||
if self:isInBounds(x, y) then
|
||||
local scrollY = self.get("scrollY")
|
||||
self.set("scrollY", math.max(0, scrollY + direction))
|
||||
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()
|
||||
return true
|
||||
end
|
||||
@@ -182,16 +191,22 @@ function TextBox:mouse_click(button, x, y)
|
||||
self.set("cursorY", targetY)
|
||||
self.set("cursorX", math.min(relX + scrollX, #lines[targetY] + 1))
|
||||
end
|
||||
self:updateRender()
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function TextBox:setText(text)
|
||||
self.set("lines", {""})
|
||||
for line in text:gmatch("[^\n]+") do
|
||||
table.insert(self.get("lines"), line)
|
||||
local lines = {}
|
||||
if text == "" then
|
||||
lines = {""}
|
||||
else
|
||||
for line in (text.."\n"):gmatch("([^\n]*)\n") do
|
||||
table.insert(lines, line)
|
||||
end
|
||||
end
|
||||
self.set("lines", lines)
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -244,7 +259,7 @@ function TextBox:render()
|
||||
local relativeX = self.get("cursorX") - scrollX
|
||||
local relativeY = self.get("cursorY") - scrollY
|
||||
if relativeX >= 1 and relativeX <= width and relativeY >= 1 and relativeY <= height then
|
||||
self:setCursor(relativeX, relativeY, true)
|
||||
self:setCursor(relativeX, relativeY, true, self.get("cursorColor") or self.get("foreground"))
|
||||
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})
|
||||
---@property expandedNodes table {} Table of nodes that are currently expanded
|
||||
Tree.defineProperty(Tree, "expandedNodes", {default = {}, type = "table", canTriggerRender = true})
|
||||
---@property scrollOffset number 0 Current scroll position
|
||||
---@property scrollOffset number 0 Current vertical scroll position
|
||||
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
|
||||
Tree.defineProperty(Tree, "nodeColor", {default = colors.white, type = "number"})
|
||||
---@property selectedColor color lightBlue Background color of selected node
|
||||
@@ -106,24 +108,24 @@ local function flattenTree(nodes, expandedNodes, level, result)
|
||||
end
|
||||
|
||||
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)
|
||||
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 flatNodes[visibleIndex] then
|
||||
local nodeInfo = flatNodes[visibleIndex]
|
||||
local node = nodeInfo.node
|
||||
if relX <= nodeInfo.level * 2 + 2 then
|
||||
self:toggleNode(node)
|
||||
end
|
||||
|
||||
if relX <= nodeInfo.level * 2 + 2 then
|
||||
self:toggleNode(node)
|
||||
self.set("selectedNode", node)
|
||||
self:fireEvent("node_select", node)
|
||||
end
|
||||
|
||||
self.set("selectedNode", node)
|
||||
self:fireEvent("node_select", node)
|
||||
return true
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function Tree:onSelect(callback)
|
||||
@@ -131,13 +133,25 @@ function Tree:onSelect(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))
|
||||
function Tree:mouse_scroll(direction, x, y)
|
||||
if VisualElement.mouse_scroll(self, direction, x, y) then
|
||||
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
|
||||
self.set("scrollOffset", newScroll)
|
||||
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
|
||||
|
||||
function Tree:render()
|
||||
@@ -148,6 +162,7 @@ function Tree:render()
|
||||
local selectedNode = self.get("selectedNode")
|
||||
local expandedNodes = self.get("expandedNodes")
|
||||
local scrollOffset = self.get("scrollOffset")
|
||||
local horizontalOffset = self.get("horizontalOffset")
|
||||
|
||||
for y = 1, height do
|
||||
local nodeInfo = flatNodes[y + scrollOffset]
|
||||
@@ -162,11 +177,10 @@ function Tree:render()
|
||||
end
|
||||
|
||||
local bg = node == selectedNode and self.get("selectedColor") or self.get("background")
|
||||
local text = indent .. symbol .." " .. (node.text or "Node")
|
||||
text = sub(text, 1, self.get("width"))
|
||||
local fullText = indent .. symbol .." " .. (node.text or "Node")
|
||||
local text = sub(fullText, horizontalOffset + 1, horizontalOffset + self.get("width"))
|
||||
|
||||
self:textFg(1, y, text .. string.rep(" ", self.get("width") - #text), self.get("foreground"))
|
||||
|
||||
else
|
||||
self:textFg(1, y, string.rep(" ", self.get("width")), self.get("foreground"), self.get("background"))
|
||||
end
|
||||
|
||||
@@ -30,6 +30,8 @@ VisualElement.defineProperty(VisualElement, "background", {default = colors.blac
|
||||
VisualElement.defineProperty(VisualElement, "foreground", {default = colors.white, type = "number", canTriggerRender = true})
|
||||
---@property clicked boolean false Whether the element is currently clicked
|
||||
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
|
||||
VisualElement.defineProperty(VisualElement, "backgroundEnabled", {default = true, type = "boolean", canTriggerRender = true})
|
||||
---@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, "blur")
|
||||
VisualElement.listenTo(VisualElement, "mouse_enter", "mouse_move")
|
||||
VisualElement.listenTo(VisualElement, "mouse_leave", "mouse_move")
|
||||
|
||||
local max, min = math.max, math.min
|
||||
|
||||
@@ -185,12 +189,12 @@ end
|
||||
--- @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)
|
||||
self.set("clicked", false)
|
||||
self:fireEvent("mouse_up", button, self:getRelativePosition(x, y))
|
||||
return true
|
||||
end
|
||||
self:fireEvent("mouse_release", button, self:getRelativePosition(x, y))
|
||||
return false
|
||||
end
|
||||
|
||||
--- Handles a mouse release event
|
||||
@@ -198,11 +202,42 @@ end
|
||||
--- @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)
|
||||
self:fireEvent("mouse_release", button, self:getRelativePosition(x, y))
|
||||
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
|
||||
end
|
||||
return false
|
||||
@@ -272,11 +307,11 @@ end
|
||||
--- @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)
|
||||
function VisualElement:setCursor(x, y, blink, color)
|
||||
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)
|
||||
return self.parent:setCursor(absX, absY, blink, color)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user