Merge branch 'main' into main
This commit is contained in:
10065
BasaltLS.lua
10065
BasaltLS.lua
File diff suppressed because it is too large
Load Diff
850
config.lua
850
config.lua
@@ -1,509 +1,517 @@
|
||||
return {
|
||||
["categories"] = {
|
||||
["core"] = {
|
||||
["description"] = "Core Files",
|
||||
["libraries"] = {
|
||||
["files"] = {
|
||||
["main"] = {
|
||||
["size"] = 19883,
|
||||
["path"] = "main.lua",
|
||||
["collectionentry"] = {
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["description"] = "",
|
||||
["default"] = true,
|
||||
["path"] = "libraries/collectionentry.lua",
|
||||
["size"] = 3551,
|
||||
},
|
||||
["errorManager"] = {
|
||||
["size"] = 3789,
|
||||
["path"] = "errorManager.lua",
|
||||
["utils"] = {
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["description"] = "",
|
||||
["default"] = true,
|
||||
["path"] = "libraries/utils.lua",
|
||||
["size"] = 2661,
|
||||
},
|
||||
["elementManager"] = {
|
||||
["size"] = 15411,
|
||||
["path"] = "elementManager.lua",
|
||||
["expect"] = {
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["description"] = "",
|
||||
["default"] = true,
|
||||
["path"] = "libraries/expect.lua",
|
||||
["size"] = 846,
|
||||
},
|
||||
["render"] = {
|
||||
["size"] = 12422,
|
||||
["path"] = "render.lua",
|
||||
["colorHex"] = {
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["description"] = "",
|
||||
["default"] = true,
|
||||
},
|
||||
["propertySystem"] = {
|
||||
["size"] = 18186,
|
||||
["path"] = "propertySystem.lua",
|
||||
["requires"] = {
|
||||
},
|
||||
["description"] = "",
|
||||
["default"] = true,
|
||||
},
|
||||
["layoutManager"] = {
|
||||
["size"] = 3634,
|
||||
["path"] = "layoutManager.lua",
|
||||
["requires"] = {
|
||||
},
|
||||
["description"] = "",
|
||||
["default"] = true,
|
||||
},
|
||||
["init"] = {
|
||||
["size"] = 622,
|
||||
["path"] = "init.lua",
|
||||
["requires"] = {
|
||||
},
|
||||
["description"] = "",
|
||||
["default"] = true,
|
||||
},
|
||||
["log"] = {
|
||||
["size"] = 3142,
|
||||
["path"] = "log.lua",
|
||||
["requires"] = {
|
||||
},
|
||||
["description"] = "",
|
||||
["default"] = true,
|
||||
["path"] = "libraries/colorHex.lua",
|
||||
["size"] = 132,
|
||||
},
|
||||
},
|
||||
["description"] = "Libraries",
|
||||
},
|
||||
["core"] = {
|
||||
["files"] = {
|
||||
["render"] = {
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = true,
|
||||
["path"] = "render.lua",
|
||||
["size"] = 12422,
|
||||
},
|
||||
["errorManager"] = {
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = true,
|
||||
["path"] = "errorManager.lua",
|
||||
["size"] = 3789,
|
||||
},
|
||||
["main"] = {
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = true,
|
||||
["path"] = "main.lua",
|
||||
["size"] = 19883,
|
||||
},
|
||||
["init"] = {
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = true,
|
||||
["path"] = "init.lua",
|
||||
["size"] = 622,
|
||||
},
|
||||
["log"] = {
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = true,
|
||||
["path"] = "log.lua",
|
||||
["size"] = 3142,
|
||||
},
|
||||
["layoutManager"] = {
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = true,
|
||||
["path"] = "layoutManager.lua",
|
||||
["size"] = 3634,
|
||||
},
|
||||
["elementManager"] = {
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = true,
|
||||
["path"] = "elementManager.lua",
|
||||
["size"] = 15411,
|
||||
},
|
||||
["propertySystem"] = {
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = true,
|
||||
["path"] = "propertySystem.lua",
|
||||
["size"] = 18307,
|
||||
},
|
||||
},
|
||||
["description"] = "Core Files",
|
||||
},
|
||||
["plugins"] = {
|
||||
["files"] = {
|
||||
["xml"] = {
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = false,
|
||||
["path"] = "plugins/xml.lua",
|
||||
["size"] = 14068,
|
||||
},
|
||||
["canvas"] = {
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = false,
|
||||
["path"] = "plugins/canvas.lua",
|
||||
["size"] = 7897,
|
||||
},
|
||||
["debug"] = {
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = false,
|
||||
["path"] = "plugins/debug.lua",
|
||||
["size"] = 6274,
|
||||
},
|
||||
["reactive"] = {
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = false,
|
||||
["path"] = "plugins/reactive.lua",
|
||||
["size"] = 11893,
|
||||
},
|
||||
["responsive"] = {
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = false,
|
||||
["path"] = "plugins/responsive.lua",
|
||||
["size"] = 5529,
|
||||
},
|
||||
["theme"] = {
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = false,
|
||||
["path"] = "plugins/theme.lua",
|
||||
["size"] = 6801,
|
||||
},
|
||||
["animation"] = {
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = false,
|
||||
["path"] = "plugins/animation.lua",
|
||||
["size"] = 18446,
|
||||
},
|
||||
["benchmark"] = {
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["default"] = false,
|
||||
["path"] = "plugins/benchmark.lua",
|
||||
["size"] = 12604,
|
||||
},
|
||||
},
|
||||
["description"] = "Plugins",
|
||||
},
|
||||
["elements"] = {
|
||||
["description"] = "UI Elements",
|
||||
["files"] = {
|
||||
["Program"] = {
|
||||
["size"] = 12753,
|
||||
["path"] = "elements/Program.lua",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["description"] = "",
|
||||
["default"] = false,
|
||||
},
|
||||
["LineChart"] = {
|
||||
["size"] = 3172,
|
||||
["path"] = "elements/LineChart.lua",
|
||||
["requires"] = {
|
||||
},
|
||||
["description"] = "",
|
||||
["default"] = true,
|
||||
},
|
||||
["BarChart"] = {
|
||||
["size"] = 3507,
|
||||
["path"] = "elements/BarChart.lua",
|
||||
["requires"] = {
|
||||
},
|
||||
["description"] = "",
|
||||
["default"] = true,
|
||||
},
|
||||
["Timer"] = {
|
||||
["size"] = 2938,
|
||||
["path"] = "elements/Timer.lua",
|
||||
["requires"] = {
|
||||
[1] = "BaseElement",
|
||||
},
|
||||
["description"] = "",
|
||||
["default"] = false,
|
||||
},
|
||||
["List"] = {
|
||||
["size"] = 15474,
|
||||
["path"] = "elements/List.lua",
|
||||
["requires"] = {
|
||||
[1] = "Collection",
|
||||
},
|
||||
["description"] = "A scrollable list of selectable items",
|
||||
["default"] = true,
|
||||
},
|
||||
["Image"] = {
|
||||
["size"] = 15076,
|
||||
["path"] = "elements/Image.lua",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["description"] = "",
|
||||
["default"] = true,
|
||||
},
|
||||
["Input"] = {
|
||||
["size"] = 9200,
|
||||
["path"] = "elements/Input.lua",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["description"] = "A text input field with various features",
|
||||
["default"] = true,
|
||||
},
|
||||
["Collection"] = {
|
||||
["size"] = 7778,
|
||||
["path"] = "elements/Collection.lua",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["description"] = "A collection of items",
|
||||
["default"] = true,
|
||||
},
|
||||
["SideNav"] = {
|
||||
["size"] = 22159,
|
||||
["path"] = "elements/SideNav.lua",
|
||||
["requires"] = {
|
||||
[1] = "Container",
|
||||
},
|
||||
["description"] = "A SideNav element that provides sidebar navigation with multiple content areas.",
|
||||
["default"] = false,
|
||||
},
|
||||
["ProgressBar"] = {
|
||||
["size"] = 3398,
|
||||
["path"] = "elements/ProgressBar.lua",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["description"] = "",
|
||||
["default"] = true,
|
||||
},
|
||||
["Label"] = {
|
||||
["size"] = 3088,
|
||||
["path"] = "elements/Label.lua",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["description"] = "A simple text display element that automatically resizes its width based on the text content.",
|
||||
["default"] = true,
|
||||
},
|
||||
["TextBox"] = {
|
||||
["size"] = 43530,
|
||||
["path"] = "elements/TextBox.lua",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["description"] = "A multi-line text editor component with cursor support and text manipulation features",
|
||||
["default"] = false,
|
||||
},
|
||||
["Display"] = {
|
||||
["size"] = 4493,
|
||||
["path"] = "elements/Display.lua",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["description"] = "The Display is a special element which uses the CC Window API which you can use.",
|
||||
["default"] = false,
|
||||
},
|
||||
["Graph"] = {
|
||||
["size"] = 6933,
|
||||
["path"] = "elements/Graph.lua",
|
||||
["requires"] = {
|
||||
},
|
||||
["description"] = "A point based graph element",
|
||||
["default"] = false,
|
||||
},
|
||||
["BaseElement"] = {
|
||||
["size"] = 13870,
|
||||
["path"] = "elements/BaseElement.lua",
|
||||
["requires"] = {
|
||||
},
|
||||
["description"] = "The base class for all UI elements in Basalt.",
|
||||
["default"] = true,
|
||||
},
|
||||
["Accordion"] = {
|
||||
["size"] = 14937,
|
||||
["path"] = "elements/Accordion.lua",
|
||||
["requires"] = {
|
||||
[1] = "Container",
|
||||
},
|
||||
["description"] = "An Accordion element that provides collapsible panels with headers.",
|
||||
["default"] = false,
|
||||
},
|
||||
["ContextMenu"] = {
|
||||
["size"] = 10660,
|
||||
["path"] = "elements/ContextMenu.lua",
|
||||
["requires"] = {
|
||||
[1] = "Container",
|
||||
},
|
||||
["description"] = "A ContextMenu element that displays a menu with items and submenus.",
|
||||
["default"] = false,
|
||||
},
|
||||
["CheckBox"] = {
|
||||
["size"] = 3700,
|
||||
["path"] = "elements/CheckBox.lua",
|
||||
["requires"] = {
|
||||
},
|
||||
["description"] = "This is a checkbox. It is a visual element that can be checked.",
|
||||
["default"] = true,
|
||||
},
|
||||
["Button"] = {
|
||||
["size"] = 2437,
|
||||
["path"] = "elements/Button.lua",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["description"] = "The Button is a standard button element with click handling and state management.",
|
||||
["default"] = true,
|
||||
},
|
||||
["Breadcrumb"] = {
|
||||
["size"] = 4381,
|
||||
["path"] = "elements/Breadcrumb.lua",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["description"] = "A breadcrumb navigation element that displays the current path.",
|
||||
["default"] = false,
|
||||
},
|
||||
["Table"] = {
|
||||
["size"] = 25512,
|
||||
["path"] = "elements/Table.lua",
|
||||
["requires"] = {
|
||||
[1] = "Collection",
|
||||
},
|
||||
["description"] = "The Table is a sortable data grid with customizable columns, row selection, and scrolling capabilities.",
|
||||
["default"] = false,
|
||||
},
|
||||
["ComboBox"] = {
|
||||
["size"] = 14751,
|
||||
["path"] = "elements/ComboBox.lua",
|
||||
["requires"] = {
|
||||
[1] = "DropDown",
|
||||
},
|
||||
["description"] = "A ComboBox that combines dropdown selection with editable text input",
|
||||
["default"] = false,
|
||||
},
|
||||
["ScrollBar"] = {
|
||||
["size"] = 9689,
|
||||
["path"] = "elements/ScrollBar.lua",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["description"] = "A ScrollBar element that can be attached to other elements to control their scroll properties.",
|
||||
["default"] = false,
|
||||
},
|
||||
["BigFont"] = {
|
||||
["size"] = 21551,
|
||||
["path"] = "elements/BigFont.lua",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["description"] = "",
|
||||
["default"] = false,
|
||||
},
|
||||
["Menu"] = {
|
||||
["size"] = 8582,
|
||||
["path"] = "elements/Menu.lua",
|
||||
["requires"] = {
|
||||
[1] = "List",
|
||||
},
|
||||
["description"] = "A horizontal menu bar with selectable items.",
|
||||
["default"] = true,
|
||||
},
|
||||
["TabControl"] = {
|
||||
["size"] = 20900,
|
||||
["path"] = "elements/TabControl.lua",
|
||||
["requires"] = {
|
||||
[1] = "Container",
|
||||
},
|
||||
["description"] = "A TabControl element that provides tabbed interface with multiple content areas.",
|
||||
["default"] = false,
|
||||
},
|
||||
["ScrollFrame"] = {
|
||||
["size"] = 17463,
|
||||
["path"] = "elements/ScrollFrame.lua",
|
||||
["requires"] = {
|
||||
[1] = "Container",
|
||||
},
|
||||
["description"] = "A scrollable container that automatically displays scrollbars when content overflows.",
|
||||
["default"] = false,
|
||||
},
|
||||
["Slider"] = {
|
||||
["size"] = 5043,
|
||||
["path"] = "elements/Slider.lua",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["description"] = "A slider control element for selecting a value within a range.",
|
||||
["default"] = false,
|
||||
["path"] = "elements/Display.lua",
|
||||
["size"] = 4549,
|
||||
},
|
||||
["DropDown"] = {
|
||||
["size"] = 7886,
|
||||
["path"] = "elements/DropDown.lua",
|
||||
["description"] = "A DropDown menu that shows a list of selectable items",
|
||||
["requires"] = {
|
||||
[1] = "List",
|
||||
},
|
||||
["description"] = "A DropDown menu that shows a list of selectable items",
|
||||
["default"] = false,
|
||||
["path"] = "elements/DropDown.lua",
|
||||
["size"] = 7988,
|
||||
},
|
||||
["Container"] = {
|
||||
["size"] = 27475,
|
||||
["path"] = "elements/Container.lua",
|
||||
["Switch"] = {
|
||||
["description"] = "The Switch is a standard Switch element with click handling and state management.",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["description"] = "The container class. It is a visual element that can contain other elements. It is the base class for all containers",
|
||||
["default"] = false,
|
||||
["path"] = "elements/Switch.lua",
|
||||
["size"] = 3375,
|
||||
},
|
||||
["BaseFrame"] = {
|
||||
["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,
|
||||
["path"] = "elements/BaseFrame.lua",
|
||||
["size"] = 8972,
|
||||
},
|
||||
["Tree"] = {
|
||||
["size"] = 22168,
|
||||
["path"] = "elements/Tree.lua",
|
||||
["Table"] = {
|
||||
["description"] = "The Table is a sortable data grid with customizable columns, row selection, and scrolling capabilities.",
|
||||
["requires"] = {
|
||||
[1] = "Collection",
|
||||
},
|
||||
["default"] = false,
|
||||
["path"] = "elements/Table.lua",
|
||||
["size"] = 25766,
|
||||
},
|
||||
["Slider"] = {
|
||||
["description"] = "A slider control element for selecting a value within a range.",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["description"] = "",
|
||||
["default"] = false,
|
||||
["path"] = "elements/Slider.lua",
|
||||
["size"] = 5211,
|
||||
},
|
||||
["Input"] = {
|
||||
["description"] = "A text input field with various features",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["default"] = true,
|
||||
["path"] = "elements/Input.lua",
|
||||
["size"] = 9456,
|
||||
},
|
||||
["Collection"] = {
|
||||
["description"] = "A collection of items",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["default"] = true,
|
||||
["path"] = "elements/Collection.lua",
|
||||
["size"] = 7874,
|
||||
},
|
||||
["TextBox"] = {
|
||||
["description"] = "A multi-line text editor component with cursor support and text manipulation features",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["default"] = false,
|
||||
["path"] = "elements/TextBox.lua",
|
||||
["size"] = 44462,
|
||||
},
|
||||
["BarChart"] = {
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = true,
|
||||
["path"] = "elements/BarChart.lua",
|
||||
["size"] = 3547,
|
||||
},
|
||||
["Accordion"] = {
|
||||
["description"] = "An Accordion element that provides collapsible panels with headers.",
|
||||
["requires"] = {
|
||||
[1] = "Container",
|
||||
},
|
||||
["default"] = false,
|
||||
["path"] = "elements/Accordion.lua",
|
||||
["size"] = 15169,
|
||||
},
|
||||
["Dialog"] = {
|
||||
["size"] = 8901,
|
||||
["path"] = "elements/Dialog.lua",
|
||||
["description"] = "A dialog overlay system with common presets (alert, confirm, prompt).",
|
||||
["requires"] = {
|
||||
[1] = "Frame",
|
||||
},
|
||||
["description"] = "A dialog overlay system with common presets (alert, confirm, prompt).",
|
||||
["default"] = false,
|
||||
},
|
||||
["Switch"] = {
|
||||
["size"] = 3293,
|
||||
["path"] = "elements/Switch.lua",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["description"] = "The Switch is a standard Switch element with click handling and state management.",
|
||||
["default"] = false,
|
||||
},
|
||||
["Frame"] = {
|
||||
["size"] = 6646,
|
||||
["path"] = "elements/Frame.lua",
|
||||
["requires"] = {
|
||||
[1] = "Container",
|
||||
},
|
||||
["description"] = "A frame element that serves as a grouping container for other elements.",
|
||||
["default"] = true,
|
||||
},
|
||||
["BaseFrame"] = {
|
||||
["size"] = 8972,
|
||||
["path"] = "elements/BaseFrame.lua",
|
||||
["requires"] = {
|
||||
[1] = "Container",
|
||||
},
|
||||
["description"] = "This is the base frame class. It is the root element of all elements and the only element without a parent.",
|
||||
["default"] = true,
|
||||
["path"] = "elements/Dialog.lua",
|
||||
["size"] = 9125,
|
||||
},
|
||||
["Toast"] = {
|
||||
["size"] = 7897,
|
||||
["path"] = "elements/Toast.lua",
|
||||
["description"] = "A toast notification element that displays temporary messages.",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["description"] = "A toast notification element that displays temporary messages.",
|
||||
["default"] = false,
|
||||
["path"] = "elements/Toast.lua",
|
||||
["size"] = 7945,
|
||||
},
|
||||
["BigFont"] = {
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["default"] = false,
|
||||
["path"] = "elements/BigFont.lua",
|
||||
["size"] = 21675,
|
||||
},
|
||||
["TabControl"] = {
|
||||
["description"] = "A TabControl element that provides tabbed interface with multiple content areas.",
|
||||
["requires"] = {
|
||||
[1] = "Container",
|
||||
},
|
||||
["default"] = false,
|
||||
["path"] = "elements/TabControl.lua",
|
||||
["size"] = 21136,
|
||||
},
|
||||
["SideNav"] = {
|
||||
["description"] = "A SideNav element that provides sidebar navigation with multiple content areas.",
|
||||
["requires"] = {
|
||||
[1] = "Container",
|
||||
},
|
||||
["default"] = false,
|
||||
["path"] = "elements/SideNav.lua",
|
||||
["size"] = 22429,
|
||||
},
|
||||
["CheckBox"] = {
|
||||
["description"] = "This is a checkbox. It is a visual element that can be checked.",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = true,
|
||||
["path"] = "elements/CheckBox.lua",
|
||||
["size"] = 3748,
|
||||
},
|
||||
["Image"] = {
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["default"] = true,
|
||||
["path"] = "elements/Image.lua",
|
||||
["size"] = 15372,
|
||||
},
|
||||
["Menu"] = {
|
||||
["description"] = "A horizontal menu bar with selectable items.",
|
||||
["requires"] = {
|
||||
[1] = "List",
|
||||
},
|
||||
["default"] = true,
|
||||
["path"] = "elements/Menu.lua",
|
||||
["size"] = 8758,
|
||||
},
|
||||
["Frame"] = {
|
||||
["description"] = "A frame element that serves as a grouping container for other elements.",
|
||||
["requires"] = {
|
||||
[1] = "Container",
|
||||
},
|
||||
["default"] = true,
|
||||
["path"] = "elements/Frame.lua",
|
||||
["size"] = 6742,
|
||||
},
|
||||
["ContextMenu"] = {
|
||||
["description"] = "A ContextMenu element that displays a menu with items and submenus.",
|
||||
["requires"] = {
|
||||
[1] = "Container",
|
||||
},
|
||||
["default"] = false,
|
||||
["path"] = "elements/ContextMenu.lua",
|
||||
["size"] = 10836,
|
||||
},
|
||||
["List"] = {
|
||||
["description"] = "A scrollable list of selectable items",
|
||||
["requires"] = {
|
||||
[1] = "Collection",
|
||||
},
|
||||
["default"] = true,
|
||||
["path"] = "elements/List.lua",
|
||||
["size"] = 15754,
|
||||
},
|
||||
["BaseElement"] = {
|
||||
["description"] = "The base class for all UI elements in Basalt.",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = true,
|
||||
["path"] = "elements/BaseElement.lua",
|
||||
["size"] = 18468,
|
||||
},
|
||||
["Program"] = {
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["default"] = false,
|
||||
["path"] = "elements/Program.lua",
|
||||
["size"] = 12833,
|
||||
},
|
||||
["ProgressBar"] = {
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["default"] = true,
|
||||
["path"] = "elements/ProgressBar.lua",
|
||||
["size"] = 3440,
|
||||
},
|
||||
["Label"] = {
|
||||
["description"] = "A simple text display element that automatically resizes its width based on the text content.",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["default"] = true,
|
||||
["path"] = "elements/Label.lua",
|
||||
["size"] = 3184,
|
||||
},
|
||||
["LineChart"] = {
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
},
|
||||
["default"] = true,
|
||||
["path"] = "elements/LineChart.lua",
|
||||
["size"] = 3228,
|
||||
},
|
||||
["ScrollBar"] = {
|
||||
["description"] = "A ScrollBar element that can be attached to other elements to control their scroll properties.",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["default"] = false,
|
||||
["path"] = "elements/ScrollBar.lua",
|
||||
["size"] = 9941,
|
||||
},
|
||||
["Button"] = {
|
||||
["description"] = "The Button is a standard button element with click handling and state management.",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["default"] = true,
|
||||
["path"] = "elements/Button.lua",
|
||||
["size"] = 2461,
|
||||
},
|
||||
["Tree"] = {
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["default"] = false,
|
||||
["path"] = "elements/Tree.lua",
|
||||
["size"] = 22552,
|
||||
},
|
||||
["Container"] = {
|
||||
["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,
|
||||
["path"] = "elements/Container.lua",
|
||||
["size"] = 27731,
|
||||
},
|
||||
["VisualElement"] = {
|
||||
["size"] = 45082,
|
||||
["path"] = "elements/VisualElement.lua",
|
||||
["description"] = "The Visual Element class which is the base class for all visual UI elements",
|
||||
["requires"] = {
|
||||
[1] = "BaseElement",
|
||||
},
|
||||
["description"] = "The Visual Element class which is the base class for all visual UI elements",
|
||||
["default"] = true,
|
||||
["path"] = "elements/VisualElement.lua",
|
||||
["size"] = 45338,
|
||||
},
|
||||
},
|
||||
},
|
||||
["plugins"] = {
|
||||
["description"] = "Plugins",
|
||||
["files"] = {
|
||||
["xml"] = {
|
||||
["size"] = 14068,
|
||||
["path"] = "plugins/xml.lua",
|
||||
["ScrollFrame"] = {
|
||||
["description"] = "A scrollable container that automatically displays scrollbars when content overflows.",
|
||||
["requires"] = {
|
||||
[1] = "Container",
|
||||
},
|
||||
["description"] = "",
|
||||
["default"] = false,
|
||||
["path"] = "elements/ScrollFrame.lua",
|
||||
["size"] = 17831,
|
||||
},
|
||||
["theme"] = {
|
||||
["size"] = 6801,
|
||||
["path"] = "plugins/theme.lua",
|
||||
["Timer"] = {
|
||||
["description"] = "",
|
||||
["requires"] = {
|
||||
[1] = "BaseElement",
|
||||
},
|
||||
["description"] = "",
|
||||
["default"] = false,
|
||||
["path"] = "elements/Timer.lua",
|
||||
["size"] = 2962,
|
||||
},
|
||||
["animation"] = {
|
||||
["size"] = 18446,
|
||||
["path"] = "plugins/animation.lua",
|
||||
["requires"] = {
|
||||
},
|
||||
["description"] = "",
|
||||
["default"] = false,
|
||||
},
|
||||
["debug"] = {
|
||||
["size"] = 6274,
|
||||
["path"] = "plugins/debug.lua",
|
||||
["requires"] = {
|
||||
},
|
||||
["description"] = "",
|
||||
["default"] = false,
|
||||
},
|
||||
["reactive"] = {
|
||||
["size"] = 11869,
|
||||
["path"] = "plugins/reactive.lua",
|
||||
["requires"] = {
|
||||
},
|
||||
["description"] = "",
|
||||
["default"] = false,
|
||||
},
|
||||
["benchmark"] = {
|
||||
["size"] = 12604,
|
||||
["path"] = "plugins/benchmark.lua",
|
||||
["Breadcrumb"] = {
|
||||
["description"] = "A breadcrumb navigation element that displays the current path.",
|
||||
["requires"] = {
|
||||
[1] = "VisualElement",
|
||||
},
|
||||
["description"] = "",
|
||||
["default"] = false,
|
||||
["path"] = "elements/Breadcrumb.lua",
|
||||
["size"] = 4461,
|
||||
},
|
||||
["canvas"] = {
|
||||
["size"] = 7897,
|
||||
["path"] = "plugins/canvas.lua",
|
||||
["ComboBox"] = {
|
||||
["description"] = "A ComboBox that combines dropdown selection with editable text input",
|
||||
["requires"] = {
|
||||
[1] = "DropDown",
|
||||
},
|
||||
["default"] = false,
|
||||
["path"] = "elements/ComboBox.lua",
|
||||
["size"] = 15143,
|
||||
},
|
||||
["Graph"] = {
|
||||
["description"] = "A point based graph element",
|
||||
["requires"] = {
|
||||
},
|
||||
["description"] = "",
|
||||
["default"] = false,
|
||||
["path"] = "elements/Graph.lua",
|
||||
["size"] = 7045,
|
||||
},
|
||||
},
|
||||
},
|
||||
["libraries"] = {
|
||||
["description"] = "Libraries",
|
||||
["files"] = {
|
||||
["utils"] = {
|
||||
["size"] = 2661,
|
||||
["path"] = "libraries/utils.lua",
|
||||
["requires"] = {
|
||||
},
|
||||
["description"] = "",
|
||||
["default"] = true,
|
||||
},
|
||||
["colorHex"] = {
|
||||
["size"] = 132,
|
||||
["path"] = "libraries/colorHex.lua",
|
||||
["requires"] = {
|
||||
},
|
||||
["description"] = "",
|
||||
["default"] = true,
|
||||
},
|
||||
["expect"] = {
|
||||
["size"] = 846,
|
||||
["path"] = "libraries/expect.lua",
|
||||
["requires"] = {
|
||||
},
|
||||
["description"] = "",
|
||||
["default"] = true,
|
||||
},
|
||||
["collectionentry"] = {
|
||||
["size"] = 3551,
|
||||
["path"] = "libraries/collectionentry.lua",
|
||||
["requires"] = {
|
||||
},
|
||||
["description"] = "",
|
||||
["default"] = true,
|
||||
},
|
||||
},
|
||||
["description"] = "UI Elements",
|
||||
},
|
||||
},
|
||||
["metadata"] = {
|
||||
["generated"] = "Wed Nov 5 00:37:16 2025",
|
||||
["version"] = "2.0",
|
||||
["generated"] = "Tue Nov 4 09:01:43 2025",
|
||||
},
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -174,14 +174,14 @@ end
|
||||
--- @param expanded boolean Whether the panel starts expanded (default: false)
|
||||
--- @return table panelContainer The container for this panel
|
||||
function Accordion:newPanel(title, expanded)
|
||||
local panels = self.get("panels") or {}
|
||||
local panels = self.getResolved("panels") or {}
|
||||
local panelId = #panels + 1
|
||||
|
||||
local panelContainer = self:addContainer()
|
||||
panelContainer.set("x", 1)
|
||||
panelContainer.set("y", 1)
|
||||
panelContainer.set("width", self.get("width"))
|
||||
panelContainer.set("height", self.get("height"))
|
||||
panelContainer.set("width", self.getResolved("width"))
|
||||
panelContainer.set("height", self.getResolved("height"))
|
||||
panelContainer.set("visible", expanded or false)
|
||||
panelContainer.set("ignoreOffset", true)
|
||||
|
||||
@@ -202,11 +202,11 @@ Accordion.addPanel = Accordion.newPanel
|
||||
--- @shortDescription Updates the layout of all panels (positions and visibility)
|
||||
--- @private
|
||||
function Accordion:updatePanelLayout()
|
||||
local panels = self.get("panels") or {}
|
||||
local headerHeight = self.get("panelHeaderHeight") or 1
|
||||
local panels = self.getResolved("panels") or {}
|
||||
local headerHeight = self.getResolved("panelHeaderHeight") or 1
|
||||
local currentY = 1
|
||||
local width = self.get("width")
|
||||
local accordionHeight = self.get("height")
|
||||
local width = self.getResolved("width")
|
||||
local accordionHeight = self.getResolved("height")
|
||||
|
||||
for _, panel in ipairs(panels) do
|
||||
local contentY = currentY + headerHeight
|
||||
@@ -238,7 +238,7 @@ function Accordion:updatePanelLayout()
|
||||
|
||||
local totalHeight = currentY - 1
|
||||
local maxOffset = math.max(0, totalHeight - accordionHeight)
|
||||
local currentOffset = self.get("offsetY")
|
||||
local currentOffset = self.getResolved("offsetY")
|
||||
|
||||
if currentOffset > maxOffset then
|
||||
self.set("offsetY", maxOffset)
|
||||
@@ -251,8 +251,8 @@ end
|
||||
--- @param panelId number The ID of the panel to toggle
|
||||
--- @return Accordion self For method chaining
|
||||
function Accordion:togglePanel(panelId)
|
||||
local panels = self.get("panels") or {}
|
||||
local allowMultiple = self.get("allowMultiple")
|
||||
local panels = self.getResolved("panels") or {}
|
||||
local allowMultiple = self.getResolved("allowMultiple")
|
||||
|
||||
for i, panel in ipairs(panels) do
|
||||
if panel.id == panelId then
|
||||
@@ -279,8 +279,8 @@ end
|
||||
--- @param panelId number The ID of the panel to expand
|
||||
--- @return Accordion self For method chaining
|
||||
function Accordion:expandPanel(panelId)
|
||||
local panels = self.get("panels") or {}
|
||||
local allowMultiple = self.get("allowMultiple")
|
||||
local panels = self.getResolved("panels") or {}
|
||||
local allowMultiple = self.getResolved("allowMultiple")
|
||||
|
||||
for i, panel in ipairs(panels) do
|
||||
if panel.id == panelId then
|
||||
@@ -309,7 +309,7 @@ end
|
||||
--- @param panelId number The ID of the panel to collapse
|
||||
--- @return Accordion self For method chaining
|
||||
function Accordion:collapsePanel(panelId)
|
||||
local panels = self.get("panels") or {}
|
||||
local panels = self.getResolved("panels") or {}
|
||||
|
||||
for _, panel in ipairs(panels) do
|
||||
if panel.id == panelId then
|
||||
@@ -329,7 +329,7 @@ end
|
||||
--- @param panelId number The ID of the panel
|
||||
--- @return table? container The panel's container or nil
|
||||
function Accordion:getPanel(panelId)
|
||||
local panels = self.get("panels") or {}
|
||||
local panels = self.getResolved("panels") or {}
|
||||
for _, panel in ipairs(panels) do
|
||||
if panel.id == panelId then
|
||||
return panel.container
|
||||
@@ -342,8 +342,8 @@ end
|
||||
--- @return table metrics Panel layout information
|
||||
--- @private
|
||||
function Accordion:_getPanelMetrics()
|
||||
local panels = self.get("panels") or {}
|
||||
local headerHeight = self.get("panelHeaderHeight") or 1
|
||||
local panels = self.getResolved("panels") or {}
|
||||
local headerHeight = self.getResolved("panelHeaderHeight") or 1
|
||||
|
||||
local positions = {}
|
||||
local currentY = 1
|
||||
@@ -381,7 +381,7 @@ function Accordion:mouse_click(button, x, y)
|
||||
end
|
||||
|
||||
local relX, relY = VisualElement.getRelativePosition(self, x, y)
|
||||
local offsetY = self.get("offsetY")
|
||||
local offsetY = self.getResolved("offsetY")
|
||||
local adjustedY = relY + offsetY
|
||||
local metrics = self:_getPanelMetrics()
|
||||
|
||||
@@ -400,12 +400,12 @@ end
|
||||
function Accordion:mouse_scroll(direction, x, y)
|
||||
if VisualElement.mouse_scroll(self, direction, x, y) then
|
||||
local metrics = self:_getPanelMetrics()
|
||||
local accordionHeight = self.get("height")
|
||||
local accordionHeight = self.getResolved("height")
|
||||
local totalHeight = metrics.totalHeight
|
||||
local maxOffset = math.max(0, totalHeight - accordionHeight)
|
||||
|
||||
if maxOffset > 0 then
|
||||
local currentOffset = self.get("offsetY")
|
||||
local currentOffset = self.getResolved("offsetY")
|
||||
local newOffset = currentOffset + direction
|
||||
newOffset = math.max(0, math.min(maxOffset, newOffset))
|
||||
self.set("offsetY", newOffset)
|
||||
@@ -422,17 +422,17 @@ end
|
||||
function Accordion:render()
|
||||
VisualElement.render(self)
|
||||
|
||||
local width = self.get("width")
|
||||
local offsetY = self.get("offsetY")
|
||||
local width = self.getResolved("width")
|
||||
local offsetY = self.getResolved("offsetY")
|
||||
local metrics = self:_getPanelMetrics()
|
||||
|
||||
for _, panelInfo in ipairs(metrics.positions) do
|
||||
local bgColor = panelInfo.expanded and self.get("expandedHeaderBackground") or self.get("headerBackground")
|
||||
local fgColor = panelInfo.expanded and self.get("expandedHeaderTextColor") or self.get("headerTextColor")
|
||||
local bgColor = panelInfo.expanded and self.getResolved("expandedHeaderBackground") or self.getResolved("headerBackground")
|
||||
local fgColor = panelInfo.expanded and self.getResolved("expandedHeaderTextColor") or self.getResolved("headerTextColor")
|
||||
|
||||
local headerY = panelInfo.headerY - offsetY
|
||||
|
||||
if headerY >= 1 and headerY <= self.get("height") then
|
||||
if headerY >= 1 and headerY <= self.getResolved("height") then
|
||||
VisualElement.multiBlit(
|
||||
self,
|
||||
1,
|
||||
@@ -450,16 +450,16 @@ function Accordion:render()
|
||||
end
|
||||
end
|
||||
|
||||
if not self.get("childrenSorted") then
|
||||
if not self.getResolved("childrenSorted") then
|
||||
self:sortChildren()
|
||||
end
|
||||
if not self.get("childrenEventsSorted") then
|
||||
if not self.getResolved("childrenEventsSorted") then
|
||||
for eventName in pairs(self._values.childrenEvents or {}) do
|
||||
self:sortChildrenEvents(eventName)
|
||||
end
|
||||
end
|
||||
|
||||
for _, child in ipairs(self.get("visibleChildren") or {}) do
|
||||
for _, child in ipairs(self.getResolved("visibleChildren") or {}) do
|
||||
if child == self then
|
||||
error("CIRCULAR REFERENCE DETECTED!")
|
||||
return
|
||||
|
||||
@@ -54,11 +54,11 @@ end
|
||||
function BarChart:render()
|
||||
VisualElement.render(self)
|
||||
|
||||
local width = self.get("width")
|
||||
local height = self.get("height")
|
||||
local minVal = self.get("minValue")
|
||||
local maxVal = self.get("maxValue")
|
||||
local series = self.get("series")
|
||||
local width = self.getResolved("width")
|
||||
local height = self.getResolved("height")
|
||||
local minVal = self.getResolved("minValue")
|
||||
local maxVal = self.getResolved("maxValue")
|
||||
local series = self.getResolved("series")
|
||||
|
||||
local activeSeriesCount = 0
|
||||
local seriesList = {}
|
||||
|
||||
@@ -225,13 +225,14 @@ end
|
||||
--- @param priority? number Optional priority override
|
||||
--- @return BaseElement self
|
||||
function BaseElement:setState(stateName, priority)
|
||||
local states = self.get("states")
|
||||
local states = self.getResolved("states")
|
||||
|
||||
if not priority and self._registeredStates[stateName] then
|
||||
priority = self._registeredStates[stateName].priority
|
||||
end
|
||||
|
||||
states[stateName] = priority or 0
|
||||
|
||||
self.set("states", states)
|
||||
return self
|
||||
end
|
||||
@@ -299,7 +300,9 @@ end
|
||||
function BaseElement:updateConditionalStates()
|
||||
for stateName, stateInfo in pairs(self._registeredStates) do
|
||||
if stateInfo.condition then
|
||||
if stateInfo.condition(self) then
|
||||
local result = stateInfo.condition(self)
|
||||
|
||||
if result then
|
||||
self:setState(stateName, stateInfo.priority)
|
||||
else
|
||||
self:unsetState(stateName)
|
||||
@@ -309,6 +312,145 @@ function BaseElement:updateConditionalStates()
|
||||
return self
|
||||
end
|
||||
|
||||
--- Registers a responsive state that reacts to parent size changes
|
||||
--- @shortDescription Registers a state that responds to parent dimensions
|
||||
--- @param stateName string The name of the state
|
||||
--- @param condition string|function Condition as string expression or function: function(element) return boolean end
|
||||
--- @param options? table|number Options table with 'priority' and 'observe', or just priority number
|
||||
--- @return BaseElement self
|
||||
function BaseElement:registerResponsiveState(stateName, condition, options)
|
||||
local priority = 100
|
||||
local observeList = {}
|
||||
if type(options) == "number" then
|
||||
priority = options
|
||||
elseif type(options) == "table" then
|
||||
priority = options.priority or 100
|
||||
observeList = options.observe or {}
|
||||
end
|
||||
|
||||
local conditionFunc
|
||||
local isStringExpr = type(condition) == "string"
|
||||
|
||||
if isStringExpr then
|
||||
conditionFunc = self:_parseResponsiveExpression(condition)
|
||||
|
||||
local autoDeps = self:_detectDependencies(condition)
|
||||
for _, dep in ipairs(autoDeps) do
|
||||
table.insert(observeList, dep)
|
||||
end
|
||||
else
|
||||
conditionFunc = condition
|
||||
end
|
||||
self:registerState(stateName, conditionFunc, priority)
|
||||
|
||||
for _, observeInfo in ipairs(observeList) do
|
||||
local element = observeInfo.element or observeInfo[1]
|
||||
local property = observeInfo.property or observeInfo[2]
|
||||
if element and property then
|
||||
element:observe(property, function()
|
||||
self:updateConditionalStates()
|
||||
end)
|
||||
end
|
||||
end
|
||||
self:updateConditionalStates()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Parses a responsive expression string into a function
|
||||
--- @private
|
||||
--- @param expr string The expression to parse
|
||||
--- @return function conditionFunc The parsed condition function
|
||||
function BaseElement:_parseResponsiveExpression(expr)
|
||||
local protectedNames = {
|
||||
colors = true,
|
||||
math = true,
|
||||
clamp = true,
|
||||
round = true
|
||||
}
|
||||
|
||||
local mathEnv = {
|
||||
clamp = function(val, min, max)
|
||||
return math.min(math.max(val, min), max)
|
||||
end,
|
||||
round = function(val)
|
||||
return math.floor(val + 0.5)
|
||||
end,
|
||||
floor = math.floor,
|
||||
ceil = math.ceil,
|
||||
abs = math.abs
|
||||
}
|
||||
|
||||
expr = expr:gsub("([%w_]+)%.([%w_]+)", function(obj, prop)
|
||||
if protectedNames[obj] or tonumber(obj) then
|
||||
return obj.."."..prop
|
||||
end
|
||||
return string.format('__getProperty("%s", "%s")', obj, prop)
|
||||
end)
|
||||
|
||||
local element = self
|
||||
local env = setmetatable({
|
||||
colors = colors,
|
||||
math = math,
|
||||
tostring = tostring,
|
||||
tonumber = tonumber,
|
||||
__getProperty = function(objName, propName)
|
||||
if objName == "self" then
|
||||
if element._properties[propName] then
|
||||
return element.get(propName)
|
||||
end
|
||||
elseif objName == "parent" then
|
||||
if element.parent and element.parent._properties[propName] then
|
||||
return element.parent.get(propName)
|
||||
end
|
||||
else
|
||||
local target = element:getBaseFrame():getChild(objName)
|
||||
if target and target._properties[propName] then
|
||||
return target.get(propName)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
}, { __index = mathEnv })
|
||||
|
||||
local func, err = load("return "..expr, "responsive", "t", env)
|
||||
if not func then
|
||||
error("Invalid responsive expression: " .. err)
|
||||
end
|
||||
|
||||
return function(self)
|
||||
local ok, result = pcall(func)
|
||||
return ok and result or false
|
||||
end
|
||||
end
|
||||
|
||||
--- Detects dependencies in a responsive expression
|
||||
--- @private
|
||||
--- @param expr string The expression to analyze
|
||||
--- @return table dependencies List of {element, property} pairs
|
||||
function BaseElement:_detectDependencies(expr)
|
||||
local deps = {}
|
||||
local protectedNames = {colors = true, math = true, clamp = true, round = true}
|
||||
|
||||
for ref, prop in expr:gmatch("([%w_]+)%.([%w_]+)") do
|
||||
if not protectedNames[ref] and not tonumber(ref) then
|
||||
local element
|
||||
if ref == "self" then
|
||||
element = self
|
||||
elseif ref == "parent" then
|
||||
element = self.parent
|
||||
else
|
||||
element = self:getBaseFrame():getChild(ref)
|
||||
end
|
||||
|
||||
if element then
|
||||
table.insert(deps, {element = element, property = prop})
|
||||
end
|
||||
end
|
||||
end
|
||||
return deps
|
||||
end
|
||||
|
||||
--- Removes a state from the registry
|
||||
--- @shortDescription Removes state definition
|
||||
--- @param stateName string The state to remove
|
||||
@@ -325,9 +467,9 @@ end
|
||||
--- @param ... any Additional arguments to pass to the callbacks
|
||||
--- @return table self The BaseElement instance
|
||||
function BaseElement:fireEvent(event, ...)
|
||||
if self.get("eventCallbacks")[event] then
|
||||
if self.getResolved("eventCallbacks")[event] then
|
||||
local lastResult
|
||||
for _, callback in ipairs(self.get("eventCallbacks")[event]) do
|
||||
for _, callback in ipairs(self.getResolved("eventCallbacks")[event]) do
|
||||
lastResult = callback(self, ...)
|
||||
end
|
||||
return lastResult
|
||||
@@ -341,7 +483,7 @@ end
|
||||
--- @return boolean? handled Whether the event was handled
|
||||
--- @protected
|
||||
function BaseElement:dispatchEvent(event, ...)
|
||||
if self.get("enabled") == false then
|
||||
if self.getResolved("enabled") == false then
|
||||
return false
|
||||
end
|
||||
if self[event] then
|
||||
|
||||
@@ -171,12 +171,12 @@ BigFont.__index = BigFont
|
||||
|
||||
---@property text string BigFont The text string to display in enlarged format
|
||||
BigFont.defineProperty(BigFont, "text", {default = "BigFont", type = "string", canTriggerRender = true, setter=function(self, value)
|
||||
self.bigfontText = makeText(self.get("fontSize"), value, self.get("foreground"), self.get("background"))
|
||||
self.bigfontText = makeText(self.getResolved("fontSize"), value, self.getResolved("foreground"), self.getResolved("background"))
|
||||
return value
|
||||
end})
|
||||
---@property fontSize number 1 Scale factor for text size (1-3, where 1 is 3x3 pixels per character)
|
||||
BigFont.defineProperty(BigFont, "fontSize", {default = 1, type = "number", canTriggerRender = true, setter=function(self, value)
|
||||
self.bigfontText = makeText(value, self.get("text"), self.get("foreground"), self.get("background"))
|
||||
self.bigfontText = makeText(value, self.getResolved("text"), self.getResolved("foreground"), self.getResolved("background"))
|
||||
return value
|
||||
end})
|
||||
|
||||
@@ -200,10 +200,10 @@ function BigFont:init(props, basalt)
|
||||
VisualElement.init(self, props, basalt)
|
||||
self.set("type", "BigFont")
|
||||
self:observe("background", function(self, value)
|
||||
self.bigfontText = makeText(self.get("fontSize"), self.get("text"), self.get("foreground"), value)
|
||||
self.bigfontText = makeText(self.getResolved("fontSize"), self.getResolved("text"), self.getResolved("foreground"), value)
|
||||
end)
|
||||
self:observe("foreground", function(self, value)
|
||||
self.bigfontText = makeText(self.get("fontSize"), self.get("text"), value, self.get("background"))
|
||||
self.bigfontText = makeText(self.getResolved("fontSize"), self.getResolved("text"), value, self.getResolved("background"))
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -212,11 +212,12 @@ end
|
||||
function BigFont:render()
|
||||
VisualElement.render(self)
|
||||
if(self.bigfontText)then
|
||||
local x, y = self.get("x"), self.get("y")
|
||||
local x, y = self.getResolved("x"), self.getResolved("y")
|
||||
local width = self.getResolved("width")
|
||||
for i = 1, #self.bigfontText[1] do
|
||||
local text = self.bigfontText[1][i]:sub(1, self.get("width"))
|
||||
local fg = self.bigfontText[2][i]:sub(1, self.get("width"))
|
||||
local bg = self.bigfontText[3][i]:sub(1, self.get("width"))
|
||||
local text = self.bigfontText[1][i]:sub(1, width)
|
||||
local fg = self.bigfontText[2][i]:sub(1, width)
|
||||
local bg = self.bigfontText[3][i]:sub(1, width)
|
||||
self:blit(x, y + i - 1, text, fg, bg)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -44,10 +44,10 @@ end
|
||||
--- @param y number
|
||||
--- @return boolean handled
|
||||
function Breadcrumb:mouse_click(button, x, y)
|
||||
if not self.get("clickable") then return false end
|
||||
if not self.getResolved("clickable") then return false end
|
||||
if VisualElement.mouse_click(self, button, x, y) then
|
||||
local path = self.get("path")
|
||||
local separator = self.get("separator")
|
||||
local path = self.getResolved("path")
|
||||
local separator = self.getResolved("separator")
|
||||
|
||||
local cursorX = 1
|
||||
for i, segment in ipairs(path) do
|
||||
@@ -81,11 +81,11 @@ end
|
||||
--- @shortDescription Renders the breadcrumb trail
|
||||
--- @protected
|
||||
function Breadcrumb:render()
|
||||
local path = self.get("path")
|
||||
local separator = self.get("separator")
|
||||
local fg = self.get("foreground")
|
||||
local clickable = self.get("clickable")
|
||||
local width = self.get("width")
|
||||
local path = self.getResolved("path")
|
||||
local separator = self.getResolved("separator")
|
||||
local fg = self.getResolved("foreground")
|
||||
local clickable = self.getResolved("clickable")
|
||||
local width = self.getResolved("width")
|
||||
|
||||
local fullText = ""
|
||||
for i, segment in ipairs(path) do
|
||||
@@ -95,8 +95,8 @@ function Breadcrumb:render()
|
||||
end
|
||||
end
|
||||
|
||||
if self.get("autoSize") then
|
||||
self.set("width", #fullText)
|
||||
if self.getResolved("autoSize") then
|
||||
self.getResolved("width", #fullText)
|
||||
else
|
||||
if #fullText > width then
|
||||
local ellipsis = "... > "
|
||||
|
||||
@@ -62,8 +62,8 @@ end
|
||||
function Button:render()
|
||||
VisualElement.render(self)
|
||||
local text = self.getResolved("text")
|
||||
text = text:sub(1, self.get("width"))
|
||||
local xO, yO = getCenteredPosition(text, self.get("width"), self.get("height"))
|
||||
text = text:sub(1, self.getResolved("width"))
|
||||
local xO, yO = getCenteredPosition(text, self.getResolved("width"), self.getResolved("height"))
|
||||
self:textFg(xO, yO, text, self.getResolved("foreground"))
|
||||
end
|
||||
|
||||
|
||||
@@ -24,18 +24,18 @@ CheckBox.__index = CheckBox
|
||||
CheckBox.defineProperty(CheckBox, "checked", {default = false, type = "boolean", canTriggerRender = true})
|
||||
---@property text string empty Text shown when the checkbox is unchecked
|
||||
CheckBox.defineProperty(CheckBox, "text", {default = " ", type = "string", canTriggerRender = true, setter=function(self, value)
|
||||
local checkedText = self.get("checkedText")
|
||||
local checkedText = self.getResolved("checkedText")
|
||||
local width = math.max(#value, #checkedText)
|
||||
if(self.get("autoSize"))then
|
||||
if(self.getResolved("autoSize"))then
|
||||
self.set("width", width)
|
||||
end
|
||||
return value
|
||||
end})
|
||||
---@property checkedText string x Text shown when the checkbox is checked
|
||||
CheckBox.defineProperty(CheckBox, "checkedText", {default = "x", type = "string", canTriggerRender = true, setter=function(self, value)
|
||||
local text = self.get("text")
|
||||
local text = self.getResolved("text")
|
||||
local width = math.max(#value, #text)
|
||||
if(self.get("autoSize"))then
|
||||
if(self.getResolved("autoSize"))then
|
||||
self.set("width", width)
|
||||
end
|
||||
return value
|
||||
@@ -74,7 +74,7 @@ end
|
||||
--- @protected
|
||||
function CheckBox:mouse_click(button, x, y)
|
||||
if VisualElement.mouse_click(self, button, x, y) then
|
||||
self.set("checked", not self.get("checked"))
|
||||
self.set("checked", not self.getResolved("checked"))
|
||||
return true
|
||||
end
|
||||
return false
|
||||
@@ -88,7 +88,7 @@ function CheckBox:render()
|
||||
local checked = self.getResolved("checked")
|
||||
local defaultText = self.getResolved("text")
|
||||
local checkedText = self.getResolved("checkedText")
|
||||
local text = string.sub(checked and checkedText or defaultText, 1, self.get("width"))
|
||||
local text = string.sub(checked and checkedText or defaultText, 1, self.getResolved("width"))
|
||||
|
||||
self:textFg(1, 1, text, self.getResolved("foreground"))
|
||||
end
|
||||
|
||||
@@ -56,7 +56,7 @@ function Collection:addItem(itemData)
|
||||
end
|
||||
local entry = CollectionEntry.new(self, itemData, self._entrySchema)
|
||||
|
||||
table.insert(self.get("items"), entry)
|
||||
table.insert(self.getResolved("items"), entry)
|
||||
self:updateRender()
|
||||
return entry
|
||||
end
|
||||
@@ -67,7 +67,7 @@ end
|
||||
--- @return Collection self The Collection instance
|
||||
--- @usage Collection:removeItem(1)
|
||||
function Collection:removeItem(index)
|
||||
local items = self.get("items")
|
||||
local items = self.getResolved("items")
|
||||
if type(index) == "number" then
|
||||
table.remove(items, index)
|
||||
else
|
||||
@@ -98,7 +98,7 @@ end
|
||||
--- @usage local selected = Collection:getSelectedItems()
|
||||
function Collection:getSelectedItems()
|
||||
local selected = {}
|
||||
for i, item in ipairs(self.get("items")) do
|
||||
for i, item in ipairs(self.getResolved("items")) do
|
||||
if type(item) == "table" and item.selected then
|
||||
local selectedItem = item
|
||||
selectedItem.index = i
|
||||
@@ -112,7 +112,7 @@ end
|
||||
--- @shortDescription Gets first selected item
|
||||
--- @return table? selected The first item
|
||||
function Collection:getSelectedItem()
|
||||
local items = self.get("items")
|
||||
local items = self.getResolved("items")
|
||||
for i, item in ipairs(items) do
|
||||
if type(item) == "table" and item.selected then
|
||||
return item
|
||||
@@ -122,7 +122,7 @@ function Collection:getSelectedItem()
|
||||
end
|
||||
|
||||
function Collection:selectItem(index)
|
||||
local items = self.get("items")
|
||||
local items = self.getResolved("items")
|
||||
if type(index) == "number" then
|
||||
if items[index] and type(items[index]) == "table" then
|
||||
items[index].selected = true
|
||||
@@ -142,7 +142,7 @@ function Collection:selectItem(index)
|
||||
end
|
||||
|
||||
function Collection:unselectItem(index)
|
||||
local items = self.get("items")
|
||||
local items = self.getResolved("items")
|
||||
if type(index) == "number" then
|
||||
if items[index] and type(items[index]) == "table" then
|
||||
items[index].selected = false
|
||||
@@ -162,7 +162,7 @@ function Collection:unselectItem(index)
|
||||
end
|
||||
|
||||
function Collection:clearItemSelection()
|
||||
local items = self.get("items")
|
||||
local items = self.getResolved("items")
|
||||
for i, item in ipairs(items) do
|
||||
item.selected = false
|
||||
end
|
||||
@@ -175,7 +175,7 @@ end
|
||||
--- @return number? index The index of the first selected item, or nil if none selected
|
||||
--- @usage local index = Collection:getSelectedIndex()
|
||||
function Collection:getSelectedIndex()
|
||||
local items = self.get("items")
|
||||
local items = self.getResolved("items")
|
||||
for i, item in ipairs(items) do
|
||||
if type(item) == "table" and item.selected then
|
||||
return i
|
||||
@@ -188,7 +188,7 @@ end
|
||||
--- @shortDescription Selects the next item
|
||||
--- @return Collection self The Collection instance
|
||||
function Collection:selectNext()
|
||||
local items = self.get("items")
|
||||
local items = self.getResolved("items")
|
||||
local currentIndex = self:getSelectedIndex()
|
||||
|
||||
if not currentIndex then
|
||||
@@ -196,7 +196,7 @@ function Collection:selectNext()
|
||||
self:selectItem(1)
|
||||
end
|
||||
elseif currentIndex < #items then
|
||||
if not self.get("multiSelection") then
|
||||
if not self.getResolved("multiSelection") then
|
||||
self:clearItemSelection()
|
||||
end
|
||||
self:selectItem(currentIndex + 1)
|
||||
@@ -210,7 +210,7 @@ end
|
||||
--- @shortDescription Selects the previous item
|
||||
--- @return Collection self The Collection instance
|
||||
function Collection:selectPrevious()
|
||||
local items = self.get("items")
|
||||
local items = self.getResolved("items")
|
||||
local currentIndex = self:getSelectedIndex()
|
||||
|
||||
if not currentIndex then
|
||||
@@ -218,7 +218,7 @@ function Collection:selectPrevious()
|
||||
self:selectItem(#items)
|
||||
end
|
||||
elseif currentIndex > 1 then
|
||||
if not self.get("multiSelection") then
|
||||
if not self.getResolved("multiSelection") then
|
||||
self:clearItemSelection()
|
||||
end
|
||||
self:selectItem(currentIndex - 1)
|
||||
|
||||
@@ -78,10 +78,10 @@ end
|
||||
--- @shortDescription Filters items for auto-complete
|
||||
--- @private
|
||||
function ComboBox:getFilteredItems()
|
||||
local allItems = self.get("items") or {}
|
||||
local currentText = self.get("text"):lower()
|
||||
local allItems = self.getResolved("items") or {}
|
||||
local currentText = self.getResolved("text"):lower()
|
||||
|
||||
if not self.get("autoComplete") or #currentText == 0 then
|
||||
if not self.getResolved("autoComplete") or #currentText == 0 then
|
||||
return allItems
|
||||
end
|
||||
|
||||
@@ -106,15 +106,15 @@ end
|
||||
--- @shortDescription Updates dropdown with filtered items
|
||||
--- @private
|
||||
function ComboBox:updateFilteredDropdown()
|
||||
if not self.get("autoComplete") then return end
|
||||
if not self.getResolved("autoComplete") then return end
|
||||
|
||||
local filteredItems = self:getFilteredItems()
|
||||
local shouldOpen = #filteredItems > 0 and #self.get("text") > 0
|
||||
local shouldOpen = #filteredItems > 0 and #self.getResolved("text") > 0
|
||||
|
||||
if shouldOpen then
|
||||
self:setState("opened")
|
||||
self.set("manuallyOpened", false)
|
||||
local dropdownHeight = self.get("dropdownHeight") or 5
|
||||
local dropdownHeight = self.getResolved("dropdownHeight") or 5
|
||||
local actualHeight = math.min(dropdownHeight, #filteredItems)
|
||||
self.set("height", 1 + actualHeight)
|
||||
else
|
||||
@@ -128,15 +128,15 @@ end
|
||||
--- @shortDescription Updates the viewport
|
||||
--- @private
|
||||
function ComboBox:updateViewport()
|
||||
local text = self.get("text")
|
||||
local cursorPos = self.get("cursorPos")
|
||||
local width = self.get("width")
|
||||
local dropSymbol = self.get("dropSymbol")
|
||||
local text = self.getResolved("text")
|
||||
local cursorPos = self.getResolved("cursorPos")
|
||||
local width = self.getResolved("width")
|
||||
local dropSymbol = self.getResolved("dropSymbol")
|
||||
|
||||
local textWidth = width - #dropSymbol
|
||||
if textWidth < 1 then textWidth = 1 end
|
||||
|
||||
local viewOffset = self.get("viewOffset")
|
||||
local viewOffset = self.getResolved("viewOffset")
|
||||
|
||||
if cursorPos - viewOffset > textWidth then
|
||||
viewOffset = cursorPos - textWidth
|
||||
@@ -151,18 +151,18 @@ end
|
||||
--- @shortDescription Handles character input
|
||||
--- @param char string The character that was typed
|
||||
function ComboBox:char(char)
|
||||
if not self.get("editable") then return end
|
||||
if not self.getResolved("editable") then return end
|
||||
if not self:hasState("focused") then return end
|
||||
|
||||
local text = self.get("text")
|
||||
local cursorPos = self.get("cursorPos")
|
||||
local text = self.getResolved("text")
|
||||
local cursorPos = self.getResolved("cursorPos")
|
||||
|
||||
local newText = text:sub(1, cursorPos - 1) .. char .. text:sub(cursorPos)
|
||||
self.set("text", newText)
|
||||
self.set("cursorPos", cursorPos + 1)
|
||||
self:updateViewport()
|
||||
|
||||
if self.get("autoComplete") then
|
||||
if self.getResolved("autoComplete") then
|
||||
self:updateFilteredDropdown()
|
||||
else
|
||||
self:updateRender()
|
||||
@@ -174,11 +174,11 @@ end
|
||||
--- @param key number The key code that was pressed
|
||||
--- @param held boolean Whether the key is being held
|
||||
function ComboBox:key(key, held)
|
||||
if not self.get("editable") then return end
|
||||
if not self.getResolved("editable") then return end
|
||||
if not self:hasState("focused") then return end
|
||||
|
||||
local text = self.get("text")
|
||||
local cursorPos = self.get("cursorPos")
|
||||
local text = self.getResolved("text")
|
||||
local cursorPos = self.getResolved("cursorPos")
|
||||
|
||||
if key == keys.left then
|
||||
self.set("cursorPos", math.max(1, cursorPos - 1))
|
||||
@@ -193,7 +193,7 @@ function ComboBox:key(key, held)
|
||||
self.set("cursorPos", cursorPos - 1)
|
||||
self:updateViewport()
|
||||
|
||||
if self.get("autoComplete") then
|
||||
if self.getResolved("autoComplete") then
|
||||
self:updateFilteredDropdown()
|
||||
else
|
||||
self:updateRender()
|
||||
@@ -205,7 +205,7 @@ function ComboBox:key(key, held)
|
||||
self.set("text", newText)
|
||||
self:updateViewport()
|
||||
|
||||
if self.get("autoComplete") then
|
||||
if self.getResolved("autoComplete") then
|
||||
self:updateFilteredDropdown()
|
||||
else
|
||||
self:updateRender()
|
||||
@@ -238,8 +238,8 @@ function ComboBox:mouse_click(button, x, y)
|
||||
if not VisualElement.mouse_click(self, button, x, y) then return false end
|
||||
|
||||
local relX, relY = self:getRelativePosition(x, y)
|
||||
local width = self.get("width")
|
||||
local dropSymbol = self.get("dropSymbol")
|
||||
local width = self.getResolved("width")
|
||||
local dropSymbol = self.getResolved("dropSymbol")
|
||||
local isOpen = self:hasState("opened")
|
||||
|
||||
if relY == 1 then
|
||||
@@ -250,8 +250,8 @@ function ComboBox:mouse_click(button, x, y)
|
||||
self.set("manuallyOpened", false)
|
||||
else
|
||||
self:setState("opened")
|
||||
local allItems = self.get("items") or {}
|
||||
local dropdownHeight = self.get("dropdownHeight") or 5
|
||||
local allItems = self.getResolved("items") or {}
|
||||
local dropdownHeight = self.getResolved("dropdownHeight") or 5
|
||||
local actualHeight = math.min(dropdownHeight, #allItems)
|
||||
self.set("height", 1 + actualHeight)
|
||||
self.set("manuallyOpened", true)
|
||||
@@ -260,17 +260,17 @@ function ComboBox:mouse_click(button, x, y)
|
||||
return true
|
||||
end
|
||||
|
||||
if relX <= width - #dropSymbol and self.get("editable") then
|
||||
local text = self.get("text")
|
||||
local viewOffset = self.get("viewOffset")
|
||||
if relX <= width - #dropSymbol and self.getResolved("editable") then
|
||||
local text = self.getResolved("text")
|
||||
local viewOffset = self.getResolved("viewOffset")
|
||||
local maxPos = #text + 1
|
||||
local targetPos = math.min(maxPos, viewOffset + relX)
|
||||
|
||||
self.set("cursorPos", targetPos)
|
||||
if not isOpen then
|
||||
self:setState("opened")
|
||||
local allItems = self.get("items") or {}
|
||||
local dropdownHeight = self.get("dropdownHeight") or 5
|
||||
local allItems = self.getResolved("items") or {}
|
||||
local dropdownHeight = self.getResolved("dropdownHeight") or 5
|
||||
local actualHeight = math.min(dropdownHeight, #allItems)
|
||||
self.set("height", 1 + actualHeight)
|
||||
self.set("manuallyOpened", true)
|
||||
@@ -299,14 +299,14 @@ function ComboBox:mouse_up(button, x, y)
|
||||
if self:hasState("opened") then
|
||||
local relX, relY = self:getRelativePosition(x, y)
|
||||
|
||||
if relY > 1 and self.get("selectable") and not self._scrollBarDragging then
|
||||
local itemIndex = (relY - 1) + self.get("offset")
|
||||
if relY > 1 and self.getResolved("selectable") and not self._scrollBarDragging then
|
||||
local itemIndex = (relY - 1) + self.getResolved("offset")
|
||||
|
||||
local items
|
||||
if self.get("autoComplete") and not self.get("manuallyOpened") then
|
||||
if self.getResolved("autoComplete") and not self.getResolved("manuallyOpened") then
|
||||
items = self:getFilteredItems()
|
||||
else
|
||||
items = self.get("items")
|
||||
items = self.getResolved("items")
|
||||
end
|
||||
|
||||
if itemIndex <= #items then
|
||||
@@ -316,8 +316,8 @@ function ComboBox:mouse_up(button, x, y)
|
||||
items[itemIndex] = item
|
||||
end
|
||||
|
||||
if not self.get("multiSelection") then
|
||||
for _, otherItem in ipairs(self.get("items")) do
|
||||
if not self.getResolved("multiSelection") then
|
||||
for _, otherItem in ipairs(self.getResolved("items")) do
|
||||
if type(otherItem) == "table" then
|
||||
otherItem.selected = false
|
||||
end
|
||||
@@ -357,12 +357,12 @@ end
|
||||
function ComboBox:render()
|
||||
VisualElement.render(self)
|
||||
|
||||
local text = self.get("text")
|
||||
local width = self.get("width")
|
||||
local dropSymbol = self.get("dropSymbol")
|
||||
local text = self.getResolved("text")
|
||||
local width = self.getResolved("width")
|
||||
local dropSymbol = self.getResolved("dropSymbol")
|
||||
local isFocused = self:hasState("focused")
|
||||
local isOpen = self:hasState("opened")
|
||||
local viewOffset = self.get("viewOffset")
|
||||
local viewOffset = self.getResolved("viewOffset")
|
||||
local selectedText = self.getResolved("selectedText")
|
||||
local bg = self.getResolved("background")
|
||||
local fg = self.getResolved("foreground")
|
||||
@@ -387,8 +387,8 @@ function ComboBox:render()
|
||||
string.rep(tHex[fg], width),
|
||||
string.rep(tHex[bg], width))
|
||||
|
||||
if isFocused and self.get("editable") then
|
||||
local cursorPos = self.get("cursorPos")
|
||||
if isFocused and self.getResolved("editable") then
|
||||
local cursorPos = self.getResolved("cursorPos")
|
||||
local cursorX = cursorPos - viewOffset
|
||||
if cursorX >= 1 and cursorX <= textWidth then
|
||||
self:setCursor(cursorX, 1, true, fg)
|
||||
@@ -396,14 +396,14 @@ function ComboBox:render()
|
||||
end
|
||||
|
||||
if isOpen then
|
||||
local actualHeight = self.get("height")
|
||||
local items = self.get("items")
|
||||
local actualHeight = self.getResolved("height")
|
||||
local items = self.getResolved("items")
|
||||
|
||||
if self.get("autoComplete") and not self.get("manuallyOpened") then
|
||||
if self.getResolved("autoComplete") and not self.getResolved("manuallyOpened") then
|
||||
items = self:getFilteredItems()
|
||||
end
|
||||
|
||||
local dropdownHeight = math.min(self.get("dropdownHeight"), #items)
|
||||
local dropdownHeight = math.min(self.getResolved("dropdownHeight"), #items)
|
||||
|
||||
local originalItems = self._values.items
|
||||
self._values.items = items
|
||||
@@ -418,8 +418,8 @@ function ComboBox:render()
|
||||
string.rep(tHex[fg], width),
|
||||
string.rep(tHex[bg], width))
|
||||
|
||||
if isFocused and self.get("editable") then
|
||||
local cursorPos = self.get("cursorPos")
|
||||
if isFocused and self.getResolved("editable") then
|
||||
local cursorPos = self.getResolved("cursorPos")
|
||||
local cursorX = cursorPos - viewOffset
|
||||
if cursorX >= 1 and cursorX <= textWidth then
|
||||
self:setCursor(cursorX, 1, true, fg)
|
||||
|
||||
@@ -121,8 +121,8 @@ function Container:isChildVisible(child)
|
||||
if not child:isType("VisualElement") then return false end
|
||||
if(child.get("visible") == false)then return false end
|
||||
if(child._destroyed)then return false end
|
||||
local containerW, containerH = self.get("width"), self.get("height")
|
||||
local offsetX, offsetY = self.get("offsetX"), self.get("offsetY")
|
||||
local containerW, containerH = self.getResolved("width"), self.getResolved("height")
|
||||
local offsetX, offsetY = self.getResolved("offsetX"), self.getResolved("offsetY")
|
||||
|
||||
local childX, childY = child.get("x"), child.get("y")
|
||||
local childW, childH = child.get("width"), child.get("height")
|
||||
@@ -353,7 +353,7 @@ local function convertMousePosition(self, event, ...)
|
||||
local args = {...}
|
||||
if event and event:find("mouse_") then
|
||||
local button, absX, absY = ...
|
||||
local xOffset, yOffset = self.get("offsetX"), self.get("offsetY")
|
||||
local xOffset, yOffset = self.getResolved("offsetX"), self.getResolved("offsetY")
|
||||
local relX, relY = self:getRelativePosition(absX + xOffset, absY + yOffset)
|
||||
args = {button, relX, relY}
|
||||
end
|
||||
@@ -368,13 +368,13 @@ end
|
||||
--- @return boolean handled Whether the event was handled
|
||||
--- @return table? child The child that handled the event
|
||||
function Container:callChildrenEvent(visibleOnly, event, ...)
|
||||
if visibleOnly and not self.get("childrenEventsSorted") then
|
||||
if visibleOnly and not self.getResolved("childrenEventsSorted") then
|
||||
for evt in pairs(self._values.childrenEvents) do
|
||||
self:sortChildrenEvents(evt)
|
||||
end
|
||||
end
|
||||
|
||||
local children = visibleOnly and self.get("visibleChildrenEvents") or self.get("childrenEvents")
|
||||
local children = visibleOnly and self.getResolved("visibleChildrenEvents") or self.getResolved("childrenEvents")
|
||||
if children[event] then
|
||||
local events = children[event]
|
||||
for i = #events, 1, -1 do
|
||||
@@ -510,8 +510,8 @@ end
|
||||
--- @return boolean handled Whether the event was handled
|
||||
--- @protected
|
||||
function Container:key(key)
|
||||
if self.get("focusedChild") then
|
||||
return self.get("focusedChild"):dispatchEvent("key", key)
|
||||
if self.getResolved("focusedChild") then
|
||||
return self.getResolved("focusedChild"):dispatchEvent("key", key)
|
||||
end
|
||||
return true
|
||||
end
|
||||
@@ -521,8 +521,8 @@ end
|
||||
--- @return boolean handled Whether the event was handled
|
||||
--- @protected
|
||||
function Container:char(char)
|
||||
if self.get("focusedChild") then
|
||||
return self.get("focusedChild"):dispatchEvent("char", char)
|
||||
if self.getResolved("focusedChild") then
|
||||
return self.getResolved("focusedChild"):dispatchEvent("char", char)
|
||||
end
|
||||
return true
|
||||
end
|
||||
@@ -532,8 +532,8 @@ end
|
||||
--- @return boolean handled Whether the event was handled
|
||||
--- @protected
|
||||
function Container:key_up(key)
|
||||
if self.get("focusedChild") then
|
||||
return self.get("focusedChild"):dispatchEvent("key_up", key)
|
||||
if self.getResolved("focusedChild") then
|
||||
return self.getResolved("focusedChild"):dispatchEvent("key_up", key)
|
||||
end
|
||||
return true
|
||||
end
|
||||
@@ -549,7 +549,7 @@ end
|
||||
--- @return Container self The container instance
|
||||
--- @protected
|
||||
function Container:multiBlit(x, y, width, height, text, fg, bg)
|
||||
local w, h = self.get("width"), self.get("height")
|
||||
local w, h = self.getResolved("width"), self.getResolved("height")
|
||||
|
||||
width = x < 1 and math.min(width + x - 1, w) or math.min(width, math.max(0, w - x + 1))
|
||||
height = y < 1 and math.min(height + y - 1, h) or math.min(height, math.max(0, h - y + 1))
|
||||
@@ -568,7 +568,7 @@ end
|
||||
--- @return Container self The container instance
|
||||
--- @protected
|
||||
function Container:textFg(x, y, text, fg)
|
||||
local w, h = self.get("width"), self.get("height")
|
||||
local w, h = self.getResolved("width"), self.getResolved("height")
|
||||
|
||||
if y < 1 or y > h then return self end
|
||||
|
||||
@@ -589,7 +589,7 @@ end
|
||||
--- @return Container self The container instance
|
||||
--- @protected
|
||||
function Container:textBg(x, y, text, bg)
|
||||
local w, h = self.get("width"), self.get("height")
|
||||
local w, h = self.getResolved("width"), self.getResolved("height")
|
||||
|
||||
if y < 1 or y > h then return self end
|
||||
|
||||
@@ -603,7 +603,7 @@ function Container:textBg(x, y, text, bg)
|
||||
end
|
||||
|
||||
function Container:drawText(x, y, text)
|
||||
local w, h = self.get("width"), self.get("height")
|
||||
local w, h = self.getResolved("width"), self.getResolved("height")
|
||||
|
||||
if y < 1 or y > h then return self end
|
||||
|
||||
@@ -617,7 +617,7 @@ function Container:drawText(x, y, text)
|
||||
end
|
||||
|
||||
function Container:drawFg(x, y, fg)
|
||||
local w, h = self.get("width"), self.get("height")
|
||||
local w, h = self.getResolved("width"), self.getResolved("height")
|
||||
|
||||
if y < 1 or y > h then return self end
|
||||
|
||||
@@ -630,7 +630,7 @@ function Container:drawFg(x, y, fg)
|
||||
end
|
||||
|
||||
function Container:drawBg(x, y, bg)
|
||||
local w, h = self.get("width"), self.get("height")
|
||||
local w, h = self.getResolved("width"), self.getResolved("height")
|
||||
|
||||
if y < 1 or y > h then return self end
|
||||
|
||||
@@ -651,7 +651,7 @@ end
|
||||
--- @return Container self The container instance
|
||||
--- @protected
|
||||
function Container:blit(x, y, text, fg, bg)
|
||||
local w, h = self.get("width"), self.get("height")
|
||||
local w, h = self.getResolved("width"), self.getResolved("height")
|
||||
|
||||
if y < 1 or y > h then return self end
|
||||
|
||||
@@ -674,15 +674,15 @@ end
|
||||
--- @protected
|
||||
function Container:render()
|
||||
VisualElement.render(self)
|
||||
if not self.get("childrenSorted")then
|
||||
if not self.getResolved("childrenSorted")then
|
||||
self:sortChildren()
|
||||
end
|
||||
if not self.get("childrenEventsSorted")then
|
||||
if not self.getResolved("childrenEventsSorted")then
|
||||
for event in pairs(self._values.childrenEvents) do
|
||||
self:sortChildrenEvents(event)
|
||||
end
|
||||
end
|
||||
for _, child in ipairs(self.get("visibleChildren")) do
|
||||
for _, child in ipairs(self.getResolved("visibleChildren")) do
|
||||
if child == self then
|
||||
errorManager.error("CIRCULAR REFERENCE DETECTED!")
|
||||
return
|
||||
|
||||
@@ -148,8 +148,8 @@ end
|
||||
--- @shortDescription Calculates menu size based on items
|
||||
--- @private
|
||||
function ContextMenu:calculateSize()
|
||||
local items = self.get("items")
|
||||
local itemHeight = self.get("itemHeight")
|
||||
local items = self.getResolved("items")
|
||||
local itemHeight = self.getResolved("itemHeight")
|
||||
|
||||
if #items == 0 then
|
||||
self.set("width", 10)
|
||||
@@ -195,7 +195,7 @@ function ContextMenu:close()
|
||||
self.set("isOpen", false)
|
||||
self.set("visible", false)
|
||||
|
||||
local openSubmenu = self.get("openSubmenu")
|
||||
local openSubmenu = self.getResolved("openSubmenu")
|
||||
if openSubmenu and openSubmenu.menu then
|
||||
openSubmenu.menu:close()
|
||||
end
|
||||
@@ -225,8 +225,8 @@ end
|
||||
--- @return table? item Item data or nil
|
||||
--- @private
|
||||
function ContextMenu:getItemAt(y)
|
||||
local items = self.get("items")
|
||||
local itemHeight = self.get("itemHeight")
|
||||
local items = self.getResolved("items")
|
||||
local itemHeight = self.getResolved("itemHeight")
|
||||
|
||||
local index = math.floor((y - 1) / itemHeight) + 1
|
||||
|
||||
@@ -243,20 +243,20 @@ function ContextMenu:createSubmenu(submenuItems, parentItem)
|
||||
local submenu = self.parent:addContextMenu()
|
||||
submenu:setItems(submenuItems)
|
||||
|
||||
submenu.set("background", self.get("background"))
|
||||
submenu.set("foreground", self.get("foreground"))
|
||||
submenu.set("background", self.getResolved("background"))
|
||||
submenu.set("foreground", self.getResolved("foreground"))
|
||||
|
||||
submenu.parentMenu = self
|
||||
|
||||
local parentX = self.get("x")
|
||||
local parentY = self.get("y")
|
||||
local parentWidth = self.get("width")
|
||||
local itemHeight = self.get("itemHeight")
|
||||
local parentX = self.getResolved("x")
|
||||
local parentY = self.getResolved("y")
|
||||
local parentWidth = self.getResolved("width")
|
||||
local itemHeight = self.getResolved("itemHeight")
|
||||
local itemIndex = parentItem._index or 1
|
||||
|
||||
submenu.set("x", parentX + parentWidth)
|
||||
submenu.set("y", parentY + (itemIndex - 1) * itemHeight)
|
||||
submenu.set("z", self.get("z") + 1)
|
||||
submenu.set("z", self.getResolved("z") + 1)
|
||||
|
||||
return submenu
|
||||
end
|
||||
@@ -278,7 +278,7 @@ function ContextMenu:mouse_click(button, x, y)
|
||||
end
|
||||
|
||||
if item.submenu then
|
||||
local openSubmenu = self.get("openSubmenu")
|
||||
local openSubmenu = self.getResolved("openSubmenu")
|
||||
if openSubmenu and openSubmenu.index == index then
|
||||
openSubmenu.menu:close()
|
||||
self.set("openSubmenu", nil)
|
||||
@@ -312,12 +312,12 @@ end
|
||||
--- @shortDescription Renders the ContextMenu
|
||||
--- @protected
|
||||
function ContextMenu:render()
|
||||
local items = self.get("items")
|
||||
local width = self.get("width")
|
||||
local height = self.get("height")
|
||||
local itemHeight = self.get("itemHeight")
|
||||
local menuBg = self.get("background")
|
||||
local menuFg = self.get("foreground")
|
||||
local items = self.getResolved("items")
|
||||
local width = self.getResolved("width")
|
||||
local height = self.getResolved("height")
|
||||
local itemHeight = self.getResolved("itemHeight")
|
||||
local menuBg = self.getResolved("background")
|
||||
local menuFg = self.getResolved("foreground")
|
||||
|
||||
for i, item in ipairs(items) do
|
||||
local y = (i - 1) * itemHeight + 1
|
||||
@@ -342,16 +342,16 @@ function ContextMenu:render()
|
||||
end
|
||||
end
|
||||
|
||||
if not self.get("childrenSorted") then
|
||||
if not self.getResolved("childrenSorted") then
|
||||
self:sortChildren()
|
||||
end
|
||||
if not self.get("childrenEventsSorted") then
|
||||
if not self.getResolved("childrenEventsSorted") then
|
||||
for eventName in pairs(self._values.childrenEvents or {}) do
|
||||
self:sortChildrenEvents(eventName)
|
||||
end
|
||||
end
|
||||
|
||||
for _, child in ipairs(self.get("visibleChildren") or {}) do
|
||||
for _, child in ipairs(self.getResolved("visibleChildren") or {}) do
|
||||
if child == self then
|
||||
error("CIRCULAR REFERENCE DETECTED!")
|
||||
return
|
||||
|
||||
@@ -62,7 +62,7 @@ function Dialog:show()
|
||||
self:center()
|
||||
self.set("visible", true)
|
||||
-- Auto-focus when modal
|
||||
if self.get("modal") then
|
||||
if self.getResolved("modal") then
|
||||
self:setFocused(true)
|
||||
end
|
||||
return self
|
||||
@@ -91,22 +91,22 @@ function Dialog:alert(title, message, callback)
|
||||
self:addLabel({
|
||||
text = message,
|
||||
x = 2, y = 3,
|
||||
width = self.get("width") - 3,
|
||||
width = self.getResolved("width") - 3,
|
||||
height = 3,
|
||||
foreground = colors.white
|
||||
})
|
||||
|
||||
local btnWidth = 10
|
||||
local btnX = math.floor((self.get("width") - btnWidth) / 2) + 1
|
||||
local btnX = math.floor((self.getResolved("width") - btnWidth) / 2) + 1
|
||||
|
||||
self:addButton({
|
||||
text = "OK",
|
||||
x = btnX,
|
||||
y = self.get("height") - 2,
|
||||
y = self.getResolved("height") - 2,
|
||||
width = btnWidth,
|
||||
height = 1,
|
||||
background = self.get("primaryColor"),
|
||||
foreground = self.get("buttonForeground")
|
||||
background = self.getResolved("primaryColor"),
|
||||
foreground = self.getResolved("buttonForeground")
|
||||
}):onClick(function()
|
||||
if callback then callback() end
|
||||
self:close()
|
||||
@@ -129,7 +129,7 @@ function Dialog:confirm(title, message, callback)
|
||||
self:addLabel({
|
||||
text = message,
|
||||
x = 2, y = 3,
|
||||
width = self.get("width") - 3,
|
||||
width = self.getResolved("width") - 3,
|
||||
height = 3,
|
||||
foreground = colors.white
|
||||
})
|
||||
@@ -137,16 +137,16 @@ function Dialog:confirm(title, message, callback)
|
||||
local btnWidth = 10
|
||||
local spacing = 2
|
||||
local totalWidth = btnWidth * 2 + spacing
|
||||
local startX = math.floor((self.get("width") - totalWidth) / 2) + 1
|
||||
local startX = math.floor((self.getResolved("width") - totalWidth) / 2) + 1
|
||||
|
||||
self:addButton({
|
||||
text = "Cancel",
|
||||
x = startX,
|
||||
y = self.get("height") - 2,
|
||||
y = self.getResolved("height") - 2,
|
||||
width = btnWidth,
|
||||
height = 1,
|
||||
background = self.get("secondaryColor"),
|
||||
foreground = self.get("buttonForeground")
|
||||
background = self.getResolved("secondaryColor"),
|
||||
foreground = self.getResolved("buttonForeground")
|
||||
}):onClick(function()
|
||||
if callback then callback(false) end
|
||||
self:close()
|
||||
@@ -155,11 +155,11 @@ function Dialog:confirm(title, message, callback)
|
||||
self:addButton({
|
||||
text = "OK",
|
||||
x = startX + btnWidth + spacing,
|
||||
y = self.get("height") - 2,
|
||||
y = self.getResolved("height") - 2,
|
||||
width = btnWidth,
|
||||
height = 1,
|
||||
background = self.get("primaryColor"),
|
||||
foreground = self.get("buttonForeground")
|
||||
background = self.getResolved("primaryColor"),
|
||||
foreground = self.getResolved("buttonForeground")
|
||||
}):onClick(function()
|
||||
if callback then callback(true) end
|
||||
self:close()
|
||||
@@ -188,7 +188,7 @@ function Dialog:prompt(title, message, default, callback)
|
||||
|
||||
local input = self:addInput({
|
||||
x = 2, y = 5,
|
||||
width = self.get("width") - 3,
|
||||
width = self.getResolved("width") - 3,
|
||||
height = 1,
|
||||
defaultText = default or "",
|
||||
background = colors.white,
|
||||
@@ -198,16 +198,16 @@ function Dialog:prompt(title, message, default, callback)
|
||||
local btnWidth = 10
|
||||
local spacing = 2
|
||||
local totalWidth = btnWidth * 2 + spacing
|
||||
local startX = math.floor((self.get("width") - totalWidth) / 2) + 1
|
||||
local startX = math.floor((self.getResolved("width") - totalWidth) / 2) + 1
|
||||
|
||||
self:addButton({
|
||||
text = "Cancel",
|
||||
x = startX,
|
||||
y = self.get("height") - 2,
|
||||
y = self.getResolved("height") - 2,
|
||||
width = btnWidth,
|
||||
height = 1,
|
||||
background = self.get("secondaryColor"),
|
||||
foreground = self.get("buttonForeground")
|
||||
background = self.getResolved("secondaryColor"),
|
||||
foreground = self.getResolved("buttonForeground")
|
||||
}):onClick(function()
|
||||
if callback then callback(nil) end
|
||||
self:close()
|
||||
@@ -216,11 +216,11 @@ function Dialog:prompt(title, message, default, callback)
|
||||
self:addButton({
|
||||
text = "OK",
|
||||
x = startX + btnWidth + spacing,
|
||||
y = self.get("height") - 2,
|
||||
y = self.getResolved("height") - 2,
|
||||
width = btnWidth,
|
||||
height = 1,
|
||||
background = self.get("primaryColor"),
|
||||
foreground = self.get("buttonForeground")
|
||||
background = self.getResolved("primaryColor"),
|
||||
foreground = self.getResolved("buttonForeground")
|
||||
}):onClick(function()
|
||||
if callback then callback(input.get("text") or "") end
|
||||
self:close()
|
||||
@@ -235,9 +235,9 @@ end
|
||||
function Dialog:render()
|
||||
Frame.render(self)
|
||||
|
||||
local title = self.get("title")
|
||||
local title = self.getResolved("title")
|
||||
if title ~= "" then
|
||||
local width = self.get("width")
|
||||
local width = self.getResolved("width")
|
||||
local titleText = title:sub(1, width - 4)
|
||||
self:textFg(2, 2, titleText, colors.white)
|
||||
end
|
||||
@@ -247,7 +247,7 @@ end
|
||||
--- @shortDescription Handles mouse click events
|
||||
--- @protected
|
||||
function Dialog:mouse_click(button, x, y)
|
||||
if self.get("modal") then
|
||||
if self.getResolved("modal") then
|
||||
if self:isInBounds(x, y) then
|
||||
return Frame.mouse_click(self, button, x, y)
|
||||
end
|
||||
@@ -260,7 +260,7 @@ end
|
||||
--- @shortDescription Handles mouse drag events
|
||||
--- @protected
|
||||
function Dialog:mouse_drag(button, x, y)
|
||||
if self.get("modal") then
|
||||
if self.getResolved("modal") then
|
||||
if self:isInBounds(x, y) then
|
||||
return Frame.mouse_drag and Frame.mouse_drag(self, button, x, y) or false
|
||||
end
|
||||
@@ -273,7 +273,7 @@ end
|
||||
--- @shortDescription Handles mouse up events
|
||||
--- @protected
|
||||
function Dialog:mouse_up(button, x, y)
|
||||
if self.get("modal") then
|
||||
if self.getResolved("modal") then
|
||||
if self:isInBounds(x, y) then
|
||||
return Frame.mouse_up and Frame.mouse_up(self, button, x, y) or false
|
||||
end
|
||||
@@ -286,7 +286,7 @@ end
|
||||
--- @shortDescription Handles mouse scroll events
|
||||
--- @protected
|
||||
function Dialog:mouse_scroll(direction, x, y)
|
||||
if self.get("modal") then
|
||||
if self.getResolved("modal") then
|
||||
if self:isInBounds(x, y) then
|
||||
return Frame.mouse_scroll and Frame.mouse_scroll(self, direction, x, y) or false
|
||||
end
|
||||
|
||||
@@ -50,7 +50,7 @@ end
|
||||
function Display:init(props, basalt)
|
||||
VisualElement.init(self, props, basalt)
|
||||
self.set("type", "Display")
|
||||
self._window = window.create(basalt.getActiveFrame():getTerm(), 1, 1, self.get("width"), self.get("height"), false)
|
||||
self._window = window.create(basalt.getActiveFrame():getTerm(), 1, 1, self.getResolved("width"), self.getResolved("height"), false)
|
||||
local reposition = self._window.reposition
|
||||
local blit = self._window.blit
|
||||
local write = self._window.write
|
||||
@@ -63,7 +63,7 @@ function Display:init(props, basalt)
|
||||
end
|
||||
|
||||
self._window.getPosition = function(self)
|
||||
return self.get("x"), self.get("y")
|
||||
return self.getResolved("x"), self.getResolved("y")
|
||||
end
|
||||
|
||||
self._window.setVisible = function(visible)
|
||||
@@ -71,7 +71,7 @@ function Display:init(props, basalt)
|
||||
end
|
||||
|
||||
self._window.isVisible = function(self)
|
||||
return self.get("visible")
|
||||
return self.getResolved("visible")
|
||||
end
|
||||
self._window.blit = function(x, y, text, fg, bg)
|
||||
blit(x, y, text, fg, bg)
|
||||
@@ -85,13 +85,13 @@ function Display:init(props, basalt)
|
||||
self:observe("width", function(self, width)
|
||||
local window = self._window
|
||||
if window then
|
||||
window.reposition(1, 1, width, self.get("height"))
|
||||
window.reposition(1, 1, width, self.getResolved("height"))
|
||||
end
|
||||
end)
|
||||
self:observe("height", function(self, height)
|
||||
local window = self._window
|
||||
if window then
|
||||
window.reposition(1, 1, self.get("width"), height)
|
||||
window.reposition(1, 1, self.getResolved("width"), height)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -107,7 +107,7 @@ function DropDown:mouse_click(button, x, y)
|
||||
self.set("height", 1)
|
||||
self:unsetState("opened")
|
||||
else
|
||||
self.set("height", 1 + math.min(self.get("dropdownHeight"), #self.get("items")))
|
||||
self.set("height", 1 + math.min(self.getResolved("dropdownHeight"), #self.getResolved("items")))
|
||||
self:setState("opened")
|
||||
end
|
||||
return true
|
||||
@@ -140,9 +140,9 @@ function DropDown:mouse_up(button, x, y)
|
||||
if self:hasState("opened") then
|
||||
local relX, relY = self:getRelativePosition(x, y)
|
||||
|
||||
if relY > 1 and self.get("selectable") and not self._scrollBarDragging then
|
||||
local itemIndex = (relY - 1) + self.get("offset")
|
||||
local items = self.get("items")
|
||||
if relY > 1 and self.getResolved("selectable") and not self._scrollBarDragging then
|
||||
local itemIndex = (relY - 1) + self.getResolved("offset")
|
||||
local items = self.getResolved("items")
|
||||
|
||||
if itemIndex <= #items then
|
||||
local item = items[itemIndex]
|
||||
@@ -151,7 +151,7 @@ function DropDown:mouse_up(button, x, y)
|
||||
items[itemIndex] = item
|
||||
end
|
||||
|
||||
if not self.get("multiSelection") then
|
||||
if not self.getResolved("multiSelection") then
|
||||
for _, otherItem in ipairs(items) do
|
||||
if type(otherItem) == "table" then
|
||||
otherItem.selected = false
|
||||
@@ -187,26 +187,28 @@ end
|
||||
function DropDown:render()
|
||||
VisualElement.render(self)
|
||||
|
||||
local text = self.get("selectedText")
|
||||
local width = self.getResolved("width")
|
||||
local height = self.getResolved("height")
|
||||
local text = self.getResolved("selectedText")
|
||||
local isOpen = self:hasState("opened")
|
||||
local selectedItems = self:getSelectedItems()
|
||||
if #selectedItems > 0 then
|
||||
local selectedItem = selectedItems[1]
|
||||
text = selectedItem.text or ""
|
||||
text = text:sub(1, self.get("width") - 2)
|
||||
text = text:sub(1, width - 2)
|
||||
end
|
||||
|
||||
if isOpen then
|
||||
local actualHeight = self.get("height")
|
||||
local dropdownHeight = math.min(self.get("dropdownHeight"), #self.get("items"))
|
||||
local actualHeight = height
|
||||
local dropdownHeight = math.min(self.getResolved("dropdownHeight"), #self.getResolved("items"))
|
||||
self.set("height", dropdownHeight)
|
||||
List.render(self, 1)
|
||||
self.set("height", actualHeight)
|
||||
end
|
||||
|
||||
self:blit(1, 1, text .. string.rep(" ", self.get("width") - #text - 1) .. (isOpen and self.dropSymbol or self.undropSymbol),
|
||||
string.rep(tHex[self.getResolved("foreground")], self.get("width")),
|
||||
string.rep(tHex[self.getResolved("background")], self.get("width")))
|
||||
self:blit(1, 1, text .. string.rep(" ", width - #text - 1) .. (isOpen and self.getResolved("dropSymbol") or self.getResolved("undropSymbol")),
|
||||
string.rep(tHex[self.getResolved("foreground")], width),
|
||||
string.rep(tHex[self.getResolved("background")], width))
|
||||
end
|
||||
|
||||
--- Called when the DropDown gains focus
|
||||
|
||||
@@ -53,21 +53,21 @@ end
|
||||
--- @protected
|
||||
function Frame:mouse_click(button, x, y)
|
||||
if self:isInBounds(x, y) then
|
||||
if self.get("draggable") then
|
||||
if self.getResolved("draggable") then
|
||||
local relX, relY = self:getRelativePosition(x, y)
|
||||
local draggingMap = self.get("draggingMap")
|
||||
local draggingMap = self.getResolved("draggingMap")
|
||||
|
||||
for _, map in ipairs(draggingMap) do
|
||||
local width = map.width or 1
|
||||
local height = map.height or 1
|
||||
|
||||
if type(width) == "string" and width == "width" then
|
||||
width = self.get("width")
|
||||
width = self.getResolved("width")
|
||||
elseif type(width) == "function" then
|
||||
width = width(self)
|
||||
end
|
||||
if type(height) == "string" and height == "height" then
|
||||
height = self.get("height")
|
||||
height = self.getResolved("height")
|
||||
elseif type(height) == "function" then
|
||||
height = height(self)
|
||||
end
|
||||
@@ -75,8 +75,8 @@ function Frame:mouse_click(button, x, y)
|
||||
local mapY = map.y or 1
|
||||
if relX >= map.x and relX <= map.x + width - 1 and
|
||||
relY >= mapY and relY <= mapY + height - 1 then
|
||||
self.dragStartX = x - self.get("x")
|
||||
self.dragStartY = y - self.get("y")
|
||||
self.dragStartX = x - self.getResolved("x")
|
||||
self.dragStartY = y - self.getResolved("y")
|
||||
self.dragging = true
|
||||
return true
|
||||
end
|
||||
@@ -126,7 +126,7 @@ end
|
||||
--- @protected
|
||||
function Frame:getChildrenHeight()
|
||||
local maxHeight = 0
|
||||
local children = self.get("children")
|
||||
local children = self.getResolved("children")
|
||||
|
||||
for _, child in ipairs(children) do
|
||||
if child.get("visible") then
|
||||
@@ -147,7 +147,7 @@ local function convertMousePosition(self, event, ...)
|
||||
local args = {...}
|
||||
if event and event:find("mouse_") then
|
||||
local button, absX, absY = ...
|
||||
local xOffset, yOffset = self.get("offsetX"), self.get("offsetY")
|
||||
local xOffset, yOffset = self.getResolved("offsetX"), self.getResolved("offsetY")
|
||||
local relX, relY = self:getRelativePosition(absX + xOffset, absY + yOffset)
|
||||
args = {button, relX, relY}
|
||||
end
|
||||
@@ -167,11 +167,11 @@ function Frame:mouse_scroll(direction, x, y)
|
||||
if success then
|
||||
return true
|
||||
end
|
||||
if self.get("scrollable") then
|
||||
local height = self.get("height")
|
||||
if self.getResolved("scrollable") then
|
||||
local height = self.getResolved("height")
|
||||
|
||||
local childrenHeight = self:getChildrenHeight()
|
||||
local currentOffset = self.get("offsetY")
|
||||
local currentOffset = self.getResolved("offsetY")
|
||||
local maxScroll = math.max(0, childrenHeight - height)
|
||||
|
||||
local newOffset = currentOffset + direction
|
||||
|
||||
@@ -60,13 +60,13 @@ end
|
||||
--- @param pointCount number The number of points in the series
|
||||
--- @return Graph self The graph instance
|
||||
function Graph:addSeries(name, symbol, bgCol, fgCol, pointCount)
|
||||
local series = self.get("series")
|
||||
local series = self.getResolved("series")
|
||||
table.insert(series, {
|
||||
name = name,
|
||||
symbol = symbol or " ",
|
||||
bgColor = bgCol or colors.white,
|
||||
fgColor = fgCol or colors.black,
|
||||
pointCount = pointCount or self.get("width"),
|
||||
pointCount = pointCount or self.getResolved("width"),
|
||||
data = {},
|
||||
visible = true
|
||||
})
|
||||
@@ -78,7 +78,7 @@ end
|
||||
--- @param name string The name of the series
|
||||
--- @return Graph self The graph instance
|
||||
function Graph:removeSeries(name)
|
||||
local series = self.get("series")
|
||||
local series = self.getResolved("series")
|
||||
for i, s in ipairs(series) do
|
||||
if s.name == name then
|
||||
table.remove(series, i)
|
||||
@@ -93,7 +93,7 @@ end
|
||||
--- @param name string The name of the series
|
||||
--- @return table? series The series
|
||||
function Graph:getSeries(name)
|
||||
local series = self.get("series")
|
||||
local series = self.getResolved("series")
|
||||
for _, s in ipairs(series) do
|
||||
if s.name == name then
|
||||
return s
|
||||
@@ -107,7 +107,7 @@ end
|
||||
--- @param visible boolean Whether the series should be visible
|
||||
--- @return Graph self The graph instance
|
||||
function Graph:changeSeriesVisibility(name, visible)
|
||||
local series = self.get("series")
|
||||
local series = self.getResolved("series")
|
||||
for _, s in ipairs(series) do
|
||||
if s.name == name then
|
||||
s.visible = visible
|
||||
@@ -123,7 +123,7 @@ end
|
||||
--- @param value number The value of the point
|
||||
--- @return Graph self The graph instance
|
||||
function Graph:addPoint(name, value)
|
||||
local series = self.get("series")
|
||||
local series = self.getResolved("series")
|
||||
|
||||
for _, s in ipairs(series) do
|
||||
if s.name == name then
|
||||
@@ -142,7 +142,7 @@ end
|
||||
--- @param name string The name of the series
|
||||
--- @return Graph self The graph instance
|
||||
function Graph:focusSeries(name)
|
||||
local series = self.get("series")
|
||||
local series = self.getResolved("series")
|
||||
for index, s in ipairs(series) do
|
||||
if s.name == name then
|
||||
table.remove(series, index)
|
||||
@@ -159,7 +159,7 @@ end
|
||||
--- @param count number The number of points in the series
|
||||
--- @return Graph self The graph instance
|
||||
function Graph:setSeriesPointCount(name, count)
|
||||
local series = self.get("series")
|
||||
local series = self.getResolved("series")
|
||||
for _, s in ipairs(series) do
|
||||
if s.name == name then
|
||||
s.pointCount = count
|
||||
@@ -178,7 +178,7 @@ end
|
||||
--- @param name? string The name of the series
|
||||
--- @return Graph self The graph instance
|
||||
function Graph:clear(seriesName)
|
||||
local series = self.get("series")
|
||||
local series = self.getResolved("series")
|
||||
if seriesName then
|
||||
for _, s in ipairs(series) do
|
||||
if s.name == seriesName then
|
||||
@@ -199,11 +199,11 @@ end
|
||||
function Graph:render()
|
||||
VisualElement.render(self)
|
||||
|
||||
local width = self.get("width")
|
||||
local height = self.get("height")
|
||||
local minVal = self.get("minValue")
|
||||
local maxVal = self.get("maxValue")
|
||||
local series = self.get("series")
|
||||
local width = self.getResolved("width")
|
||||
local height = self.getResolved("height")
|
||||
local minVal = self.getResolved("minValue")
|
||||
local maxVal = self.getResolved("maxValue")
|
||||
local series = self.getResolved("series")
|
||||
|
||||
for _, s in pairs(series) do
|
||||
if(s.visible)then
|
||||
|
||||
@@ -53,7 +53,7 @@ end
|
||||
--- @param height number The new height of the image
|
||||
--- @return Image self The Image instance
|
||||
function Image:resizeImage(width, height)
|
||||
local frames = self.get("bimg")
|
||||
local frames = self.getResolved("bimg")
|
||||
|
||||
for frameIndex, frame in ipairs(frames) do
|
||||
local newFrame = {}
|
||||
@@ -86,7 +86,7 @@ end
|
||||
--- @return number width The width of the image
|
||||
--- @return number height The height of the image
|
||||
function Image:getImageSize()
|
||||
local bimg = self.get("bimg")
|
||||
local bimg = self.getResolved("bimg")
|
||||
if not bimg[1] or not bimg[1][1] then return 0, 0 end
|
||||
return #bimg[1][1][1], #bimg[1]
|
||||
end
|
||||
@@ -99,7 +99,7 @@ end
|
||||
--- @return number? bg Background color
|
||||
--- @return string? char Character at position
|
||||
function Image:getPixelData(x, y)
|
||||
local frame = self.get("bimg")[self.get("currentFrame")]
|
||||
local frame = self.getResolved("bimg")[self.getResolved("currentFrame")]
|
||||
if not frame or not frame[y] then return end
|
||||
|
||||
local text = frame[y][1]
|
||||
@@ -116,10 +116,10 @@ function Image:getPixelData(x, y)
|
||||
end
|
||||
|
||||
local function ensureFrame(self, y)
|
||||
local frame = self.get("bimg")[self.get("currentFrame")]
|
||||
local frame = self.getResolved("bimg")[self.getResolved("currentFrame")]
|
||||
if not frame then
|
||||
frame = {}
|
||||
self.get("bimg")[self.get("currentFrame")] = frame
|
||||
self.getResolved("bimg")[self.getResolved("currentFrame")] = frame
|
||||
end
|
||||
if not frame[y] then
|
||||
frame[y] = {"", "", ""}
|
||||
@@ -128,9 +128,9 @@ local function ensureFrame(self, y)
|
||||
end
|
||||
|
||||
local function updateFrameSize(self, neededWidth, neededHeight)
|
||||
if not self.get("autoResize") then return end
|
||||
if not self.getResolved("autoResize") then return end
|
||||
|
||||
local frames = self.get("bimg")
|
||||
local frames = self.getResolved("bimg")
|
||||
|
||||
local maxWidth = neededWidth
|
||||
local maxHeight = neededHeight
|
||||
@@ -164,13 +164,13 @@ end
|
||||
--- @return Image self The Image instance
|
||||
function Image:setText(x, y, text)
|
||||
if type(text) ~= "string" or #text < 1 or x < 1 or y < 1 then return self end
|
||||
if not self.get("autoResize")then
|
||||
if not self.getResolved("autoResize")then
|
||||
local imgWidth, imgHeight = self:getImageSize()
|
||||
if y > imgHeight then return self end
|
||||
end
|
||||
local frame = ensureFrame(self, y)
|
||||
|
||||
if self.get("autoResize") then
|
||||
if self.getResolved("autoResize") then
|
||||
updateFrameSize(self, x + #text - 1, y)
|
||||
else
|
||||
local maxLen = #frame[y][1]
|
||||
@@ -193,7 +193,7 @@ end
|
||||
--- @return string text The text at the specified position
|
||||
function Image:getText(x, y, length)
|
||||
if not x or not y then return "" end
|
||||
local frame = self.get("bimg")[self.get("currentFrame")]
|
||||
local frame = self.getResolved("bimg")[self.getResolved("currentFrame")]
|
||||
if not frame or not frame[y] then return "" end
|
||||
|
||||
local text = frame[y][1]
|
||||
@@ -214,13 +214,13 @@ end
|
||||
--- @return Image self The Image instance
|
||||
function Image:setFg(x, y, pattern)
|
||||
if type(pattern) ~= "string" or #pattern < 1 or x < 1 or y < 1 then return self end
|
||||
if not self.get("autoResize")then
|
||||
if not self.getResolved("autoResize")then
|
||||
local imgWidth, imgHeight = self:getImageSize()
|
||||
if y > imgHeight then return self end
|
||||
end
|
||||
local frame = ensureFrame(self, y)
|
||||
|
||||
if self.get("autoResize") then
|
||||
if self.getResolved("autoResize") then
|
||||
updateFrameSize(self, x + #pattern - 1, y)
|
||||
else
|
||||
local maxLen = #frame[y][2]
|
||||
@@ -243,7 +243,7 @@ end
|
||||
--- @return string fg The foreground color pattern
|
||||
function Image:getFg(x, y, length)
|
||||
if not x or not y then return "" end
|
||||
local frame = self.get("bimg")[self.get("currentFrame")]
|
||||
local frame = self.getResolved("bimg")[self.getResolved("currentFrame")]
|
||||
if not frame or not frame[y] then return "" end
|
||||
|
||||
local fg = frame[y][2]
|
||||
@@ -264,13 +264,13 @@ end
|
||||
--- @return Image self The Image instance
|
||||
function Image:setBg(x, y, pattern)
|
||||
if type(pattern) ~= "string" or #pattern < 1 or x < 1 or y < 1 then return self end
|
||||
if not self.get("autoResize")then
|
||||
if not self.getResolved("autoResize")then
|
||||
local imgWidth, imgHeight = self:getImageSize()
|
||||
if y > imgHeight then return self end
|
||||
end
|
||||
local frame = ensureFrame(self, y)
|
||||
|
||||
if self.get("autoResize") then
|
||||
if self.getResolved("autoResize") then
|
||||
updateFrameSize(self, x + #pattern - 1, y)
|
||||
else
|
||||
local maxLen = #frame[y][3]
|
||||
@@ -293,7 +293,7 @@ end
|
||||
--- @return string bg The background color pattern
|
||||
function Image:getBg(x, y, length)
|
||||
if not x or not y then return "" end
|
||||
local frame = self.get("bimg")[self.get("currentFrame")]
|
||||
local frame = self.getResolved("bimg")[self.getResolved("currentFrame")]
|
||||
if not frame or not frame[y] then return "" end
|
||||
|
||||
local bg = frame[y][3]
|
||||
@@ -325,10 +325,10 @@ end
|
||||
--- @shortDescription Advances to the next frame in the animation
|
||||
--- @return Image self The Image instance
|
||||
function Image:nextFrame()
|
||||
if not self.get("bimg").animation then return self end
|
||||
if not self.getResolved("bimg").animation then return self end
|
||||
|
||||
local frames = self.get("bimg")
|
||||
local current = self.get("currentFrame")
|
||||
local frames = self.getResolved("bimg")
|
||||
local current = self.getResolved("currentFrame")
|
||||
local next = current + 1
|
||||
if next > #frames then next = 1 end
|
||||
|
||||
@@ -340,7 +340,7 @@ end
|
||||
--- @shortDescription Adds a new frame to the image
|
||||
--- @return Image self The Image instance
|
||||
function Image:addFrame()
|
||||
local frames = self.get("bimg")
|
||||
local frames = self.getResolved("bimg")
|
||||
local width = frames.width or #frames[1][1][1]
|
||||
local height = frames.height or #frames[1]
|
||||
local frame = {}
|
||||
@@ -360,7 +360,7 @@ end
|
||||
--- @param frame table The new frame data
|
||||
--- @return Image self The Image instance
|
||||
function Image:updateFrame(frameIndex, frame)
|
||||
local frames = self.get("bimg")
|
||||
local frames = self.getResolved("bimg")
|
||||
frames[frameIndex] = frame
|
||||
self:updateRender()
|
||||
return self
|
||||
@@ -371,8 +371,8 @@ end
|
||||
--- @param frameIndex number The index of the frame to get
|
||||
--- @return table frame The frame data
|
||||
function Image:getFrame(frameIndex)
|
||||
local frames = self.get("bimg")
|
||||
return frames[frameIndex or self.get("currentFrame")]
|
||||
local frames = self.getResolved("bimg")
|
||||
return frames[frameIndex or self.getResolved("currentFrame")]
|
||||
end
|
||||
|
||||
--- Gets the metadata of the image
|
||||
@@ -380,7 +380,7 @@ end
|
||||
--- @return table metadata The metadata of the image
|
||||
function Image:getMetadata()
|
||||
local metadata = {}
|
||||
local bimg = self.get("bimg")
|
||||
local bimg = self.getResolved("bimg")
|
||||
for k,v in pairs(bimg)do
|
||||
if(type(v)=="string")then
|
||||
metadata[k] = v
|
||||
@@ -401,7 +401,7 @@ function Image:setMetadata(key, value)
|
||||
end
|
||||
return self
|
||||
end
|
||||
local bimg = self.get("bimg")
|
||||
local bimg = self.getResolved("bimg")
|
||||
if(type(value)=="string")then
|
||||
bimg[key] = value
|
||||
end
|
||||
@@ -413,13 +413,13 @@ end
|
||||
function Image:render()
|
||||
VisualElement.render(self)
|
||||
|
||||
local frame = self.get("bimg")[self.get("currentFrame")]
|
||||
local frame = self.getResolved("bimg")[self.getResolved("currentFrame")]
|
||||
if not frame then return end
|
||||
|
||||
local offsetX = self.get("offsetX")
|
||||
local offsetY = self.get("offsetY")
|
||||
local elementWidth = self.get("width")
|
||||
local elementHeight = self.get("height")
|
||||
local offsetX = self.getResolved("offsetX")
|
||||
local offsetY = self.getResolved("offsetY")
|
||||
local elementWidth = self.getResolved("width")
|
||||
local elementHeight = self.getResolved("height")
|
||||
|
||||
for y = 1, elementHeight do
|
||||
local frameY = y + offsetY
|
||||
|
||||
@@ -62,7 +62,7 @@ end
|
||||
--- @param blink boolean Whether the cursor should blink
|
||||
--- @param color number The color of the cursor
|
||||
function Input:setCursor(x, y, blink, color)
|
||||
x = math.min(self.get("width"), math.max(1, x))
|
||||
x = math.min(self.getResolved("width"), math.max(1, x))
|
||||
return VisualElement.setCursor(self, x, y, blink, color)
|
||||
end
|
||||
|
||||
@@ -72,10 +72,10 @@ end
|
||||
--- @protected
|
||||
function Input:char(char)
|
||||
if not self:hasState("focused") then return false end
|
||||
local text = self.get("text")
|
||||
local pos = self.get("cursorPos")
|
||||
local maxLength = self.get("maxLength")
|
||||
local pattern = self.get("pattern")
|
||||
local text = self.getResolved("text")
|
||||
local pos = self.getResolved("cursorPos")
|
||||
local maxLength = self.getResolved("maxLength")
|
||||
local pattern = self.getResolved("pattern")
|
||||
|
||||
if maxLength and #text >= maxLength then return false end
|
||||
if pattern and not char:match(pattern) then return false end
|
||||
@@ -84,8 +84,8 @@ function Input:char(char)
|
||||
self.set("cursorPos", pos + 1)
|
||||
self:updateViewport()
|
||||
|
||||
local relPos = self.get("cursorPos") - self.get("viewOffset")
|
||||
self:setCursor(relPos, 1, true, self.get("cursorColor") or self.get("foreground"))
|
||||
local relPos = self.getResolved("cursorPos") - self.getResolved("viewOffset")
|
||||
self:setCursor(relPos, 1, true, self.getResolved("cursorColor") or self.getResolved("foreground"))
|
||||
VisualElement.char(self, char)
|
||||
return true
|
||||
end
|
||||
@@ -96,10 +96,10 @@ end
|
||||
--- @protected
|
||||
function Input:key(key, held)
|
||||
if not self:hasState("focused") then return false end
|
||||
local pos = self.get("cursorPos")
|
||||
local text = self.get("text")
|
||||
local viewOffset = self.get("viewOffset")
|
||||
local width = self.get("width")
|
||||
local pos = self.getResolved("cursorPos")
|
||||
local text = self.getResolved("text")
|
||||
local viewOffset = self.getResolved("viewOffset")
|
||||
local width = self.getResolved("width")
|
||||
|
||||
if key == keys.left then
|
||||
if pos > 1 then
|
||||
@@ -124,7 +124,7 @@ function Input:key(key, held)
|
||||
end
|
||||
end
|
||||
|
||||
local relativePos = self.get("cursorPos") - self.get("viewOffset")
|
||||
local relativePos = self.getResolved("cursorPos") - self.getResolved("viewOffset")
|
||||
self:setCursor(relativePos, 1, true, self.getResolved("cursorColor") or self.getResolved("foreground"))
|
||||
VisualElement.key(self, key, held)
|
||||
return true
|
||||
@@ -139,8 +139,8 @@ end
|
||||
function Input:mouse_click(button, x, y)
|
||||
if VisualElement.mouse_click(self, button, x, y) then
|
||||
local relX, relY = self:getRelativePosition(x, y)
|
||||
local text = self.get("text")
|
||||
local viewOffset = self.get("viewOffset")
|
||||
local text = self.getResolved("text")
|
||||
local viewOffset = self.getResolved("viewOffset")
|
||||
|
||||
local maxPos = #text + 1
|
||||
local targetPos = math.min(maxPos, viewOffset + relX)
|
||||
@@ -158,10 +158,10 @@ end
|
||||
--- @shortDescription Updates the input's viewport
|
||||
--- @return Input self The updated instance
|
||||
function Input:updateViewport()
|
||||
local width = self.get("width")
|
||||
local cursorPos = self.get("cursorPos")
|
||||
local viewOffset = self.get("viewOffset")
|
||||
local textLength = #self.get("text")
|
||||
local width = self.getResolved("width")
|
||||
local cursorPos = self.getResolved("cursorPos")
|
||||
local viewOffset = self.getResolved("viewOffset")
|
||||
local textLength = #self.getResolved("text")
|
||||
|
||||
if cursorPos - viewOffset >= width then
|
||||
self.set("viewOffset", cursorPos - width + 1)
|
||||
@@ -169,7 +169,7 @@ function Input:updateViewport()
|
||||
self.set("viewOffset", cursorPos - 1)
|
||||
end
|
||||
|
||||
self.set("viewOffset", math.max(0, math.min(self.get("viewOffset"), textLength - width + 1)))
|
||||
self.set("viewOffset", math.max(0, math.min(self.getResolved("viewOffset"), textLength - width + 1)))
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -178,7 +178,7 @@ end
|
||||
--- @protected
|
||||
function Input:focus()
|
||||
VisualElement.focus(self)
|
||||
self:setCursor(self.get("cursorPos") - self.get("viewOffset"), 1, true, self.getResolved("cursorColor") or self.getResolved("foreground"))
|
||||
self:setCursor(self.getResolved("cursorPos") - self.getResolved("viewOffset"), 1, true, self.getResolved("cursorColor") or self.getResolved("foreground"))
|
||||
self:updateRender()
|
||||
end
|
||||
|
||||
@@ -194,10 +194,10 @@ end
|
||||
--- @protected
|
||||
function Input:paste(content)
|
||||
if not self:hasState("focused") then return false end
|
||||
local text = self.get("text")
|
||||
local pos = self.get("cursorPos")
|
||||
local maxLength = self.get("maxLength")
|
||||
local pattern = self.get("pattern")
|
||||
local text = self.getResolved("text")
|
||||
local pos = self.getResolved("cursorPos")
|
||||
local maxLength = self.getResolved("maxLength")
|
||||
local pattern = self.getResolved("pattern")
|
||||
local newText = text:sub(1, pos - 1) .. content .. text:sub(pos)
|
||||
if maxLength and #newText > maxLength then
|
||||
newText = newText:sub(1, maxLength)
|
||||
@@ -214,10 +214,10 @@ end
|
||||
--- @protected
|
||||
function Input:render()
|
||||
local text = self.getResolved("text")
|
||||
local viewOffset = self.get("viewOffset")
|
||||
local viewOffset = self.getResolved("viewOffset")
|
||||
local placeholder = self.getResolved("placeholder")
|
||||
local focused = self:hasState("focused")
|
||||
local width, height = self.get("width"), self.get("height")
|
||||
local width, height = self.getResolved("width"), self.getResolved("height")
|
||||
local replaceChar = self.getResolved("replaceChar")
|
||||
self:multiBlit(1, 1, width, height, " ", tHex[self.getResolved("foreground")], tHex[self.getResolved("background")])
|
||||
|
||||
@@ -227,7 +227,7 @@ function Input:render()
|
||||
end
|
||||
|
||||
if(focused) then
|
||||
self:setCursor(self.get("cursorPos") - viewOffset, 1, true, self.getResolved("cursorColor") or self.getResolved("foreground"))
|
||||
self:setCursor(self.getResolved("cursorPos") - viewOffset, 1, true, self.getResolved("cursorColor") or self.getResolved("foreground"))
|
||||
end
|
||||
|
||||
local visibleText = text:sub(viewOffset + 1, viewOffset + width)
|
||||
|
||||
@@ -11,10 +11,10 @@ Label.__index = Label
|
||||
---@property text string Label The text content to display. Can be a string or a function that returns a string
|
||||
Label.defineProperty(Label, "text", {default = "Label", type = "string", canTriggerRender = true, setter = function(self, value)
|
||||
if(type(value)=="function")then value = value() end
|
||||
if(self.get("autoSize"))then
|
||||
if(self.getResolved("autoSize"))then
|
||||
self.set("width", #value)
|
||||
else
|
||||
self.set("height", #wrapText(value, self.get("width")))
|
||||
self.set("height", #wrapText(value, self.getResolved("width")))
|
||||
end
|
||||
return value
|
||||
end})
|
||||
@@ -22,9 +22,9 @@ end})
|
||||
---@property autoSize boolean true Whether the label should automatically resize its width based on the text content
|
||||
Label.defineProperty(Label, "autoSize", {default = true, type = "boolean", canTriggerRender = true, setter = function(self, value)
|
||||
if(value)then
|
||||
self.set("width", #self.get("text"))
|
||||
self.set("width", #self.getResolved("text"))
|
||||
else
|
||||
self.set("height", #wrapText(self.get("text"), self.get("width")))
|
||||
self.set("height", #wrapText(self.getResolved("text"), self.getResolved("width")))
|
||||
end
|
||||
return value
|
||||
end})
|
||||
@@ -61,8 +61,8 @@ end
|
||||
--- @shortDescription Gets the wrapped lines of the Label
|
||||
--- @return table wrappedText The wrapped lines of the Label
|
||||
function Label:getWrappedText()
|
||||
local text = self.get("text")
|
||||
local wrappedText = wrapText(text, self.get("width"))
|
||||
local text = self.getResolved("text")
|
||||
local wrappedText = wrapText(text, self.getResolved("width"))
|
||||
return wrappedText
|
||||
end
|
||||
|
||||
@@ -70,13 +70,13 @@ end
|
||||
--- @protected
|
||||
function Label:render()
|
||||
VisualElement.render(self)
|
||||
local text = self.get("text")
|
||||
if(self.get("autoSize"))then
|
||||
self:textFg(1, 1, text, self.get("foreground"))
|
||||
local text = self.getResolved("text")
|
||||
if(self.getResolved("autoSize"))then
|
||||
self:textFg(1, 1, text, self.getResolved("foreground"))
|
||||
else
|
||||
local wrappedText = wrapText(text, self.get("width"))
|
||||
local wrappedText = wrapText(text, self.getResolved("width"))
|
||||
for i, line in ipairs(wrappedText) do
|
||||
self:textFg(1, i, line, self.get("foreground"))
|
||||
self:textFg(1, i, line, self.getResolved("foreground"))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -53,7 +53,7 @@ local function drawLine(self, x1, y1, x2, y2, symbol, bgColor, fgColor)
|
||||
local t = steps == 0 and 0 or i / steps
|
||||
local x = math.floor(x1 + dx * t)
|
||||
local y = math.floor(y1 + dy * t)
|
||||
if x >= 1 and x <= self.get("width") and y >= 1 and y <= self.get("height") then
|
||||
if x >= 1 and x <= self.getResolved("width") and y >= 1 and y <= self.getResolved("height") then
|
||||
self:blit(x, y, symbol, tHex[bgColor], tHex[fgColor])
|
||||
end
|
||||
end
|
||||
@@ -64,11 +64,11 @@ end
|
||||
function LineChart:render()
|
||||
VisualElement.render(self)
|
||||
|
||||
local width = self.get("width")
|
||||
local height = self.get("height")
|
||||
local minVal = self.get("minValue")
|
||||
local maxVal = self.get("maxValue")
|
||||
local series = self.get("series")
|
||||
local width = self.getResolved("width")
|
||||
local height = self.getResolved("height")
|
||||
local minVal = self.getResolved("minValue")
|
||||
local maxVal = self.getResolved("maxValue")
|
||||
local series = self.getResolved("series")
|
||||
|
||||
for _, s in pairs(series) do
|
||||
if(s.visible)then
|
||||
|
||||
@@ -14,7 +14,7 @@ List.defineProperty(List, "offset", {
|
||||
type = "number",
|
||||
canTriggerRender = true,
|
||||
setter = function(self, value)
|
||||
local maxOffset = math.max(0, #self.get("items") - self.get("height"))
|
||||
local maxOffset = math.max(0, #self.getResolved("items") - self.getResolved("height"))
|
||||
return math.min(maxOffset, math.max(0, value))
|
||||
end
|
||||
})
|
||||
@@ -86,15 +86,15 @@ function List:init(props, basalt)
|
||||
self.set("type", "List")
|
||||
|
||||
self:observe("items", function()
|
||||
local maxOffset = math.max(0, #self.get("items") - self.get("height"))
|
||||
if self.get("offset") > maxOffset then
|
||||
local maxOffset = math.max(0, #self.getResolved("items") - self.getResolved("height"))
|
||||
if self.getResolved("offset") > maxOffset then
|
||||
self.set("offset", maxOffset)
|
||||
end
|
||||
end)
|
||||
|
||||
self:observe("height", function()
|
||||
local maxOffset = math.max(0, #self.get("items") - self.get("height"))
|
||||
if self.get("offset") > maxOffset then
|
||||
local maxOffset = math.max(0, #self.getResolved("items") - self.getResolved("height"))
|
||||
if self.getResolved("offset") > maxOffset then
|
||||
self.set("offset", maxOffset)
|
||||
end
|
||||
end)
|
||||
@@ -111,16 +111,16 @@ end
|
||||
function List:mouse_click(button, x, y)
|
||||
if Collection.mouse_click(self, button, x, y) then
|
||||
local relX, relY = self:getRelativePosition(x, y)
|
||||
local width = self.get("width")
|
||||
local items = self.get("items")
|
||||
local height = self.get("height")
|
||||
local showScrollBar = self.get("showScrollBar")
|
||||
local width = self.getResolved("width")
|
||||
local items = self.getResolved("items")
|
||||
local height = self.getResolved("height")
|
||||
local showScrollBar = self.getResolved("showScrollBar")
|
||||
|
||||
if showScrollBar and #items > height and relX == width then
|
||||
local maxOffset = #items - height
|
||||
local handleSize = math.max(1, math.floor((height / #items) * height))
|
||||
|
||||
local currentPercent = maxOffset > 0 and (self.get("offset") / maxOffset * 100) or 0
|
||||
local currentPercent = maxOffset > 0 and (self.getResolved("offset") / maxOffset * 100) or 0
|
||||
local handlePos = math.floor((currentPercent / 100) * (height - handleSize)) + 1
|
||||
|
||||
if relY >= handlePos and relY < handlePos + handleSize then
|
||||
@@ -134,12 +134,12 @@ function List:mouse_click(button, x, y)
|
||||
return true
|
||||
end
|
||||
|
||||
if self.get("selectable") then
|
||||
local adjustedIndex = relY + self.get("offset")
|
||||
if self.getResolved("selectable") then
|
||||
local adjustedIndex = relY + self.getResolved("offset")
|
||||
|
||||
if adjustedIndex <= #items then
|
||||
local item = items[adjustedIndex]
|
||||
if not self.get("multiSelection") then
|
||||
if not self.getResolved("multiSelection") then
|
||||
for _, otherItem in ipairs(items) do
|
||||
if type(otherItem) == "table" then
|
||||
otherItem.selected = false
|
||||
@@ -170,8 +170,8 @@ end
|
||||
function List:mouse_drag(button, x, y)
|
||||
if self._scrollBarDragging then
|
||||
local _, relY = self:getRelativePosition(x, y)
|
||||
local items = self.get("items")
|
||||
local height = self.get("height")
|
||||
local items = self.getResolved("items")
|
||||
local height = self.getResolved("height")
|
||||
local handleSize = math.max(1, math.floor((height / #items) * height))
|
||||
local maxOffset = #items - height
|
||||
relY = math.max(1, math.min(height, relY))
|
||||
@@ -209,8 +209,8 @@ end
|
||||
--- @protected
|
||||
function List:mouse_scroll(direction, x, y)
|
||||
if Collection.mouse_scroll(self, direction, x, y) then
|
||||
local offset = self.get("offset")
|
||||
local maxOffset = math.max(0, #self.get("items") - self.get("height"))
|
||||
local offset = self.getResolved("offset")
|
||||
local maxOffset = math.max(0, #self.getResolved("items") - self.getResolved("height"))
|
||||
|
||||
offset = math.min(maxOffset, math.max(0, offset + direction))
|
||||
self.set("offset", offset)
|
||||
@@ -233,7 +233,7 @@ end
|
||||
--- @shortDescription Scrolls the list to the bottom
|
||||
--- @return List self The List instance
|
||||
function List:scrollToBottom()
|
||||
local maxOffset = math.max(0, #self.get("items") - self.get("height"))
|
||||
local maxOffset = math.max(0, #self.getResolved("items") - self.getResolved("height"))
|
||||
self.set("offset", maxOffset)
|
||||
return self
|
||||
end
|
||||
@@ -252,8 +252,8 @@ end
|
||||
--- @return List self The List instance
|
||||
--- @usage list:scrollToItem(5)
|
||||
function List:scrollToItem(index)
|
||||
local height = self.get("height")
|
||||
local offset = self.get("offset")
|
||||
local height = self.getResolved("height")
|
||||
local offset = self.getResolved("offset")
|
||||
|
||||
if index < offset + 1 then
|
||||
self.set("offset", math.max(0, index - 1))
|
||||
@@ -270,8 +270,8 @@ end
|
||||
--- @return boolean Whether the event was handled
|
||||
--- @protected
|
||||
function List:key(keyCode)
|
||||
if Collection.key(self, keyCode) and self.get("selectable") then
|
||||
local items = self.get("items")
|
||||
if Collection.key(self, keyCode) and self.getResolved("selectable") then
|
||||
local items = self.getResolved("items")
|
||||
local currentIndex = self:getSelectedIndex()
|
||||
|
||||
if keyCode == keys.up then
|
||||
@@ -297,14 +297,14 @@ function List:key(keyCode)
|
||||
self:scrollToBottom()
|
||||
return true
|
||||
elseif keyCode == keys.pageUp then
|
||||
local height = self.get("height")
|
||||
local height = self.getResolved("height")
|
||||
local newIndex = math.max(1, (currentIndex or 1) - height)
|
||||
self:clearItemSelection()
|
||||
self:selectItem(newIndex)
|
||||
self:scrollToItem(newIndex)
|
||||
return true
|
||||
elseif keyCode == keys.pageDown then
|
||||
local height = self.get("height")
|
||||
local height = self.getResolved("height")
|
||||
local newIndex = math.min(#items, (currentIndex or 1) + height)
|
||||
self:clearItemSelection()
|
||||
self:selectItem(newIndex)
|
||||
@@ -321,19 +321,19 @@ function List:render(vOffset)
|
||||
vOffset = vOffset or 0
|
||||
Collection.render(self)
|
||||
|
||||
local items = self.get("items")
|
||||
local height = self.get("height")
|
||||
local offset = self.get("offset")
|
||||
local width = self.get("width")
|
||||
local items = self.getResolved("items")
|
||||
local height = self.getResolved("height")
|
||||
local offset = self.getResolved("offset")
|
||||
local width = self.getResolved("width")
|
||||
local listBg = self.getResolved("background")
|
||||
local listFg = self.getResolved("foreground")
|
||||
local showScrollBar = self.get("showScrollBar")
|
||||
local showScrollBar = self.getResolved("showScrollBar")
|
||||
|
||||
local needsScrollBar = showScrollBar and #items > height
|
||||
local contentWidth = needsScrollBar and width - 1 or width
|
||||
|
||||
if #items == 0 then
|
||||
local emptyText = self.get("emptyText")
|
||||
local emptyText = self.getResolved("emptyText")
|
||||
local y = math.floor(height / 2) + vOffset
|
||||
local x = math.max(1, math.floor((width - #emptyText) / 2) + 1)
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ Menu.defineProperty(Menu, "horizontalOffset", {
|
||||
type = "number",
|
||||
canTriggerRender = true,
|
||||
setter = function(self, value)
|
||||
local maxOffset = math.max(0, self:getTotalWidth() - self.get("width"))
|
||||
local maxOffset = math.max(0, self:getTotalWidth() - self.getResolved("width"))
|
||||
return math.min(maxOffset, math.max(0, value))
|
||||
end
|
||||
})
|
||||
@@ -59,7 +59,7 @@ function Menu:init(props, basalt)
|
||||
self.set("type", "Menu")
|
||||
|
||||
self:observe("items", function()
|
||||
local maxWidth = self.get("maxWidth")
|
||||
local maxWidth = self.getResolved("maxWidth")
|
||||
if maxWidth then
|
||||
self.set("width", math.min(maxWidth, self:getTotalWidth()), true)
|
||||
else
|
||||
@@ -74,8 +74,8 @@ end
|
||||
--- @shortDescription Calculates total width of menu items
|
||||
--- @return number totalWidth The total width of all items
|
||||
function Menu:getTotalWidth()
|
||||
local items = self.get("items")
|
||||
local spacing = self.get("spacing")
|
||||
local items = self.getResolved("items")
|
||||
local spacing = self.getResolved("spacing")
|
||||
local totalWidth = 0
|
||||
|
||||
for i, item in ipairs(items) do
|
||||
@@ -97,10 +97,10 @@ end
|
||||
--- @protected
|
||||
function Menu:render()
|
||||
VisualElement.render(self)
|
||||
local viewportWidth = self.get("width")
|
||||
local spacing = self.get("spacing")
|
||||
local offset = self.get("horizontalOffset")
|
||||
local items = self.get("items")
|
||||
local viewportWidth = self.getResolved("width")
|
||||
local spacing = self.getResolved("spacing")
|
||||
local offset = self.getResolved("horizontalOffset")
|
||||
local items = self.getResolved("items")
|
||||
|
||||
local itemPositions = {}
|
||||
local currentX = 1
|
||||
@@ -143,13 +143,13 @@ function Menu:render()
|
||||
|
||||
if #visibleText > 0 then
|
||||
local isSelected = item.selected
|
||||
local fg = item.selectable == false and self.get("separatorColor") or
|
||||
(isSelected and (item.selectedForeground or self.get("selectedForeground")) or
|
||||
(item.foreground or self.get("foreground")))
|
||||
local fg = item.selectable == false and self.getResolved("separatorColor") or
|
||||
(isSelected and (item.selectedForeground or self.getResolved("selectedForeground")) or
|
||||
(item.foreground or self.getResolved("foreground")))
|
||||
|
||||
local bg = isSelected and
|
||||
(item.selectedBackground or self.get("selectedBackground")) or
|
||||
(item.background or self.get("background"))
|
||||
(item.selectedBackground or self.getResolved("selectedBackground")) or
|
||||
(item.background or self.getResolved("background"))
|
||||
|
||||
self:blit(visibleStart, 1, visibleText,
|
||||
string.rep(tHex[fg], #visibleText),
|
||||
@@ -168,8 +168,8 @@ function Menu:render()
|
||||
if spacingWidth > 0 then
|
||||
local spacingText = string.rep(" ", spacingWidth)
|
||||
self:blit(visibleSpacingStart, 1, spacingText,
|
||||
string.rep(tHex[self.get("foreground")], spacingWidth),
|
||||
string.rep(tHex[self.get("background")], spacingWidth))
|
||||
string.rep(tHex[self.getResolved("foreground")], spacingWidth),
|
||||
string.rep(tHex[self.getResolved("background")], spacingWidth))
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -181,11 +181,11 @@ end
|
||||
--- @protected
|
||||
function Menu:mouse_click(button, x, y)
|
||||
if not VisualElement.mouse_click(self, button, x, y) then return false end
|
||||
if(self.get("selectable") == false) then return false end
|
||||
if(self.getResolved("selectable") == false) then return false end
|
||||
local relX = select(1, self:getRelativePosition(x, y))
|
||||
local offset = self.get("horizontalOffset")
|
||||
local spacing = self.get("spacing")
|
||||
local items = self.get("items")
|
||||
local offset = self.getResolved("horizontalOffset")
|
||||
local spacing = self.getResolved("spacing")
|
||||
local items = self.getResolved("items")
|
||||
|
||||
local virtualX = relX + offset
|
||||
local currentX = 1
|
||||
@@ -200,7 +200,7 @@ function Menu:mouse_click(button, x, y)
|
||||
items[i] = item
|
||||
end
|
||||
|
||||
if not self.get("multiSelection") then
|
||||
if not self.getResolved("multiSelection") then
|
||||
for _, otherItem in ipairs(items) do
|
||||
if type(otherItem) == "table" then
|
||||
otherItem.selected = false
|
||||
@@ -230,8 +230,8 @@ end
|
||||
--- @protected
|
||||
function Menu:mouse_scroll(direction, x, y)
|
||||
if VisualElement.mouse_scroll(self, direction, x, y) then
|
||||
local offset = self.get("horizontalOffset")
|
||||
local maxOffset = math.max(0, self:getTotalWidth() - self.get("width"))
|
||||
local offset = self.getResolved("horizontalOffset")
|
||||
local maxOffset = math.max(0, self:getTotalWidth() - self.getResolved("width"))
|
||||
|
||||
offset = math.min(maxOffset, math.max(0, offset + (direction * 3)))
|
||||
self.set("horizontalOffset", offset)
|
||||
|
||||
@@ -254,15 +254,15 @@ function Program:init(props, basalt)
|
||||
VisualElement.init(self, props, basalt)
|
||||
self.set("type", "Program")
|
||||
self:observe("width", function(self, width)
|
||||
local program = self.get("program")
|
||||
local program = self.getResolved("program")
|
||||
if program then
|
||||
program:resize(width, self.get("height"))
|
||||
program:resize(width, self.getResolved("height"))
|
||||
end
|
||||
end)
|
||||
self:observe("height", function(self, height)
|
||||
local program = self.get("program")
|
||||
local program = self.getResolved("program")
|
||||
if program then
|
||||
program:resize(self.get("width"), height)
|
||||
program:resize(self.getResolved("width"), height)
|
||||
end
|
||||
end)
|
||||
return self
|
||||
@@ -280,7 +280,7 @@ function Program:execute(path, env, addEnvironment, ...)
|
||||
local program = BasaltProgram.new(self, env, addEnvironment)
|
||||
self.set("program", program)
|
||||
program:setArgs(...)
|
||||
program:run(path, self.get("width"), self.get("height"), ...)
|
||||
program:run(path, self.getResolved("width"), self.getResolved("height"), ...)
|
||||
self:updateRender()
|
||||
return self
|
||||
end
|
||||
@@ -289,7 +289,7 @@ end
|
||||
--- @shortDescription Stops the program
|
||||
--- @return Program self The Program instance
|
||||
function Program:stop()
|
||||
local program = self.get("program")
|
||||
local program = self.getResolved("program")
|
||||
if program then
|
||||
program:stop()
|
||||
self.set("running", false)
|
||||
@@ -332,7 +332,7 @@ end
|
||||
--- @return any result The event result
|
||||
--- @protected
|
||||
function Program:dispatchEvent(event, ...)
|
||||
local program = self.get("program")
|
||||
local program = self.getResolved("program")
|
||||
local result = VisualElement.dispatchEvent(self, event, ...)
|
||||
if program then
|
||||
program:resume(event, ...)
|
||||
@@ -350,7 +350,7 @@ end
|
||||
--- @protected
|
||||
function Program:focus()
|
||||
if(VisualElement.focus(self))then
|
||||
local program = self.get("program")
|
||||
local program = self.getResolved("program")
|
||||
if program then
|
||||
local cursorBlink = program.window.getCursorBlink()
|
||||
local cursorX, cursorY = program.window.getCursorPos()
|
||||
@@ -363,7 +363,7 @@ end
|
||||
--- @protected
|
||||
function Program:render()
|
||||
VisualElement.render(self)
|
||||
local program = self.get("program")
|
||||
local program = self.getResolved("program")
|
||||
if program then
|
||||
local _, height = program.window.getSize()
|
||||
for y = 1, height do
|
||||
|
||||
@@ -47,29 +47,30 @@ end
|
||||
--- @protected
|
||||
function ProgressBar:render()
|
||||
VisualElement.render(self)
|
||||
local width = self.get("width")
|
||||
local height = self.get("height")
|
||||
local progress = math.min(100, math.max(0, self.get("progress")))
|
||||
local width = self.getResolved("width")
|
||||
local height = self.getResolved("height")
|
||||
local progress = math.min(100, math.max(0, self.getResolved("progress")))
|
||||
local fillWidth = math.floor((width * progress) / 100)
|
||||
local fillHeight = math.floor((height * progress) / 100)
|
||||
local direction = self.get("direction")
|
||||
local progressColor = self.get("progressColor")
|
||||
local direction = self.getResolved("direction")
|
||||
local progressColor = self.getResolved("progressColor")
|
||||
local foreground = self.getResolved("foreground")
|
||||
|
||||
if direction == "right" then
|
||||
self:multiBlit(1, 1, fillWidth, height, " ", tHex[self.get("foreground")], tHex[progressColor])
|
||||
self:multiBlit(1, 1, fillWidth, height, " ", tHex[foreground], tHex[progressColor])
|
||||
elseif direction == "left" then
|
||||
self:multiBlit(width - fillWidth + 1, 1, fillWidth, height, " ", tHex[self.get("foreground")], tHex[progressColor])
|
||||
self:multiBlit(width - fillWidth + 1, 1, fillWidth, height, " ", tHex[foreground], tHex[progressColor])
|
||||
elseif direction == "up" then
|
||||
self:multiBlit(1, height - fillHeight + 1, width, fillHeight, " ", tHex[self.get("foreground")], tHex[progressColor])
|
||||
self:multiBlit(1, height - fillHeight + 1, width, fillHeight, " ", tHex[foreground], tHex[progressColor])
|
||||
elseif direction == "down" then
|
||||
self:multiBlit(1, 1, width, fillHeight, " ", tHex[self.get("foreground")], tHex[progressColor])
|
||||
self:multiBlit(1, 1, width, fillHeight, " ", tHex[foreground], tHex[progressColor])
|
||||
end
|
||||
|
||||
if self.get("showPercentage") then
|
||||
if self.getResolved("showPercentage") then
|
||||
local text = tostring(progress).."%"
|
||||
local x = math.floor((width - #text) / 2) + 1
|
||||
local y = math.floor((height - 1) / 2) + 1
|
||||
self:textFg(x, y, text, self.get("foreground"))
|
||||
self:textFg(x, y, text, foreground)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -80,8 +80,8 @@ function ScrollBar:attach(element, config)
|
||||
self.set("maxValue", config.max or 100)
|
||||
element:observe(config.property, function(_, value)
|
||||
if value then
|
||||
local min = self.get("minValue")
|
||||
local max = self.get("maxValue")
|
||||
local min = self.getResolved("minValue")
|
||||
local max = self.getResolved("maxValue")
|
||||
if min == max then return end
|
||||
|
||||
self.set("value", math.floor(
|
||||
@@ -96,28 +96,28 @@ end
|
||||
--- @shortDescription Updates the attached element's property based on the ScrollBar value
|
||||
--- @return ScrollBar self The ScrollBar instance
|
||||
function ScrollBar:updateAttachedElement()
|
||||
local element = self.get("attachedElement")
|
||||
local element = self.getResolved("attachedElement")
|
||||
if not element then return end
|
||||
|
||||
local value = self.get("value")
|
||||
local min = self.get("minValue")
|
||||
local max = self.get("maxValue")
|
||||
local value = self.getResolved("value")
|
||||
local min = self.getResolved("minValue")
|
||||
local max = self.getResolved("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))
|
||||
element.set(self.getResolved("attachedProperty"), math.floor(mappedValue + 0.5))
|
||||
return self
|
||||
end
|
||||
|
||||
local function getScrollbarSize(self)
|
||||
return self.get("orientation") == "vertical" and self.get("height") or self.get("width")
|
||||
return self.getResolved("orientation") == "vertical" and self.getResolved("height") or self.getResolved("width")
|
||||
end
|
||||
|
||||
local function getRelativeScrollPosition(self, x, y)
|
||||
local relX, relY = self:getRelativePosition(x, y)
|
||||
return self.get("orientation") == "vertical" and relY or relX
|
||||
return self.getResolved("orientation") == "vertical" and relY or relX
|
||||
end
|
||||
|
||||
--- @shortDescription Handles mouse click events
|
||||
@@ -129,8 +129,8 @@ 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 value = self.getResolved("value")
|
||||
local handleSize = self.getResolved("handleSize")
|
||||
|
||||
local handlePos = math.floor((value / 100) * (size - handleSize)) + 1
|
||||
local relPos = getRelativeScrollPosition(self, x, y)
|
||||
@@ -155,8 +155,8 @@ 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 handleSize = self.getResolved("handleSize")
|
||||
local dragMultiplier = self.getResolved("dragMultiplier")
|
||||
local relPos = getRelativeScrollPosition(self, x, y)
|
||||
|
||||
relPos = math.max(1, math.min(size, relPos))
|
||||
@@ -179,8 +179,8 @@ 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 step = self.getResolved("step")
|
||||
local currentValue = self.getResolved("value")
|
||||
local newValue = currentValue - direction * step
|
||||
|
||||
self.set("value", math.min(100, math.max(0, newValue)))
|
||||
@@ -194,21 +194,23 @@ 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 value = self.getResolved("value")
|
||||
local handleSize = self.getResolved("handleSize")
|
||||
local symbol = self.getResolved("symbol")
|
||||
local symbolColor = self.getResolved("symbolColor")
|
||||
local symbolBackgroundColor = self.getResolved("symbolBackgroundColor")
|
||||
local bgSymbol = self.getResolved("backgroundSymbol")
|
||||
local isVertical = self.getResolved("orientation") == "vertical"
|
||||
local foreground = self.getResolved("foreground")
|
||||
local background = self.getResolved("background")
|
||||
|
||||
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")])
|
||||
self:blit(1, i, bgSymbol, tHex[foreground], tHex[background])
|
||||
else
|
||||
self:blit(i, 1, bgSymbol, tHex[self.get("foreground")], tHex[self.get("background")])
|
||||
self:blit(i, 1, bgSymbol, tHex[foreground], tHex[background])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ ScrollFrame.defineProperty(ScrollFrame, "contentWidth", {
|
||||
type = "number",
|
||||
getter = function(self)
|
||||
local maxWidth = 0
|
||||
local children = self.get("children")
|
||||
local children = self.getResolved("children")
|
||||
for _, child in ipairs(children) do
|
||||
local childX = child.get("x")
|
||||
local childWidth = child.get("width")
|
||||
@@ -129,7 +129,7 @@ ScrollFrame.defineProperty(ScrollFrame, "contentHeight", {
|
||||
type = "number",
|
||||
getter = function(self)
|
||||
local maxHeight = 0
|
||||
local children = self.get("children")
|
||||
local children = self.getResolved("children")
|
||||
for _, child in ipairs(children) do
|
||||
local childY = child.get("y")
|
||||
local childHeight = child.get("height")
|
||||
@@ -182,11 +182,11 @@ end
|
||||
function ScrollFrame:mouse_click(button, x, y)
|
||||
if Container.mouse_click(self, button, x, y) then
|
||||
local relX, relY = self:getRelativePosition(x, y)
|
||||
local width = self.get("width")
|
||||
local height = self.get("height")
|
||||
local showScrollBar = self.get("showScrollBar")
|
||||
local contentWidth = self.get("contentWidth")
|
||||
local contentHeight = self.get("contentHeight")
|
||||
local width = self.getResolved("width")
|
||||
local height = self.getResolved("height")
|
||||
local showScrollBar = self.getResolved("showScrollBar")
|
||||
local contentWidth = self.getResolved("contentWidth")
|
||||
local contentHeight = self.getResolved("contentHeight")
|
||||
local needsHorizontalScrollBar = showScrollBar and contentWidth > width
|
||||
local viewportHeight = needsHorizontalScrollBar and height - 1 or height
|
||||
local needsVerticalScrollBar = showScrollBar and contentHeight > viewportHeight
|
||||
@@ -197,7 +197,7 @@ function ScrollFrame:mouse_click(button, x, y)
|
||||
local handleSize = math.max(1, math.floor((viewportHeight / contentHeight) * scrollHeight))
|
||||
local maxOffset = contentHeight - viewportHeight
|
||||
|
||||
local currentPercent = maxOffset > 0 and (self.get("offsetY") / maxOffset * 100) or 0
|
||||
local currentPercent = maxOffset > 0 and (self.getResolved("offsetY") / maxOffset * 100) or 0
|
||||
local handlePos = math.floor((currentPercent / 100) * (scrollHeight - handleSize)) + 1
|
||||
|
||||
if relY >= handlePos and relY < handlePos + handleSize then
|
||||
@@ -216,7 +216,7 @@ function ScrollFrame:mouse_click(button, x, y)
|
||||
local handleSize = math.max(1, math.floor((viewportWidth / contentWidth) * scrollWidth))
|
||||
local maxOffset = contentWidth - viewportWidth
|
||||
|
||||
local currentPercent = maxOffset > 0 and (self.get("offsetX") / maxOffset * 100) or 0
|
||||
local currentPercent = maxOffset > 0 and (self.getResolved("offsetX") / maxOffset * 100) or 0
|
||||
local handlePos = math.floor((currentPercent / 100) * (scrollWidth - handleSize)) + 1
|
||||
|
||||
if relX >= handlePos and relX < handlePos + handleSize then
|
||||
@@ -245,11 +245,11 @@ end
|
||||
function ScrollFrame:mouse_drag(button, x, y)
|
||||
if self._scrollBarDragging then
|
||||
local _, relY = self:getRelativePosition(x, y)
|
||||
local height = self.get("height")
|
||||
local contentWidth = self.get("contentWidth")
|
||||
local contentHeight = self.get("contentHeight")
|
||||
local width = self.get("width")
|
||||
local needsHorizontalScrollBar = self.get("showScrollBar") and contentWidth > width
|
||||
local height = self.getResolved("height")
|
||||
local contentWidth = self.getResolved("contentWidth")
|
||||
local contentHeight = self.getResolved("contentHeight")
|
||||
local width = self.getResolved("width")
|
||||
local needsHorizontalScrollBar = self.getResolved("showScrollBar") and contentWidth > width
|
||||
|
||||
local viewportHeight = needsHorizontalScrollBar and height - 1 or height
|
||||
local scrollHeight = viewportHeight
|
||||
@@ -268,13 +268,13 @@ function ScrollFrame:mouse_drag(button, x, y)
|
||||
|
||||
if self._hScrollBarDragging then
|
||||
local relX, _ = self:getRelativePosition(x, y)
|
||||
local width = self.get("width")
|
||||
local contentWidth = self.get("contentWidth")
|
||||
local contentHeight = self.get("contentHeight")
|
||||
local height = self.get("height")
|
||||
local needsHorizontalScrollBar = self.get("showScrollBar") and contentWidth > width
|
||||
local width = self.getResolved("width")
|
||||
local contentWidth = self.getResolved("contentWidth")
|
||||
local contentHeight = self.getResolved("contentHeight")
|
||||
local height = self.getResolved("height")
|
||||
local needsHorizontalScrollBar = self.getResolved("showScrollBar") and contentWidth > width
|
||||
local viewportHeight = needsHorizontalScrollBar and height - 1 or height
|
||||
local needsVerticalScrollBar = self.get("showScrollBar") and contentHeight > viewportHeight
|
||||
local needsVerticalScrollBar = self.getResolved("showScrollBar") and contentHeight > viewportHeight
|
||||
local viewportWidth = needsVerticalScrollBar and width - 1 or width
|
||||
local scrollWidth = viewportWidth
|
||||
local handleSize = math.max(1, math.floor((viewportWidth / contentWidth) * scrollWidth))
|
||||
@@ -325,7 +325,7 @@ end
|
||||
--- @protected
|
||||
function ScrollFrame:mouse_scroll(direction, x, y)
|
||||
if self:isInBounds(x, y) then
|
||||
local xOffset, yOffset = self.get("offsetX"), self.get("offsetY")
|
||||
local xOffset, yOffset = self.getResolved("offsetX"), self.getResolved("offsetY")
|
||||
local relX, relY = self:getRelativePosition(x + xOffset, y + yOffset)
|
||||
|
||||
local success, child = self:callChildrenEvent(true, "mouse_scroll", direction, relX, relY)
|
||||
@@ -333,16 +333,16 @@ function ScrollFrame:mouse_scroll(direction, x, y)
|
||||
return true
|
||||
end
|
||||
|
||||
local height = self.get("height")
|
||||
local width = self.get("width")
|
||||
local offsetY = self.get("offsetY")
|
||||
local offsetX = self.get("offsetX")
|
||||
local contentWidth = self.get("contentWidth")
|
||||
local contentHeight = self.get("contentHeight")
|
||||
local height = self.getResolved("height")
|
||||
local width = self.getResolved("width")
|
||||
local offsetY = self.getResolved("offsetY")
|
||||
local offsetX = self.getResolved("offsetX")
|
||||
local contentWidth = self.getResolved("contentWidth")
|
||||
local contentHeight = self.getResolved("contentHeight")
|
||||
|
||||
local needsHorizontalScrollBar = self.get("showScrollBar") and contentWidth > width
|
||||
local needsHorizontalScrollBar = self.getResolved("showScrollBar") and contentWidth > width
|
||||
local viewportHeight = needsHorizontalScrollBar and height - 1 or height
|
||||
local needsVerticalScrollBar = self.get("showScrollBar") and contentHeight > viewportHeight
|
||||
local needsVerticalScrollBar = self.getResolved("showScrollBar") and contentHeight > viewportHeight
|
||||
local viewportWidth = needsVerticalScrollBar and width - 1 or width
|
||||
|
||||
if needsVerticalScrollBar then
|
||||
@@ -366,13 +366,13 @@ end
|
||||
function ScrollFrame:render()
|
||||
Container.render(self)
|
||||
|
||||
local height = self.get("height")
|
||||
local width = self.get("width")
|
||||
local offsetY = self.get("offsetY")
|
||||
local offsetX = self.get("offsetX")
|
||||
local showScrollBar = self.get("showScrollBar")
|
||||
local contentWidth = self.get("contentWidth")
|
||||
local contentHeight = self.get("contentHeight")
|
||||
local height = self.getResolved("height")
|
||||
local width = self.getResolved("width")
|
||||
local offsetY = self.getResolved("offsetY")
|
||||
local offsetX = self.getResolved("offsetX")
|
||||
local showScrollBar = self.getResolved("showScrollBar")
|
||||
local contentWidth = self.getResolved("contentWidth")
|
||||
local contentHeight = self.getResolved("contentHeight")
|
||||
local needsHorizontalScrollBar = showScrollBar and contentWidth > width
|
||||
local viewportHeight = needsHorizontalScrollBar and height - 1 or height
|
||||
local needsVerticalScrollBar = showScrollBar and contentHeight > viewportHeight
|
||||
@@ -382,10 +382,10 @@ function ScrollFrame:render()
|
||||
local scrollHeight = viewportHeight
|
||||
local handleSize = math.max(1, math.floor((viewportHeight / contentHeight) * scrollHeight))
|
||||
local maxOffset = contentHeight - viewportHeight
|
||||
local scrollBarBg = self.get("scrollBarBackgroundSymbol")
|
||||
local scrollBarColor = self.get("scrollBarColor")
|
||||
local scrollBarBgColor = self.get("scrollBarBackgroundColor")
|
||||
local scrollBarBg2Color = self.get("scrollBarBackgroundColor2")
|
||||
local scrollBarBg = self.getResolved("scrollBarBackgroundSymbol")
|
||||
local scrollBarColor = self.getResolved("scrollBarColor")
|
||||
local scrollBarBgColor = self.getResolved("scrollBarBackgroundColor")
|
||||
local scrollBarBg2Color = self.getResolved("scrollBarBackgroundColor2")
|
||||
|
||||
local currentPercent = maxOffset > 0 and (offsetY / maxOffset * 100) or 0
|
||||
local handlePos = math.floor((currentPercent / 100) * (scrollHeight - handleSize)) + 1
|
||||
@@ -403,10 +403,10 @@ function ScrollFrame:render()
|
||||
local scrollWidth = viewportWidth
|
||||
local handleSize = math.max(1, math.floor((viewportWidth / contentWidth) * scrollWidth))
|
||||
local maxOffset = contentWidth - viewportWidth
|
||||
local scrollBarBg = self.get("scrollBarBackgroundSymbol")
|
||||
local scrollBarColor = self.get("scrollBarColor")
|
||||
local scrollBarBgColor = self.get("scrollBarBackgroundColor")
|
||||
local scrollBarBg2Color = self.get("scrollBarBackgroundColor2")
|
||||
local scrollBarBg = self.getResolved("scrollBarBackgroundSymbol")
|
||||
local scrollBarColor = self.getResolved("scrollBarColor")
|
||||
local scrollBarBgColor = self.getResolved("scrollBarBackgroundColor")
|
||||
local scrollBarBg2Color = self.getResolved("scrollBarBackgroundColor2")
|
||||
|
||||
local currentPercent = maxOffset > 0 and (offsetX / maxOffset * 100) or 0
|
||||
local handlePos = math.floor((currentPercent / 100) * (scrollWidth - handleSize)) + 1
|
||||
@@ -421,7 +421,7 @@ function ScrollFrame:render()
|
||||
end
|
||||
|
||||
if needsVerticalScrollBar and needsHorizontalScrollBar then
|
||||
local background = self.get("background")
|
||||
local background = self.getResolved("background")
|
||||
self:blit(width, height, " ", tHex[background], tHex[background])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -154,7 +154,7 @@ end
|
||||
--- @param title string The title of the navigation item
|
||||
--- @return table tabHandler The navigation item handler proxy for adding elements
|
||||
function SideNav:newTab(title)
|
||||
local tabs = self.get("tabs") or {}
|
||||
local tabs = self.getResolved("tabs") or {}
|
||||
local tabId = #tabs + 1
|
||||
|
||||
table.insert(tabs, {
|
||||
@@ -164,7 +164,7 @@ function SideNav:newTab(title)
|
||||
|
||||
self.set("tabs", tabs)
|
||||
|
||||
if not self.get("activeTab") then
|
||||
if not self.getResolved("activeTab") then
|
||||
self.set("activeTab", tabId)
|
||||
end
|
||||
self:updateTabVisibility()
|
||||
@@ -215,7 +215,7 @@ end
|
||||
--- @return table element The created element
|
||||
function SideNav:addElement(elementType, tabId)
|
||||
local element = Container.addElement(self, elementType)
|
||||
local targetTab = tabId or self.get("activeTab")
|
||||
local targetTab = tabId or self.getResolved("activeTab")
|
||||
if targetTab then
|
||||
element._tabId = targetTab
|
||||
self:updateTabVisibility()
|
||||
@@ -230,7 +230,7 @@ end
|
||||
function SideNav:addChild(child)
|
||||
Container.addChild(self, child)
|
||||
if not child._tabId then
|
||||
local tabs = self.get("tabs") or {}
|
||||
local tabs = self.getResolved("tabs") or {}
|
||||
if #tabs > 0 then
|
||||
child._tabId = 1
|
||||
self:updateTabVisibility()
|
||||
@@ -249,7 +249,7 @@ end
|
||||
--- @shortDescription Sets the active navigation item
|
||||
--- @param tabId number The ID of the navigation item to activate
|
||||
function SideNav:setActiveTab(tabId)
|
||||
local oldTab = self.get("activeTab")
|
||||
local oldTab = self.getResolved("activeTab")
|
||||
if oldTab == tabId then return self end
|
||||
self.set("activeTab", tabId)
|
||||
self:updateTabVisibility()
|
||||
@@ -266,7 +266,7 @@ function SideNav:isChildVisible(child)
|
||||
return false
|
||||
end
|
||||
if child._tabId then
|
||||
return child._tabId == self.get("activeTab")
|
||||
return child._tabId == self.getResolved("activeTab")
|
||||
end
|
||||
return true
|
||||
end
|
||||
@@ -280,11 +280,11 @@ function SideNav:getContentXOffset()
|
||||
end
|
||||
|
||||
function SideNav:_getSidebarMetrics()
|
||||
local tabs = self.get("tabs") or {}
|
||||
local height = self.get("height") or 1
|
||||
local sidebarWidth = self.get("sidebarWidth") or 12
|
||||
local scrollOffset = self.get("sidebarScrollOffset") or 0
|
||||
local sidebarPos = self.get("sidebarPosition") or "left"
|
||||
local tabs = self.getResolved("tabs") or {}
|
||||
local height = self.getResolved("height") or 1
|
||||
local sidebarWidth = self.getResolved("sidebarWidth") or 12
|
||||
local scrollOffset = self.getResolved("sidebarScrollOffset") or 0
|
||||
local sidebarPos = self.getResolved("sidebarPosition") or "left"
|
||||
|
||||
local positions = {}
|
||||
local actualY = 1
|
||||
@@ -348,7 +348,7 @@ function SideNav:mouse_click(button, x, y)
|
||||
|
||||
local baseRelX, baseRelY = VisualElement.getRelativePosition(self, x, y)
|
||||
local metrics = self:_getSidebarMetrics()
|
||||
local width = self.get("width") or 1
|
||||
local width = self.getResolved("width") or 1
|
||||
|
||||
local inSidebar = false
|
||||
if metrics.sidebarPosition == "right" then
|
||||
@@ -373,7 +373,7 @@ end
|
||||
|
||||
function SideNav:getRelativePosition(x, y)
|
||||
local metrics = self:_getSidebarMetrics()
|
||||
local width = self.get("width") or 1
|
||||
local width = self.getResolved("width") or 1
|
||||
|
||||
if x == nil or y == nil then
|
||||
return VisualElement.getRelativePosition(self)
|
||||
@@ -456,7 +456,7 @@ function SideNav:mouse_up(button, x, y)
|
||||
end
|
||||
local baseRelX, baseRelY = VisualElement.getRelativePosition(self, x, y)
|
||||
local metrics = self:_getSidebarMetrics()
|
||||
local width = self.get("width") or 1
|
||||
local width = self.getResolved("width") or 1
|
||||
|
||||
local inSidebar = false
|
||||
if metrics.sidebarPosition == "right" then
|
||||
@@ -475,7 +475,7 @@ function SideNav:mouse_release(button, x, y)
|
||||
VisualElement.mouse_release(self, button, x, y)
|
||||
local baseRelX, baseRelY = VisualElement.getRelativePosition(self, x, y)
|
||||
local metrics = self:_getSidebarMetrics()
|
||||
local width = self.get("width") or 1
|
||||
local width = self.getResolved("width") or 1
|
||||
|
||||
local inSidebar = false
|
||||
if metrics.sidebarPosition == "right" then
|
||||
@@ -494,7 +494,7 @@ function SideNav:mouse_move(_, x, y)
|
||||
if VisualElement.mouse_move(self, _, x, y) then
|
||||
local baseRelX, baseRelY = VisualElement.getRelativePosition(self, x, y)
|
||||
local metrics = self:_getSidebarMetrics()
|
||||
local width = self.get("width") or 1
|
||||
local width = self.getResolved("width") or 1
|
||||
|
||||
local inSidebar = false
|
||||
if metrics.sidebarPosition == "right" then
|
||||
@@ -519,7 +519,7 @@ function SideNav:mouse_drag(button, x, y)
|
||||
if VisualElement.mouse_drag(self, button, x, y) then
|
||||
local baseRelX, baseRelY = VisualElement.getRelativePosition(self, x, y)
|
||||
local metrics = self:_getSidebarMetrics()
|
||||
local width = self.get("width") or 1
|
||||
local width = self.getResolved("width") or 1
|
||||
|
||||
local inSidebar = false
|
||||
if metrics.sidebarPosition == "right" then
|
||||
@@ -542,7 +542,7 @@ end
|
||||
--- @return SideNav self For method chaining
|
||||
function SideNav:scrollSidebar(direction)
|
||||
local metrics = self:_getSidebarMetrics()
|
||||
local currentOffset = self.get("sidebarScrollOffset") or 0
|
||||
local currentOffset = self.getResolved("sidebarScrollOffset") or 0
|
||||
local maxScroll = metrics.maxScroll or 0
|
||||
|
||||
local newOffset = currentOffset + (direction * 2)
|
||||
@@ -556,7 +556,7 @@ function SideNav:mouse_scroll(direction, x, y)
|
||||
if VisualElement.mouse_scroll(self, direction, x, y) then
|
||||
local baseRelX, baseRelY = VisualElement.getRelativePosition(self, x, y)
|
||||
local metrics = self:_getSidebarMetrics()
|
||||
local width = self.get("width") or 1
|
||||
local width = self.getResolved("width") or 1
|
||||
|
||||
local inSidebar = false
|
||||
if metrics.sidebarPosition == "right" then
|
||||
@@ -603,23 +603,25 @@ end
|
||||
--- @protected
|
||||
function SideNav:render()
|
||||
VisualElement.render(self)
|
||||
local height = self.get("height")
|
||||
local height = self.getResolved("height")
|
||||
local foreground = self.getResolved("foreground")
|
||||
local sidebarBackground = self.getResolved("sidebarBackground")
|
||||
local metrics = self:_getSidebarMetrics()
|
||||
local sidebarW = metrics.sidebarWidth or 12
|
||||
|
||||
for y = 1, height do
|
||||
VisualElement.multiBlit(self, 1, y, sidebarW, 1, " ", tHex[self.get("foreground")], tHex[self.get("sidebarBackground")])
|
||||
VisualElement.multiBlit(self, 1, y, sidebarW, 1, " ", tHex[foreground], tHex[sidebarBackground])
|
||||
end
|
||||
|
||||
local activeTab = self.get("activeTab")
|
||||
local activeTab = self.getResolved("activeTab")
|
||||
|
||||
for _, pos in ipairs(metrics.positions) do
|
||||
local bgColor = (pos.id == activeTab) and self.get("activeTabBackground") or self.get("sidebarBackground")
|
||||
local fgColor = (pos.id == activeTab) and self.get("activeTabTextColor") or self.get("foreground")
|
||||
local bgColor = (pos.id == activeTab) and self.getResolved("activeTabBackground") or sidebarBackground
|
||||
local fgColor = (pos.id == activeTab) and self.getResolved("activeTabTextColor") or foreground
|
||||
|
||||
local itemHeight = pos.displayHeight or (pos.y2 - pos.y1 + 1)
|
||||
for dy = 0, itemHeight - 1 do
|
||||
VisualElement.multiBlit(self, 1, pos.y1 + dy, sidebarW, 1, " ", tHex[self.get("foreground")], tHex[bgColor])
|
||||
VisualElement.multiBlit(self, 1, pos.y1 + dy, sidebarW, 1, " ", tHex[foreground], tHex[bgColor])
|
||||
end
|
||||
|
||||
local displayTitle = pos.title
|
||||
@@ -630,16 +632,16 @@ function SideNav:render()
|
||||
VisualElement.textFg(self, 2, pos.y1, displayTitle, fgColor)
|
||||
end
|
||||
|
||||
if not self.get("childrenSorted") then
|
||||
if not self.getResolved("childrenSorted") then
|
||||
self:sortChildren()
|
||||
end
|
||||
if not self.get("childrenEventsSorted") then
|
||||
if not self.getResolved("childrenEventsSorted") then
|
||||
for eventName in pairs(self._values.childrenEvents or {}) do
|
||||
self:sortChildrenEvents(eventName)
|
||||
end
|
||||
end
|
||||
|
||||
for _, child in ipairs(self.get("visibleChildren") or {}) do
|
||||
for _, child in ipairs(self.getResolved("visibleChildren") or {}) do
|
||||
if child == self then error("CIRCULAR REFERENCE DETECTED!") return end
|
||||
child:render()
|
||||
child:postRender()
|
||||
|
||||
@@ -59,9 +59,9 @@ end
|
||||
--- @return number value The current value (0 to max)
|
||||
--- @usage local value = slider:getValue()
|
||||
function Slider:getValue()
|
||||
local step = self.get("step")
|
||||
local max = self.get("max")
|
||||
local maxSteps = self.get("horizontal") and self.get("width") or self.get("height")
|
||||
local step = self.getResolved("step")
|
||||
local max = self.getResolved("max")
|
||||
local maxSteps = self.getResolved("horizontal") and self.getResolved("width") or self.getResolved("height")
|
||||
return math.floor((step - 1) * (max / (maxSteps - 1)))
|
||||
end
|
||||
|
||||
@@ -74,8 +74,8 @@ end
|
||||
function Slider:mouse_click(button, x, y)
|
||||
if self:isInBounds(x, y) then
|
||||
local relX, relY = self:getRelativePosition(x, y)
|
||||
local pos = self.get("horizontal") and relX or relY
|
||||
local maxSteps = self.get("horizontal") and self.get("width") or self.get("height")
|
||||
local pos = self.getResolved("horizontal") and relX or relY
|
||||
local maxSteps = self.getResolved("horizontal") and self.getResolved("width") or self.getResolved("height")
|
||||
|
||||
self.set("step", math.min(maxSteps, math.max(1, pos)))
|
||||
self:updateRender()
|
||||
@@ -93,8 +93,8 @@ Slider.mouse_drag = Slider.mouse_click
|
||||
--- @protected
|
||||
function Slider:mouse_scroll(direction, x, y)
|
||||
if self:isInBounds(x, y) then
|
||||
local step = self.get("step")
|
||||
local maxSteps = self.get("horizontal") and self.get("width") or self.get("height")
|
||||
local step = self.getResolved("step")
|
||||
local maxSteps = self.getResolved("horizontal") and self.getResolved("width") or self.getResolved("height")
|
||||
self.set("step", math.min(maxSteps, math.max(1, step + direction)))
|
||||
self:updateRender()
|
||||
return true
|
||||
@@ -106,23 +106,23 @@ end
|
||||
--- @protected
|
||||
function Slider:render()
|
||||
VisualElement.render(self)
|
||||
local width = self.get("width")
|
||||
local height = self.get("height")
|
||||
local horizontal = self.get("horizontal")
|
||||
local step = self.get("step")
|
||||
local width = self.getResolved("width")
|
||||
local height = self.getResolved("height")
|
||||
local horizontal = self.getResolved("horizontal")
|
||||
local step = self.getResolved("step")
|
||||
|
||||
local barChar = horizontal and "\140" or " "
|
||||
local text = string.rep(barChar, horizontal and width or height)
|
||||
|
||||
if horizontal then
|
||||
self:textFg(1, 1, text, self.get("barColor"))
|
||||
self:textBg(step, 1, " ", self.get("sliderColor"))
|
||||
self:textFg(1, 1, text, self.getResolved("barColor"))
|
||||
self:textBg(step, 1, " ", self.getResolved("sliderColor"))
|
||||
else
|
||||
local bg = self.get("background")
|
||||
local bg = self.getResolved("background")
|
||||
for y = 1, height do
|
||||
self:textBg(1, y, " ", bg)
|
||||
end
|
||||
self:textBg(1, step, " ", self.get("sliderColor"))
|
||||
self:textBg(1, step, " ", self.getResolved("sliderColor"))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ end
|
||||
--- @protected
|
||||
function Switch:mouse_click(button, x, y)
|
||||
if VisualElement.mouse_click(self, button, x, y) then
|
||||
self.set("checked", not self.get("checked"))
|
||||
self.set("checked", not self.getResolved("checked"))
|
||||
return true
|
||||
end
|
||||
return false
|
||||
@@ -62,20 +62,21 @@ end
|
||||
--- @shortDescription Renders the Switch
|
||||
--- @protected
|
||||
function Switch:render()
|
||||
local checked = self.get("checked")
|
||||
local text = self.get("text")
|
||||
local switchWidth = self.get("width")
|
||||
local switchHeight = self.get("height")
|
||||
local checked = self.getResolved("checked")
|
||||
local text = self.getResolved("text")
|
||||
local switchWidth = self.getResolved("width")
|
||||
local switchHeight = self.getResolved("height")
|
||||
local foreground = self.getResolved("foreground")
|
||||
|
||||
local bgColor = checked and self.get("onBackground") or self.get("offBackground")
|
||||
self:multiBlit(1, 1, switchWidth, switchHeight, " ", tHex[self.get("foreground")], tHex[bgColor])
|
||||
local bgColor = checked and self.getResolved("onBackground") or self.getResolved("offBackground")
|
||||
self:multiBlit(1, 1, switchWidth, switchHeight, " ", tHex[foreground], tHex[bgColor])
|
||||
|
||||
local sliderSize = math.floor(switchWidth / 2)
|
||||
local sliderStart = checked and (switchWidth - sliderSize + 1) or 1
|
||||
self:multiBlit(sliderStart, 1, sliderSize, switchHeight, " ", tHex[self.get("foreground")], tHex[self.get("background")])
|
||||
self:multiBlit(sliderStart, 1, sliderSize, switchHeight, " ", tHex[foreground], tHex[self.getResolved("background")])
|
||||
|
||||
if text ~= "" then
|
||||
self:textFg(switchWidth + 2, 1, text, self.get("foreground"))
|
||||
self:textFg(switchWidth + 2, 1, text, foreground)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ end
|
||||
--- @param title string The title of the tab
|
||||
--- @return table tabHandler The tab handler proxy for adding elements to the new tab
|
||||
function TabControl:newTab(title)
|
||||
local tabs = self.get("tabs") or {}
|
||||
local tabs = self.getResolved("tabs") or {}
|
||||
local tabId = #tabs + 1
|
||||
|
||||
table.insert(tabs, {
|
||||
@@ -165,7 +165,7 @@ function TabControl:newTab(title)
|
||||
|
||||
self.set("tabs", tabs)
|
||||
|
||||
if not self.get("activeTab") then
|
||||
if not self.getResolved("activeTab") then
|
||||
self.set("activeTab", tabId)
|
||||
end
|
||||
self:updateTabVisibility()
|
||||
@@ -216,7 +216,7 @@ end
|
||||
--- @return table element The created element
|
||||
function TabControl:addElement(elementType, tabId)
|
||||
local element = Container.addElement(self, elementType)
|
||||
local targetTab = tabId or self.get("activeTab")
|
||||
local targetTab = tabId or self.getResolved("activeTab")
|
||||
if targetTab then
|
||||
element._tabId = targetTab
|
||||
self:updateTabVisibility()
|
||||
@@ -231,7 +231,7 @@ end
|
||||
function TabControl:addChild(child)
|
||||
Container.addChild(self, child)
|
||||
if not child._tabId then
|
||||
local tabs = self.get("tabs") or {}
|
||||
local tabs = self.getResolved("tabs") or {}
|
||||
if #tabs > 0 then
|
||||
child._tabId = 1
|
||||
self:updateTabVisibility()
|
||||
@@ -250,7 +250,7 @@ end
|
||||
--- @shortDescription Sets the active tab
|
||||
--- @param tabId number The ID of the tab to activate
|
||||
function TabControl:setActiveTab(tabId)
|
||||
local oldTab = self.get("activeTab")
|
||||
local oldTab = self.getResolved("activeTab")
|
||||
if oldTab == tabId then return self end
|
||||
self.set("activeTab", tabId)
|
||||
self:updateTabVisibility()
|
||||
@@ -267,7 +267,7 @@ function TabControl:isChildVisible(child)
|
||||
return false
|
||||
end
|
||||
if child._tabId then
|
||||
return child._tabId == self.get("activeTab")
|
||||
return child._tabId == self.getResolved("activeTab")
|
||||
end
|
||||
return true
|
||||
end
|
||||
@@ -281,15 +281,15 @@ function TabControl:getContentYOffset()
|
||||
end
|
||||
|
||||
function TabControl:_getHeaderMetrics()
|
||||
local tabs = self.get("tabs") or {}
|
||||
local width = self.get("width") or 1
|
||||
local minTabH = self.get("tabHeight") or 1
|
||||
local scrollable = self.get("scrollableTab")
|
||||
local tabs = self.getResolved("tabs") or {}
|
||||
local width = self.getResolved("width") or 1
|
||||
local minTabH = self.getResolved("tabHeight") or 1
|
||||
local scrollable = self.getResolved("scrollableTab")
|
||||
|
||||
local positions = {}
|
||||
|
||||
if scrollable then
|
||||
local scrollOffset = self.get("tabScrollOffset") or 0
|
||||
local scrollOffset = self.getResolved("tabScrollOffset") or 0
|
||||
local actualX = 1
|
||||
local totalWidth = 0
|
||||
|
||||
@@ -500,10 +500,10 @@ end
|
||||
--- @param direction number -1 to scroll left, 1 to scroll right
|
||||
--- @return TabControl self For method chaining
|
||||
function TabControl:scrollTabs(direction)
|
||||
if not self.get("scrollableTab") then return self end
|
||||
if not self.getResolved("scrollableTab") then return self end
|
||||
|
||||
local metrics = self:_getHeaderMetrics()
|
||||
local currentOffset = self.get("tabScrollOffset") or 0
|
||||
local currentOffset = self.getResolved("tabScrollOffset") or 0
|
||||
local maxScroll = metrics.maxScroll or 0
|
||||
|
||||
local newOffset = currentOffset + (direction * 5)
|
||||
@@ -517,7 +517,7 @@ function TabControl:mouse_scroll(direction, x, y)
|
||||
if VisualElement.mouse_scroll(self, direction, x, y) then
|
||||
local headerH = self:_getHeaderMetrics().headerHeight
|
||||
|
||||
if self.get("scrollableTab") and y == self.get("y") then
|
||||
if self.getResolved("scrollableTab") and y == self.getResolved("y") then
|
||||
self:scrollTabs(direction)
|
||||
return true
|
||||
end
|
||||
@@ -549,18 +549,20 @@ end
|
||||
--- @protected
|
||||
function TabControl:render()
|
||||
VisualElement.render(self)
|
||||
local width = self.get("width")
|
||||
local width = self.getResolved("width")
|
||||
local foreground = self.getResolved("foreground")
|
||||
local headerBackground = self.getResolved("headerBackground")
|
||||
local metrics = self:_getHeaderMetrics()
|
||||
local headerH = metrics.headerHeight or 1
|
||||
|
||||
VisualElement.multiBlit(self, 1, 1, width, headerH, " ", tHex[self.get("foreground")], tHex[self.get("headerBackground")])
|
||||
local activeTab = self.get("activeTab")
|
||||
VisualElement.multiBlit(self, 1, 1, width, headerH, " ", tHex[foreground], tHex[headerBackground])
|
||||
local activeTab = self.getResolved("activeTab")
|
||||
|
||||
for _, pos in ipairs(metrics.positions) do
|
||||
local bgColor = (pos.id == activeTab) and self.get("activeTabBackground") or self.get("headerBackground")
|
||||
local fgColor = (pos.id == activeTab) and self.get("activeTabTextColor") or self.get("foreground")
|
||||
local bgColor = (pos.id == activeTab) and self.getResolved("activeTabBackground") or headerBackground
|
||||
local fgColor = (pos.id == activeTab) and self.getResolved("activeTabTextColor") or foreground
|
||||
|
||||
VisualElement.multiBlit(self, pos.x1, pos.line, pos.displayWidth or (pos.x2 - pos.x1 + 1), 1, " ", tHex[self.get("foreground")], tHex[bgColor])
|
||||
VisualElement.multiBlit(self, pos.x1, pos.line, pos.displayWidth or (pos.x2 - pos.x1 + 1), 1, " ", tHex[foreground], tHex[bgColor])
|
||||
|
||||
local displayTitle = pos.title
|
||||
local textStartInTitle = 1 + (pos.startClip or 0)
|
||||
@@ -576,16 +578,16 @@ function TabControl:render()
|
||||
end
|
||||
end
|
||||
|
||||
if not self.get("childrenSorted") then
|
||||
if not self.getResolved("childrenSorted") then
|
||||
self:sortChildren()
|
||||
end
|
||||
if not self.get("childrenEventsSorted") then
|
||||
if not self.getResolved("childrenEventsSorted") then
|
||||
for eventName in pairs(self._values.childrenEvents or {}) do
|
||||
self:sortChildrenEvents(eventName)
|
||||
end
|
||||
end
|
||||
|
||||
for _, child in ipairs(self.get("visibleChildren") or {}) do
|
||||
for _, child in ipairs(self.getResolved("visibleChildren") or {}) do
|
||||
if child == self then error("CIRCULAR REFERENCE DETECTED!") return end
|
||||
child:render()
|
||||
child:postRender()
|
||||
|
||||
@@ -70,7 +70,7 @@ Table.defineProperty(Table, "offset", {
|
||||
type = "number",
|
||||
canTriggerRender = true,
|
||||
setter = function(self, value)
|
||||
local maxOffset = math.max(0, #self.get("items") - (self.get("height") - 1))
|
||||
local maxOffset = math.max(0, #self.getResolved("items") - (self.getResolved("height") - 1))
|
||||
return math.min(maxOffset, math.max(0, value))
|
||||
end
|
||||
})
|
||||
@@ -127,8 +127,8 @@ function Table:init(props, basalt)
|
||||
self.set("type", "Table")
|
||||
|
||||
self:observe("sortColumn", function()
|
||||
if self.get("sortColumn") then
|
||||
self:sortByColumn(self.get("sortColumn"))
|
||||
if self.getResolved("sortColumn") then
|
||||
self:sortByColumn(self.getResolved("sortColumn"))
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -155,7 +155,7 @@ end
|
||||
--- @param rowIndex number The index of the row to remove
|
||||
--- @return Table self The Table instance
|
||||
function Table:removeRow(rowIndex)
|
||||
local items = self.get("items")
|
||||
local items = self.getResolved("items")
|
||||
if items[rowIndex] then
|
||||
table.remove(items, rowIndex)
|
||||
self.set("items", items)
|
||||
@@ -168,7 +168,7 @@ end
|
||||
--- @param rowIndex number The index of the row
|
||||
--- @return table? row The row data or nil
|
||||
function Table:getRow(rowIndex)
|
||||
local items = self.get("items")
|
||||
local items = self.getResolved("items")
|
||||
return items[rowIndex]
|
||||
end
|
||||
|
||||
@@ -179,7 +179,7 @@ end
|
||||
--- @param value any The new value
|
||||
--- @return Table self The Table instance
|
||||
function Table:updateCell(rowIndex, colIndex, value)
|
||||
local items = self.get("items")
|
||||
local items = self.getResolved("items")
|
||||
if items[rowIndex] and items[rowIndex].cells then
|
||||
items[rowIndex].cells[colIndex] = value
|
||||
self.set("items", items)
|
||||
@@ -191,7 +191,7 @@ end
|
||||
--- @shortDescription Gets the currently selected row data
|
||||
--- @return table? row The selected row or nil
|
||||
function Table:getSelectedRow()
|
||||
local items = self.get("items")
|
||||
local items = self.getResolved("items")
|
||||
for _, item in ipairs(items) do
|
||||
local isSelected = item._data and item._data.selected or item.selected
|
||||
if isSelected then
|
||||
@@ -215,7 +215,7 @@ end
|
||||
--- @param width number|string The width of the column (number, "auto", or "30%")
|
||||
--- @return Table self The Table instance
|
||||
function Table:addColumn(name, width)
|
||||
local columns = self.get("columns")
|
||||
local columns = self.getResolved("columns")
|
||||
table.insert(columns, {name = name, width = width})
|
||||
self.set("columns", columns)
|
||||
return self
|
||||
@@ -227,7 +227,7 @@ end
|
||||
--- @param sortFn function Function that takes (rowA, rowB) and returns comparison result
|
||||
--- @return Table self The Table instance
|
||||
function Table:setColumnSortFunction(columnIndex, sortFn)
|
||||
local customSorts = self.get("customSortFunction")
|
||||
local customSorts = self.getResolved("customSortFunction")
|
||||
customSorts[columnIndex] = sortFn
|
||||
self.set("customSortFunction", customSorts)
|
||||
return self
|
||||
@@ -270,7 +270,7 @@ end
|
||||
--- @shortDescription Gets all rows as array of cell arrays
|
||||
--- @return table data Array of row cell arrays
|
||||
function Table:getData()
|
||||
local items = self.get("items")
|
||||
local items = self.getResolved("items")
|
||||
local data = {}
|
||||
|
||||
for _, item in ipairs(items) do
|
||||
@@ -359,9 +359,9 @@ end
|
||||
--- @param fn function? Optional custom sorting function
|
||||
--- @return Table self The Table instance
|
||||
function Table:sortByColumn(columnIndex, fn)
|
||||
local items = self.get("items")
|
||||
local direction = self.get("sortDirection")
|
||||
local customSorts = self.get("customSortFunction")
|
||||
local items = self.getResolved("items")
|
||||
local direction = self.getResolved("sortDirection")
|
||||
local customSorts = self.getResolved("customSortFunction")
|
||||
|
||||
local sortFn = fn or customSorts[columnIndex]
|
||||
|
||||
@@ -429,10 +429,10 @@ function Table:mouse_click(button, x, y)
|
||||
if not Collection.mouse_click(self, button, x, y) then return false end
|
||||
|
||||
local relX, relY = self:getRelativePosition(x, y)
|
||||
local width = self.get("width")
|
||||
local height = self.get("height")
|
||||
local items = self.get("items")
|
||||
local showScrollBar = self.get("showScrollBar")
|
||||
local width = self.getResolved("width")
|
||||
local height = self.getResolved("height")
|
||||
local items = self.getResolved("items")
|
||||
local showScrollBar = self.getResolved("showScrollBar")
|
||||
local visibleRows = height - 1
|
||||
|
||||
if showScrollBar and #items > visibleRows and relX == width and relY > 1 then
|
||||
@@ -440,7 +440,7 @@ function Table:mouse_click(button, x, y)
|
||||
local maxOffset = #items - visibleRows
|
||||
local handleSize = math.max(1, math.floor((visibleRows / #items) * scrollBarHeight))
|
||||
|
||||
local currentPercent = maxOffset > 0 and (self.get("offset") / maxOffset * 100) or 0
|
||||
local currentPercent = maxOffset > 0 and (self.getResolved("offset") / maxOffset * 100) or 0
|
||||
local handlePos = math.floor((currentPercent / 100) * (scrollBarHeight - handleSize)) + 1
|
||||
|
||||
local scrollBarRelY = relY - 1
|
||||
@@ -457,15 +457,15 @@ function Table:mouse_click(button, x, y)
|
||||
end
|
||||
|
||||
if relY == 1 then
|
||||
local columns = self.get("columns")
|
||||
local columns = self.getResolved("columns")
|
||||
local calculatedColumns = self:calculateColumnWidths(columns, width)
|
||||
|
||||
local currentX = 1
|
||||
for i, col in ipairs(calculatedColumns) do
|
||||
local colWidth = col.visibleWidth or col.width or 10
|
||||
if relX >= currentX and relX < currentX + colWidth then
|
||||
if self.get("sortColumn") == i then
|
||||
self.set("sortDirection", self.get("sortDirection") == "asc" and "desc" or "asc")
|
||||
if self.getResolved("sortColumn") == i then
|
||||
self.set("sortDirection", self.getResolved("sortDirection") == "asc" and "desc" or "asc")
|
||||
else
|
||||
self.set("sortColumn", i)
|
||||
self.set("sortDirection", "asc")
|
||||
@@ -480,7 +480,7 @@ function Table:mouse_click(button, x, y)
|
||||
end
|
||||
|
||||
if relY > 1 then
|
||||
local rowIndex = relY - 2 + self.get("offset")
|
||||
local rowIndex = relY - 2 + self.getResolved("offset")
|
||||
|
||||
if rowIndex >= 0 and rowIndex < #items then
|
||||
local actualIndex = rowIndex + 1
|
||||
@@ -514,8 +514,8 @@ end
|
||||
function Table:mouse_drag(button, x, y)
|
||||
if self._scrollBarDragging then
|
||||
local _, relY = self:getRelativePosition(x, y)
|
||||
local items = self.get("items")
|
||||
local height = self.get("height")
|
||||
local items = self.getResolved("items")
|
||||
local height = self.getResolved("height")
|
||||
local visibleRows = height - 1
|
||||
local scrollBarHeight = height - 1
|
||||
local handleSize = math.max(1, math.floor((visibleRows / #items) * scrollBarHeight))
|
||||
@@ -549,11 +549,11 @@ end
|
||||
--- @protected
|
||||
function Table:mouse_scroll(direction, x, y)
|
||||
if Collection.mouse_scroll(self, direction, x, y) then
|
||||
local items = self.get("items")
|
||||
local height = self.get("height")
|
||||
local items = self.getResolved("items")
|
||||
local height = self.getResolved("height")
|
||||
local visibleRows = height - 1 -- Subtract header
|
||||
local maxOffset = math.max(0, #items - visibleRows)
|
||||
local newOffset = math.min(maxOffset, math.max(0, self.get("offset") + direction))
|
||||
local newOffset = math.min(maxOffset, math.max(0, self.getResolved("offset") + direction))
|
||||
|
||||
self.set("offset", newOffset)
|
||||
self:updateRender()
|
||||
@@ -570,9 +570,11 @@ function Table:render()
|
||||
local items = self.getResolved("items")
|
||||
local sortCol = self.getResolved("sortColumn")
|
||||
local offset = self.getResolved("offset")
|
||||
local height = self.get("height")
|
||||
local width = self.get("width")
|
||||
local height = self.getResolved("height")
|
||||
local width = self.getResolved("width")
|
||||
local showScrollBar = self.getResolved("showScrollBar")
|
||||
local background = self.getResolved("background")
|
||||
local foreground = self.getResolved("foreground")
|
||||
local visibleRows = height - 1
|
||||
|
||||
local needsScrollBar = showScrollBar and #items > visibleRows
|
||||
@@ -595,14 +597,14 @@ function Table:render()
|
||||
if i > lastVisibleColumn then break end
|
||||
local text = col.name
|
||||
if i == sortCol then
|
||||
text = text .. (self.get("sortDirection") == "asc" and "\30" or "\31")
|
||||
text = text .. (self.getResolved("sortDirection") == "asc" and "\30" or "\31")
|
||||
end
|
||||
self:textFg(currentX, 1, text:sub(1, col.visibleWidth), self.get("headerColor"))
|
||||
self:textFg(currentX, 1, text:sub(1, col.visibleWidth), self.getResolved("headerColor"))
|
||||
currentX = currentX + col.visibleWidth
|
||||
end
|
||||
|
||||
if currentX <= contentWidth then
|
||||
self:textBg(currentX, 1, string.rep(" ", contentWidth - currentX + 1), self.get("background"))
|
||||
self:textBg(currentX, 1, string.rep(" ", contentWidth - currentX + 1), background)
|
||||
end
|
||||
|
||||
for y = 2, height do
|
||||
@@ -615,7 +617,7 @@ function Table:render()
|
||||
|
||||
if cells then
|
||||
currentX = 1
|
||||
local bg = isSelected and self.get("selectedBackground") or self.get("background")
|
||||
local bg = isSelected and self.getResolved("selectedBackground") or background
|
||||
|
||||
for i, col in ipairs(calculatedColumns) do
|
||||
if i > lastVisibleColumn then break end
|
||||
@@ -625,7 +627,7 @@ function Table:render()
|
||||
paddedText = string.sub(paddedText, 1, col.visibleWidth - 1) .. " "
|
||||
end
|
||||
local finalText = string.sub(paddedText, 1, col.visibleWidth)
|
||||
local finalForeground = string.rep(tHex[self.get("foreground")], col.visibleWidth)
|
||||
local finalForeground = string.rep(tHex[foreground], col.visibleWidth)
|
||||
local finalBackground = string.rep(tHex[bg], col.visibleWidth)
|
||||
|
||||
self:blit(currentX, y, finalText, finalForeground, finalBackground)
|
||||
@@ -638,8 +640,8 @@ function Table:render()
|
||||
end
|
||||
else
|
||||
self:blit(1, y, string.rep(" ", contentWidth),
|
||||
string.rep(tHex[self.get("foreground")], contentWidth),
|
||||
string.rep(tHex[self.get("background")], contentWidth))
|
||||
string.rep(tHex[foreground], contentWidth),
|
||||
string.rep(tHex[background], contentWidth))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -655,7 +657,6 @@ function Table:render()
|
||||
local scrollBarBg = self.getResolved("scrollBarBackground")
|
||||
local scrollBarColor = self.getResolved("scrollBarColor")
|
||||
local scrollBarBgColor = self.getResolved("scrollBarBackgroundColor")
|
||||
local foreground = self.getResolved("foreground")
|
||||
|
||||
for i = 2, height do
|
||||
self:blit(width, i, scrollBarBg, tHex[foreground], tHex[scrollBarBgColor])
|
||||
|
||||
@@ -94,20 +94,20 @@ local function autoCompleteVisible(self)
|
||||
end
|
||||
|
||||
local function getBorderPadding(self)
|
||||
return self.get("autoCompleteShowBorder") and 1 or 0
|
||||
return self.getResolved("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"))
|
||||
frame:setBackground(self.getResolved("autoCompleteBackground"))
|
||||
frame:setForeground(self.getResolved("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:setBackground(self.getResolved("autoCompleteBackground"))
|
||||
list:setForeground(self.getResolved("autoCompleteForeground"))
|
||||
list:setSelectedBackground(self.getResolved("autoCompleteSelectedBackground"))
|
||||
list:setSelectedForeground(self.getResolved("autoCompleteSelectedForeground"))
|
||||
list:updateRender()
|
||||
end
|
||||
layoutAutoCompleteList(self)
|
||||
@@ -165,9 +165,9 @@ local function applyAutoCompleteSelection(self, item)
|
||||
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 lines = self.getResolved("lines")
|
||||
local cursorY = self.getResolved("cursorY")
|
||||
local cursorX = self.getResolved("cursorX")
|
||||
local line = lines[cursorY] or ""
|
||||
local startIndex = self._autoCompleteTokenStart or cursorX
|
||||
if startIndex < 1 then startIndex = 1 end
|
||||
@@ -184,7 +184,7 @@ local function applyAutoCompleteSelection(self, item)
|
||||
end
|
||||
|
||||
local function ensureAutoCompleteUI(self)
|
||||
if not self.get("autoCompleteEnabled") then return nil end
|
||||
if not self.getResolved("autoCompleteEnabled") then return nil end
|
||||
local frame = self._autoCompleteFrame
|
||||
if frame and not frame._destroyed then
|
||||
return self._autoCompleteList
|
||||
@@ -194,15 +194,15 @@ local function ensureAutoCompleteUI(self)
|
||||
if not base or not base.addFrame then return nil end
|
||||
|
||||
frame = base:addFrame({
|
||||
width = self.get("width"),
|
||||
width = self.getResolved("width"),
|
||||
height = 1,
|
||||
x = 1,
|
||||
y = 1,
|
||||
visible = false,
|
||||
background = self.get("autoCompleteBackground"),
|
||||
foreground = self.get("autoCompleteForeground"),
|
||||
background = self.getResolved("autoCompleteBackground"),
|
||||
foreground = self.getResolved("autoCompleteForeground"),
|
||||
ignoreOffset = true,
|
||||
z = self.get("z") + self.get("autoCompleteZOffset"),
|
||||
z = self.getResolved("z") + self.getResolved("autoCompleteZOffset"),
|
||||
})
|
||||
frame:setIgnoreOffset(true)
|
||||
frame:setVisible(false)
|
||||
@@ -215,16 +215,16 @@ local function ensureAutoCompleteUI(self)
|
||||
height = math.max(1, frame.get("height") - padding * 2),
|
||||
selectable = true,
|
||||
multiSelection = false,
|
||||
background = self.get("autoCompleteBackground"),
|
||||
foreground = self.get("autoCompleteForeground"),
|
||||
background = self.getResolved("autoCompleteBackground"),
|
||||
foreground = self.getResolved("autoCompleteForeground"),
|
||||
})
|
||||
list:setSelectedBackground(self.get("autoCompleteSelectedBackground"))
|
||||
list:setSelectedForeground(self.get("autoCompleteSelectedForeground"))
|
||||
list:setSelectedBackground(self.getResolved("autoCompleteSelectedBackground"))
|
||||
list:setSelectedForeground(self.getResolved("autoCompleteSelectedForeground"))
|
||||
list:setOffset(0)
|
||||
list:onSelect(function(_, index, selectedItem)
|
||||
if not autoCompleteVisible(self) then return end
|
||||
setAutoCompleteSelection(self, index)
|
||||
if self.get("autoCompleteAcceptOnClick") then
|
||||
if self.getResolved("autoCompleteAcceptOnClick") then
|
||||
applyAutoCompleteSelection(self, selectedItem)
|
||||
end
|
||||
end)
|
||||
@@ -272,12 +272,12 @@ updateAutoCompleteBorder = function(self)
|
||||
frame._autoCompleteBorderCommand = nil
|
||||
end
|
||||
|
||||
if not self.get("autoCompleteShowBorder") then
|
||||
if not self.getResolved("autoCompleteShowBorder") then
|
||||
frame:updateRender()
|
||||
return
|
||||
end
|
||||
|
||||
local borderColor = self.get("autoCompleteBorderColor") or colors.black
|
||||
local borderColor = self.getResolved("autoCompleteBorderColor") or colors.black
|
||||
|
||||
local commandIndex = canvas:addCommand(function(element)
|
||||
local width = element.get("width") or 0
|
||||
@@ -303,12 +303,12 @@ updateAutoCompleteBorder = function(self)
|
||||
end
|
||||
|
||||
local function getTokenInfo(self)
|
||||
local lines = self.get("lines")
|
||||
local cursorY = self.get("cursorY")
|
||||
local cursorX = self.get("cursorX")
|
||||
local lines = self.getResolved("lines")
|
||||
local cursorY = self.getResolved("cursorY")
|
||||
local cursorX = self.getResolved("cursorX")
|
||||
local line = lines[cursorY] or ""
|
||||
local uptoCursor = line:sub(1, math.max(cursorX - 1, 0))
|
||||
local pattern = self.get("autoCompleteTokenPattern") or "[%w_]+"
|
||||
local pattern = self.getResolved("autoCompleteTokenPattern") or "[%w_]+"
|
||||
|
||||
local token = ""
|
||||
if pattern ~= "" then
|
||||
@@ -355,7 +355,7 @@ local function iterateSuggestions(source, handler)
|
||||
end
|
||||
|
||||
local function gatherSuggestions(self, token)
|
||||
local provider = self.get("autoCompleteProvider")
|
||||
local provider = self.getResolved("autoCompleteProvider")
|
||||
local source = {}
|
||||
if provider then
|
||||
local ok, result = pcall(provider, self, token)
|
||||
@@ -363,11 +363,11 @@ local function gatherSuggestions(self, token)
|
||||
source = result
|
||||
end
|
||||
else
|
||||
source = self.get("autoCompleteItems") or {}
|
||||
source = self.getResolved("autoCompleteItems") or {}
|
||||
end
|
||||
|
||||
local suggestions = {}
|
||||
local caseInsensitive = self.get("autoCompleteCaseInsensitive")
|
||||
local caseInsensitive = self.getResolved("autoCompleteCaseInsensitive")
|
||||
local target = caseInsensitive and token:lower() or token
|
||||
iterateSuggestions(source, function(entry)
|
||||
local normalized = normalizeSuggestion(entry)
|
||||
@@ -379,7 +379,7 @@ local function gatherSuggestions(self, token)
|
||||
end
|
||||
end)
|
||||
|
||||
local maxItems = self.get("autoCompleteMaxItems")
|
||||
local maxItems = self.getResolved("autoCompleteMaxItems")
|
||||
if #suggestions > maxItems then
|
||||
while #suggestions > maxItems do
|
||||
table.remove(suggestions)
|
||||
@@ -403,8 +403,8 @@ local function measureSuggestionWidth(self, suggestions)
|
||||
end
|
||||
end
|
||||
|
||||
local limit = self.get("autoCompleteMaxWidth")
|
||||
local maxWidth = self.get("width")
|
||||
local limit = self.getResolved("autoCompleteMaxWidth")
|
||||
local maxWidth = self.getResolved("width")
|
||||
if limit and limit > 0 then
|
||||
maxWidth = math.min(maxWidth, limit)
|
||||
end
|
||||
@@ -430,7 +430,7 @@ local function placeAutoCompleteFrame(self, visibleCount, width)
|
||||
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 contentWidth = math.max(1, width or self.getResolved("width"))
|
||||
local contentHeight = math.max(1, visibleCount or 1)
|
||||
|
||||
local base = self:getBaseFrame()
|
||||
@@ -457,17 +457,17 @@ local function placeAutoCompleteFrame(self, visibleCount, width)
|
||||
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 scrollX = self.getResolved("scrollX") or 0
|
||||
local scrollY = self.getResolved("scrollY") or 0
|
||||
local tokenStart = (self._autoCompleteTokenStart or self.getResolved("cursorX"))
|
||||
local column = tokenStart - scrollX
|
||||
column = math.max(1, math.min(self.get("width"), column))
|
||||
column = math.max(1, math.min(self.getResolved("width"), column))
|
||||
|
||||
local cursorRow = self.get("cursorY") - scrollY
|
||||
cursorRow = math.max(1, math.min(self.get("height"), cursorRow))
|
||||
local cursorRow = self.getResolved("cursorY") - scrollY
|
||||
cursorRow = math.max(1, math.min(self.getResolved("height"), cursorRow))
|
||||
|
||||
local offsetX = self.get("autoCompleteOffsetX")
|
||||
local offsetY = self.get("autoCompleteOffsetY")
|
||||
local offsetX = self.getResolved("autoCompleteOffsetX")
|
||||
local offsetY = self.getResolved("autoCompleteOffsetY")
|
||||
|
||||
local baseX = originX + column - 1 + offsetX
|
||||
local x = baseX - border
|
||||
@@ -520,7 +520,7 @@ local function placeAutoCompleteFrame(self, visibleCount, width)
|
||||
frame:setPosition(x, y)
|
||||
frame:setWidth(frameWidth)
|
||||
frame:setHeight(frameHeight)
|
||||
frame:setZ(self.get("z") + self.get("autoCompleteZOffset"))
|
||||
frame:setZ(self.getResolved("z") + self.getResolved("autoCompleteZOffset"))
|
||||
|
||||
layoutAutoCompleteList(self, contentWidth, contentHeight)
|
||||
|
||||
@@ -531,7 +531,7 @@ local function placeAutoCompleteFrame(self, visibleCount, width)
|
||||
end
|
||||
|
||||
local function refreshAutoComplete(self)
|
||||
if not self.get("autoCompleteEnabled") then
|
||||
if not self.getResolved("autoCompleteEnabled") then
|
||||
hideAutoComplete(self, true)
|
||||
return
|
||||
end
|
||||
@@ -544,7 +544,7 @@ local function refreshAutoComplete(self)
|
||||
self._autoCompleteToken = token
|
||||
self._autoCompleteTokenStart = startIndex
|
||||
|
||||
if #token < self.get("autoCompleteMinChars") then
|
||||
if #token < self.getResolved("autoCompleteMinChars") then
|
||||
hideAutoComplete(self)
|
||||
return
|
||||
end
|
||||
@@ -576,7 +576,7 @@ 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
|
||||
if key == keys.tab or (key == keys.enter and self.getResolved("autoCompleteAcceptOnEnter")) then
|
||||
applyAutoCompleteSelection(self)
|
||||
return true
|
||||
elseif key == keys.up then
|
||||
@@ -593,7 +593,7 @@ local function handleAutoCompleteKey(self, key)
|
||||
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
|
||||
elseif key == keys.escape and self.getResolved("autoCompleteCloseOnEscape") then
|
||||
hideAutoComplete(self)
|
||||
return true
|
||||
end
|
||||
@@ -647,7 +647,7 @@ function TextBox:init(props, basalt)
|
||||
self.set("type", "TextBox")
|
||||
|
||||
local function refreshIfEnabled()
|
||||
if self.get("autoCompleteEnabled") and self:hasState("focused") then
|
||||
if self.getResolved("autoCompleteEnabled") and self:hasState("focused") then
|
||||
refreshAutoComplete(self)
|
||||
end
|
||||
end
|
||||
@@ -659,7 +659,7 @@ function TextBox:init(props, basalt)
|
||||
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"))
|
||||
placeAutoCompleteFrame(self, math.max(#suggestions, 1), rawget(self, "_autoCompletePopupWidth") or self.getResolved("width"))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -690,12 +690,12 @@ function TextBox:init(props, basalt)
|
||||
|
||||
self:observe("autoCompleteZOffset", function()
|
||||
if self._autoCompleteFrame and not self._autoCompleteFrame._destroyed then
|
||||
self._autoCompleteFrame:setZ(self.get("z") + self.get("autoCompleteZOffset"))
|
||||
self._autoCompleteFrame:setZ(self.getResolved("z") + self.getResolved("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"))
|
||||
self._autoCompleteFrame:setZ(self.getResolved("z") + self.getResolved("autoCompleteZOffset"))
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -749,7 +749,7 @@ end
|
||||
--- @param color number The color to apply
|
||||
--- @return TextBox self The TextBox instance
|
||||
function TextBox:addSyntaxPattern(pattern, color)
|
||||
table.insert(self.get("syntaxPatterns"), {pattern = pattern, color = color})
|
||||
table.insert(self.getResolved("syntaxPatterns"), {pattern = pattern, color = color})
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -757,7 +757,7 @@ end
|
||||
--- @param index number The index of the pattern to remove
|
||||
--- @return TextBox self
|
||||
function TextBox:removeSyntaxPattern(index)
|
||||
local patterns = self.get("syntaxPatterns") or {}
|
||||
local patterns = self.getResolved("syntaxPatterns") or {}
|
||||
if type(index) ~= "number" then return self end
|
||||
if index >= 1 and index <= #patterns then
|
||||
table.remove(patterns, index)
|
||||
@@ -776,9 +776,9 @@ function TextBox:clearSyntaxPatterns()
|
||||
end
|
||||
|
||||
local function insertChar(self, char)
|
||||
local lines = self.get("lines")
|
||||
local cursorX = self.get("cursorX")
|
||||
local cursorY = self.get("cursorY")
|
||||
local lines = self.getResolved("lines")
|
||||
local cursorX = self.getResolved("cursorX")
|
||||
local cursorY = self.getResolved("cursorY")
|
||||
local currentLine = lines[cursorY]
|
||||
lines[cursorY] = currentLine:sub(1, cursorX-1) .. char .. currentLine:sub(cursorX)
|
||||
self.set("cursorX", cursorX + 1)
|
||||
@@ -793,9 +793,9 @@ local function insertText(self, text)
|
||||
end
|
||||
|
||||
local function newLine(self)
|
||||
local lines = self.get("lines")
|
||||
local cursorX = self.get("cursorX")
|
||||
local cursorY = self.get("cursorY")
|
||||
local lines = self.getResolved("lines")
|
||||
local cursorX = self.getResolved("cursorX")
|
||||
local cursorY = self.getResolved("cursorY")
|
||||
local currentLine = lines[cursorY]
|
||||
|
||||
local restOfLine = currentLine:sub(cursorX)
|
||||
@@ -809,9 +809,9 @@ local function newLine(self)
|
||||
end
|
||||
|
||||
local function backspace(self)
|
||||
local lines = self.get("lines")
|
||||
local cursorX = self.get("cursorX")
|
||||
local cursorY = self.get("cursorY")
|
||||
local lines = self.getResolved("lines")
|
||||
local cursorX = self.getResolved("cursorX")
|
||||
local cursorY = self.getResolved("cursorY")
|
||||
local currentLine = lines[cursorY]
|
||||
|
||||
if cursorX > 1 then
|
||||
@@ -832,12 +832,12 @@ end
|
||||
--- @shortDescription Updates the viewport to keep the cursor in view
|
||||
--- @return TextBox self The TextBox instance
|
||||
function TextBox:updateViewport()
|
||||
local cursorX = self.get("cursorX")
|
||||
local cursorY = self.get("cursorY")
|
||||
local scrollX = self.get("scrollX")
|
||||
local scrollY = self.get("scrollY")
|
||||
local width = self.get("width")
|
||||
local height = self.get("height")
|
||||
local cursorX = self.getResolved("cursorX")
|
||||
local cursorY = self.getResolved("cursorY")
|
||||
local scrollX = self.getResolved("scrollX")
|
||||
local scrollY = self.getResolved("scrollY")
|
||||
local width = self.getResolved("width")
|
||||
local height = self.getResolved("height")
|
||||
|
||||
-- Horizontal scrolling
|
||||
if cursorX - scrollX > width then
|
||||
@@ -860,14 +860,14 @@ end
|
||||
--- @return boolean handled Whether the event was handled
|
||||
--- @protected
|
||||
function TextBox:char(char)
|
||||
if not self.get("editable") or not self:hasState("focused") then return false end
|
||||
if not self.getResolved("editable") or not self:hasState("focused") then return false end
|
||||
-- Auto-pair logic only triggers for single characters
|
||||
local autoPair = self.get("autoPairEnabled")
|
||||
local autoPair = self.getResolved("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 map = self.getResolved("autoPairCharacters") or {}
|
||||
local lines = self.getResolved("lines")
|
||||
local cursorX = self.getResolved("cursorX")
|
||||
local cursorY = self.getResolved("cursorY")
|
||||
local line = lines[cursorY] or ""
|
||||
local afterChar = line:sub(cursorX, cursorX)
|
||||
|
||||
@@ -876,22 +876,22 @@ function TextBox:char(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 self.getResolved("autoPairSkipClosing") then
|
||||
if afterChar ~= closing then
|
||||
insertChar(self, closing)
|
||||
-- Move cursor back inside pair
|
||||
self.set("cursorX", self.get("cursorX") - 1)
|
||||
self.set("cursorX", self.getResolved("cursorX") - 1)
|
||||
end
|
||||
else
|
||||
insertChar(self, closing)
|
||||
self.set("cursorX", self.get("cursorX") - 1)
|
||||
self.set("cursorX", self.getResolved("cursorX") - 1)
|
||||
end
|
||||
refreshAutoComplete(self)
|
||||
return true
|
||||
end
|
||||
|
||||
-- If typed char is a closing we might want to overtype
|
||||
if self.get("autoPairOverType") then
|
||||
if self.getResolved("autoPairOverType") then
|
||||
for open, close in pairs(map) do
|
||||
if char == close and afterChar == close then
|
||||
-- move over instead of inserting
|
||||
@@ -913,24 +913,24 @@ end
|
||||
--- @return boolean handled Whether the event was handled
|
||||
--- @protected
|
||||
function TextBox:key(key)
|
||||
if not self.get("editable") or not self:hasState("focused") then return false end
|
||||
if not self.getResolved("editable") or not self:hasState("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")
|
||||
local lines = self.getResolved("lines")
|
||||
local cursorX = self.getResolved("cursorX")
|
||||
local cursorY = self.getResolved("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")
|
||||
if self.getResolved("autoPairEnabled") and self.getResolved("autoPairNewlineIndent") then
|
||||
local lines = self.getResolved("lines")
|
||||
local cursorX = self.getResolved("cursorX")
|
||||
local cursorY = self.getResolved("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 pairMap = self.getResolved("autoPairCharacters") or {}
|
||||
local inverse = {}
|
||||
for o,c in pairs(pairMap) do inverse[c]=o end
|
||||
local prevChar = before:sub(-1)
|
||||
@@ -989,9 +989,9 @@ function TextBox:mouse_scroll(direction, x, y)
|
||||
return true
|
||||
end
|
||||
if self:isInBounds(x, y) then
|
||||
local scrollY = self.get("scrollY")
|
||||
local height = self.get("height")
|
||||
local lines = self.get("lines")
|
||||
local scrollY = self.getResolved("scrollY")
|
||||
local height = self.getResolved("height")
|
||||
local lines = self.getResolved("lines")
|
||||
|
||||
local maxScroll = math.max(0, #lines - height + 2)
|
||||
|
||||
@@ -1013,11 +1013,11 @@ end
|
||||
function TextBox:mouse_click(button, x, y)
|
||||
if VisualElement.mouse_click(self, button, x, y) then
|
||||
local relX, relY = self:getRelativePosition(x, y)
|
||||
local scrollX = self.get("scrollX")
|
||||
local scrollY = self.get("scrollY")
|
||||
local scrollX = self.getResolved("scrollX")
|
||||
local scrollY = self.getResolved("scrollY")
|
||||
|
||||
local targetY = (relY or 0) + (scrollY or 0)
|
||||
local lines = self.get("lines") or {}
|
||||
local lines = self.getResolved("lines") or {}
|
||||
|
||||
-- clamp and validate before indexing to avoid nil errors
|
||||
if targetY < 1 then targetY = 1 end
|
||||
@@ -1042,7 +1042,7 @@ end
|
||||
--- @shortDescription Handles paste events
|
||||
--- @protected
|
||||
function TextBox:paste(text)
|
||||
if not self.get("editable") or not self:hasState("focused") then return false end
|
||||
if not self.getResolved("editable") or not self:hasState("focused") then return false end
|
||||
|
||||
for char in text:gmatch(".") do
|
||||
if char == "\n" then
|
||||
@@ -1078,13 +1078,13 @@ end
|
||||
--- @shortDescription Gets the text of the TextBox
|
||||
--- @return string text The text of the TextBox
|
||||
function TextBox:getText()
|
||||
return table.concat(self.get("lines"), "\n")
|
||||
return table.concat(self.getResolved("lines"), "\n")
|
||||
end
|
||||
|
||||
local function applySyntaxHighlighting(self, line)
|
||||
local text = line
|
||||
local colors = string.rep(tHex[self.get("foreground")], #text)
|
||||
local patterns = self.get("syntaxPatterns")
|
||||
local colors = string.rep(tHex[self.getResolved("foreground")], #text)
|
||||
local patterns = self.getResolved("syntaxPatterns")
|
||||
|
||||
for _, syntax in ipairs(patterns) do
|
||||
local start = 1
|
||||
@@ -1111,13 +1111,15 @@ end
|
||||
function TextBox:render()
|
||||
VisualElement.render(self)
|
||||
|
||||
local lines = self.get("lines")
|
||||
local scrollX = self.get("scrollX")
|
||||
local scrollY = self.get("scrollY")
|
||||
local width = self.get("width")
|
||||
local height = self.get("height")
|
||||
local fg = tHex[self.get("foreground")]
|
||||
local bg = tHex[self.get("background")]
|
||||
local lines = self.getResolved("lines")
|
||||
local scrollX = self.getResolved("scrollX")
|
||||
local scrollY = self.getResolved("scrollY")
|
||||
local width = self.getResolved("width")
|
||||
local height = self.getResolved("height")
|
||||
local foreground = self.getResolved("foreground")
|
||||
local background = self.getResolved("background")
|
||||
local fg = tHex[foreground]
|
||||
local bg = tHex[background]
|
||||
|
||||
for y = 1, height do
|
||||
local lineNum = y + scrollY
|
||||
@@ -1130,17 +1132,17 @@ function TextBox:render()
|
||||
local padLen = width - #text
|
||||
if padLen > 0 then
|
||||
text = text .. string.rep(" ", padLen)
|
||||
colors = colors .. string.rep(tHex[self.get("foreground")], padLen)
|
||||
colors = colors .. string.rep(tHex[foreground], padLen)
|
||||
end
|
||||
|
||||
self:blit(1, y, text, colors, string.rep(bg, #text))
|
||||
end
|
||||
|
||||
if self:hasState("focused") then
|
||||
local relativeX = self.get("cursorX") - scrollX
|
||||
local relativeY = self.get("cursorY") - scrollY
|
||||
local relativeX = self.getResolved("cursorX") - scrollX
|
||||
local relativeY = self.getResolved("cursorY") - scrollY
|
||||
if relativeX >= 1 and relativeX <= width and relativeY >= 1 and relativeY <= height then
|
||||
self:setCursor(relativeX, relativeY, true, self.get("cursorColor") or self.get("foreground"))
|
||||
self:setCursor(relativeX, relativeY, true, self.getResolved("cursorColor") or foreground)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -44,7 +44,7 @@ end
|
||||
function Timer:start()
|
||||
if not self.running then
|
||||
self.running = true
|
||||
local time = self.get("interval")
|
||||
local time = self.getResolved("interval")
|
||||
self.timerId = os.startTimer(time)
|
||||
end
|
||||
return self
|
||||
@@ -70,12 +70,12 @@ function Timer:dispatchEvent(event, ...)
|
||||
local timerId = select(1, ...)
|
||||
if timerId == self.timerId then
|
||||
self.action()
|
||||
local amount = self.get("amount")
|
||||
local amount = self.getResolved("amount")
|
||||
if amount > 0 then
|
||||
self.set("amount", amount - 1)
|
||||
end
|
||||
if amount ~= 0 then
|
||||
self.timerId = os.startTimer(self.get("interval"))
|
||||
self.timerId = os.startTimer(self.getResolved("interval"))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -76,7 +76,7 @@ function Toast:show(titleOrMessage, messageOrDuration, duration)
|
||||
if type(messageOrDuration) == "string" then
|
||||
title = titleOrMessage
|
||||
message = messageOrDuration
|
||||
dur = duration or self.get("duration")
|
||||
dur = duration or self.getResolved("duration")
|
||||
elseif type(messageOrDuration) == "number" then
|
||||
title = ""
|
||||
message = titleOrMessage
|
||||
@@ -84,7 +84,7 @@ function Toast:show(titleOrMessage, messageOrDuration, duration)
|
||||
else
|
||||
title = ""
|
||||
message = titleOrMessage
|
||||
dur = self.get("duration")
|
||||
dur = self.getResolved("duration")
|
||||
end
|
||||
|
||||
self.set("title", title)
|
||||
@@ -96,7 +96,7 @@ function Toast:show(titleOrMessage, messageOrDuration, duration)
|
||||
self._hideTimerId = nil
|
||||
end
|
||||
|
||||
if self.get("autoHide") and dur > 0 then
|
||||
if self.getResolved("autoHide") and dur > 0 then
|
||||
self._hideTimerId = os.startTimer(dur)
|
||||
end
|
||||
|
||||
@@ -179,12 +179,12 @@ end
|
||||
--- @protected
|
||||
function Toast:render()
|
||||
VisualElement.render(self)
|
||||
if not self.get("active") then
|
||||
if not self.getResolved("active") then
|
||||
return
|
||||
end
|
||||
|
||||
local width = self.get("width")
|
||||
local height = self.get("height")
|
||||
local width = self.getResolved("width")
|
||||
local height = self.getResolved("height")
|
||||
local title = self.getResolved("title")
|
||||
local message = self.getResolved("message")
|
||||
local toastType = self.getResolved("toastType")
|
||||
|
||||
@@ -129,7 +129,7 @@ Tree.__index = Tree
|
||||
---@property nodes table {} The tree structure containing node objects with {text, children} properties
|
||||
Tree.defineProperty(Tree, "nodes", {default = {}, type = "table", canTriggerRender = true, setter = function(self, value)
|
||||
if #value > 0 then
|
||||
self.get("expandedNodes")[value[1]] = true
|
||||
self.getResolved("expandedNodes")[value[1]] = true
|
||||
end
|
||||
return value
|
||||
end})
|
||||
@@ -210,7 +210,7 @@ end
|
||||
--- @param node table The node to expand
|
||||
--- @return Tree self The Tree instance
|
||||
function Tree:expandNode(node)
|
||||
self.get("expandedNodes")[node] = true
|
||||
self.getResolved("expandedNodes")[node] = true
|
||||
self:updateRender()
|
||||
return self
|
||||
end
|
||||
@@ -220,7 +220,7 @@ end
|
||||
--- @param node table The node to collapse
|
||||
--- @return Tree self The Tree instance
|
||||
function Tree:collapseNode(node)
|
||||
self.get("expandedNodes")[node] = nil
|
||||
self.getResolved("expandedNodes")[node] = nil
|
||||
self:updateRender()
|
||||
return self
|
||||
end
|
||||
@@ -230,7 +230,7 @@ end
|
||||
--- @param node table The node to toggle
|
||||
--- @return Tree self The Tree instance
|
||||
function Tree:toggleNode(node)
|
||||
if self.get("expandedNodes")[node] then
|
||||
if self.getResolved("expandedNodes")[node] then
|
||||
self:collapseNode(node)
|
||||
else
|
||||
self:expandNode(node)
|
||||
@@ -248,10 +248,10 @@ end
|
||||
function Tree:mouse_click(button, x, y)
|
||||
if VisualElement.mouse_click(self, button, x, y) then
|
||||
local relX, relY = self:getRelativePosition(x, y)
|
||||
local width = self.get("width")
|
||||
local height = self.get("height")
|
||||
local flatNodes = flattenTree(self.get("nodes"), self.get("expandedNodes"))
|
||||
local showScrollBar = self.get("showScrollBar")
|
||||
local width = self.getResolved("width")
|
||||
local height = self.getResolved("height")
|
||||
local flatNodes = flattenTree(self.getResolved("nodes"), self.getResolved("expandedNodes"))
|
||||
local showScrollBar = self.getResolved("showScrollBar")
|
||||
local maxContentWidth, _ = self:getNodeSize()
|
||||
local needsHorizontalScrollBar = showScrollBar and maxContentWidth > width
|
||||
local contentHeight = needsHorizontalScrollBar and height - 1 or height
|
||||
@@ -262,7 +262,7 @@ function Tree:mouse_click(button, x, y)
|
||||
local handleSize = math.max(1, math.floor((contentHeight / #flatNodes) * scrollHeight))
|
||||
local maxOffset = #flatNodes - contentHeight
|
||||
|
||||
local currentPercent = maxOffset > 0 and (self.get("offset") / maxOffset * 100) or 0
|
||||
local currentPercent = maxOffset > 0 and (self.getResolved("offset") / maxOffset * 100) or 0
|
||||
local handlePos = math.floor((currentPercent / 100) * (scrollHeight - handleSize)) + 1
|
||||
|
||||
if relY >= handlePos and relY < handlePos + handleSize then
|
||||
@@ -281,7 +281,7 @@ function Tree:mouse_click(button, x, y)
|
||||
local handleSize = math.max(1, math.floor((contentWidth / maxContentWidth) * contentWidth))
|
||||
local maxOffset = maxContentWidth - contentWidth
|
||||
|
||||
local currentPercent = maxOffset > 0 and (self.get("horizontalOffset") / maxOffset * 100) or 0
|
||||
local currentPercent = maxOffset > 0 and (self.getResolved("horizontalOffset") / maxOffset * 100) or 0
|
||||
local handlePos = math.floor((currentPercent / 100) * (contentWidth - handleSize)) + 1
|
||||
|
||||
if relX >= handlePos and relX < handlePos + handleSize then
|
||||
@@ -295,7 +295,7 @@ function Tree:mouse_click(button, x, y)
|
||||
return true
|
||||
end
|
||||
|
||||
local visibleIndex = relY + self.get("offset")
|
||||
local visibleIndex = relY + self.getResolved("offset")
|
||||
|
||||
if flatNodes[visibleIndex] then
|
||||
local nodeInfo = flatNodes[visibleIndex]
|
||||
@@ -331,10 +331,10 @@ end
|
||||
function Tree:mouse_drag(button, x, y)
|
||||
if self._scrollBarDragging then
|
||||
local _, relY = self:getRelativePosition(x, y)
|
||||
local flatNodes = flattenTree(self.get("nodes"), self.get("expandedNodes"))
|
||||
local height = self.get("height")
|
||||
local flatNodes = flattenTree(self.getResolved("nodes"), self.getResolved("expandedNodes"))
|
||||
local height = self.getResolved("height")
|
||||
local maxContentWidth, _ = self:getNodeSize()
|
||||
local needsHorizontalScrollBar = self.get("showScrollBar") and maxContentWidth > self.get("width")
|
||||
local needsHorizontalScrollBar = self.getResolved("showScrollBar") and maxContentWidth > self.getResolved("width")
|
||||
local contentHeight = needsHorizontalScrollBar and height - 1 or height
|
||||
local scrollHeight = contentHeight
|
||||
local handleSize = math.max(1, math.floor((contentHeight / #flatNodes) * scrollHeight))
|
||||
@@ -352,13 +352,13 @@ function Tree:mouse_drag(button, x, y)
|
||||
|
||||
if self._hScrollBarDragging then
|
||||
local relX, _ = self:getRelativePosition(x, y)
|
||||
local width = self.get("width")
|
||||
local width = self.getResolved("width")
|
||||
local maxContentWidth, _ = self:getNodeSize()
|
||||
local flatNodes = flattenTree(self.get("nodes"), self.get("expandedNodes"))
|
||||
local height = self.get("height")
|
||||
local needsHorizontalScrollBar = self.get("showScrollBar") and maxContentWidth > width
|
||||
local flatNodes = flattenTree(self.getResolved("nodes"), self.getResolved("expandedNodes"))
|
||||
local height = self.getResolved("height")
|
||||
local needsHorizontalScrollBar = self.getResolved("showScrollBar") and maxContentWidth > width
|
||||
local contentHeight = needsHorizontalScrollBar and height - 1 or height
|
||||
local needsVerticalScrollBar = self.get("showScrollBar") and #flatNodes > contentHeight
|
||||
local needsVerticalScrollBar = self.getResolved("showScrollBar") and #flatNodes > contentHeight
|
||||
local contentWidth = needsVerticalScrollBar and width - 1 or width
|
||||
local handleSize = math.max(1, math.floor((contentWidth / maxContentWidth) * contentWidth))
|
||||
local maxOffset = maxContentWidth - contentWidth
|
||||
@@ -406,15 +406,15 @@ end
|
||||
--- @protected
|
||||
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 height = self.get("height")
|
||||
local width = self.get("width")
|
||||
local showScrollBar = self.get("showScrollBar")
|
||||
local flatNodes = flattenTree(self.getResolved("nodes"), self.getResolved("expandedNodes"))
|
||||
local height = self.getResolved("height")
|
||||
local width = self.getResolved("width")
|
||||
local showScrollBar = self.getResolved("showScrollBar")
|
||||
local maxContentWidth, _ = self:getNodeSize()
|
||||
local needsHorizontalScrollBar = showScrollBar and maxContentWidth > width
|
||||
local contentHeight = needsHorizontalScrollBar and height - 1 or height
|
||||
local maxScroll = math.max(0, #flatNodes - contentHeight)
|
||||
local newScroll = math.min(maxScroll, math.max(0, self.get("offset") + direction))
|
||||
local newScroll = math.min(maxScroll, math.max(0, self.getResolved("offset") + direction))
|
||||
|
||||
self.set("offset", newScroll)
|
||||
return true
|
||||
@@ -428,8 +428,8 @@ end
|
||||
--- @return number height The height of the tree
|
||||
function Tree:getNodeSize()
|
||||
local width, height = 0, 0
|
||||
local flatNodes = flattenTree(self.get("nodes"), self.get("expandedNodes"))
|
||||
local expandedNodes = self.get("expandedNodes")
|
||||
local flatNodes = flattenTree(self.getResolved("nodes"), self.getResolved("expandedNodes"))
|
||||
local expandedNodes = self.getResolved("expandedNodes")
|
||||
|
||||
for _, nodeInfo in ipairs(flatNodes) do
|
||||
local node = nodeInfo.node
|
||||
@@ -453,14 +453,14 @@ end
|
||||
function Tree:render()
|
||||
VisualElement.render(self)
|
||||
|
||||
local flatNodes = flattenTree(self.get("nodes"), self.get("expandedNodes"))
|
||||
local height = self.get("height")
|
||||
local width = self.get("width")
|
||||
local selectedNode = self.get("selectedNode")
|
||||
local expandedNodes = self.get("expandedNodes")
|
||||
local offset = self.get("offset")
|
||||
local horizontalOffset = self.get("horizontalOffset")
|
||||
local showScrollBar = self.get("showScrollBar")
|
||||
local flatNodes = flattenTree(self.getResolved("nodes"), self.getResolved("expandedNodes"))
|
||||
local height = self.getResolved("height")
|
||||
local width = self.getResolved("width")
|
||||
local selectedNode = self.getResolved("selectedNode")
|
||||
local expandedNodes = self.getResolved("expandedNodes")
|
||||
local offset = self.getResolved("offset")
|
||||
local horizontalOffset = self.getResolved("horizontalOffset")
|
||||
local showScrollBar = self.getResolved("showScrollBar")
|
||||
local maxContentWidth, _ = self:getNodeSize()
|
||||
local needsHorizontalScrollBar = showScrollBar and maxContentWidth > width
|
||||
local contentHeight = needsHorizontalScrollBar and height - 1 or height
|
||||
@@ -480,8 +480,8 @@ function Tree:render()
|
||||
end
|
||||
|
||||
local isSelected = node == selectedNode
|
||||
local _bg = isSelected and self.get("selectedBackgroundColor") or (node.background or node.bg or self.get("background"))
|
||||
local _fg = isSelected and self.get("selectedForegroundColor") or (node.foreground or node.fg or self.get("foreground"))
|
||||
local _bg = isSelected and self.getResolved("selectedBackgroundColor") or (node.background or node.bg or self.getResolved("background"))
|
||||
local _fg = isSelected and self.getResolved("selectedForegroundColor") or (node.foreground or node.fg or self.getResolved("foreground"))
|
||||
|
||||
local fullText = indent .. symbol .. " " .. (node.text or "Node")
|
||||
local text = sub(fullText, horizontalOffset + 1, horizontalOffset + contentWidth)
|
||||
@@ -492,7 +492,7 @@ function Tree:render()
|
||||
|
||||
self:blit(1, y, paddedText, fg, bg)
|
||||
else
|
||||
self:blit(1, y, string.rep(" ", contentWidth), tHex[self.get("foreground")]:rep(contentWidth), tHex[self.get("background")]:rep(contentWidth))
|
||||
self:blit(1, y, string.rep(" ", contentWidth), tHex[self.getResolved("foreground")]:rep(contentWidth), tHex[self.getResolved("background")]:rep(contentWidth))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -537,7 +537,7 @@ function Tree:render()
|
||||
end
|
||||
|
||||
if needsVerticalScrollBar and needsHorizontalScrollBar then
|
||||
self:blit(width, height, " ", tHex[foreground], tHex[self.get("background")])
|
||||
self:blit(width, height, " ", tHex[foreground], tHex[self.getResolved("background")])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@ end
|
||||
--- @param offset number The offset to apply (negative = inside, positive = outside, fractional = percentage)
|
||||
--- @return VisualElement self The element instance
|
||||
function VisualElement:setConstraint(property, targetElement, targetProperty, offset)
|
||||
local constraints = self.get("constraints")
|
||||
local constraints = self.getResolved("constraints")
|
||||
if constraints[property] then
|
||||
self:_removeConstraintObservers(property, constraints[property])
|
||||
end
|
||||
@@ -187,7 +187,7 @@ end
|
||||
--- @param value any The value to set for the property
|
||||
--- @return VisualElement self The element instance
|
||||
function VisualElement:setLayoutConfigProperty(key, value)
|
||||
local layoutConfig = self.get("layoutConfig")
|
||||
local layoutConfig = self.getResolved("layoutConfig")
|
||||
layoutConfig[key] = value
|
||||
self.set("layoutConfig", layoutConfig)
|
||||
return self
|
||||
@@ -198,7 +198,7 @@ end
|
||||
--- @param key string The layout config property to get
|
||||
--- @return any value The value of the property, or nil if not set
|
||||
function VisualElement:getLayoutConfigProperty(key)
|
||||
local layoutConfig = self.get("layoutConfig")
|
||||
local layoutConfig = self.getResolved("layoutConfig")
|
||||
return layoutConfig[key]
|
||||
end
|
||||
|
||||
@@ -207,7 +207,7 @@ end
|
||||
--- @return VisualElement self The element instance
|
||||
function VisualElement:resolveAllConstraints()
|
||||
if not self._constraintsDirty then return self end
|
||||
local constraints = self.get("constraints")
|
||||
local constraints = self.getResolved("constraints")
|
||||
if not constraints or not next(constraints) then return self end
|
||||
|
||||
local order = {"width", "height", "left", "right", "top", "bottom", "x", "y", "centerX", "centerY"}
|
||||
@@ -236,7 +236,7 @@ function VisualElement:_applyConstraintValue(property, value, constraints)
|
||||
self.set("width", width)
|
||||
self.set("x", leftValue)
|
||||
else
|
||||
local width = self.get("width")
|
||||
local width = self.getResolved("width")
|
||||
self.set("x", value - width + 1)
|
||||
end
|
||||
elseif property == "bottom" then
|
||||
@@ -246,14 +246,14 @@ function VisualElement:_applyConstraintValue(property, value, constraints)
|
||||
self.set("height", height)
|
||||
self.set("y", topValue)
|
||||
else
|
||||
local height = self.get("height")
|
||||
local height = self.getResolved("height")
|
||||
self.set("y", value - height + 1)
|
||||
end
|
||||
elseif property == "centerX" then
|
||||
local width = self.get("width")
|
||||
local width = self.getResolved("width")
|
||||
self.set("x", value - math.floor(width / 2))
|
||||
elseif property == "centerY" then
|
||||
local height = self.get("height")
|
||||
local height = self.getResolved("height")
|
||||
self.set("y", value - math.floor(height / 2))
|
||||
elseif property == "width" then
|
||||
self.set("width", value)
|
||||
@@ -351,7 +351,7 @@ end
|
||||
--- @param property string The property of the constraint to remove
|
||||
--- @return VisualElement self The element instance
|
||||
function VisualElement:removeConstraint(property)
|
||||
local constraints = self.get("constraints")
|
||||
local constraints = self.getResolved("constraints")
|
||||
constraints[property] = nil
|
||||
self.set("constraints", constraints)
|
||||
self:updateConstraints()
|
||||
@@ -362,7 +362,7 @@ end
|
||||
--- @shortDescription Updates all constraints, recalculating positions and sizes
|
||||
--- @return VisualElement self The element instance
|
||||
function VisualElement:updateConstraints()
|
||||
local constraints = self.get("constraints")
|
||||
local constraints = self.getResolved("constraints")
|
||||
|
||||
for property, constraint in pairs(constraints) do
|
||||
local value = self:_resolveConstraint(property, constraint)
|
||||
@@ -372,16 +372,16 @@ function VisualElement:updateConstraints()
|
||||
elseif property == "y" or property == "top" then
|
||||
self.set("y", value)
|
||||
elseif property == "right" then
|
||||
local width = self.get("width")
|
||||
local width = self.getResolved("width")
|
||||
self.set("x", value - width + 1)
|
||||
elseif property == "bottom" then
|
||||
local height = self.get("height")
|
||||
local height = self.getResolved("height")
|
||||
self.set("y", value - height + 1)
|
||||
elseif property == "centerX" then
|
||||
local width = self.get("width")
|
||||
local width = self.getResolved("width")
|
||||
self.set("x", value - math.floor(width / 2))
|
||||
elseif property == "centerY" then
|
||||
local height = self.get("height")
|
||||
local height = self.getResolved("height")
|
||||
self.set("y", value - math.floor(height / 2))
|
||||
elseif property == "width" then
|
||||
self.set("width", value)
|
||||
@@ -403,7 +403,7 @@ function VisualElement:_resolveConstraint(property, constraint)
|
||||
end
|
||||
|
||||
if not targetEl then
|
||||
return self.get(property) or 1
|
||||
return self.getResolved(property) or 1
|
||||
end
|
||||
|
||||
local value
|
||||
@@ -761,9 +761,9 @@ end
|
||||
--- @param y number The y position to check
|
||||
--- @return boolean isInBounds Whether the coordinates are within the bounds of the element
|
||||
function VisualElement:isInBounds(x, y)
|
||||
local xPos, yPos = self.get("x"), self.get("y")
|
||||
local width, height = self.get("width"), self.get("height")
|
||||
if(self.get("ignoreOffset"))then
|
||||
local xPos, yPos = self.getResolved("x"), self.getResolved("y")
|
||||
local width, height = self.getResolved("width"), self.getResolved("height")
|
||||
if(self.getResolved("ignoreOffset"))then
|
||||
if(self.parent)then
|
||||
x = x - self.parent.get("offsetX")
|
||||
y = y - self.parent.get("offsetY")
|
||||
@@ -824,7 +824,7 @@ end
|
||||
--- @protected
|
||||
function VisualElement:mouse_move(_, x, y)
|
||||
if(x==nil)or(y==nil)then return false end
|
||||
local hover = self.get("hover")
|
||||
local hover = self.getResolved("hover")
|
||||
if(self:isInBounds(x, y))then
|
||||
if(not hover)then
|
||||
self.set("hover", true)
|
||||
@@ -1000,8 +1000,8 @@ end
|
||||
--- @return number y The y position
|
||||
function VisualElement:calculatePosition()
|
||||
self:resolveAllConstraints()
|
||||
local x, y = self.get("x"), self.get("y")
|
||||
if not self.get("ignoreOffset") then
|
||||
local x, y = self.getResolved("x"), self.getResolved("y")
|
||||
if not self.getResolved("ignoreOffset") then
|
||||
if self.parent ~= nil then
|
||||
local xO, yO = self.parent.get("offsetX"), self.parent.get("offsetY")
|
||||
x = x - xO
|
||||
@@ -1018,7 +1018,7 @@ end
|
||||
---@return number x The absolute x position
|
||||
---@return number y The absolute y position
|
||||
function VisualElement:getAbsolutePosition(x, y)
|
||||
local xPos, yPos = self.get("x"), self.get("y")
|
||||
local xPos, yPos = self.getResolved("x"), self.getResolved("y")
|
||||
if(x ~= nil) then
|
||||
xPos = xPos + x - 1
|
||||
end
|
||||
@@ -1045,7 +1045,7 @@ end
|
||||
--- @return number y The relative y position
|
||||
function VisualElement:getRelativePosition(x, y)
|
||||
if (x == nil) or (y == nil) then
|
||||
x, y = self.get("x"), self.get("y")
|
||||
x, y = self.getResolved("x"), self.getResolved("y")
|
||||
end
|
||||
|
||||
local parentX, parentY = 1, 1
|
||||
@@ -1053,7 +1053,7 @@ function VisualElement:getRelativePosition(x, y)
|
||||
parentX, parentY = self.parent:getRelativePosition()
|
||||
end
|
||||
|
||||
local elementX, elementY = self.get("x"), self.get("y")
|
||||
local elementX, elementY = self.getResolved("x"), self.getResolved("y")
|
||||
return x - (elementX - 1) - (parentX - 1),
|
||||
y - (elementY - 1) - (parentY - 1)
|
||||
end
|
||||
@@ -1094,7 +1094,7 @@ end
|
||||
--- @protected
|
||||
function VisualElement:render()
|
||||
if(not self.getResolved("backgroundEnabled"))then return end
|
||||
local width, height = self.get("width"), self.get("height")
|
||||
local width, height = self.getResolved("width"), self.getResolved("height")
|
||||
local fgHex = tHex[self.getResolved("foreground")]
|
||||
local bgHex = tHex[self.getResolved("background")]
|
||||
local bTop, bBottom, bLeft, bRight =
|
||||
|
||||
@@ -90,7 +90,7 @@ local function parseExpression(expr, element, propName)
|
||||
if objName == "self" then
|
||||
-- Check if property exists
|
||||
if element._properties[propName] then
|
||||
return element.get(propName)
|
||||
return element.getResolved(propName)
|
||||
end
|
||||
if element._registeredStates and element._registeredStates[propName] then
|
||||
return element:hasState(propName)
|
||||
@@ -104,7 +104,7 @@ local function parseExpression(expr, element, propName)
|
||||
return nil
|
||||
elseif objName == "parent" then
|
||||
if element.parent._properties[propName] then
|
||||
return element.parent.get(propName)
|
||||
return element.parent.getResolved(propName)
|
||||
end
|
||||
if element.parent._registeredStates and element.parent._registeredStates[propName] then
|
||||
return element.parent:hasState(propName)
|
||||
@@ -125,7 +125,7 @@ local function parseExpression(expr, element, propName)
|
||||
end
|
||||
|
||||
if target._properties[propName] then
|
||||
return target.get(propName)
|
||||
return target.getResolved(propName)
|
||||
end
|
||||
if target._registeredStates and target._registeredStates[propName] then
|
||||
return target:hasState(propName)
|
||||
|
||||
157
src/plugins/responsive.lua
Normal file
157
src/plugins/responsive.lua
Normal file
@@ -0,0 +1,157 @@
|
||||
local errorManager = require("errorManager")
|
||||
---@configDefault false
|
||||
|
||||
--- This is the responsive plugin. It provides a fluent builder API for creating responsive states with an intuitive when/apply/otherwise syntax.
|
||||
---@class BaseElement
|
||||
local BaseElement = {}
|
||||
|
||||
--- Creates a responsive builder for defining responsive states
|
||||
--- @shortDescription Creates a responsive state builder
|
||||
--- @param self BaseElement The element to create the builder for
|
||||
--- @return ResponsiveBuilder builder The responsive builder instance
|
||||
function BaseElement:responsive()
|
||||
local builder = {
|
||||
_element = self,
|
||||
_rules = {},
|
||||
_currentStateName = nil,
|
||||
_currentCondition = nil,
|
||||
_stateCounter = 0
|
||||
}
|
||||
|
||||
--- Defines a condition for responsive behavior
|
||||
--- @param condition string|function The condition as string expression or function
|
||||
--- @return ResponsiveBuilder self For method chaining
|
||||
function builder:when(condition)
|
||||
if self._currentCondition then
|
||||
errorManager.header = "Responsive Builder Error"
|
||||
errorManager.error("Previous when() must be followed by apply() before starting a new when()")
|
||||
end
|
||||
|
||||
self._stateCounter = self._stateCounter + 1
|
||||
self._currentStateName = "__responsive_" .. self._stateCounter
|
||||
self._currentCondition = condition
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Applies properties when the current condition is met
|
||||
--- @param properties table The properties to apply {property = value, ...}
|
||||
--- @return ResponsiveBuilder self For method chaining
|
||||
function builder:apply(properties)
|
||||
if not self._currentCondition then
|
||||
errorManager.header = "Responsive Builder Error"
|
||||
errorManager.error("apply() must follow a when() call")
|
||||
end
|
||||
|
||||
if type(properties) ~= "table" then
|
||||
errorManager.header = "Responsive Builder Error"
|
||||
errorManager.error("apply() requires a table of properties")
|
||||
end
|
||||
|
||||
self._element:registerResponsiveState(
|
||||
self._currentStateName,
|
||||
self._currentCondition,
|
||||
100
|
||||
)
|
||||
|
||||
for propName, value in pairs(properties) do
|
||||
local capitalizedName = propName:sub(1,1):upper() .. propName:sub(2)
|
||||
local setter = "set" .. capitalizedName .. "State"
|
||||
|
||||
if self._element[setter] then
|
||||
self._element[setter](self._element, self._currentStateName, value)
|
||||
else
|
||||
errorManager.header = "Responsive Builder Error"
|
||||
errorManager.error("Unknown property: " .. propName)
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(self._rules, {
|
||||
stateName = self._currentStateName,
|
||||
condition = self._currentCondition,
|
||||
properties = properties
|
||||
})
|
||||
|
||||
self._currentCondition = nil
|
||||
self._currentStateName = nil
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Defines a fallback condition (else case)
|
||||
--- @param properties table The properties to apply when no other conditions match
|
||||
--- @return ResponsiveBuilder self For method chaining
|
||||
function builder:otherwise(properties)
|
||||
if self._currentCondition then
|
||||
errorManager.header = "Responsive Builder Error"
|
||||
errorManager.error("otherwise() cannot be used after when() without apply()")
|
||||
end
|
||||
|
||||
if type(properties) ~= "table" then
|
||||
errorManager.header = "Responsive Builder Error"
|
||||
errorManager.error("otherwise() requires a table of properties")
|
||||
end
|
||||
|
||||
self._stateCounter = self._stateCounter + 1
|
||||
local otherwiseStateName = "__responsive_otherwise_" .. self._stateCounter
|
||||
|
||||
local otherRules = {}
|
||||
for _, rule in ipairs(self._rules) do
|
||||
table.insert(otherRules, rule.condition)
|
||||
end
|
||||
|
||||
local otherwiseCondition
|
||||
if type(otherRules[1]) == "string" then
|
||||
local negatedExprs = {}
|
||||
for _, cond in ipairs(otherRules) do
|
||||
table.insert(negatedExprs, "not (" .. cond .. ")")
|
||||
end
|
||||
otherwiseCondition = table.concat(negatedExprs, " and ")
|
||||
else
|
||||
otherwiseCondition = function(elem)
|
||||
for _, cond in ipairs(otherRules) do
|
||||
if cond(elem) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
self._element:registerResponsiveState(
|
||||
otherwiseStateName,
|
||||
otherwiseCondition,
|
||||
50
|
||||
)
|
||||
|
||||
for propName, value in pairs(properties) do
|
||||
local capitalizedName = propName:sub(1,1):upper() .. propName:sub(2)
|
||||
local setter = "set" .. capitalizedName .. "State"
|
||||
|
||||
if self._element[setter] then
|
||||
self._element[setter](self._element, otherwiseStateName, value)
|
||||
else
|
||||
errorManager.header = "Responsive Builder Error"
|
||||
errorManager.error("Unknown property: " .. propName)
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Completes the builder (optional, for clarity)
|
||||
--- @return BaseElement element The original element
|
||||
function builder:done()
|
||||
if self._currentCondition then
|
||||
errorManager.header = "Responsive Builder Error"
|
||||
errorManager.error("Unfinished when() without apply()")
|
||||
end
|
||||
return self._element
|
||||
end
|
||||
|
||||
return builder
|
||||
end
|
||||
|
||||
return {
|
||||
BaseElement = BaseElement
|
||||
}
|
||||
@@ -335,12 +335,16 @@ function PropertySystem:__init()
|
||||
end
|
||||
|
||||
self.getResolved = function(name, ...)
|
||||
local currentState = self:getCurrentState()
|
||||
local value
|
||||
local activeStates = self:getActiveStates()
|
||||
local value = nil
|
||||
for _, stateInfo in ipairs(activeStates) do
|
||||
if self._states and self._states[stateInfo.name] and self._states[stateInfo.name][name] ~= nil then
|
||||
value = self._states[stateInfo.name][name]
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if currentState and self._states and self._states[currentState] and self._states[currentState][name] ~= nil then
|
||||
value = self._states[currentState][name]
|
||||
else
|
||||
if value == nil then
|
||||
value = self._values[name]
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user