local mp = require("mp") local msg = require("mp.msg") local misc = require("src.utils.misc") ---@class list.item ---@field ass string ---@field style string ---@class list ---@field open boolean is the list open ---@field header string default header ---@field default_style string default ass styling for each item ---@field osd table mpv osd overlay table ---@field items list.item[] ---@field selected number index representing which item in items is selected ---@field keybinds table[] a list of keybinds to be applied ---@field max_items number the maximum number of items which can fit on the screen local list = {} --- create a new list ---@return list list function list:new() ---@type list ---@diagnostic disable-next-line: missing-fields local o = {} setmetatable(o, self) self.__index = self o.header = [[{\c&HC35CAA&}J{\c&HC66691&}e{\c&HCA7079&}l{\c&HCD7A61&}l{\c&HD18548&}y{\c&HD48F30&}f{\c&HD89918&}i{\c&HDCA400&}n]] o.default_style = [[{\r\fs23\fnmonospace}]] o.osd = mp.create_osd_overlay("ass-events") o.osd.data = "" o.selected = 1 o.keybinds = {} o.max_items = 26 return o end --- small wrapper around the list to set all items and redraw immediately ---@param items list.item[]? function list:set(items) self.items = items or {} self:update() end --- display text fading in and out ---@param rate number time between each step ---@param text string ---@return table timer function list:set_fade_animation(rate, text) self.selected = 0 local opacity, direction = 255, -1 local function load_animation() opacity = opacity + direction * 10 if opacity >= 255 then opacity = 255 direction = -1 elseif opacity <= 0 then opacity = 0 direction = 1 end self:set({{ style = string.format( [[{\alpha%s&}{\c&Hffffff&}]], string.format("&H%02X&", 255 - math.floor(opacity)) ), ass = text, }}) end return misc.mpv_timer(rate, load_animation, text) end --- small wrapper around list:set to log the text provided ---@param item list.item function list:log(item) self.selected = 0 msg.fatal(item.ass) self:set({ item }) end --- draw the list to the screen function list:update() self.open = true -- add all keybinds when we open the ui for _, v in ipairs(self.keybinds) do mp.add_forced_key_binding(v[1], "dynamic/"..self.osd.id.."/"..v[2], v[3], v[4]) end ---@type list.item[] local scrolled_items = {} local selected = self.selected if self.selected > (self.max_items / 2) then if (#self.items - self.selected) > (self.max_items / 2) then scrolled_items = { table.unpack( self.items or {}, self.selected - (self.max_items / 2) + 1 ) } selected = (self.max_items / 2) else scrolled_items = { table.unpack( self.items or {}, (#self.items - self.max_items) + 1 ) } selected = (self.max_items / 2) + (((self.max_items / 2) + self.selected) - #self.items) end else scrolled_items = self.items or {} end -- put all the items in the list self.osd.data = [[{\r\fnmonospace}]]..self.header..[[\N]] for i, item in ipairs(scrolled_items) do -- if self.selected <= 0 then there should be no selection indicators drawn if self.selected > 0 then if i == selected then self.osd.data = self.osd.data..self.default_style..[[● ]] else self.osd.data = self.osd.data..self.default_style..[[○ ]] end else self.osd.data = self.osd.data..self.default_style end self.osd.data = self.osd.data ..(item.style or [[{\c&Hffffff&}]]) ..item.ass if self.items[i + 1] then self.osd.data = self.osd.data..[[{\r}\N]] end end self.osd:update() end --- close the list function list:close() -- remove all the keybinds for _,v in ipairs(self.keybinds) do mp.remove_key_binding("dynamic/"..self.osd.id.."/"..v[2]) end -- close the list self.osd:remove() self.open = false end --- go up function list:up() if self.selected > 1 then self.selected = self.selected - 1 self:update() end end --- go down function list:down() if self.selected < #self.items then self.selected = self.selected + 1 self:update() end end --- helper to addkeys to the list view ---@param keys string keybind ---@param name string action name ---@param fn function callback ---@param opts? table options function list:addkey(keys, name, fn, opts) opts = opts or { repeatable = true } local i = 1 for key in keys:gmatch("%S+") do table.insert(self.keybinds, { key, name..i, fn, opts }) i = i + 1 end end return list