Dropdown Fix
Progressbar Fix List 2 new Methods: scrollToBottom scrollToTop Readme Update Generate-config size addition
This commit is contained in:
20
README.md
20
README.md
@@ -1,30 +1,28 @@
|
|||||||
# Basalt - A UI Framework for CC:Tweaked
|
# Basalt 2 - A UI Framework for CC:Tweaked
|
||||||
|
|
||||||

|

|
||||||
[](https://discord.gg/yNNnmBVBpE)
|
[](https://discord.gg/yNNnmBVBpE)
|
||||||
|
|
||||||
This is a complete rework of Basalt. It provides an intuitive way to create complex user interfaces for your CC:Tweaked programs.
|
Welcome,
|
||||||
|
|
||||||
Basalt is intended to be an easy-to-understand UI Framework designed for CC:Tweaked - a popular minecraft mod. For more information about CC:Tweaked, checkout the project's [wiki](https://tweaked.cc/) or [download](https://modrinth.com/mod/cc-tweaked).
|
This is a complete rework of [Basalt](https://github.com/Pyroxenium/Basalt). There are many new features, including auto-generated LuaLS annotations and auto-generated documentation, ensuring that everything is always up-to-date.
|
||||||
**Note:** Basalt is still under developement and you may find bugs!
|
|
||||||
|
|
||||||
Check out the [wiki](https://basalt.madefor.cc/) for more information.
|
Basalt is a UI framework designed for [CC:Tweaked](https://tweaked.cc/) - a popular Minecraft mod. For more information about CC:Tweaked, check out the project's [wiki](https://tweaked.cc/) or download it from [Modrinth](https://modrinth.com/mod/cc-tweaked) or [CurseForge](https://www.curseforge.com/minecraft/mc-mods/cc-tweaked).
|
||||||
If you have questions, feel free to join the discord server: [discord.gg/yNNnmBVBpE](https://discord.gg/yNNnmBVBpE).
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
For detailed documentation, examples and guides, visit [basalt.madefor.cc](https://basalt.madefor.cc/)
|
For detailed documentation, examples, and guides, visit our documentation on [basalt.madefor.cc](https://basalt.madefor.cc/).
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
If you need help or have questions:
|
If you need help or have questions:
|
||||||
- Check the [documentation](https://basalt.madefor.cc/)
|
- Check the [documentation](https://basalt.madefor.cc/).
|
||||||
- Join our [Discord](https://discord.gg/yNNnmBVBpE)
|
- Join our [Discord](https://discord.gg/yNNnmBVBpE).
|
||||||
- Report issues on [GitHub](https://github.com/Pyroxenium/Basalt2/issues)
|
- Report issues or contribute on [GitHub](https://github.com/Pyroxenium/Basalt2/issues).
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This project is licensed under the MIT License.
|
This project is licensed under the [MIT License](https://opensource.org/licenses/MIT).
|
||||||
|
|
||||||
## Demo
|
## Demo
|
||||||
|
|
||||||
|
|||||||
84
install.lua
84
install.lua
@@ -1,4 +1,5 @@
|
|||||||
local basalt = require("src")
|
local basalt = require("src")
|
||||||
|
local url = "https://raw.githubusercontent.com/Pyroxenium/Basalt2/refs/heads/main/src/"
|
||||||
local configPath = "https://raw.githubusercontent.com/Pyroxenium/Basalt2/refs/heads/main/config.lua"
|
local configPath = "https://raw.githubusercontent.com/Pyroxenium/Basalt2/refs/heads/main/config.lua"
|
||||||
|
|
||||||
local coloring = {foreground=colors.black, background=colors.white}
|
local coloring = {foreground=colors.black, background=colors.white}
|
||||||
@@ -135,19 +136,33 @@ end)
|
|||||||
|
|
||||||
installScreen:addLabel(coloring)
|
installScreen:addLabel(coloring)
|
||||||
:setText("Path:")
|
:setText("Path:")
|
||||||
:setPosition(2, "{versionDesc.y + versionDesc.height + 1}")
|
:setPosition(2, "{versionDesc.y + versionDesc.height + 5}")
|
||||||
|
|
||||||
installScreen:addLabel(coloring)
|
installScreen:addLabel(coloring)
|
||||||
:setText("Additional Components:")
|
:setText("Additional Components:")
|
||||||
:setPosition(2, "{versionDesc.y + versionDesc.height + 1}")
|
:setPosition(2, "{versionDesc.y + versionDesc.height + 1}")
|
||||||
|
|
||||||
local luaLSCheckbox = installScreen:addCheckbox()
|
local luaLSCheckbox = installScreen:addCheckbox()
|
||||||
:setPosition(2, 12)
|
|
||||||
:setText("[ ] LLS definitions")
|
:setText("[ ] LLS definitions")
|
||||||
:setCheckedText("[x] LLS definitions")
|
:setCheckedText("[x] LLS definitions")
|
||||||
|
:setPosition(2, "{versionDesc.y + versionDesc.height + 2}")
|
||||||
:setBackground(colors.white)
|
:setBackground(colors.white)
|
||||||
:setForeground(colors.black)
|
:setForeground(colors.black)
|
||||||
|
|
||||||
|
local luaMinifyCheckbox = installScreen:addCheckbox()
|
||||||
|
:setText("[ ] Minify Project")
|
||||||
|
:setCheckedText("[x] Minify Project")
|
||||||
|
:setPosition(2, "{versionDesc.y + versionDesc.height + 3}")
|
||||||
|
:setBackground(colors.white)
|
||||||
|
:setForeground(colors.black)
|
||||||
|
|
||||||
|
local installPathInput = installScreen:addInput()
|
||||||
|
:setPosition(8, "{versionDesc.y + versionDesc.height + 5}")
|
||||||
|
:setPlaceholder("basalt")
|
||||||
|
:setSize(12, 1)
|
||||||
|
:setBackground(colors.black)
|
||||||
|
:setForeground(colors.white)
|
||||||
|
|
||||||
-- Screen 3: Elements
|
-- Screen 3: Elements
|
||||||
local elementsScreen = createScreen(3)
|
local elementsScreen = createScreen(3)
|
||||||
elementsScreen:addLabel(coloring)
|
elementsScreen:addLabel(coloring)
|
||||||
@@ -179,7 +194,13 @@ local eleScreenDesc = elementsScreen:addLabel()
|
|||||||
local function addElements()
|
local function addElements()
|
||||||
elementsList:clear()
|
elementsList:clear()
|
||||||
for k,v in pairs(getConfig().categories.elements.files)do
|
for k,v in pairs(getConfig().categories.elements.files)do
|
||||||
elementsList:addItem({selected=true, text=v.name, callback=function() elementDesc:setText(v.description) end})
|
elementsList:addItem({selected=v.default, text=k, item=v, callback=function()
|
||||||
|
if(v.description)and(v.description~="")then
|
||||||
|
elementDesc:setText(v.description)
|
||||||
|
else
|
||||||
|
elementDesc:setText("No description available.")
|
||||||
|
end
|
||||||
|
end})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
addElements()
|
addElements()
|
||||||
@@ -215,11 +236,24 @@ local pluScreenDesc = pluginScreen:addLabel()
|
|||||||
local function addPlugins()
|
local function addPlugins()
|
||||||
pluginList:clear()
|
pluginList:clear()
|
||||||
for k,v in pairs(getConfig().categories.plugins.files)do
|
for k,v in pairs(getConfig().categories.plugins.files)do
|
||||||
pluginList:addItem({selected = true, text= v.name, callback=function() pluginDesc:setText(v.description) end})
|
pluginList:addItem({selected = v.default, text=k, item=v, callback=function()
|
||||||
|
if(v.description)and(v.description~="")then
|
||||||
|
elementDesc:setText(v.description)
|
||||||
|
else
|
||||||
|
elementDesc:setText("No description available.")
|
||||||
|
end
|
||||||
|
end})
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
addPlugins()
|
addPlugins()
|
||||||
|
|
||||||
|
local function tableGet(t)
|
||||||
|
local count = 0
|
||||||
|
for _ in pairs(t) do count = count + 1 end
|
||||||
|
return count
|
||||||
|
end
|
||||||
|
|
||||||
-- Screen 5 Installation Progress
|
-- Screen 5 Installation Progress
|
||||||
local progressScreen = createScreen(5)
|
local progressScreen = createScreen(5)
|
||||||
local progressBar = progressScreen:addProgressBar()
|
local progressBar = progressScreen:addProgressBar()
|
||||||
@@ -234,8 +268,14 @@ local log = progressScreen:addList("log")
|
|||||||
local function install()
|
local function install()
|
||||||
local function logMessage(message)
|
local function logMessage(message)
|
||||||
log:addItem(message)
|
log:addItem(message)
|
||||||
|
log:scrollToBottom()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local fileCount = tableGet(getConfig().categories.core.files) + tableGet(getConfig().categories.libraries.files)
|
||||||
|
fileCount = fileCount + tableGet(elementsList:getSelectedItems()) + tableGet(pluginList:getSelectedItems())
|
||||||
|
progressBar:setProgress(0)
|
||||||
|
local progressStep = math.ceil(100 / fileCount)
|
||||||
|
|
||||||
local function downloadFile(url, path)
|
local function downloadFile(url, path)
|
||||||
logMessage("Downloading " .. url .. "...")
|
logMessage("Downloading " .. url .. "...")
|
||||||
local request = http.get(url)
|
local request = http.get(url)
|
||||||
@@ -250,25 +290,43 @@ local function install()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function installElement(name, url)
|
local path = installPathInput:getText()
|
||||||
|
if path == "" then
|
||||||
|
path = "basalt"
|
||||||
|
end
|
||||||
|
|
||||||
|
for k, v in pairs(getConfig().categories.core.files) do
|
||||||
|
logMessage("Installing core: " .. k)
|
||||||
|
downloadFile(url..v.path, fs.combine(path, v.path))
|
||||||
|
progressBar:setProgress(progressBar:getProgress() + progressStep)
|
||||||
|
end
|
||||||
|
|
||||||
|
for k, v in pairs(getConfig().categories.libraries.files) do
|
||||||
|
logMessage("Installing library: " .. k)
|
||||||
|
downloadFile(url..v.path, fs.combine(path, v.path))
|
||||||
|
progressBar:setProgress(progressBar:getProgress() + progressStep)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function installElement(name, item)
|
||||||
logMessage("Installing element: " .. name)
|
logMessage("Installing element: " .. name)
|
||||||
--downloadFile(url, "/path/to/install/" .. name)
|
downloadFile(url..item.path, fs.combine(path, item.path))
|
||||||
|
progressBar:setProgress(progressBar:getProgress() + progressStep)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function installPlugin(name, url)
|
local function installPlugin(name, item)
|
||||||
logMessage("Installing plugin: " .. name)
|
logMessage("Installing plugin: " .. name)
|
||||||
--downloadFile(url, "/path/to/install/" .. name)
|
downloadFile(url..item.path, fs.combine(path, item.path))
|
||||||
|
progressBar:setProgress(progressBar:getProgress() + progressStep)
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, element in ipairs(elementsList:getSelectedItems()) do
|
for k, element in ipairs(elementsList:getSelectedItems()) do
|
||||||
local item = element.item
|
local item = element.item
|
||||||
basalt.LOGGER.debug(item.text)
|
installElement(item.text, item.item)
|
||||||
installElement(item.text, getConfig().categories.elements.files[item.text].url)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, plugin in ipairs(pluginList:getSelectedItems()) do
|
for _, plugin in ipairs(pluginList:getSelectedItems()) do
|
||||||
local item = plugin.item
|
local item = plugin.item
|
||||||
installPlugin(item.text, getConfig().categories.plugins.files[item.text].url)
|
installPlugin(item.text, item.item)
|
||||||
end
|
end
|
||||||
|
|
||||||
progressBar:setProgress(100)
|
progressBar:setProgress(100)
|
||||||
|
|||||||
@@ -62,13 +62,37 @@ function Dropdown:mouse_click(button, x, y)
|
|||||||
self.set("height", 1 + math.min(self.get("dropdownHeight"), #self.get("items")))
|
self.set("height", 1 + math.min(self.get("dropdownHeight"), #self.get("items")))
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
elseif self.get("isOpen") and relY > 1 then
|
elseif self.get("isOpen") and relY > 1 and self.get("selectable") then
|
||||||
-- Nutze List's mouse_click für Item-Selektion
|
local itemIndex = (relY - 1) + self.get("offset")
|
||||||
List.mouse_click(self, button, x, y)
|
local items = self.get("items")
|
||||||
-- Nach Selektion Dropdown schließen
|
|
||||||
self.set("isOpen", false)
|
if itemIndex <= #items then
|
||||||
self.set("height", 1)
|
local item = items[itemIndex]
|
||||||
return true
|
if type(item) == "string" then
|
||||||
|
item = {text = item}
|
||||||
|
items[itemIndex] = item
|
||||||
|
end
|
||||||
|
|
||||||
|
if not self.get("multiSelection") then
|
||||||
|
for _, otherItem in ipairs(items) do
|
||||||
|
if type(otherItem) == "table" then
|
||||||
|
otherItem.selected = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
item.selected = not item.selected
|
||||||
|
|
||||||
|
if item.callback then
|
||||||
|
item.callback(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
self:fireEvent("select", itemIndex, item)
|
||||||
|
self.set("isOpen", false)
|
||||||
|
self.set("height", 1)
|
||||||
|
self:updateRender()
|
||||||
|
return true
|
||||||
|
end
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
@@ -78,10 +102,8 @@ end
|
|||||||
function Dropdown:render()
|
function Dropdown:render()
|
||||||
VisualElement.render(self)
|
VisualElement.render(self)
|
||||||
|
|
||||||
-- Header rendern
|
|
||||||
local text = self.get("selectedText")
|
local text = self.get("selectedText")
|
||||||
if #text == 0 then
|
if #text == 0 then
|
||||||
-- Suche nach selektiertem Item
|
|
||||||
local selectedItems = self:getSelectedItems()
|
local selectedItems = self:getSelectedItems()
|
||||||
if #selectedItems > 0 then
|
if #selectedItems > 0 then
|
||||||
local selectedItem = selectedItems[1].item
|
local selectedItem = selectedItems[1].item
|
||||||
@@ -89,20 +111,51 @@ function Dropdown:render()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Header mit Dropdown Symbol
|
|
||||||
self:blit(1, 1, text .. string.rep(" ", self.get("width") - #text - 1) .. (self.get("isOpen") and "\31" or "\17"),
|
self:blit(1, 1, text .. string.rep(" ", self.get("width") - #text - 1) .. (self.get("isOpen") and "\31" or "\17"),
|
||||||
string.rep(tHex[self.get("foreground")], self.get("width")),
|
string.rep(tHex[self.get("foreground")], self.get("width")),
|
||||||
string.rep(tHex[self.get("background")], self.get("width")))
|
string.rep(tHex[self.get("background")], self.get("width")))
|
||||||
|
|
||||||
-- Liste rendern wenn offen
|
|
||||||
if self.get("isOpen") then
|
if self.get("isOpen") then
|
||||||
-- Offset um 1 verschieben wegen Header
|
local items = self.get("items")
|
||||||
local oldOffset = self.get("offset")
|
local height = self.get("height") - 1
|
||||||
self.set("offset", oldOffset + 1)
|
local offset = self.get("offset")
|
||||||
-- Liste ab Zeile 2 rendern
|
local width = self.get("width")
|
||||||
List.render(self)
|
|
||||||
-- Offset zurücksetzen
|
for i = 1, height do
|
||||||
self.set("offset", oldOffset)
|
local itemIndex = i + offset
|
||||||
|
local item = items[itemIndex]
|
||||||
|
|
||||||
|
if item then
|
||||||
|
if type(item) == "string" then
|
||||||
|
item = {text = item}
|
||||||
|
items[itemIndex] = item
|
||||||
|
end
|
||||||
|
|
||||||
|
if item.separator then
|
||||||
|
local separatorChar = (item.text or "-"):sub(1,1)
|
||||||
|
local separatorText = string.rep(separatorChar, width)
|
||||||
|
local fg = item.foreground or self.get("foreground")
|
||||||
|
local bg = item.background or self.get("background")
|
||||||
|
|
||||||
|
self:textBg(1, i + 1, string.rep(" ", width), bg)
|
||||||
|
self:textFg(1, i + 1, separatorText, fg)
|
||||||
|
else
|
||||||
|
local text = item.text
|
||||||
|
local isSelected = item.selected
|
||||||
|
|
||||||
|
local bg = isSelected and
|
||||||
|
(item.selectedBackground or self.get("selectedBackground")) or
|
||||||
|
(item.background or self.get("background"))
|
||||||
|
|
||||||
|
local fg = isSelected and
|
||||||
|
(item.selectedForeground or self.get("selectedForeground")) or
|
||||||
|
(item.foreground or self.get("foreground"))
|
||||||
|
|
||||||
|
self:textBg(1, i + 1, string.rep(" ", width), bg)
|
||||||
|
self:textFg(1, i + 1, text, fg)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -167,6 +167,23 @@ function List:onSelect(callback)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Scrolls the list to the bottom
|
||||||
|
--- @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"))
|
||||||
|
self.set("offset", maxOffset)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Scrolls the list to the top
|
||||||
|
--- @shortDescription Scrolls the list to the top
|
||||||
|
--- @return List self The List instance
|
||||||
|
function List:scrollToTop()
|
||||||
|
self.set("offset", 0)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Renders the list
|
--- Renders the list
|
||||||
--- @shortDescription Renders the list
|
--- @shortDescription Renders the list
|
||||||
function List:render()
|
function List:render()
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ ProgressBar.defineProperty(ProgressBar, "progress", {default = 0, type = "number
|
|||||||
---@property showPercentage boolean false Whether to show the percentage text in the center
|
---@property showPercentage boolean false Whether to show the percentage text in the center
|
||||||
ProgressBar.defineProperty(ProgressBar, "showPercentage", {default = false, type = "boolean"})
|
ProgressBar.defineProperty(ProgressBar, "showPercentage", {default = false, type = "boolean"})
|
||||||
---@property progressColor color lime The color used for the filled portion of the progress bar
|
---@property progressColor color lime The color used for the filled portion of the progress bar
|
||||||
ProgressBar.defineProperty(ProgressBar, "progressColor", {default = colors.lime, type = "number"})
|
ProgressBar.defineProperty(ProgressBar, "progressColor", {default = colors.black, type = "number"})
|
||||||
|
|
||||||
--- Creates a new ProgressBar instance
|
--- Creates a new ProgressBar instance
|
||||||
--- @shortDescription Creates a new ProgressBar instance
|
--- @shortDescription Creates a new ProgressBar instance
|
||||||
@@ -42,7 +42,9 @@ function ProgressBar:render()
|
|||||||
local progress = math.min(100, math.max(0, self.get("progress")))
|
local progress = math.min(100, math.max(0, self.get("progress")))
|
||||||
local fillWidth = math.floor((width * progress) / 100)
|
local fillWidth = math.floor((width * progress) / 100)
|
||||||
|
|
||||||
self:textBg(1, 1, string.rep(" ", fillWidth), self.get("progressColor"))
|
for i = 1, self.get("height") do
|
||||||
|
self:textBg(1, i, string.rep(" ", fillWidth), self.get("progressColor"))
|
||||||
|
end
|
||||||
|
|
||||||
if self.get("showPercentage") then
|
if self.get("showPercentage") then
|
||||||
local text = tostring(progress).."%"
|
local text = tostring(progress).."%"
|
||||||
|
|||||||
@@ -24,16 +24,20 @@ local function serialize(t, indent)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function parseFile(filePath)
|
local function parseFile(filePath)
|
||||||
|
if filePath:match("LuaLS%.lua$") then return nil end
|
||||||
|
|
||||||
local file = io.open(filePath, "r")
|
local file = io.open(filePath, "r")
|
||||||
if not file then return nil end
|
if not file then return nil end
|
||||||
|
|
||||||
local content = file:read("*all")
|
local content = file:read("*all")
|
||||||
|
local size = #content
|
||||||
file:close()
|
file:close()
|
||||||
|
|
||||||
local config = {
|
local config = {
|
||||||
description = "",
|
description = "",
|
||||||
default = true,
|
default = true,
|
||||||
requires = {}
|
requires = {},
|
||||||
|
size = size
|
||||||
}
|
}
|
||||||
|
|
||||||
local description = content:match("%-%-%-@configDescription%s*(.-)%s*\n")
|
local description = content:match("%-%-%-@configDescription%s*(.-)%s*\n")
|
||||||
@@ -79,11 +83,13 @@ local function scanDirectory(srcPath)
|
|||||||
if not pipe then return end
|
if not pipe then return end
|
||||||
|
|
||||||
for path in pipe:lines() do
|
for path in pipe:lines() do
|
||||||
local config = parseFile(path)
|
if(path~="LuaLS.lua")then
|
||||||
if config then
|
local config = parseFile(path)
|
||||||
config.name = path:match("([^/]+)%.lua$")
|
if config then
|
||||||
config.path = path:gsub("^" .. srcPath .. "/", "")
|
config.name = path:match("([^/]+)%.lua$")
|
||||||
files[path] = config
|
config.path = path:gsub("^" .. srcPath .. "/", "")
|
||||||
|
files[path] = config
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
pipe:close()
|
pipe:close()
|
||||||
@@ -109,7 +115,8 @@ local function generateConfig(srcPath)
|
|||||||
path = fileConfig.path,
|
path = fileConfig.path,
|
||||||
description = fileConfig.description,
|
description = fileConfig.description,
|
||||||
default = fileConfig.default,
|
default = fileConfig.default,
|
||||||
requires = fileConfig.requires
|
requires = fileConfig.requires,
|
||||||
|
size = fileConfig.size
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -139,7 +146,6 @@ local function generateConfig(srcPath)
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Config generieren und speichern
|
|
||||||
local config = generateConfig("src")
|
local config = generateConfig("src")
|
||||||
local configFile = io.open("config.lua", "w")
|
local configFile = io.open("config.lua", "w")
|
||||||
configFile:write("return " .. serialize(config))
|
configFile:write("return " .. serialize(config))
|
||||||
|
|||||||
Reference in New Issue
Block a user