diff options
Diffstat (limited to 'state.lua')
| -rw-r--r-- | state.lua | 114 |
1 files changed, 112 insertions, 2 deletions
@@ -1,9 +1,14 @@ -- Copyright 2025 David Vazgenovich Shakaryan +local util = require('util') + local state = {} local mt = {} mt.__index = mt +local menu_mt = {} +menu_mt.__index = menu_mt + function state.new() return setmetatable({ menus = {}, @@ -19,18 +24,28 @@ function mt:menu() end function mt:push_menu(t) - local menu = { + local menu = setmetatable({ options = {}, cursor = 1, view_top = 1, - } + }, menu_mt) for k, v in pairs(t) do menu[k] = v end + if menu.type == 'search' then + menu.search_options = menu.options + menu.search_text = menu.search_text or '' + menu.search_cursor = menu.search_cursor or + #menu.search_text + 1 + menu:update_search_matches() + end + self.depth = self.depth + 1 self.menus[self.depth] = menu + + return menu end -- returns index if found @@ -67,4 +82,99 @@ function mt:insert_favourite_before_next_in_menu(id) self:add_favourite(id) end +function menu_mt:set_cursor(pos, lines, opts) + local pos = math.max(1, math.min(pos, #self.options)) + local top = self.view_top + if opts and opts.centre then + top = pos - math.floor((lines - 1) / 2) + elseif opts and opts.keep_offset then + top = top + pos - self.cursor + end + + -- move view to keep selected option visible + if pos < top then + top = pos + elseif pos > top + lines - 1 then + top = pos - lines + 1 + end + + top = math.max(1, math.min(top, #self.options - lines + 1)) + + self.cursor = pos + self.view_top = top +end + +function menu_mt:set_sort(bool, f) + if not self.sorted == not bool then + return + end + + local key = self.type == 'search' and 'search_options' or 'options' + if bool then + self['orig_' .. key] = self[key] + self[key] = util.copy_table(self[key]) + f(self[key]) + else + self[key] = self['orig_' .. key] + self['orig_' .. key] = nil + end + + if self.type == 'search' then + self:update_search_matches() + end + self.sorted = bool +end + +function menu_mt:set_search_cursor(pos) + local pos = math.max(1, math.min(#self.search_text + 1, pos)) + if pos == self.search_cursor then + return false + end + + self.search_cursor = pos + return true +end + +function menu_mt:set_search_text(str) + self.search_text = str + self:update_search_matches() +end + +function menu_mt:update_search_matches() + if #self.search_text == 0 then + self.options = self.search_options + return + end + + -- no utf8 :( + local case_sensitive = not not self.search_text:find('%u') + + local options = {} + for _, v in ipairs(self.search_options) do + local matches = {} + + local name = v.name + if not case_sensitive then + name = name:lower() + end + + local i, j = 0, 0 + while true do + i, j = name:find(self.search_text, j + 1, true) + if not i then + break + end + matches[#matches+1] = {start = i, stop = j} + end + + if #matches > 0 then + local t = util.copy_table(v) + t.matches = matches + options[#options+1] = t + end + end + + self.options = options +end + return state |
