Initial commit

This commit is contained in:
Patrick Alvin Alcala 2025-06-26 16:53:43 +08:00
commit 209ba130c0
4852 changed files with 1517959 additions and 0 deletions

View file

@ -0,0 +1,269 @@
--
-- SelectBox Widget.
-- @copyright Jefferson Gonzalez
-- @license MIT
--
local common = require "core.common"
local style = require "core.style"
local Widget = require "libraries.widget"
local ListBox = require "libraries.widget.listbox"
---@class widget.selectbox : widget
---@field private list_container widget
---@field private list widget.listbox
---@field private selected integer
---@field private hover_text renderer.color
local SelectBox = Widget:extend()
---Constructor
---@param parent widget
---@param label string
function SelectBox:new(parent, label)
SelectBox.super.new(self, parent)
self.type_name = "widget.selectbox"
self.size.x = 200 + (style.padding.x * 2)
self.size.y = self:get_font():get_height() + (style.padding.y * 2)
self.list_container = Widget()
self.list_container:set_size(
self.size.x - self.list_container.border.width,
150
)
self.list = ListBox(self.list_container)
self.list.border.width = 0
self.list:enable_expand(true)
self.selected = 0
local list_on_row_click = self.list.on_row_click
self.list.on_row_click = function(this, idx, data)
list_on_row_click(this, idx, data)
if idx ~= 1 then
self.selected = idx-1
self:on_selected(idx-1, data)
self:on_change(self.selected)
end
self.list_container:hide_animated(true)
end
self:set_label(label or "select")
end
---Set the text displayed when no item is selected.
---@param text string
function SelectBox:set_label(text)
SelectBox.super.set_label(self, "- "..text.." -")
if not self.list.rows[1] then
self.list:add_row({"- "..text.." -"})
else
self.list.rows[1][1] = "- "..text.." -"
end
end
---Add selectable option to the selectbox.
---@param text widget.styledtext|string
---@param data any
function SelectBox:add_option(text, data)
if type(text) == "string" then
self.list:add_row({ text }, data)
else
self.list:add_row(text, data)
end
end
---Check if a text is longer than the given maximum width and if larger returns
---a chopped version with the overflow_chars appended to it.
---@param text string
---@param max_width number
---@param font widget.font Default is style.font
---@param overflow_chars? string Default is '...'
---@return string chopped_text
---@return boolean overflows True if the text overflows
function SelectBox:text_overflow(text, max_width, font, overflow_chars)
font = self:get_font(font)
overflow_chars = overflow_chars or "..."
local overflow = false
local overflow_chars_width = font:get_width(overflow_chars)
if font:get_width(text) > max_width then
overflow = true
for i = 1, #text do
local reduced_text = text:sub(1, #text - i)
if
font:get_width(reduced_text) + overflow_chars_width
<=
max_width
then
text = reduced_text .. ""
break
end
end
end
return text, overflow
end
---Set the active option index.
---@param idx integer
function SelectBox:set_selected(idx)
if self.list.rows[idx+1] then
self.selected = idx
self.list:set_selected(idx+1)
else
self.selected = 0
self.list:set_selected()
end
self:on_change(self.selected)
end
---Get the currently selected option index.
---@return integer
function SelectBox:get_selected()
return self.selected
end
---Get the currently selected option text.
---@return string|nil
function SelectBox:get_selected_text()
if self.selected > 0 then
return self.list:get_row_text(self.selected + 1)
end
return nil
end
---Get the currently selected option associated data.
---@return any|nil
function SelectBox:get_selected_data()
if self.selected > 0 then
return self.list:get_row_data(self.selected + 1)
end
return nil
end
---Repositions the listbox container according to the selectbox position
---and available screensize.
function SelectBox:reposition_container()
local y1 = self.position.y + self:get_height()
local y2 = self.position.y - self.list:get_height()
local _, h = system.get_window_size()
if y1 + self.list:get_height() <= h then
self.list_container:set_position(
self.position.x,
y1
)
else
self.list_container:set_position(
self.position.x,
y2
)
end
self.list_container.size.x = self.size.x - (self.border.width * 2)
end
---Overrided to destroy the floating listbox container.
function SelectBox:destroy_childs()
SelectBox.super.destroy_childs(self)
self.list_container:destroy()
end
--
-- Events
--
---Overwrite to listen to on_selected events.
---@param item_idx integer
---@param item_data widget.listbox.row
---@diagnostic disable-next-line
function SelectBox:on_selected(item_idx, item_data) end
function SelectBox:on_mouse_enter(...)
SelectBox.super.on_mouse_enter(self, ...)
self.hover_text = style.accent
end
function SelectBox:on_mouse_leave(...)
SelectBox.super.on_mouse_leave(self, ...)
self.hover_text = nil
end
function SelectBox:on_click(button, x, y)
SelectBox.super.on_click(self, button, x, y)
if button == "left" then
self:reposition_container()
self.list_container.border.color = style.caret
self.list_container:toggle_visible(true, true)
self.list:resize_to_parent()
end
end
function SelectBox:update()
if not SelectBox.super.update(self) then return false end
local font = self:get_font()
self.size.y = font:get_height() + style.padding.y * 2
if
self.list_container.visible
and
self.list_container.position.y ~= self.position.y + self:get_height()
then
self:reposition_container()
end
return true
end
function SelectBox:draw()
if not SelectBox.super.draw(self) then return false end
local font = self:get_font()
local icon_width = style.icon_font:get_width("+")
local max_width = self.size.x
- icon_width
- (style.padding.x * 2) -- left/right paddings
- (style.padding.x / 2) -- space between icon and text
local item_text = self.selected == 0 and
self.label or self.list:get_row_text(self.selected+1)
local text = self:text_overflow(item_text, max_width, font)
-- draw label or selected item
common.draw_text(
font,
self.hover_text or self.foreground_color or style.text,
text,
"left",
self.position.x + style.padding.x,
self.position.y,
self.size.x - style.padding.x,
self.size.y
)
-- draw arrow down icon
common.draw_text(
style.icon_font,
self.hover_text or self.foreground_color or style.text,
"-",
"right",
self.position.x,
self.position.y,
self.size.x - style.padding.x,
self.size.y
)
return true
end
return SelectBox