Merge branch 'main' of https://github.com/Pyroxenium/Basalt2
This commit is contained in:
7579
BasaltLS.lua
7579
BasaltLS.lua
File diff suppressed because it is too large
Load Diff
786
config.lua
786
config.lua
@@ -1,440 +1,440 @@
|
||||
return {
|
||||
["metadata"] = {
|
||||
["generated"] = "Mon Sep 15 08:45:49 2025",
|
||||
["version"] = "2.0",
|
||||
["generated"] = "Mon Sep 29 18:10:08 2025",
|
||||
},
|
||||
["categories"] = {
|
||||
["plugins"] = {
|
||||
["files"] = {
|
||||
["reactive"] = {
|
||||
["path"] = "plugins/reactive.lua",
|
||||
["size"] = 7580,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
["benchmark"] = {
|
||||
["path"] = "plugins/benchmark.lua",
|
||||
["size"] = 12581,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
["debug"] = {
|
||||
["path"] = "plugins/debug.lua",
|
||||
["size"] = 6250,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
["xml"] = {
|
||||
["path"] = "plugins/xml.lua",
|
||||
["size"] = 9940,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
["state"] = {
|
||||
["path"] = "plugins/state.lua",
|
||||
["size"] = 6896,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
[1] = "Container",
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
["theme"] = {
|
||||
["path"] = "plugins/theme.lua",
|
||||
["size"] = 7042,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
["animation"] = {
|
||||
["path"] = "plugins/animation.lua",
|
||||
["size"] = 18421,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
["canvas"] = {
|
||||
["path"] = "plugins/canvas.lua",
|
||||
["size"] = 7873,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
},
|
||||
["description"] = "Plugins",
|
||||
},
|
||||
["core"] = {
|
||||
["files"] = {
|
||||
["log"] = {
|
||||
["path"] = "log.lua",
|
||||
["size"] = 3142,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
["errorManager"] = {
|
||||
["path"] = "errorManager.lua",
|
||||
["size"] = 3789,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
["render"] = {
|
||||
["path"] = "render.lua",
|
||||
["size"] = 12422,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
["propertySystem"] = {
|
||||
["path"] = "propertySystem.lua",
|
||||
["size"] = 15524,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
["main"] = {
|
||||
["path"] = "main.lua",
|
||||
["size"] = 14085,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
["init"] = {
|
||||
["path"] = "init.lua",
|
||||
["size"] = 622,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
["elementManager"] = {
|
||||
["path"] = "elementManager.lua",
|
||||
["size"] = 6297,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
},
|
||||
["description"] = "Core Files",
|
||||
},
|
||||
["libraries"] = {
|
||||
["files"] = {
|
||||
["expect"] = {
|
||||
["path"] = "libraries/expect.lua",
|
||||
["size"] = 846,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
["utils"] = {
|
||||
["path"] = "libraries/utils.lua",
|
||||
["size"] = 2661,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
["colorHex"] = {
|
||||
["path"] = "libraries/colorHex.lua",
|
||||
["size"] = 132,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
},
|
||||
["description"] = "Libraries",
|
||||
},
|
||||
["elements"] = {
|
||||
["files"] = {
|
||||
["Container"] = {
|
||||
["path"] = "elements/Container.lua",
|
||||
["size"] = 25383,
|
||||
["description"] = "The container class. It is a visual element that can contain other elements. It is the base class for all containers",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
["BigFont"] = {
|
||||
["path"] = "elements/BigFont.lua",
|
||||
["size"] = 21049,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["default"] = false,
|
||||
},
|
||||
["VisualElement"] = {
|
||||
["path"] = "elements/VisualElement.lua",
|
||||
["size"] = 18564,
|
||||
["description"] = "The Visual Element class which is the base class for all visual UI elements",
|
||||
["requires"] = {
|
||||
[1] = "BaseElement",
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
["FlexBox"] = {
|
||||
["path"] = "elements/FlexBox.lua",
|
||||
["size"] = 32431,
|
||||
["description"] = "A flexbox container that arranges its children in a flexible layout.",
|
||||
["requires"] = {
|
||||
[1] = "Container",
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
["Tree"] = {
|
||||
["path"] = "elements/Tree.lua",
|
||||
["size"] = 7942,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
["BarChart"] = {
|
||||
["path"] = "elements/BarChart.lua",
|
||||
["size"] = 3190,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = false,
|
||||
},
|
||||
["Input"] = {
|
||||
["path"] = "elements/Input.lua",
|
||||
["size"] = 9558,
|
||||
["description"] = "A text input field with various features",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
["Graph"] = {
|
||||
["path"] = "elements/Graph.lua",
|
||||
["size"] = 6989,
|
||||
["description"] = "A point based graph element",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = false,
|
||||
},
|
||||
["Label"] = {
|
||||
["path"] = "elements/Label.lua",
|
||||
["size"] = 3092,
|
||||
["description"] = "A simple text display element that automatically resizes its width based on the text content.",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
["Program"] = {
|
||||
["path"] = "elements/Program.lua",
|
||||
["size"] = 11430,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
["CheckBox"] = {
|
||||
["path"] = "elements/CheckBox.lua",
|
||||
["size"] = 2928,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
["Switch"] = {
|
||||
["path"] = "elements/Switch.lua",
|
||||
["size"] = 3269,
|
||||
["description"] = "The Switch is a standard Switch element with click handling and state management.",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
["Display"] = {
|
||||
["path"] = "elements/Display.lua",
|
||||
["size"] = 4243,
|
||||
["description"] = "The Display is a special element which uses the cc window API which you can use.",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["default"] = false,
|
||||
},
|
||||
["LineChart"] = {
|
||||
["path"] = "elements/LineChart.lua",
|
||||
["size"] = 3227,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = false,
|
||||
},
|
||||
["ScrollBar"] = {
|
||||
["path"] = "elements/ScrollBar.lua",
|
||||
["size"] = 9665,
|
||||
["description"] = "A ScrollBar element that can be attached to other elements to control their scroll properties.",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
["DropDown"] = {
|
||||
["path"] = "elements/DropDown.lua",
|
||||
["size"] = 6359,
|
||||
["default"] = false,
|
||||
["description"] = "A DropDown menu that shows a list of selectable items",
|
||||
["requires"] = {
|
||||
[1] = "List",
|
||||
},
|
||||
["default"] = false,
|
||||
["size"] = 6359,
|
||||
["path"] = "elements/DropDown.lua",
|
||||
},
|
||||
["Menu"] = {
|
||||
["path"] = "elements/Menu.lua",
|
||||
["size"] = 4679,
|
||||
["description"] = "A horizontal menu bar with selectable items.",
|
||||
["requires"] = {
|
||||
[1] = "List",
|
||||
},
|
||||
["Switch"] = {
|
||||
["default"] = true,
|
||||
},
|
||||
["Slider"] = {
|
||||
["path"] = "elements/Slider.lua",
|
||||
["size"] = 4977,
|
||||
["description"] = "",
|
||||
["description"] = "The Switch is a standard Switch element with click handling and state management.",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
["Frame"] = {
|
||||
["path"] = "elements/Frame.lua",
|
||||
["size"] = 6508,
|
||||
["description"] = "A frame element that serves as a grouping container for other elements.",
|
||||
["requires"] = {
|
||||
[1] = "Container",
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
["Button"] = {
|
||||
["path"] = "elements/Button.lua",
|
||||
["size"] = 1656,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
["ProgressBar"] = {
|
||||
["path"] = "elements/ProgressBar.lua",
|
||||
["size"] = 3397,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
["ComboBox"] = {
|
||||
["path"] = "elements/ComboBox.lua",
|
||||
["size"] = 14649,
|
||||
["description"] = "A ComboBox that combines dropdown selection with editable text input",
|
||||
["requires"] = {
|
||||
[1] = "DropDown",
|
||||
},
|
||||
["default"] = false,
|
||||
["size"] = 3269,
|
||||
["path"] = "elements/Switch.lua",
|
||||
},
|
||||
["TextBox"] = {
|
||||
["path"] = "elements/TextBox.lua",
|
||||
["size"] = 12667,
|
||||
["default"] = false,
|
||||
["description"] = "A multi-line text editor component with cursor support and text manipulation features",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["default"] = false,
|
||||
["size"] = 43466,
|
||||
["path"] = "elements/TextBox.lua",
|
||||
},
|
||||
["List"] = {
|
||||
["path"] = "elements/List.lua",
|
||||
["size"] = 8758,
|
||||
["description"] = "A scrollable list of selectable items",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["Frame"] = {
|
||||
["default"] = true,
|
||||
},
|
||||
["BaseElement"] = {
|
||||
["path"] = "elements/BaseElement.lua",
|
||||
["size"] = 9686,
|
||||
["description"] = "The base class for all UI elements in Basalt.",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
["TabControl"] = {
|
||||
["path"] = "elements/TabControl.lua",
|
||||
["size"] = 15300,
|
||||
["description"] = "A TabControl element that provides tabbed interface with multiple content areas.",
|
||||
["description"] = "A frame element that serves as a grouping container for other elements.",
|
||||
["requires"] = {
|
||||
[1] = "Container",
|
||||
},
|
||||
["size"] = 6508,
|
||||
["path"] = "elements/Frame.lua",
|
||||
},
|
||||
["Menu"] = {
|
||||
["default"] = true,
|
||||
["description"] = "A horizontal menu bar with selectable items.",
|
||||
["requires"] = {
|
||||
[1] = "List",
|
||||
},
|
||||
["size"] = 4679,
|
||||
["path"] = "elements/Menu.lua",
|
||||
},
|
||||
["Display"] = {
|
||||
["default"] = false,
|
||||
["description"] = "The Display is a special element which uses the cc window API which you can use.",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["size"] = 4243,
|
||||
["path"] = "elements/Display.lua",
|
||||
},
|
||||
["Container"] = {
|
||||
["default"] = true,
|
||||
["description"] = "The container class. It is a visual element that can contain other elements. It is the base class for all containers",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["size"] = 25383,
|
||||
["path"] = "elements/Container.lua",
|
||||
},
|
||||
["Table"] = {
|
||||
["default"] = true,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["size"] = 16214,
|
||||
["path"] = "elements/Table.lua",
|
||||
},
|
||||
["Label"] = {
|
||||
["default"] = true,
|
||||
["description"] = "A simple text display element that automatically resizes its width based on the text content.",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["size"] = 3092,
|
||||
["path"] = "elements/Label.lua",
|
||||
},
|
||||
["Image"] = {
|
||||
["path"] = "elements/Image.lua",
|
||||
["size"] = 15125,
|
||||
["default"] = false,
|
||||
["description"] = "An element that displays an image in bimg format",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["size"] = 15125,
|
||||
["path"] = "elements/Image.lua",
|
||||
},
|
||||
["BigFont"] = {
|
||||
["default"] = false,
|
||||
},
|
||||
["BaseFrame"] = {
|
||||
["path"] = "elements/BaseFrame.lua",
|
||||
["size"] = 8466,
|
||||
["description"] = "This is the base frame class. It is the root element of all elements and the only element without a parent.",
|
||||
["requires"] = {
|
||||
[1] = "Container",
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
["Timer"] = {
|
||||
["path"] = "elements/Timer.lua",
|
||||
["size"] = 2914,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
[1] = "BaseElement",
|
||||
},
|
||||
["default"] = true,
|
||||
},
|
||||
["Table"] = {
|
||||
["path"] = "elements/Table.lua",
|
||||
["size"] = 16214,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["size"] = 21049,
|
||||
["path"] = "elements/BigFont.lua",
|
||||
},
|
||||
["ComboBox"] = {
|
||||
["default"] = false,
|
||||
["description"] = "A ComboBox that combines dropdown selection with editable text input",
|
||||
["requires"] = {
|
||||
[1] = "DropDown",
|
||||
},
|
||||
["size"] = 14649,
|
||||
["path"] = "elements/ComboBox.lua",
|
||||
},
|
||||
["List"] = {
|
||||
["default"] = true,
|
||||
["description"] = "A scrollable list of selectable items",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["size"] = 8758,
|
||||
["path"] = "elements/List.lua",
|
||||
},
|
||||
["Slider"] = {
|
||||
["default"] = true,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["size"] = 4977,
|
||||
["path"] = "elements/Slider.lua",
|
||||
},
|
||||
["BarChart"] = {
|
||||
["default"] = false,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["size"] = 3190,
|
||||
["path"] = "elements/BarChart.lua",
|
||||
},
|
||||
["Button"] = {
|
||||
["default"] = true,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["size"] = 1656,
|
||||
["path"] = "elements/Button.lua",
|
||||
},
|
||||
["BaseElement"] = {
|
||||
["default"] = true,
|
||||
["description"] = "The base class for all UI elements in Basalt.",
|
||||
["requires"] = {
|
||||
},
|
||||
["size"] = 9686,
|
||||
["path"] = "elements/BaseElement.lua",
|
||||
},
|
||||
["VisualElement"] = {
|
||||
["default"] = true,
|
||||
["description"] = "The Visual Element class which is the base class for all visual UI elements",
|
||||
["requires"] = {
|
||||
[1] = "BaseElement",
|
||||
},
|
||||
["size"] = 22480,
|
||||
["path"] = "elements/VisualElement.lua",
|
||||
},
|
||||
["ScrollBar"] = {
|
||||
["default"] = true,
|
||||
["description"] = "A ScrollBar element that can be attached to other elements to control their scroll properties.",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["size"] = 9665,
|
||||
["path"] = "elements/ScrollBar.lua",
|
||||
},
|
||||
["Tree"] = {
|
||||
["default"] = true,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["size"] = 7942,
|
||||
["path"] = "elements/Tree.lua",
|
||||
},
|
||||
["Program"] = {
|
||||
["default"] = true,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["size"] = 11430,
|
||||
["path"] = "elements/Program.lua",
|
||||
},
|
||||
["CheckBox"] = {
|
||||
["default"] = true,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["size"] = 2928,
|
||||
["path"] = "elements/CheckBox.lua",
|
||||
},
|
||||
["BaseFrame"] = {
|
||||
["default"] = true,
|
||||
["description"] = "This is the base frame class. It is the root element of all elements and the only element without a parent.",
|
||||
["requires"] = {
|
||||
[1] = "Container",
|
||||
},
|
||||
["size"] = 8466,
|
||||
["path"] = "elements/BaseFrame.lua",
|
||||
},
|
||||
["Input"] = {
|
||||
["default"] = true,
|
||||
["description"] = "A text input field with various features",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["size"] = 9558,
|
||||
["path"] = "elements/Input.lua",
|
||||
},
|
||||
["ProgressBar"] = {
|
||||
["default"] = true,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["size"] = 3397,
|
||||
["path"] = "elements/ProgressBar.lua",
|
||||
},
|
||||
["LineChart"] = {
|
||||
["default"] = false,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["size"] = 3227,
|
||||
["path"] = "elements/LineChart.lua",
|
||||
},
|
||||
["FlexBox"] = {
|
||||
["default"] = true,
|
||||
["description"] = "A flexbox container that arranges its children in a flexible layout.",
|
||||
["requires"] = {
|
||||
[1] = "Container",
|
||||
},
|
||||
["size"] = 32431,
|
||||
["path"] = "elements/FlexBox.lua",
|
||||
},
|
||||
["Timer"] = {
|
||||
["default"] = true,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
[1] = "BaseElement",
|
||||
},
|
||||
["size"] = 2914,
|
||||
["path"] = "elements/Timer.lua",
|
||||
},
|
||||
["Graph"] = {
|
||||
["default"] = false,
|
||||
["description"] = "A point based graph element",
|
||||
["requires"] = {
|
||||
},
|
||||
["size"] = 6989,
|
||||
["path"] = "elements/Graph.lua",
|
||||
},
|
||||
["TabControl"] = {
|
||||
["default"] = true,
|
||||
["description"] = "A TabControl element that provides tabbed interface with multiple content areas.",
|
||||
["requires"] = {
|
||||
[1] = "Container",
|
||||
},
|
||||
["size"] = 15300,
|
||||
["path"] = "elements/TabControl.lua",
|
||||
},
|
||||
},
|
||||
["description"] = "UI Elements",
|
||||
},
|
||||
["libraries"] = {
|
||||
["files"] = {
|
||||
["colorHex"] = {
|
||||
["default"] = true,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["size"] = 132,
|
||||
["path"] = "libraries/colorHex.lua",
|
||||
},
|
||||
["expect"] = {
|
||||
["default"] = true,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["size"] = 846,
|
||||
["path"] = "libraries/expect.lua",
|
||||
},
|
||||
["utils"] = {
|
||||
["default"] = true,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["size"] = 2661,
|
||||
["path"] = "libraries/utils.lua",
|
||||
},
|
||||
},
|
||||
["description"] = "Libraries",
|
||||
},
|
||||
["core"] = {
|
||||
["files"] = {
|
||||
["render"] = {
|
||||
["default"] = true,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["size"] = 12422,
|
||||
["path"] = "render.lua",
|
||||
},
|
||||
["errorManager"] = {
|
||||
["default"] = true,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["size"] = 3789,
|
||||
["path"] = "errorManager.lua",
|
||||
},
|
||||
["main"] = {
|
||||
["default"] = true,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["size"] = 14085,
|
||||
["path"] = "main.lua",
|
||||
},
|
||||
["log"] = {
|
||||
["default"] = true,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["size"] = 3142,
|
||||
["path"] = "log.lua",
|
||||
},
|
||||
["propertySystem"] = {
|
||||
["default"] = true,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["size"] = 15524,
|
||||
["path"] = "propertySystem.lua",
|
||||
},
|
||||
["init"] = {
|
||||
["default"] = true,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["size"] = 622,
|
||||
["path"] = "init.lua",
|
||||
},
|
||||
["elementManager"] = {
|
||||
["default"] = true,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["size"] = 6297,
|
||||
["path"] = "elementManager.lua",
|
||||
},
|
||||
},
|
||||
["description"] = "Core Files",
|
||||
},
|
||||
["plugins"] = {
|
||||
["files"] = {
|
||||
["animation"] = {
|
||||
["default"] = true,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["size"] = 18421,
|
||||
["path"] = "plugins/animation.lua",
|
||||
},
|
||||
["reactive"] = {
|
||||
["default"] = true,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["size"] = 7580,
|
||||
["path"] = "plugins/reactive.lua",
|
||||
},
|
||||
["theme"] = {
|
||||
["default"] = true,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["size"] = 7042,
|
||||
["path"] = "plugins/theme.lua",
|
||||
},
|
||||
["benchmark"] = {
|
||||
["default"] = true,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["size"] = 12581,
|
||||
["path"] = "plugins/benchmark.lua",
|
||||
},
|
||||
["xml"] = {
|
||||
["default"] = true,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["size"] = 9940,
|
||||
["path"] = "plugins/xml.lua",
|
||||
},
|
||||
["state"] = {
|
||||
["default"] = true,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
[1] = "Container",
|
||||
},
|
||||
["size"] = 6896,
|
||||
["path"] = "plugins/state.lua",
|
||||
},
|
||||
["canvas"] = {
|
||||
["default"] = true,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["size"] = 7873,
|
||||
["path"] = "plugins/canvas.lua",
|
||||
},
|
||||
["debug"] = {
|
||||
["default"] = true,
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["size"] = 6250,
|
||||
["path"] = "plugins/debug.lua",
|
||||
},
|
||||
},
|
||||
["description"] = "Plugins",
|
||||
},
|
||||
},
|
||||
}
|
||||
6078
release/basalt.lua
6078
release/basalt.lua
File diff suppressed because one or more lines are too long
@@ -1,3 +1,4 @@
|
||||
---@diagnostic disable: duplicate-set-field
|
||||
local VisualElement = require("elements/VisualElement")
|
||||
local tHex = require("libraries/colorHex")
|
||||
---@configDescription A multi-line text editor component with cursor support and text manipulation features
|
||||
@@ -24,12 +25,605 @@ TextBox.defineProperty(TextBox, "editable", {default = true, type = "boolean"})
|
||||
TextBox.defineProperty(TextBox, "syntaxPatterns", {default = {}, type = "table"})
|
||||
---@property cursorColor number nil Color of the cursor
|
||||
TextBox.defineProperty(TextBox, "cursorColor", {default = nil, type = "color"})
|
||||
---@property autoPairEnabled boolean true Whether automatic bracket/quote pairing is enabled
|
||||
TextBox.defineProperty(TextBox, "autoPairEnabled", {default = true, type = "boolean"})
|
||||
---@property autoPairCharacters table { ["("]=")", ["["]="]", ["{"]="}", ['"']='"', ['\'']='\'', ['`']='`'} Mapping of opening to closing characters for auto pairing
|
||||
TextBox.defineProperty(TextBox, "autoPairCharacters", {default = { ["("]=")", ["["]="]", ["{"]="}", ['"']='"', ['\'']='\'', ['`']='`' }, type = "table"})
|
||||
---@property autoPairSkipClosing boolean true Skip inserting a closing char if the same one is already at cursor
|
||||
TextBox.defineProperty(TextBox, "autoPairSkipClosing", {default = true, type = "boolean"})
|
||||
---@property autoPairOverType boolean true When pressing a closing char that matches the next char, move over it instead of inserting
|
||||
TextBox.defineProperty(TextBox, "autoPairOverType", {default = true, type = "boolean"})
|
||||
---@property autoPairNewlineIndent boolean true On Enter between matching braces, create blank line and keep closing aligned
|
||||
TextBox.defineProperty(TextBox, "autoPairNewlineIndent", {default = true, type = "boolean"})
|
||||
---@property autoCompleteEnabled boolean false Whether autocomplete suggestions are enabled
|
||||
TextBox.defineProperty(TextBox, "autoCompleteEnabled", {default = false, type = "boolean"})
|
||||
---@property autoCompleteItems table {} List of suggestions used when no provider is supplied
|
||||
TextBox.defineProperty(TextBox, "autoCompleteItems", {default = {}, type = "table"})
|
||||
---@property autoCompleteProvider function nil Optional suggestion provider returning a list for the current prefix
|
||||
TextBox.defineProperty(TextBox, "autoCompleteProvider", {default = nil, type = "function", allowNil = true})
|
||||
---@property autoCompleteMinChars number 1 Minimum characters required before showing suggestions
|
||||
TextBox.defineProperty(TextBox, "autoCompleteMinChars", {default = 1, type = "number"})
|
||||
---@property autoCompleteMaxItems number 6 Maximum number of visible suggestions
|
||||
TextBox.defineProperty(TextBox, "autoCompleteMaxItems", {default = 6, type = "number"})
|
||||
---@property autoCompleteCaseInsensitive boolean true Whether suggestions should match case-insensitively
|
||||
TextBox.defineProperty(TextBox, "autoCompleteCaseInsensitive", {default = true, type = "boolean"})
|
||||
---@property autoCompleteTokenPattern string "[%w_]+" Pattern used to extract the current token for suggestions
|
||||
TextBox.defineProperty(TextBox, "autoCompleteTokenPattern", {default = "[%w_]+", type = "string"})
|
||||
---@property autoCompleteOffsetX number 0 Horizontal offset applied to the popup frame relative to the TextBox
|
||||
TextBox.defineProperty(TextBox, "autoCompleteOffsetX", {default = 0, type = "number"})
|
||||
---@property autoCompleteOffsetY number 1 Vertical offset applied to the popup frame relative to the TextBox bottom edge
|
||||
TextBox.defineProperty(TextBox, "autoCompleteOffsetY", {default = 1, type = "number"})
|
||||
---@property autoCompleteZOffset number 1 Z-index offset applied to the popup frame
|
||||
TextBox.defineProperty(TextBox, "autoCompleteZOffset", {default = 1, type = "number"})
|
||||
---@property autoCompleteMaxWidth number 0 Maximum width of the autocomplete popup (0 uses the textbox width)
|
||||
TextBox.defineProperty(TextBox, "autoCompleteMaxWidth", {default = 0, type = "number"})
|
||||
---@property autoCompleteShowBorder boolean true Whether to render a character border around the popup
|
||||
TextBox.defineProperty(TextBox, "autoCompleteShowBorder", {default = true, type = "boolean"})
|
||||
---@property autoCompleteBorderColor color black Color of the popup border when enabled
|
||||
TextBox.defineProperty(TextBox, "autoCompleteBorderColor", {default = colors.black, type = "color"})
|
||||
---@property autoCompleteBackground color lightGray Background color of the suggestion popup
|
||||
TextBox.defineProperty(TextBox, "autoCompleteBackground", {default = colors.lightGray, type = "color"})
|
||||
---@property autoCompleteForeground color black Foreground color of the suggestion popup
|
||||
TextBox.defineProperty(TextBox, "autoCompleteForeground", {default = colors.black, type = "color"})
|
||||
---@property autoCompleteSelectedBackground color gray Background color for the selected suggestion
|
||||
TextBox.defineProperty(TextBox, "autoCompleteSelectedBackground", {default = colors.gray, type = "color"})
|
||||
---@property autoCompleteSelectedForeground color white Foreground color for the selected suggestion
|
||||
TextBox.defineProperty(TextBox, "autoCompleteSelectedForeground", {default = colors.white, type = "color"})
|
||||
---@property autoCompleteAcceptOnEnter boolean true Whether pressing Enter accepts the current suggestion
|
||||
TextBox.defineProperty(TextBox, "autoCompleteAcceptOnEnter", {default = true, type = "boolean"})
|
||||
---@property autoCompleteAcceptOnClick boolean true Whether clicking a suggestion accepts it immediately
|
||||
TextBox.defineProperty(TextBox, "autoCompleteAcceptOnClick", {default = true, type = "boolean"})
|
||||
---@property autoCompleteCloseOnEscape boolean true Whether pressing Escape closes the popup
|
||||
TextBox.defineProperty(TextBox, "autoCompleteCloseOnEscape", {default = true, type = "boolean"})
|
||||
|
||||
TextBox.defineEvent(TextBox, "mouse_click")
|
||||
TextBox.defineEvent(TextBox, "key")
|
||||
TextBox.defineEvent(TextBox, "char")
|
||||
TextBox.defineEvent(TextBox, "mouse_scroll")
|
||||
TextBox.defineEvent(TextBox, "paste")
|
||||
TextBox.defineEvent(TextBox, "auto_complete_open")
|
||||
TextBox.defineEvent(TextBox, "auto_complete_close")
|
||||
TextBox.defineEvent(TextBox, "auto_complete_accept")
|
||||
|
||||
local updateAutoCompleteBorder
|
||||
local layoutAutoCompleteList
|
||||
|
||||
local function autoCompleteVisible(self)
|
||||
local frame = self._autoCompleteFrame
|
||||
return frame and not frame._destroyed and frame.get and frame.get("visible")
|
||||
end
|
||||
|
||||
local function getBorderPadding(self)
|
||||
return self.get("autoCompleteShowBorder") and 1 or 0
|
||||
end
|
||||
|
||||
local function updateAutoCompleteStyles(self)
|
||||
local frame = self._autoCompleteFrame
|
||||
local list = self._autoCompleteList
|
||||
if not frame or frame._destroyed then return end
|
||||
frame:setBackground(self.get("autoCompleteBackground"))
|
||||
frame:setForeground(self.get("autoCompleteForeground"))
|
||||
if list and not list._destroyed then
|
||||
list:setBackground(self.get("autoCompleteBackground"))
|
||||
list:setForeground(self.get("autoCompleteForeground"))
|
||||
list:setSelectedBackground(self.get("autoCompleteSelectedBackground"))
|
||||
list:setSelectedForeground(self.get("autoCompleteSelectedForeground"))
|
||||
list:updateRender()
|
||||
end
|
||||
layoutAutoCompleteList(self)
|
||||
updateAutoCompleteBorder(self)
|
||||
frame:updateRender()
|
||||
end
|
||||
|
||||
local function setAutoCompleteSelection(self, index, clampOnly)
|
||||
local list = self._autoCompleteList
|
||||
if not list or list._destroyed then return end
|
||||
local items = list.get("items")
|
||||
local count = #items
|
||||
if count == 0 then return end
|
||||
if index < 1 then index = 1 end
|
||||
if index > count then index = count end
|
||||
self._autoCompleteIndex = index
|
||||
|
||||
for i, item in ipairs(items) do
|
||||
if type(item) == "table" then
|
||||
item.selected = (i == index)
|
||||
end
|
||||
end
|
||||
|
||||
local height = list.get("height") or 0
|
||||
local offset = list.get("offset") or 0
|
||||
if not clampOnly and height > 0 then
|
||||
if index > offset + height then
|
||||
list:setOffset(math.max(0, index - height))
|
||||
elseif index <= offset then
|
||||
list:setOffset(math.max(0, index - 1))
|
||||
end
|
||||
end
|
||||
list:updateRender()
|
||||
end
|
||||
|
||||
local function hideAutoComplete(self, silent)
|
||||
if autoCompleteVisible(self) then
|
||||
self._autoCompleteFrame:setVisible(false)
|
||||
if not silent then
|
||||
self:fireEvent("auto_complete_close")
|
||||
end
|
||||
end
|
||||
self._autoCompleteIndex = nil
|
||||
self._autoCompleteSuggestions = nil
|
||||
self._autoCompleteToken = nil
|
||||
self._autoCompleteTokenStart = nil
|
||||
self._autoCompletePopupWidth = nil
|
||||
end
|
||||
|
||||
local function applyAutoCompleteSelection(self, item)
|
||||
local suggestions = self._autoCompleteSuggestions or {}
|
||||
local index = self._autoCompleteIndex or 1
|
||||
local entry = item or suggestions[index]
|
||||
if not entry then return end
|
||||
local insertText = entry.insert or entry.text or ""
|
||||
if insertText == "" then return end
|
||||
|
||||
local lines = self.get("lines")
|
||||
local cursorY = self.get("cursorY")
|
||||
local cursorX = self.get("cursorX")
|
||||
local line = lines[cursorY] or ""
|
||||
local startIndex = self._autoCompleteTokenStart or cursorX
|
||||
if startIndex < 1 then startIndex = 1 end
|
||||
|
||||
local before = line:sub(1, startIndex - 1)
|
||||
local after = line:sub(cursorX)
|
||||
lines[cursorY] = before .. insertText .. after
|
||||
|
||||
self.set("cursorX", startIndex + #insertText)
|
||||
self:updateViewport()
|
||||
self:updateRender()
|
||||
hideAutoComplete(self, true)
|
||||
self:fireEvent("auto_complete_accept", insertText, entry.source or entry)
|
||||
end
|
||||
|
||||
local function ensureAutoCompleteUI(self)
|
||||
if not self.get("autoCompleteEnabled") then return nil end
|
||||
local frame = self._autoCompleteFrame
|
||||
if frame and not frame._destroyed then
|
||||
return self._autoCompleteList
|
||||
end
|
||||
|
||||
local base = self:getBaseFrame()
|
||||
if not base or not base.addFrame then return nil end
|
||||
|
||||
frame = base:addFrame({
|
||||
width = self.get("width"),
|
||||
height = 1,
|
||||
x = 1,
|
||||
y = 1,
|
||||
visible = false,
|
||||
background = self.get("autoCompleteBackground"),
|
||||
foreground = self.get("autoCompleteForeground"),
|
||||
ignoreOffset = true,
|
||||
z = self.get("z") + self.get("autoCompleteZOffset"),
|
||||
})
|
||||
frame:setIgnoreOffset(true)
|
||||
frame:setVisible(false)
|
||||
|
||||
local padding = getBorderPadding(self)
|
||||
local list = frame:addList({
|
||||
x = padding + 1,
|
||||
y = padding + 1,
|
||||
width = math.max(1, frame.get("width") - padding * 2),
|
||||
height = math.max(1, frame.get("height") - padding * 2),
|
||||
selectable = true,
|
||||
multiSelection = false,
|
||||
background = self.get("autoCompleteBackground"),
|
||||
foreground = self.get("autoCompleteForeground"),
|
||||
})
|
||||
list:setSelectedBackground(self.get("autoCompleteSelectedBackground"))
|
||||
list:setSelectedForeground(self.get("autoCompleteSelectedForeground"))
|
||||
list:setOffset(0)
|
||||
list:onSelect(function(_, index, selectedItem)
|
||||
if not autoCompleteVisible(self) then return end
|
||||
setAutoCompleteSelection(self, index)
|
||||
if self.get("autoCompleteAcceptOnClick") then
|
||||
applyAutoCompleteSelection(self, selectedItem)
|
||||
end
|
||||
end)
|
||||
|
||||
self._autoCompleteFrame = frame
|
||||
self._autoCompleteList = list
|
||||
updateAutoCompleteStyles(self)
|
||||
return list
|
||||
end
|
||||
|
||||
layoutAutoCompleteList = function(self, contentWidth, visibleCount)
|
||||
local frame = self._autoCompleteFrame
|
||||
local list = self._autoCompleteList
|
||||
if not frame or frame._destroyed or not list or list._destroyed then return end
|
||||
|
||||
local border = getBorderPadding(self)
|
||||
local width = tonumber(contentWidth) or rawget(self, "_autoCompletePopupWidth") or list.get("width") or frame.get("width")
|
||||
local height = tonumber(visibleCount) or (list.get and list.get("height")) or (#(rawget(self, "_autoCompleteSuggestions") or {}))
|
||||
|
||||
width = math.max(1, width or 1)
|
||||
height = math.max(1, height or 1)
|
||||
|
||||
local frameWidth = frame.get and frame.get("width") or width
|
||||
local frameHeight = frame.get and frame.get("height") or height
|
||||
local maxWidth = math.max(1, frameWidth - border * 2)
|
||||
local maxHeight = math.max(1, frameHeight - border * 2)
|
||||
if width > maxWidth then width = maxWidth end
|
||||
if height > maxHeight then height = maxHeight end
|
||||
|
||||
list:setPosition(border + 1, border + 1)
|
||||
list:setWidth(math.max(1, width))
|
||||
list:setHeight(math.max(1, height))
|
||||
end
|
||||
|
||||
updateAutoCompleteBorder = function(self)
|
||||
local frame = self._autoCompleteFrame
|
||||
if not frame or frame._destroyed then return end
|
||||
|
||||
local canvas = frame.get and frame.get("canvas")
|
||||
if not canvas then return end
|
||||
|
||||
canvas:setType("post")
|
||||
if frame._autoCompleteBorderCommand then
|
||||
canvas:removeCommand(frame._autoCompleteBorderCommand)
|
||||
frame._autoCompleteBorderCommand = nil
|
||||
end
|
||||
|
||||
if not self.get("autoCompleteShowBorder") then
|
||||
frame:updateRender()
|
||||
return
|
||||
end
|
||||
|
||||
local borderColor = self.get("autoCompleteBorderColor") or colors.black
|
||||
|
||||
local commandIndex = canvas:addCommand(function(element)
|
||||
local width = element.get("width") or 0
|
||||
local height = element.get("height") or 0
|
||||
if width < 1 or height < 1 then return end
|
||||
|
||||
local bgColor = element.get("background") or colors.black
|
||||
local bgHex = tHex[bgColor] or tHex[colors.black]
|
||||
local borderHex = tHex[borderColor] or tHex[colors.black]
|
||||
|
||||
element:textFg(1, 1, ("\131"):rep(width), borderColor)
|
||||
element:multiBlit(1, height, width, 1, "\143", bgHex, borderHex)
|
||||
element:multiBlit(1, 1, 1, height, "\149", borderHex, bgHex)
|
||||
element:multiBlit(width, 1, 1, height, "\149", bgHex, borderHex)
|
||||
element:blit(1, 1, "\151", borderHex, bgHex)
|
||||
element:blit(width, 1, "\148", bgHex, borderHex)
|
||||
element:blit(1, height, "\138", bgHex, borderHex)
|
||||
element:blit(width, height, "\133", bgHex, borderHex)
|
||||
end)
|
||||
|
||||
frame._autoCompleteBorderCommand = commandIndex
|
||||
frame:updateRender()
|
||||
end
|
||||
|
||||
local function getTokenInfo(self)
|
||||
local lines = self.get("lines")
|
||||
local cursorY = self.get("cursorY")
|
||||
local cursorX = self.get("cursorX")
|
||||
local line = lines[cursorY] or ""
|
||||
local uptoCursor = line:sub(1, math.max(cursorX - 1, 0))
|
||||
local pattern = self.get("autoCompleteTokenPattern") or "[%w_]+"
|
||||
|
||||
local token = ""
|
||||
if pattern ~= "" then
|
||||
token = uptoCursor:match("(" .. pattern .. ")$") or ""
|
||||
end
|
||||
local startIndex = cursorX - #token
|
||||
if startIndex < 1 then startIndex = 1 end
|
||||
return token, startIndex
|
||||
end
|
||||
|
||||
local function normalizeSuggestion(entry)
|
||||
if type(entry) == "string" then
|
||||
return {text = entry, insert = entry, source = entry}
|
||||
elseif type(entry) == "table" then
|
||||
local text = entry.text or entry.label or entry.value or entry.insert or entry[1]
|
||||
if not text then return nil end
|
||||
local item = {
|
||||
text = text,
|
||||
insert = entry.insert or entry.value or text,
|
||||
source = entry,
|
||||
}
|
||||
if entry.foreground then item.foreground = entry.foreground end
|
||||
if entry.background then item.background = entry.background end
|
||||
if entry.selectedForeground then item.selectedForeground = entry.selectedForeground end
|
||||
if entry.selectedBackground then item.selectedBackground = entry.selectedBackground end
|
||||
if entry.icon then item.icon = entry.icon end
|
||||
if entry.info then item.info = entry.info end
|
||||
return item
|
||||
end
|
||||
end
|
||||
|
||||
local function iterateSuggestions(source, handler)
|
||||
if type(source) ~= "table" then return end
|
||||
local length = #source
|
||||
if length > 0 then
|
||||
for index = 1, length do
|
||||
handler(source[index])
|
||||
end
|
||||
else
|
||||
for _, value in pairs(source) do
|
||||
handler(value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function gatherSuggestions(self, token)
|
||||
local provider = self.get("autoCompleteProvider")
|
||||
local source = {}
|
||||
if provider then
|
||||
local ok, result = pcall(provider, self, token)
|
||||
if ok and type(result) == "table" then
|
||||
source = result
|
||||
end
|
||||
else
|
||||
source = self.get("autoCompleteItems") or {}
|
||||
end
|
||||
|
||||
local suggestions = {}
|
||||
local caseInsensitive = self.get("autoCompleteCaseInsensitive")
|
||||
local target = caseInsensitive and token:lower() or token
|
||||
iterateSuggestions(source, function(entry)
|
||||
local normalized = normalizeSuggestion(entry)
|
||||
if normalized and normalized.text then
|
||||
local compare = caseInsensitive and normalized.text:lower() or normalized.text
|
||||
if target == "" or compare:find(target, 1, true) == 1 then
|
||||
table.insert(suggestions, normalized)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
local maxItems = self.get("autoCompleteMaxItems")
|
||||
if #suggestions > maxItems then
|
||||
while #suggestions > maxItems do
|
||||
table.remove(suggestions)
|
||||
end
|
||||
end
|
||||
return suggestions
|
||||
end
|
||||
|
||||
local function measureSuggestionWidth(self, suggestions)
|
||||
local maxLen = 0
|
||||
for _, entry in ipairs(suggestions) do
|
||||
local text = entry
|
||||
if type(entry) == "table" then
|
||||
text = entry.text or entry.label or entry.value or entry.insert or entry[1]
|
||||
end
|
||||
if text ~= nil then
|
||||
local len = #tostring(text)
|
||||
if len > maxLen then
|
||||
maxLen = len
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local limit = self.get("autoCompleteMaxWidth")
|
||||
local maxWidth = self.get("width")
|
||||
if limit and limit > 0 then
|
||||
maxWidth = math.min(maxWidth, limit)
|
||||
end
|
||||
|
||||
local border = getBorderPadding(self)
|
||||
local base = self:getBaseFrame()
|
||||
if base and base.get then
|
||||
local baseWidth = base.get("width")
|
||||
if baseWidth and baseWidth > 0 then
|
||||
local available = baseWidth - border * 2
|
||||
if available < 1 then available = 1 end
|
||||
maxWidth = math.min(maxWidth, available)
|
||||
end
|
||||
end
|
||||
|
||||
maxLen = math.min(maxLen, maxWidth)
|
||||
|
||||
return math.max(1, maxLen)
|
||||
end
|
||||
|
||||
local function placeAutoCompleteFrame(self, visibleCount, width)
|
||||
local frame = self._autoCompleteFrame
|
||||
local list = self._autoCompleteList
|
||||
if not frame or frame._destroyed then return end
|
||||
local border = getBorderPadding(self)
|
||||
local contentWidth = math.max(1, width or self.get("width"))
|
||||
local contentHeight = math.max(1, visibleCount or 1)
|
||||
|
||||
local base = self:getBaseFrame()
|
||||
if not base then return end
|
||||
local baseWidth = base.get and base.get("width")
|
||||
local baseHeight = base.get and base.get("height")
|
||||
|
||||
if baseWidth and baseWidth > 0 then
|
||||
local maxContentWidth = baseWidth - border * 2
|
||||
if maxContentWidth < 1 then maxContentWidth = 1 end
|
||||
if contentWidth > maxContentWidth then
|
||||
contentWidth = maxContentWidth
|
||||
end
|
||||
end
|
||||
|
||||
if baseHeight and baseHeight > 0 then
|
||||
local maxContentHeight = baseHeight - border * 2
|
||||
if maxContentHeight < 1 then maxContentHeight = 1 end
|
||||
if contentHeight > maxContentHeight then
|
||||
contentHeight = maxContentHeight
|
||||
end
|
||||
end
|
||||
|
||||
local frameWidth = contentWidth + border * 2
|
||||
local frameHeight = contentHeight + border * 2
|
||||
local originX, originY = self:calculatePosition()
|
||||
local scrollX = self.get("scrollX") or 0
|
||||
local scrollY = self.get("scrollY") or 0
|
||||
local tokenStart = (self._autoCompleteTokenStart or self.get("cursorX"))
|
||||
local column = tokenStart - scrollX
|
||||
column = math.max(1, math.min(self.get("width"), column))
|
||||
|
||||
local cursorRow = self.get("cursorY") - scrollY
|
||||
cursorRow = math.max(1, math.min(self.get("height"), cursorRow))
|
||||
|
||||
local offsetX = self.get("autoCompleteOffsetX")
|
||||
local offsetY = self.get("autoCompleteOffsetY")
|
||||
|
||||
local baseX = originX + column - 1 + offsetX
|
||||
local x = baseX - border
|
||||
if border > 0 then
|
||||
x = x + 1
|
||||
end
|
||||
local listTopBelow = originY + cursorRow + offsetY
|
||||
local listBottomAbove = originY + cursorRow - offsetY - 1
|
||||
local belowY = listTopBelow - border
|
||||
local aboveY = listBottomAbove - contentHeight + 1 - border
|
||||
local y = belowY
|
||||
|
||||
if baseWidth and baseWidth > 0 then
|
||||
if frameWidth > baseWidth then
|
||||
frameWidth = baseWidth
|
||||
contentWidth = math.max(1, frameWidth - border * 2)
|
||||
end
|
||||
if x + frameWidth - 1 > baseWidth then
|
||||
x = math.max(1, baseWidth - frameWidth + 1)
|
||||
end
|
||||
if x < 1 then
|
||||
x = 1
|
||||
end
|
||||
else
|
||||
if x < 1 then x = 1 end
|
||||
end
|
||||
|
||||
if baseHeight and baseHeight > 0 then
|
||||
if y + frameHeight - 1 > baseHeight then
|
||||
-- Place above
|
||||
y = aboveY
|
||||
if border > 0 then
|
||||
-- Shift further up so lower border does not overlap the text line
|
||||
y = y - border
|
||||
end
|
||||
if y < 1 then
|
||||
y = math.max(1, baseHeight - frameHeight + 1)
|
||||
end
|
||||
end
|
||||
if y < 1 then
|
||||
y = 1
|
||||
end
|
||||
else
|
||||
if y < 1 then y = 1 end
|
||||
if y == aboveY and border > 0 then
|
||||
y = math.max(1, y - border)
|
||||
end
|
||||
end
|
||||
|
||||
frame:setPosition(x, y)
|
||||
frame:setWidth(frameWidth)
|
||||
frame:setHeight(frameHeight)
|
||||
frame:setZ(self.get("z") + self.get("autoCompleteZOffset"))
|
||||
|
||||
layoutAutoCompleteList(self, contentWidth, contentHeight)
|
||||
|
||||
if list and not list._destroyed then
|
||||
list:updateRender()
|
||||
end
|
||||
frame:updateRender()
|
||||
end
|
||||
|
||||
local function refreshAutoComplete(self)
|
||||
if not self.get("autoCompleteEnabled") then
|
||||
hideAutoComplete(self, true)
|
||||
return
|
||||
end
|
||||
if not self.get("focused") then
|
||||
hideAutoComplete(self, true)
|
||||
return
|
||||
end
|
||||
|
||||
local token, startIndex = getTokenInfo(self)
|
||||
self._autoCompleteToken = token
|
||||
self._autoCompleteTokenStart = startIndex
|
||||
|
||||
if #token < self.get("autoCompleteMinChars") then
|
||||
hideAutoComplete(self)
|
||||
return
|
||||
end
|
||||
|
||||
local suggestions = gatherSuggestions(self, token)
|
||||
if #suggestions == 0 then
|
||||
hideAutoComplete(self)
|
||||
return
|
||||
end
|
||||
|
||||
local list = ensureAutoCompleteUI(self)
|
||||
if not list then return end
|
||||
|
||||
list:setOffset(0)
|
||||
list:setItems(suggestions)
|
||||
self._autoCompleteSuggestions = suggestions
|
||||
setAutoCompleteSelection(self, 1, true)
|
||||
|
||||
local popupWidth = measureSuggestionWidth(self, suggestions)
|
||||
self._autoCompletePopupWidth = popupWidth
|
||||
placeAutoCompleteFrame(self, #suggestions, popupWidth)
|
||||
updateAutoCompleteStyles(self)
|
||||
self._autoCompleteFrame:setVisible(true)
|
||||
self._autoCompleteList:updateRender()
|
||||
self._autoCompleteFrame:updateRender()
|
||||
self:fireEvent("auto_complete_open", token, suggestions)
|
||||
end
|
||||
|
||||
local function handleAutoCompleteKey(self, key)
|
||||
if not autoCompleteVisible(self) then return false end
|
||||
|
||||
if key == keys.tab or (key == keys.enter and self.get("autoCompleteAcceptOnEnter")) then
|
||||
applyAutoCompleteSelection(self)
|
||||
return true
|
||||
elseif key == keys.up then
|
||||
setAutoCompleteSelection(self, (self._autoCompleteIndex or 1) - 1)
|
||||
return true
|
||||
elseif key == keys.down then
|
||||
setAutoCompleteSelection(self, (self._autoCompleteIndex or 1) + 1)
|
||||
return true
|
||||
elseif key == keys.pageUp then
|
||||
local height = (self._autoCompleteList and self._autoCompleteList.get("height")) or 1
|
||||
setAutoCompleteSelection(self, (self._autoCompleteIndex or 1) - height)
|
||||
return true
|
||||
elseif key == keys.pageDown then
|
||||
local height = (self._autoCompleteList and self._autoCompleteList.get("height")) or 1
|
||||
setAutoCompleteSelection(self, (self._autoCompleteIndex or 1) + height)
|
||||
return true
|
||||
elseif key == keys.escape and self.get("autoCompleteCloseOnEscape") then
|
||||
hideAutoComplete(self)
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function handleAutoCompleteScroll(self, direction)
|
||||
if not autoCompleteVisible(self) then return false end
|
||||
local list = self._autoCompleteList
|
||||
if not list or list._destroyed then return false end
|
||||
local items = list.get("items")
|
||||
local height = list.get("height") or 1
|
||||
local offset = list.get("offset") or 0
|
||||
local count = #items
|
||||
if count == 0 then return false end
|
||||
|
||||
local maxOffset = math.max(0, count - height)
|
||||
local newOffset = math.max(0, math.min(maxOffset, offset + direction))
|
||||
if newOffset ~= offset then
|
||||
list:setOffset(newOffset)
|
||||
end
|
||||
|
||||
local target = (self._autoCompleteIndex or 1) + direction
|
||||
if target >= 1 and target <= count then
|
||||
setAutoCompleteSelection(self, target)
|
||||
else
|
||||
list:updateRender()
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- Creates a new TextBox instance
|
||||
--- @shortDescription Creates a new TextBox instance
|
||||
@@ -51,6 +645,100 @@ end
|
||||
function TextBox:init(props, basalt)
|
||||
VisualElement.init(self, props, basalt)
|
||||
self.set("type", "TextBox")
|
||||
|
||||
local function refreshIfEnabled()
|
||||
if self.get("autoCompleteEnabled") and self.get("focused") then
|
||||
refreshAutoComplete(self)
|
||||
end
|
||||
end
|
||||
|
||||
local function restyle()
|
||||
updateAutoCompleteStyles(self)
|
||||
end
|
||||
|
||||
local function reposition()
|
||||
if autoCompleteVisible(self) then
|
||||
local suggestions = rawget(self, "_autoCompleteSuggestions") or {}
|
||||
placeAutoCompleteFrame(self, math.max(#suggestions, 1), rawget(self, "_autoCompletePopupWidth") or self.get("width"))
|
||||
end
|
||||
end
|
||||
|
||||
self:observe("autoCompleteEnabled", function(_, value)
|
||||
if not value then
|
||||
hideAutoComplete(self, true)
|
||||
elseif self.get("focused") then
|
||||
refreshAutoComplete(self)
|
||||
end
|
||||
end)
|
||||
|
||||
self:observe("focused", function(_, focused)
|
||||
if focused then
|
||||
refreshIfEnabled()
|
||||
else
|
||||
hideAutoComplete(self, true)
|
||||
end
|
||||
end)
|
||||
|
||||
self:observe("foreground", restyle)
|
||||
self:observe("background", restyle)
|
||||
self:observe("autoCompleteBackground", restyle)
|
||||
self:observe("autoCompleteForeground", restyle)
|
||||
self:observe("autoCompleteSelectedBackground", restyle)
|
||||
self:observe("autoCompleteSelectedForeground", restyle)
|
||||
self:observe("autoCompleteBorderColor", restyle)
|
||||
|
||||
self:observe("autoCompleteZOffset", function()
|
||||
if self._autoCompleteFrame and not self._autoCompleteFrame._destroyed then
|
||||
self._autoCompleteFrame:setZ(self.get("z") + self.get("autoCompleteZOffset"))
|
||||
end
|
||||
end)
|
||||
self:observe("z", function()
|
||||
if self._autoCompleteFrame and not self._autoCompleteFrame._destroyed then
|
||||
self._autoCompleteFrame:setZ(self.get("z") + self.get("autoCompleteZOffset"))
|
||||
end
|
||||
end)
|
||||
|
||||
self:observe("autoCompleteShowBorder", function()
|
||||
restyle()
|
||||
reposition()
|
||||
end)
|
||||
|
||||
for _, prop in ipairs({
|
||||
"autoCompleteItems",
|
||||
"autoCompleteProvider",
|
||||
"autoCompleteMinChars",
|
||||
"autoCompleteMaxItems",
|
||||
"autoCompleteCaseInsensitive",
|
||||
"autoCompleteTokenPattern",
|
||||
"autoCompleteOffsetX",
|
||||
"autoCompleteOffsetY",
|
||||
}) do
|
||||
self:observe(prop, refreshIfEnabled)
|
||||
end
|
||||
|
||||
self:observe("x", reposition)
|
||||
self:observe("y", reposition)
|
||||
self:observe("width", function()
|
||||
reposition()
|
||||
refreshIfEnabled()
|
||||
end)
|
||||
self:observe("height", reposition)
|
||||
self:observe("cursorX", reposition)
|
||||
self:observe("cursorY", reposition)
|
||||
self:observe("scrollX", reposition)
|
||||
self:observe("scrollY", reposition)
|
||||
self:observe("autoCompleteOffsetX", reposition)
|
||||
self:observe("autoCompleteOffsetY", reposition)
|
||||
self:observe("autoCompleteMaxWidth", function()
|
||||
if autoCompleteVisible(self) then
|
||||
local suggestions = rawget(self, "_autoCompleteSuggestions") or {}
|
||||
if #suggestions > 0 then
|
||||
local popupWidth = measureSuggestionWidth(self, suggestions)
|
||||
self._autoCompletePopupWidth = popupWidth
|
||||
placeAutoCompleteFrame(self, math.max(#suggestions, 1), popupWidth)
|
||||
end
|
||||
end
|
||||
end)
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -97,6 +785,12 @@ local function insertChar(self, char)
|
||||
self:updateRender()
|
||||
end
|
||||
|
||||
local function insertText(self, text)
|
||||
for i = 1, #text do
|
||||
insertChar(self, text:sub(i,i))
|
||||
end
|
||||
end
|
||||
|
||||
local function newLine(self)
|
||||
local lines = self.get("lines")
|
||||
local cursorX = self.get("cursorX")
|
||||
@@ -166,7 +860,50 @@ end
|
||||
--- @protected
|
||||
function TextBox:char(char)
|
||||
if not self.get("editable") or not self.get("focused") then return false end
|
||||
-- Auto-pair logic only triggers for single characters
|
||||
local autoPair = self.get("autoPairEnabled")
|
||||
if autoPair and #char == 1 then
|
||||
local map = self.get("autoPairCharacters") or {}
|
||||
local lines = self.get("lines")
|
||||
local cursorX = self.get("cursorX")
|
||||
local cursorY = self.get("cursorY")
|
||||
local line = lines[cursorY] or ""
|
||||
local afterChar = line:sub(cursorX, cursorX)
|
||||
|
||||
-- If typed char is an opening pair and we should skip duplicating closing when already there
|
||||
local closing = map[char]
|
||||
if closing then
|
||||
-- If skip closing and same closing already directly after, just insert opening?
|
||||
insertChar(self, char)
|
||||
if self.get("autoPairSkipClosing") then
|
||||
if afterChar ~= closing then
|
||||
insertChar(self, closing)
|
||||
-- Move cursor back inside pair
|
||||
self.set("cursorX", self.get("cursorX") - 1)
|
||||
end
|
||||
else
|
||||
insertChar(self, closing)
|
||||
self.set("cursorX", self.get("cursorX") - 1)
|
||||
end
|
||||
refreshAutoComplete(self)
|
||||
return true
|
||||
end
|
||||
|
||||
-- If typed char is a closing we might want to overtype
|
||||
if self.get("autoPairOverType") then
|
||||
for open, close in pairs(map) do
|
||||
if char == close and afterChar == close then
|
||||
-- move over instead of inserting
|
||||
self.set("cursorX", cursorX + 1)
|
||||
refreshAutoComplete(self)
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
insertChar(self, char)
|
||||
refreshAutoComplete(self)
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -176,11 +913,40 @@ end
|
||||
--- @protected
|
||||
function TextBox:key(key)
|
||||
if not self.get("editable") or not self.get("focused") then return false end
|
||||
if handleAutoCompleteKey(self, key) then
|
||||
return true
|
||||
end
|
||||
local lines = self.get("lines")
|
||||
local cursorX = self.get("cursorX")
|
||||
local cursorY = self.get("cursorY")
|
||||
|
||||
if key == keys.enter then
|
||||
-- Smart newline between matching braces/brackets if enabled
|
||||
if self.get("autoPairEnabled") and self.get("autoPairNewlineIndent") then
|
||||
local lines = self.get("lines")
|
||||
local cursorX = self.get("cursorX")
|
||||
local cursorY = self.get("cursorY")
|
||||
local line = lines[cursorY] or ""
|
||||
local before = line:sub(1, cursorX - 1)
|
||||
local after = line:sub(cursorX)
|
||||
local pairMap = self.get("autoPairCharacters") or {}
|
||||
local inverse = {}
|
||||
for o,c in pairs(pairMap) do inverse[c]=o end
|
||||
local prevChar = before:sub(-1)
|
||||
local nextChar = after:sub(1,1)
|
||||
if prevChar ~= "" and nextChar ~= "" and pairMap[prevChar] == nextChar then
|
||||
-- Split line into two with an empty line between, caret positioned on inner line
|
||||
lines[cursorY] = before
|
||||
table.insert(lines, cursorY + 1, "")
|
||||
table.insert(lines, cursorY + 2, after)
|
||||
self.set("cursorY", cursorY + 1)
|
||||
self.set("cursorX", 1)
|
||||
self:updateViewport()
|
||||
self:updateRender()
|
||||
refreshAutoComplete(self)
|
||||
return true
|
||||
end
|
||||
end
|
||||
newLine(self)
|
||||
elseif key == keys.backspace then
|
||||
backspace(self)
|
||||
@@ -207,6 +973,7 @@ function TextBox:key(key)
|
||||
end
|
||||
self:updateRender()
|
||||
self:updateViewport()
|
||||
refreshAutoComplete(self)
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -217,6 +984,9 @@ end
|
||||
--- @return boolean handled Whether the event was handled
|
||||
--- @protected
|
||||
function TextBox:mouse_scroll(direction, x, y)
|
||||
if handleAutoCompleteScroll(self, direction) then
|
||||
return true
|
||||
end
|
||||
if self:isInBounds(x, y) then
|
||||
local scrollY = self.get("scrollY")
|
||||
local height = self.get("height")
|
||||
@@ -256,8 +1026,15 @@ function TextBox:mouse_click(button, x, y)
|
||||
self.set("cursorX", math.min((relX or 1) + (scrollX or 0), lineLen + 1))
|
||||
end
|
||||
self:updateRender()
|
||||
refreshAutoComplete(self)
|
||||
return true
|
||||
end
|
||||
if autoCompleteVisible(self) then
|
||||
local frame = self._autoCompleteFrame
|
||||
if not (frame and frame:isInBounds(x, y)) and not self:isInBounds(x, y) then
|
||||
hideAutoComplete(self)
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
@@ -274,6 +1051,7 @@ function TextBox:paste(text)
|
||||
end
|
||||
end
|
||||
|
||||
refreshAutoComplete(self)
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -291,6 +1069,7 @@ function TextBox:setText(text)
|
||||
end
|
||||
end
|
||||
self.set("lines", lines)
|
||||
hideAutoComplete(self, true)
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -365,4 +1144,14 @@ function TextBox:render()
|
||||
end
|
||||
end
|
||||
|
||||
return TextBox
|
||||
function TextBox:destroy()
|
||||
if self._autoCompleteFrame and not self._autoCompleteFrame._destroyed then
|
||||
self._autoCompleteFrame:destroy()
|
||||
end
|
||||
self._autoCompleteFrame = nil
|
||||
self._autoCompleteList = nil
|
||||
self._autoCompletePopupWidth = nil
|
||||
VisualElement.destroy(self)
|
||||
end
|
||||
|
||||
return TextBox
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
---@diagnostic disable: duplicate-set-field, undefined-field, undefined-doc-name, param-type-mismatch, redundant-return-value
|
||||
local elementManager = require("elementManager")
|
||||
local BaseElement = elementManager.getElement("BaseElement")
|
||||
local tHex = require("libraries/colorHex")
|
||||
@@ -35,6 +36,16 @@ VisualElement.defineProperty(VisualElement, "clicked", {default = false, type =
|
||||
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 borderTop boolean false Draw top border
|
||||
VisualElement.defineProperty(VisualElement, "borderTop", {default = false, type = "boolean", canTriggerRender = true})
|
||||
---@property borderBottom boolean false Draw bottom border
|
||||
VisualElement.defineProperty(VisualElement, "borderBottom", {default = false, type = "boolean", canTriggerRender = true})
|
||||
---@property borderLeft boolean false Draw left border
|
||||
VisualElement.defineProperty(VisualElement, "borderLeft", {default = false, type = "boolean", canTriggerRender = true})
|
||||
---@property borderRight boolean false Draw right border
|
||||
VisualElement.defineProperty(VisualElement, "borderRight", {default = false, type = "boolean", canTriggerRender = true})
|
||||
---@property borderColor color white Border color
|
||||
VisualElement.defineProperty(VisualElement, "borderColor", {default = colors.white, type = "color", canTriggerRender = true})
|
||||
---@property focused boolean false Whether the element has input focus
|
||||
VisualElement.defineProperty(VisualElement, "focused", {default = false, type = "boolean", setter = function(self, value, internal)
|
||||
local curValue = self.get("focused")
|
||||
@@ -295,9 +306,7 @@ end
|
||||
---@return boolean hover Whether the mouse has moved over the element
|
||||
--- @protected
|
||||
function VisualElement:mouse_move(_, x, y)
|
||||
if(x==nil)or(y==nil)then
|
||||
return
|
||||
end
|
||||
if(x==nil)or(y==nil)then return false end
|
||||
local hover = self.get("hover")
|
||||
if(self:isInBounds(x, y))then
|
||||
if(not hover)then
|
||||
@@ -352,7 +361,49 @@ end
|
||||
--- @protected
|
||||
function VisualElement:blur()
|
||||
self:fireEvent("blur")
|
||||
self:setCursor(1,1, false)
|
||||
-- Attempt to clear cursor; signature may expect (x,y,blink,fg,bg)
|
||||
pcall(function() self:setCursor(1,1,false, self.get and self.get("foreground")) end)
|
||||
end
|
||||
|
||||
--- Adds or updates a drawable character border around the element using the canvas plugin.
|
||||
--- The border will automatically adapt to size/background changes because the command
|
||||
--- reads current properties each render.
|
||||
-- @param colorOrOptions any Border color or options table
|
||||
--- @return VisualElement self
|
||||
function VisualElement:addBorder(colorOrOptions, sideOptions)
|
||||
local col = nil
|
||||
local spec = nil
|
||||
if type(colorOrOptions) == "table" and (colorOrOptions.color or colorOrOptions.top ~= nil or colorOrOptions.left ~= nil) then
|
||||
col = colorOrOptions.color
|
||||
spec = colorOrOptions
|
||||
else
|
||||
col = colorOrOptions
|
||||
spec = sideOptions
|
||||
end
|
||||
if spec then
|
||||
if spec.top ~= nil then self.set("borderTop", spec.top) end
|
||||
if spec.bottom ~= nil then self.set("borderBottom", spec.bottom) end
|
||||
if spec.left ~= nil then self.set("borderLeft", spec.left) end
|
||||
if spec.right ~= nil then self.set("borderRight", spec.right) end
|
||||
else
|
||||
-- default: enable all sides
|
||||
self.set("borderTop", true)
|
||||
self.set("borderBottom", true)
|
||||
self.set("borderLeft", true)
|
||||
self.set("borderRight", true)
|
||||
end
|
||||
if col then self.set("borderColor", col) end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Removes the previously added border (if any)
|
||||
--- @return VisualElement self
|
||||
function VisualElement:removeBorder()
|
||||
self.set("borderTop", false)
|
||||
self.set("borderBottom", false)
|
||||
self.set("borderLeft", false)
|
||||
self.set("borderRight", false)
|
||||
return self
|
||||
end
|
||||
|
||||
--- @shortDescription Handles a key event
|
||||
@@ -480,11 +531,33 @@ end
|
||||
--- @shortDescription Renders the element
|
||||
--- @protected
|
||||
function VisualElement:render()
|
||||
if(not self.get("backgroundEnabled"))then
|
||||
return
|
||||
end
|
||||
if(not self.get("backgroundEnabled"))then return end
|
||||
local width, height = self.get("width"), self.get("height")
|
||||
self:multiBlit(1, 1, width, height, " ", tHex[self.get("foreground")], tHex[self.get("background")])
|
||||
local fgHex = tHex[self.get("foreground")]
|
||||
local bgHex = tHex[self.get("background")]
|
||||
self:multiBlit(1, 1, width, height, " ", fgHex, bgHex)
|
||||
-- Draw integrated border after background fill
|
||||
if (self.get("borderTop") or self.get("borderBottom") or self.get("borderLeft") or self.get("borderRight")) then
|
||||
local bColor = self.get("borderColor") or self.get("foreground")
|
||||
local bHex = tHex[bColor] or fgHex
|
||||
if self.get("borderTop") then
|
||||
self:textFg(1,1,("\131"):rep(width), bColor)
|
||||
end
|
||||
if self.get("borderBottom") then
|
||||
self:multiBlit(1,height,width,1,"\143", bgHex, bHex)
|
||||
end
|
||||
if self.get("borderLeft") then
|
||||
self:multiBlit(1,1,1,height,"\149", bHex, bgHex)
|
||||
end
|
||||
if self.get("borderRight") then
|
||||
self:multiBlit(width,1,1,height,"\149", bgHex, bHex)
|
||||
end
|
||||
-- Corners
|
||||
if self.get("borderTop") and self.get("borderLeft") then self:blit(1,1,"\151", bHex, bgHex) end
|
||||
if self.get("borderTop") and self.get("borderRight") then self:blit(width,1,"\148", bgHex, bHex) end
|
||||
if self.get("borderBottom") and self.get("borderLeft") then self:blit(1,height,"\138", bgHex, bHex) end
|
||||
if self.get("borderBottom") and self.get("borderRight") then self:blit(width,height,"\133", bgHex, bHex) end
|
||||
end
|
||||
end
|
||||
|
||||
--- @shortDescription Post-rendering function for the element
|
||||
@@ -497,4 +570,4 @@ function VisualElement:destroy()
|
||||
BaseElement.destroy(self)
|
||||
end
|
||||
|
||||
return VisualElement
|
||||
return VisualElement
|
||||
|
||||
Reference in New Issue
Block a user