summaryrefslogtreecommitdiff
path: root/state.lua
diff options
context:
space:
mode:
authorDavid Vazgenovich Shakaryan <dvshakaryan@gmail.com>2026-01-07 15:22:39 -0800
committerDavid Vazgenovich Shakaryan <dvshakaryan@gmail.com>2026-01-07 15:22:39 -0800
commit2dc4809f5cba14838def5abea3220793ae0d8e4a (patch)
treea0a2b746e37d99413e3d3d033553d15ce0bf6529 /state.lua
parentc442abc24ebbddaf8d9fab36375128e06547d65e (diff)
downloadmpv-iptv-menu-2dc4809f5cba14838def5abea3220793ae0d8e4a.tar.gz
mpv-iptv-menu-2dc4809f5cba14838def5abea3220793ae0d8e4a.tar.xz
move generic menu operations to metatable
Diffstat (limited to 'state.lua')
-rw-r--r--state.lua114
1 files changed, 112 insertions, 2 deletions
diff --git a/state.lua b/state.lua
index 472e4d2..2f609c9 100644
--- a/state.lua
+++ b/state.lua
@@ -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