mirror of
https://github.com/Reuh/daccord.git
synced 2025-10-27 04:39:30 +00:00
Performance improvement and cleaning
This commit is contained in:
parent
96e0dc2c83
commit
d21e9b67a0
2 changed files with 320 additions and 209 deletions
147
daccord.can
147
daccord.can
|
|
@ -73,9 +73,9 @@ let config = {
|
|||
port = 6600,
|
||||
password = "", -- leave empty if you don't use a password
|
||||
-- Default behaviour
|
||||
filenameSearch = false, -- instant search search also search in filenames (not only when using the file= syntax), slower
|
||||
filenameSearch = true, -- instant search search also search in filenames for untitled tracks (not only when using the file= syntax), slightly slower when handling large searches
|
||||
-- Interface
|
||||
songDisplay = { "Track", { "Title", "file" }, "Artist", "Album" } -- list of tags or list of alternative tags (first one to exist will be used) to display for each song
|
||||
songDisplay = { "Track", { "Title", "Name", "file" }, "Artist", "Album" } -- list of tags or list of alternative tags (first one to exist will be used) to display for each song
|
||||
}
|
||||
(loadfile("config.lua", "t", config) or () end)() -- GATHER UP EVERYONE! I WANT YOU TO MEET... THE AMAZING CONFIG FILE LOADER!
|
||||
|
||||
|
|
@ -152,13 +152,14 @@ gui {
|
|||
else
|
||||
start, sel, val, stop = @sub(1, @cursorPosition):match("()([A-Za-z_]+)=\"([^\"]*)()$")
|
||||
end
|
||||
-- TODO: candran "str":thing
|
||||
|
||||
-- music tags
|
||||
for _, tag in ipairs(tags) do
|
||||
if tag:lower() == sel:lower() then
|
||||
results = {}
|
||||
|
||||
let r, songs = mpc:list(tag)
|
||||
if r then
|
||||
results = {}
|
||||
for _, s in ipairs(songs) do
|
||||
if s[tag]:lower():match(val:lower()) then -- filter val
|
||||
table.insert(results, s)
|
||||
|
|
@ -177,14 +178,17 @@ gui {
|
|||
|
||||
-- file search
|
||||
if sel:lower() == "file" then
|
||||
let r, songs = mpc:search(("(file == %q)"):format(val), "window", "0:"..tostring(list.h))
|
||||
if r then
|
||||
results = {}
|
||||
for _, s in ipairs(songs) do
|
||||
table.insert(results, s)
|
||||
list:insert{tostring(s.file)}
|
||||
results = {}
|
||||
|
||||
list:setPump(10, :(start, stop)
|
||||
let r, songs = mpc:search("(file == %q)":format(val), "window", "%s:%s":format(start-1, stop))
|
||||
if r then
|
||||
for _, s in ipairs(songs) do
|
||||
table.insert(results, s)
|
||||
list:insert{tostring(s.file)}
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
tagCompleting.tag = "file"
|
||||
tagCompleting.start = start
|
||||
|
|
@ -192,59 +196,85 @@ gui {
|
|||
end
|
||||
-- Song search
|
||||
else
|
||||
results = {
|
||||
_filenameSearchOffset = 0, -- where the filename search begin in the result list
|
||||
_filenameSearchStart = 0 -- where the search window should start in the filename search
|
||||
}
|
||||
|
||||
-- Build query
|
||||
let query = {}
|
||||
|
||||
-- Any selectors
|
||||
let withoutSel = @content:gsub("[A-Za-z_]+=[^\" ]+", ""):gsub("[A-Za-z_]+=\"[^\"]*\"", "")
|
||||
for word in withoutSel:gmatch("[^%s]+") do
|
||||
table.insert(query, ("(any == %q)"):format(word))
|
||||
table.insert(query, "(any == %q)":format(word))
|
||||
end
|
||||
|
||||
-- Tag selectors
|
||||
for tag, val in @content:gmatch("([A-Za-z_]+)=([^\" ]+)") do
|
||||
table.insert(query, ("(%s == %q)"):format(tag, val))
|
||||
table.insert(query, "(%s == %q)":format(tag, val))
|
||||
end
|
||||
for tag, val in @content:gmatch("([A-Za-z_]+)=\"([^\"]*)\"") do
|
||||
table.insert(query, ("(%s == %q)"):format(tag, val))
|
||||
table.insert(query, "(%s == %q)":format(tag, val))
|
||||
end
|
||||
|
||||
-- Limit
|
||||
table.insert(query, "window")
|
||||
table.insert(query, "0:"..tostring(list.h))
|
||||
table.insert(query, "0:0")
|
||||
|
||||
-- Search
|
||||
let r, songs = mpc:search(unpack(query))
|
||||
if r then results = songs end
|
||||
-- And they pumped...
|
||||
list:setPump(10, :(start, stop)
|
||||
let filenameSearchStop -- where the filename search window should end
|
||||
|
||||
-- Filename search
|
||||
if config.filenameSearch then
|
||||
for i=1, #query, 1 do
|
||||
query[i] = query[i]:gsub("^%(any", "(file")
|
||||
end
|
||||
-- only search if didn't reache filename search
|
||||
if results._filenameSearchStart == 0 then
|
||||
-- Update limit
|
||||
query[#query] = "%s:%s":format(start-1, stop)
|
||||
|
||||
let r, songs = mpc:search(unpack(query))
|
||||
if r then
|
||||
-- Merge
|
||||
for _, newSong in ipairs(songs) do -- TODO more efficient (using a sorted thing)
|
||||
let found = false
|
||||
for _, existingSong in ipairs(results) do
|
||||
if newSong.file == existingSong.file then
|
||||
found = true
|
||||
break
|
||||
end
|
||||
-- Search
|
||||
let r, songs = mpc:search(unpack(query))
|
||||
if r then
|
||||
-- Update widget
|
||||
for _, s in ipairs(songs) do
|
||||
table.insert(results, songs)
|
||||
@insert(songTable(s))
|
||||
end
|
||||
if not found then
|
||||
table.insert(results, newSong)
|
||||
|
||||
-- Fill what's left with filename search
|
||||
if config.filenameSearch and #results < stop then
|
||||
results._filenameSearchOffset = #results
|
||||
filenameSearchStop = stop-results._filenameSearchOffset+results._filenameSearchStart
|
||||
end
|
||||
end
|
||||
else
|
||||
filenameSearchStop = stop-results._filenameSearchOffset+results._filenameSearchStart
|
||||
end
|
||||
end
|
||||
|
||||
-- Update widget
|
||||
for _, s in ipairs(results) do
|
||||
list:insert(songTable(s))
|
||||
end
|
||||
-- Filename search
|
||||
if filenameSearchStop then
|
||||
for i=1, #query-2, 1 do
|
||||
query[i] = query[i]:gsub("^%(any", "(file")
|
||||
end
|
||||
|
||||
-- Loop to fill as much as possible (since we skip tracks with a title)
|
||||
repeat
|
||||
query[#query] = "%s:%s":format(results._filenameSearchStart, filenameSearchStop)
|
||||
results._filenameSearchStart = filenameSearchStop
|
||||
|
||||
let r, songs = mpc:search(unpack(query))
|
||||
if r then
|
||||
for _, newSong in ipairs(songs) do
|
||||
if not newSong.Title then
|
||||
table.insert(results, newSong)
|
||||
@insert(songTable(newSong))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
filenameSearchStop = stop-results._filenameSearchOffset+results._filenameSearchStart
|
||||
until filenameSearchStop == results._filenameSearchStart or #songs == 0
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
},
|
||||
|
|
@ -303,26 +333,33 @@ gui {
|
|||
|
||||
updateInterval = 5,
|
||||
|
||||
onUpdate = :()
|
||||
let r, songs = mpc:playlistinfo()
|
||||
if r then playlist = songs end
|
||||
|
||||
for i, s in ipairs(playlist) do
|
||||
let item = songTable(s)
|
||||
if @content[i] then
|
||||
@replace(i, item)
|
||||
else
|
||||
@insert(i, item)
|
||||
pump = :(start, stop)
|
||||
let r, songs = mpc:playlistinfo("%s:%s":format(start-1, stop))
|
||||
if r then
|
||||
for i=1, stop-start+1, 1 do
|
||||
if songs[i] then
|
||||
playlist[start+i-1] = songs[i]
|
||||
let item = songTable(songs[i])
|
||||
if @content[start+i-1] then
|
||||
@replace(start+i-1, item)
|
||||
else
|
||||
@insert(start+i-1, item)
|
||||
end
|
||||
else
|
||||
playlist[start+i-1] = 0
|
||||
@remove(start+i-1)
|
||||
start -= 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
while #@content > #playlist do
|
||||
@remove()
|
||||
end
|
||||
end,
|
||||
|
||||
onUpdate = :()
|
||||
@repump()
|
||||
end,
|
||||
|
||||
onSelect = :(l)
|
||||
if #playlist > 0 then
|
||||
if playlist[l[1]] then
|
||||
mpc:playid(playlist[l[1]].Id)
|
||||
end
|
||||
end,
|
||||
|
|
|
|||
382
gui.can
382
gui.can
|
|
@ -26,21 +26,17 @@ os.setlocale("")
|
|||
let everyWidget = {}
|
||||
|
||||
let widget = class {
|
||||
_exit = false,
|
||||
id = nil, -- (optional) widget id
|
||||
type = nil, -- widget type
|
||||
|
||||
_redraw = true,
|
||||
x = 0,
|
||||
y = 0,
|
||||
w = 0,
|
||||
h = 0,
|
||||
width = nil, -- height
|
||||
height = nil, -- width
|
||||
|
||||
parent = {},
|
||||
focused = false,
|
||||
focused = false, -- true if take inputs
|
||||
|
||||
updateInterval = -1,
|
||||
_nextUpdate = os.time(), -- -1 = no update
|
||||
updateInterval = -1, -- time in seconds between updates
|
||||
|
||||
new = :(data)
|
||||
new = :(data) -- create a new widget
|
||||
table.insert(everyWidget, self)
|
||||
|
||||
-- Copy properties
|
||||
|
|
@ -49,60 +45,74 @@ let widget = class {
|
|||
end
|
||||
|
||||
-- Dimensions
|
||||
if not @parent._firstX then @parent._firstX, @parent._firstY = 0, 0 end
|
||||
@x, @y = @parent._firstX, @parent._firstY
|
||||
if not @_parent._firstX then @_parent._firstX, @_parent._firstY = 0, 0 end
|
||||
@_x, @_y = @_parent._firstX, @_parent._firstY
|
||||
|
||||
if @width == "extend" @w = @parent.w - @x
|
||||
elseif @width:match("%d+em$") @w = tonumber(@width:match("%d+"))
|
||||
if @width == "extend" @_w = @_parent._w - @_x
|
||||
elseif @width:match("%d+em$") @_w = tonumber(@width:match("%d+"))
|
||||
if @height == "extend" then
|
||||
@h = @parent.h - @y
|
||||
@parent._lastVerticalExtend = self -- will be resized if vertical space is needed
|
||||
@parent._afterLastVerticalExtend = {}
|
||||
elseif @height:match("%d+em$") @h = tonumber(@height:match("%d+"))
|
||||
@_h = @_parent._h - @_y
|
||||
@_parent._lastVerticalExtend = self -- will be resized if vertical space is needed
|
||||
@_parent._afterLastVerticalExtend = {}
|
||||
elseif @height:match("%d+em$") @_h = tonumber(@height:match("%d+"))
|
||||
|
||||
if @y >= @parent.h then -- resize
|
||||
@parent._lastVerticalExtend:_resize(@parent._lastVerticalExtend.w, @parent._lastVerticalExtend.h - @h)
|
||||
@parent._firstY -= @h
|
||||
@y -= @h
|
||||
for _, el in ipairs(@parent._afterLastVerticalExtend) do -- move widgets
|
||||
el.y -= @h
|
||||
if @_y >= @_parent._h then -- resize
|
||||
@_parent._lastVerticalExtend:_resize(@_parent._lastVerticalExtend._w, @_parent._lastVerticalExtend._h - @_h)
|
||||
@_parent._firstY -= @_h
|
||||
@_y -= @_h
|
||||
for _, el in ipairs(@_parent._afterLastVerticalExtend) do -- move widgets
|
||||
el._y -= @_h
|
||||
end
|
||||
end
|
||||
|
||||
@parent._firstX += @w
|
||||
if @parent._firstX >= @parent.w then @parent._firstX = 0 end -- newline
|
||||
@parent._firstY += @h
|
||||
if @parent._lastVerticalExtend ~= self and @parent._afterLastVerticalExtend then table.insert(@parent._afterLastVerticalExtend, self) end
|
||||
@_parent._firstX += @_w
|
||||
if @_parent._firstX >= @_parent._w then @_parent._firstX = 0 end -- newline
|
||||
@_parent._firstY += @_h
|
||||
if @_parent._lastVerticalExtend ~= self and @_parent._afterLastVerticalExtend then table.insert(@_parent._afterLastVerticalExtend, self) end
|
||||
|
||||
-- Setup
|
||||
if @_setup then @_setup() end
|
||||
|
||||
screen:move(@parent.y + @y + @h, @parent.x + @x + @w)
|
||||
screen:move(@_parent._y + @_y + @_h, @_parent._x + @_x + @_w)
|
||||
end,
|
||||
|
||||
exit = :()
|
||||
exit = :() -- exit the application
|
||||
@_exit = true
|
||||
end,
|
||||
|
||||
byId = :(id)
|
||||
byId = :(id) -- return a widget by its id
|
||||
for _, el in ipairs(everyWidget) do
|
||||
if el.id == id return el
|
||||
end
|
||||
error("no element with id "..tostring(id))
|
||||
end,
|
||||
|
||||
updateAfter = :(time)
|
||||
updateAfter = :(time) -- reschedule the next widget update
|
||||
@_nextUpdate = os.time() + time
|
||||
end
|
||||
end,
|
||||
|
||||
_parent = {},
|
||||
|
||||
_exit = false,
|
||||
|
||||
_redraw = true,
|
||||
_x = 0,
|
||||
_y = 0,
|
||||
_w = 0,
|
||||
_h = 0,
|
||||
|
||||
_nextUpdate = os.time(), -- -1 = no update
|
||||
}
|
||||
|
||||
let widgets = setmetatable({
|
||||
fill = widget {
|
||||
fill = nil, -- filling type
|
||||
|
||||
_draw = :()
|
||||
if type(@fill) == "string" then @fill = curses[@fill] end
|
||||
for y = @parent.y + @y, @parent.y + @y + @h - 1 do
|
||||
screen:move(y, @parent.x + @x)
|
||||
for x = @parent.x + @x, @parent.x + @x + @w - 1 do
|
||||
for y = @_parent._y + @_y, @_parent._y + @_y + @_h - 1 do
|
||||
screen:move(y, @_parent._x + @_x)
|
||||
for x = @_parent._x + @_x, @_parent._x + @_x + @_w - 1 do
|
||||
screen:addch(@fill or 32)
|
||||
end
|
||||
end
|
||||
|
|
@ -110,10 +120,27 @@ let widgets = setmetatable({
|
|||
},
|
||||
|
||||
input = widget {
|
||||
content = "",
|
||||
cursorPosition = 1,
|
||||
content = "", -- text content
|
||||
cursorPosition = 1, -- cursor position, first character is 1
|
||||
sub = :(start, stop=utf8.len(@content)) -- get the substring
|
||||
if stop < 1 or start >= utf8.len(@content) then
|
||||
return ""
|
||||
else
|
||||
return @content:sub(utf8.offset(@content, start), (utf8.offset(@content, stop+1) or (#@content+1))-1)
|
||||
end
|
||||
end,
|
||||
replace = :(start, stop, newText) -- replace a substring
|
||||
if @cursorPosition >= stop then
|
||||
@cursorPosition += utf8.len(newText) - (stop - start)
|
||||
end
|
||||
@content = @sub(1, start-1) .. newText .. @sub(stop+1)
|
||||
@onTextInput()
|
||||
@_redraw = true
|
||||
end,
|
||||
onTextInput = :() end, -- called when the text change
|
||||
|
||||
_input = :(charbuffer, control)
|
||||
let y, x = @parent.y + @y, @parent.x + @x + @cursorPosition-1
|
||||
let y, x = @_parent._y + @_y, @_parent._x + @_x + @cursorPosition-1
|
||||
if control == "backspace" then
|
||||
screen:mvdelch(y, x-1)
|
||||
if @cursorPosition > 1 then
|
||||
|
|
@ -159,41 +186,108 @@ let widgets = setmetatable({
|
|||
end
|
||||
end,
|
||||
_draw = :()
|
||||
for y = @parent.y + @y, @parent.y + @y + @h - 1 do
|
||||
screen:move(y, @parent.x + @x)
|
||||
for x = @parent.x + @x, @parent.x + @x + @w - 1 do
|
||||
for y = @_parent._y + @_y, @_parent._y + @_y + @_h - 1 do
|
||||
screen:move(y, @_parent._x + @_x)
|
||||
for x = @_parent._x + @_x, @_parent._x + @_x + @_w - 1 do
|
||||
screen:addch(32)
|
||||
end
|
||||
end
|
||||
screen:mvaddstr(@parent.y + @y, @parent.x + @x, @content)
|
||||
screen:mvaddstr(@_parent._y + @_y, @_parent._x + @_x, @content)
|
||||
end,
|
||||
_placeCursor = :()
|
||||
screen:move(@parent.y + @y, @parent.x + @x + @cursorPosition-1)
|
||||
screen:move(@_parent._y + @_y, @_parent._x + @_x + @cursorPosition-1)
|
||||
return true
|
||||
end,
|
||||
sub = :(start, stop=utf8.len(@content))
|
||||
if stop < 1 or start >= utf8.len(@content) then
|
||||
return ""
|
||||
else
|
||||
return @content:sub(utf8.offset(@content, start), (utf8.offset(@content, stop+1) or (#@content+1))-1)
|
||||
end
|
||||
end,
|
||||
replace = :(start, stop, newText)
|
||||
if @cursorPosition >= stop then
|
||||
@cursorPosition += utf8.len(newText) - (stop - start)
|
||||
end
|
||||
@content = @sub(1, start-1) .. newText .. @sub(stop+1)
|
||||
@onTextInput()
|
||||
@_redraw = true
|
||||
end,
|
||||
onTextInput = :() end
|
||||
end
|
||||
},
|
||||
|
||||
list = widget {
|
||||
content = {},
|
||||
columnWidth = {},
|
||||
selected = 1,
|
||||
scroll = 0,
|
||||
content = {}, -- list content (list of tables)
|
||||
selected = 1, -- last selected line
|
||||
pump = nil, -- function used to pump. See :setPump().
|
||||
insert = :(pos, item) -- insert a line, shifting elements after it
|
||||
if item then
|
||||
table.insert(@content, pos, item)
|
||||
if @selected > pos and @selected < #@content then
|
||||
@selected += 1
|
||||
end
|
||||
else
|
||||
item = pos
|
||||
table.insert(@content, item)
|
||||
end
|
||||
if #@content == 1 then -- column count is determined by 1st item (other items can do what they want, this isn't a dictatorship)
|
||||
@_columnWidth = [for _=1,#item do 0 end]
|
||||
end
|
||||
if #item >= #@_columnWidth then -- if the column fits into our dictatorship, update column width
|
||||
for c=1, #@_columnWidth do
|
||||
let l = utf8.len(item[c]) -- if it isn't valid UTF8, ignore (can happen for files in Windows-made zipfiles). Should probably raise a warning or something... TODO.
|
||||
if l and l > @_columnWidth[c] then
|
||||
@_columnWidth[c] = utf8.len(item[c]) + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
@_redraw = true
|
||||
end,
|
||||
remove = :(pos=#@content) -- remove a line, shifting elements after it
|
||||
table.remove(@content, pos)
|
||||
if @selected > pos and @selected > 1 then
|
||||
@selected -= 1
|
||||
end
|
||||
@selected = math.min(@selected, #@content)
|
||||
@_redraw = true
|
||||
end,
|
||||
replace = :(pos, item) -- replace a line
|
||||
@content[pos] = item
|
||||
if #@content == 1 then -- column count is determined by 1st item (other items can do what they want, this isn't a dictatorship)
|
||||
@_columnWidth = [for _=1,#item do 0 end]
|
||||
end
|
||||
if #item >= #@_columnWidth then -- if the column fits into our dictatorship, update column width
|
||||
for c=1, #@_columnWidth do
|
||||
let l = utf8.len(item[c]) -- if it isn't valid UTF8, ignore (can happen for files in Windows-made zipfiles). Should probably raise a warning or something... TODO.
|
||||
if l and l > @_columnWidth[c] then
|
||||
@_columnWidth[c] = utf8.len(item[c]) + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
@_redraw = true
|
||||
end,
|
||||
-- Give a pump function (startPosition, stopPosition) -> {tables...} which load the lines between startPosition and stopPosition.
|
||||
-- The pump may returns the lines instead of inserting them itself.
|
||||
-- step is the preferance of number of line to be loaded at one.
|
||||
setPump = :(step, newPump)
|
||||
@pump = newPump
|
||||
@_pumpStep = step
|
||||
if #@content == 0 then
|
||||
let pumped = @pump(1, math.max(@_h, @_pumpStep))
|
||||
if pumped then
|
||||
for _, l in ipairs(pumped) do
|
||||
@insert(l)
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
repump = :() -- force already pumped elements to be repumped. The rest of the list state will be kept.
|
||||
@content = {}
|
||||
@_columnWidth = {}
|
||||
let pumped = @pump(1, math.max(@_scroll+@_h, @_scroll+@_pumpStep)) -- TODO: change everything so we only need to pump what is currently displayed
|
||||
if pumped then
|
||||
for i, l in ipairs(pumped) do
|
||||
@replace(@_scroll+i-1, l)
|
||||
end
|
||||
end
|
||||
end,
|
||||
clear = :() -- reset list: content, current selection, current pump
|
||||
@content = {}
|
||||
@_columnWidth = {}
|
||||
@_redraw = true
|
||||
@selected = 1
|
||||
@_scroll = 0
|
||||
@pump = nil
|
||||
end,
|
||||
onSelect = :() end, -- called when selecting a line
|
||||
|
||||
_columnWidth = {},
|
||||
_scroll = 0,
|
||||
_pumpStep = 5,
|
||||
_redraw = true,
|
||||
_input = :(charbuffer, control)
|
||||
if control == "up" then
|
||||
|
|
@ -209,13 +303,23 @@ let widgets = setmetatable({
|
|||
@selected += 10
|
||||
@_redraw = true
|
||||
end
|
||||
|
||||
if @pump and @selected > #@content then -- pump data if needed
|
||||
let pumped = @pump(#@content+1, #@content+@_pumpStep+1)
|
||||
if pumped then
|
||||
for _, l in ipairs(pumped) do
|
||||
@insert(l)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@selected = math.min(math.max(@selected, 1), math.max(#@content, 1))
|
||||
|
||||
while @selected <= @scroll do
|
||||
@scroll -= 1
|
||||
while @selected <= @_scroll do
|
||||
@_scroll -= 1
|
||||
end
|
||||
while @selected >= @scroll + @h + 1 do
|
||||
@scroll += 1
|
||||
while @selected >= @_scroll + @_h + 1 do
|
||||
@_scroll += 1
|
||||
end
|
||||
|
||||
if control == "enter" then
|
||||
|
|
@ -227,79 +331,37 @@ let widgets = setmetatable({
|
|||
end,
|
||||
_draw = :()
|
||||
let oY, oX = screen:getyx()
|
||||
for y = @parent.y + @y, @parent.y + @y + @h -1 do -- clear
|
||||
screen:mvaddstr(y, @parent.x + @x, (" "):rep(@w))
|
||||
for y = @_parent._y + @_y, @_parent._y + @_y + @_h -1 do -- clear
|
||||
screen:mvaddstr(y, @_parent._x + @_x, (" "):rep(@_w))
|
||||
end
|
||||
screen:move(@parent.y + @y, @parent.x + @x)
|
||||
for i=@scroll+1, @scroll + @h do -- draw
|
||||
screen:move(@_parent._y + @_y, @_parent._x + @_x)
|
||||
for i=@_scroll+1, @_scroll + @_h do -- draw
|
||||
if i == @selected then
|
||||
screen:standout()
|
||||
end
|
||||
let colx = @parent.x+@x
|
||||
for c=1, #@columnWidth do
|
||||
screen:mvaddstr(@parent.y+@y+i-1-@scroll, colx, @content[i] and @content[i][c] or "") -- TODO: make sure it doesn't go too far right or something
|
||||
colx += @columnWidth[c]
|
||||
let colx = @_parent._x+@_x
|
||||
for c=1, #@_columnWidth do
|
||||
screen:mvaddstr(@_parent._y+@_y+i-1-@_scroll, colx, @content[i] and @content[i][c] or "") -- TODO: make sure it doesn't go too far right or something
|
||||
colx += @_columnWidth[c]
|
||||
end
|
||||
if i == @selected then
|
||||
screen:standend()
|
||||
end
|
||||
end
|
||||
screen:move(oY, oX)
|
||||
end,
|
||||
insert = :(pos, item)
|
||||
if item then
|
||||
table.insert(@content, pos, item)
|
||||
if @selected > pos and @selected < #@content then
|
||||
@selected += 1
|
||||
end
|
||||
else
|
||||
item = pos
|
||||
table.insert(@content, item)
|
||||
end
|
||||
if #@content == 1 then -- column count is determined by 1st item (other items can do what they want, this isn't a dictatorship)
|
||||
@columnWidth = [for _=1,#item do 0 end]
|
||||
end
|
||||
if #item >= #@columnWidth then -- if the column fits into our dictatorship, update column width
|
||||
for c=1, #@columnWidth do
|
||||
let l = utf8.len(item[c]) -- if it isn't valid UTF8, ignore (can happen for files in Windows-made zipfiles). Should probably raise a warning or something... TODO.
|
||||
if l and l > @columnWidth[c] then
|
||||
@columnWidth[c] = utf8.len(item[c]) + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
@_redraw = true
|
||||
end,
|
||||
remove = :(pos=#@content)
|
||||
table.remove(@content, pos)
|
||||
if @selected > pos and @selected > 1 then
|
||||
@selected -= 1
|
||||
end
|
||||
@selected = math.min(@selected, #@content)
|
||||
@_redraw = true
|
||||
end,
|
||||
replace = :(pos, item)
|
||||
@content[pos] = item
|
||||
@_redraw = true
|
||||
end,
|
||||
clear = :()
|
||||
@content = {}
|
||||
@columnWidth = {}
|
||||
@_redraw = true
|
||||
@selected = 1
|
||||
@scroll = 0
|
||||
end,
|
||||
onSelect = :() end
|
||||
end
|
||||
},
|
||||
|
||||
tabs = widget {
|
||||
selected = 1,
|
||||
children = {},
|
||||
selected = 1, -- selected tab
|
||||
children = {}, -- children elements
|
||||
|
||||
_children = {},
|
||||
_setup = :()
|
||||
for i, tab in ipairs(@) do
|
||||
@children[i] = { x = @x, y = @y, w = @w, h = @h }
|
||||
@children[i] = { _x = @_x, _y = @_y, _w = @_w, _h = @_h }
|
||||
for _, el in ipairs(tab) do
|
||||
el.parent = @children[i]
|
||||
el._parent = @children[i]
|
||||
table.insert(@children[i], widgets[el.type]:new(el))
|
||||
end
|
||||
end
|
||||
|
|
@ -316,53 +378,55 @@ let widgets = setmetatable({
|
|||
end
|
||||
end,
|
||||
_resize = :(w, h)
|
||||
@w, @h = w, h
|
||||
@_w, @_h = w, h
|
||||
for i, tab in ipairs(@) do
|
||||
for _, el in ipairs(@children[i]) do
|
||||
if el.x + el.w > w then el.w = w - el.x end
|
||||
if el.y + el.h > h then el.h = h - el.y end
|
||||
if el._x + el._w > w then el._w = w - el._x end
|
||||
if el._y + el._h > h then el._h = h - el._y end
|
||||
end
|
||||
end
|
||||
end
|
||||
},
|
||||
|
||||
label = widget {
|
||||
content = "Label",
|
||||
_draw = :()
|
||||
screen:mvaddstr(@parent.y + @y, @parent.x + @x, @content .. (" "):rep(@w - #@content))
|
||||
end,
|
||||
set = :(str)
|
||||
content = "Label", -- text
|
||||
set = :(str) -- set text
|
||||
@content = str
|
||||
@_redraw = true
|
||||
end,
|
||||
|
||||
_draw = :()
|
||||
screen:mvaddstr(@_parent._y + @_y, @_parent._x + @_x, @content .. (" "):rep(@_w - #@content))
|
||||
end
|
||||
},
|
||||
|
||||
slider = widget {
|
||||
min = 0,
|
||||
max = 1,
|
||||
current = 0,
|
||||
head = "⏸",
|
||||
_draw = :()
|
||||
let len = math.ceil((@current - @min) / (@max - @min) * @w)
|
||||
screen:mvaddstr(@parent.y + @y, @parent.x + @x, ("="):rep(len-1))
|
||||
screen:addstr(@head .. (" "):rep(@w - len))
|
||||
end,
|
||||
set = :(current)
|
||||
@current = current
|
||||
min = 0, -- min value
|
||||
max = 1, -- max value
|
||||
value = 0, -- current value
|
||||
head = "⏸", -- associated symbol
|
||||
set = :(value) -- change value
|
||||
@value = value
|
||||
@_redraw = true
|
||||
end,
|
||||
setMax = :(max)
|
||||
setMax = :(max) -- change maximum
|
||||
@max = max
|
||||
@_redraw = true
|
||||
end,
|
||||
setMin = :(min)
|
||||
setMin = :(min) -- change minimum
|
||||
@min = min
|
||||
@_redraw = true
|
||||
end,
|
||||
setHead = :(head)
|
||||
setHead = :(head) -- change symbol
|
||||
@head = head
|
||||
@_redraw = true
|
||||
end
|
||||
end,
|
||||
|
||||
_draw = :()
|
||||
let len = math.ceil((@value - @min) / (@max - @min) * @_w)
|
||||
screen:mvaddstr(@_parent._y + @_y, @_parent._x + @_x, ("="):rep(len-1))
|
||||
screen:addstr(@head .. (" "):rep(@_w - len))
|
||||
end,
|
||||
}
|
||||
}, {
|
||||
__index = (t, k)
|
||||
|
|
@ -396,11 +460,11 @@ return (ui)
|
|||
screen:clear()
|
||||
let h, w = screen:getmaxyx()
|
||||
let parent = {
|
||||
x = 0, y = 0,
|
||||
w = w, h = h
|
||||
_x = 0, _y = 0,
|
||||
_w = w, _h = h
|
||||
}
|
||||
for _, el in ipairs(ui) do
|
||||
el.parent = parent
|
||||
el._parent = parent
|
||||
el._widget = widgets[el.type]:new(el)
|
||||
end
|
||||
|
||||
|
|
@ -444,9 +508,19 @@ return (ui)
|
|||
elseif k == "[B" then
|
||||
control = "down"
|
||||
elseif k == "[5" then
|
||||
control = "pgup"
|
||||
k ..= string.char(screen:getch())
|
||||
if k == "[5~" then
|
||||
control = "pgup"
|
||||
else
|
||||
error("unknown control "..tostring(k))
|
||||
end
|
||||
elseif k == "[6" then
|
||||
control = "pgdown"
|
||||
k ..= string.char(screen:getch())
|
||||
if k == "[6~" then
|
||||
control = "pgdown"
|
||||
else
|
||||
error("unknown control "..tostring(k))
|
||||
end
|
||||
elseif k == "[3" then
|
||||
k ..= string.char(screen:getch())
|
||||
if k == "[3~" then
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue