diff options
| author | David Vazgenovich Shakaryan <dvshakaryan@gmail.com> | 2026-01-18 00:00:24 -0800 |
|---|---|---|
| committer | David Vazgenovich Shakaryan <dvshakaryan@gmail.com> | 2026-01-18 00:00:24 -0800 |
| commit | b3665cc09ea0363beafc05a867c057e5c122985a (patch) | |
| tree | 162299bf2515205c83202083016ec2337271dd26 /osd.lua | |
| parent | b3ec50d92451b99dd08b6030b08854c8cc77524b (diff) | |
| download | mpv-iptv-menu-b3665cc09ea0363beafc05a867c057e5c122985a.tar.gz mpv-iptv-menu-b3665cc09ea0363beafc05a867c057e5c122985a.tar.xz | |
support timed status/error messages
To support this, render() has been pulled out of redraw(), allowing
updating the actual OSD without having to pass in the 'state' object,
avoiding complexity and unnecessary computation.
Diffstat (limited to 'osd.lua')
| -rw-r--r-- | osd.lua | 147 |
1 files changed, 108 insertions, 39 deletions
@@ -70,10 +70,36 @@ function mt:resize(w, h) self.scale = h / 720 end -function mt:set_status(msg, level) +function mt:set_status(msg, level, no_dirty) + if self.status_timer then + self.status_timer:kill() + self.status_timer = nil + end + self.status_msg = msg self.status_level = level - self:dirty() + + if not no_dirty then + self:dirty() + end +end + +function mt:flash_status(msg, level, secs) + self:set_status(msg, level, true) + self:render() + + self.status_timer = mp.add_timeout(secs or 3, function() + self.status_timer = nil + + if self.status_msg == msg then + self:set_status(nil, nil, true) + self:render() + end + end) +end + +function mt:flash_error(msg, secs) + self:flash_status(msg, 'error', secs) end function mt:status_line() @@ -228,7 +254,14 @@ function mt:load_img() self.magick_cmd = nil self.magick_cmd_id = nil - if not self.img or not res or res.status ~= 0 then + if not self.img then + return + end + + if not res or res.status ~= 0 then + if self.img_fail_cb then + self.img_fail_cb() + end return end @@ -370,9 +403,66 @@ function mt:draw_scrollbar(state) '{\\pos(2,' .. top + pos .. ')}' .. draw_rect(0, 0, w, hh)) end -function mt:redraw(state) +-- this takes the data generated by redraw() and related functions, prepares it +-- for rendering and pushes the result to the actual OSD. since it has no +-- dependence on `state', it's simpler and more efficient to use this directly +-- for things like displaying status messages. +function mt:render() local out = {} + for _, v in ipairs(self.out.titles) do + out[#out+1] = v + end + + -- use spacer line between titles and options for status messages + out[#out+1] = self:status_line() + + for _, v in ipairs(self.out.options) do + out[#out+1] = v + end + + local buf = {} + -- each line uses a new ASS event with explicit positioning. otherwise, + -- lines with nonstandard height break things. + -- \q2 disables line wrapping + local pre = '{\\q2\\fs' .. config.font_size .. + '\\pos(' .. self.padding .. ',%s)}' + for i, v in ipairs(out) do + local offset = self.padding + ((i - 1) * config.font_size) + buf[#buf+1] = pre:format(offset) .. v + end + -- rendering `max' is needed to compute bounds only when there is an + -- image to display. + if self.out.img then + buf[#buf+1] = '{\\alpha&HFF&}' .. pre:format(0) .. + table.concat(self.out.max, '\\N') + end + self.fg.data = table.concat(buf, '\n') + + self.fg.compute_bounds = not not self.out.img_path + local res = self.fg:update() + -- unset compute_bounds because it is relatively expensive and we don't + -- need it when update() is called to toggle visibility of the last + -- rendered data. + self.fg.compute_bounds = false + self.bg:update() + if self.out.img_path then + local upd, new = self:set_img(self.out.img_path, res) + if upd then + if new then + self:clear_img() + end + self:draw_img() + end + else + self:clear_img(true) + end +end + +function mt:redraw(state) + local out_titles = {} + local out_options = {} + -- to determine the area left for images, we use the computed bounds of -- the rendered menu, which shifts when the cursor is moved to/from the -- longest line. to prevent this, this table stores the menu options @@ -387,12 +477,9 @@ function mt:redraw(state) local out_max = {} for i = 1, state.depth do - out[#out+1] = self:menu_title(state.menus[i]) + out_titles[#out_titles+1] = self:menu_title(state.menus[i]) end - -- use spacer line between titles and options for status messages - out[#out+1] = self:status_line() - local menu = state:menu() local img @@ -422,7 +509,8 @@ function mt:redraw(state) } local str = self:option_text(opt, info) .. self:option_path(opt, info) - out[#out+1] = self:option_icons(opt, info) .. str + out_options[#out_options+1] = self:option_icons(opt, info) .. + str if selected and opt.img_url then img = opt.img_url @@ -432,22 +520,14 @@ function mt:redraw(state) out_max[#out_max+1] = self:option_icons(opt, info) .. str end - local buf = {} - -- each line uses a new ASS event with explicit positioning. otherwise, - -- lines with nonstandard height break things. - -- \q2 disables line wrapping - local pre = '{\\q2\\fs' .. config.font_size .. - '\\pos(' .. self.padding .. ',%s)}' - for i, v in ipairs(out) do - local offset = self.padding + ((i - 1) * config.font_size) - buf[#buf+1] = pre:format(offset) .. v - end - if img then - buf[#buf+1] = '{\\alpha&HFF&}' .. pre:format(0) .. - table.concat(out_max, '\\N') - end - self.fg.data = table.concat(buf, '\n') + self.out = { + titles = out_titles, + options = out_options, + max = out_max, + } + -- we can draw this directly from here since any changes handled by + -- render() will not modify this. self.bg.data = '{\\pos(0,0)\\alpha&H' .. config.bg_alpha .. '&\\c&H&}' .. draw_rect(0, 0, 7680, 720) @@ -456,28 +536,17 @@ function mt:redraw(state) self.bg.data = self.bg.data .. '\n' .. sb end - self.fg.compute_bounds = not not img - local res = self.fg:update() - self.fg.compute_bounds = false - self.bg:update() if img and self.img_path_func then - local path = self.img_path_func(img, function(path) + self.out.img_path = self.img_path_func(img, function(path) + -- these are set by render(), which we always call + -- before returning. if self.img and path == self.img.path then self:draw_img() end end) - - local upd, new = self:set_img(path, res) - if upd then - if new then - self:clear_img() - end - self:draw_img() - end - else - self:clear_img(true) end + self:render() self.is_dirty = false end |
