From 81d9426930c1a56bec3caa0edd1cc4b40225d527 Mon Sep 17 00:00:00 2001 From: tomasklaen Date: Tue, 20 Sep 2022 11:55:13 +0200 Subject: [PATCH] feat: show current chapter as a top bar subtitle ref #228 --- scripts/uosc.lua | 88 ++++++++++++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 37 deletions(-) diff --git a/scripts/uosc.lua b/scripts/uosc.lua index cf47ced..95cfac0 100644 --- a/scripts/uosc.lua +++ b/scripts/uosc.lua @@ -55,11 +55,13 @@ end ---@param itable table ---@param compare fun(value: any, index: number) +---@param from_end? boolean Search from the end of the table. ---@return number|nil index ---@return any|nil value -function itable_find(itable, compare) - for index, value in ipairs(itable) do - if compare(value, index) then return index, value end +function itable_find(itable, compare, from_end) + local from, to, step = from_end and #itable or 1, from_end and 1 or #itable, from_end and -1 or 1 + for index = from, to, step do + if compare(itable[index], index) then return index, itable[index] end end end @@ -401,6 +403,7 @@ local state = { duration_or_remaining_time_human = nil, -- depends on options.total_time pause = mp.get_property_native('pause'), chapters = {}, + current_chapter = nil, chapter_ranges = {}, border = mp.get_property_native('border'), fullscreen = mp.get_property_native('fullscreen'), @@ -884,17 +887,12 @@ end function serialize_chapters(chapters) chapters = normalize_chapters(chapters) if not chapters then return end - - -- Reset custom ranges - serialize_chapter_ranges(chapters) - - for _, chapter in ipairs(chapters) do + for index, chapter in ipairs(chapters) do + chapter.index = index chapter.title_wrapped, chapter.title_wrapped_width = wrap_text(chapter.title, 25) chapter.title_wrapped = ass_escape(chapter.title_wrapped) end - - state.chapters = chapters - request_render() + return chapters end --[[ ASSDRAW EXTENSIONS ]] @@ -1820,14 +1818,16 @@ end ---@param menu? MenuStack function Menu:activate_value(value, menu) menu = menu or self.current - self:activate_index(itable_find(menu.items, function(_, item) return item.value == value end), menu) + local index = itable_find(menu.items, function(_, item) return item.value == value end) + self:activate_index(index, menu) end ---@param value? any ---@param menu? MenuStack function Menu:activate_unique_value(value, menu) menu = menu or self.current - self:activate_unique_index(itable_find(menu.items, function(_, item) return item.value == value end), menu) + local index = itable_find(menu.items, function(_, item) return item.value == value end) + self:activate_unique_index(index, menu) end ---@param id string @@ -2867,18 +2867,13 @@ function Timeline:render() -- Hovered time and chapter if (self.proximity_raw == 0 or self.pressed) and not (Elements.speed and Elements.speed.dragging) then local hovered_seconds = self:get_time_at_x(cursor.x) - local chapter_title, chapter_title_width = nil, nil + local chapter_title, chapter_title_width - if (options.timeline_chapters ~= 'never' and state.chapters) then - for i = #state.chapters, 1, -1 do - local chapter = state.chapters[i] - if hovered_seconds >= chapter.time then - if not chapter.is_end_only then - chapter_title = chapter.title_wrapped - chapter_title_width = chapter.title_wrapped_width - end - break - end + if state.chapters then + local _, chapter = itable_find(state.chapters, function(c) return hovered_seconds >= c.time end, true) + if chapter and not chapter.is_end_only then + chapter_title = chapter.title_wrapped + chapter_title_width = chapter.title_wrapped_width end end @@ -3032,6 +3027,7 @@ function TopBar:render() local bg_margin = math.floor((self.size - self.font_size) / 4) local padding = self.font_size / 2 local title_ax = self.ax + bg_margin + local title_ay = self.ay + bg_margin local max_bx = self.title_bx - self.spacing -- Playlist position @@ -3039,30 +3035,41 @@ function TopBar:render() local text = state.playlist_pos .. '' .. state.playlist_count local formatted_text = '{\\b1}' .. state.playlist_pos .. '{\\b0\\fs' .. self.font_size * 0.9 .. '}/' .. state.playlist_count - local bg_bx = round(title_ax + text_length_width_estimate(#text, self.font_size) + padding * 2) - - ass:rect(title_ax, self.ay + bg_margin, bg_bx, self.by - bg_margin, { + local bx = round(title_ax + text_length_width_estimate(#text, self.font_size) + padding * 2) + ass:rect(title_ax, title_ay, bx, self.by - bg_margin, { color = options.foreground, opacity = visibility, radius = 2, }) - ass:txt(title_ax + (bg_bx - title_ax) / 2, self.ay + (self.size / 2), 5, formatted_text, { + ass:txt(title_ax + (bx - title_ax) / 2, self.ay + (self.size / 2), 5, formatted_text, { size = self.font_size, wrap = 2, color = options.background, opacity = visibility, }) - - title_ax = bg_bx + bg_margin + title_ax = bx + bg_margin end -- Title if max_bx - title_ax > self.font_size * 3 then local text = state.title or 'n/a' - local bg_bx = math.min(max_bx, title_ax + text_width_estimate(text, self.font_size) + padding * 2) - - ass:rect(title_ax, self.ay + bg_margin, bg_bx, self.by - bg_margin, { - color = options.background, opacity = visibility * 0.8, radius = 2, - }) + local bx = math.min(max_bx, title_ax + text_width_estimate(text, self.font_size) + padding * 2) + local by = self.by - bg_margin + ass:rect(title_ax, title_ay, bx, by, { color = options.background, opacity = visibility * 0.8, radius = 2, }) ass:txt(title_ax + padding, self.ay + (self.size / 2), 4, text, { size = self.font_size, wrap = 2, color = options.foreground, border = 1, border_color = options.background, opacity = visibility, clip = string.format('\\clip(%d, %d, %d, %d)', self.ax, self.ay, max_bx, self.by), }) + title_ay = by + 1 + end + + -- Subtitle: current chapter + if state.current_chapter and max_bx - title_ax > self.font_size * 3 then + local font_size = self.font_size * 0.8 + local height = font_size * 1.5 + local text = '└ ' .. state.current_chapter.index .. ': ' .. state.current_chapter.title + local ax, by = title_ax + padding / 2, title_ay + height + local bx = math.min(max_bx, title_ax + text_width_estimate(text, font_size) + padding * 2) + ass:rect(ax, title_ay, bx, by, {color = options.background, opacity = visibility * 0.8, radius = 2}) + ass:txt(ax + padding, title_ay + height / 2, 4, '{\\i1}' .. text ..'{\\i0}', { + size = font_size, wrap = 2, color = options.foreground, border = 1, border_color = options.background, + opacity = visibility * 0.8, clip = string.format('\\clip(%d, %d, %d, %d)', title_ax, title_ay, bx, by), + }) end end @@ -4106,7 +4113,12 @@ mp.observe_property('title', 'string', function(_, title) -- Don't change title if there is currently none if state.title then update_title(title) end end) -mp.observe_property('playback-time', 'number', create_state_setter('time', update_human_times)) +mp.observe_property('playback-time', 'number', create_state_setter('time', function() + update_human_times() + -- Select current chapter + local _, current_chapter = itable_find(state.chapters, function(c) return state.time >= c.time end, true) + set_state('current_chapter', current_chapter) +end)) mp.observe_property('duration', 'number', create_state_setter('duration', update_human_times)) mp.observe_property('speed', 'number', create_state_setter('speed', update_human_times)) mp.observe_property('track-list', 'native', function(name, value) @@ -4130,9 +4142,11 @@ mp.observe_property('track-list', 'native', function(name, value) Elements:trigger('dispositions') end) mp.observe_property('chapter-list', 'native', function(_, chapters) + local chapters = serialize_chapters(chapters) + set_state('chapters', chapters) + serialize_chapter_ranges(chapters) set_state('has_chapter', #chapters > 0) Elements:trigger('dispositions') - serialize_chapters(chapters) end) mp.observe_property('border', 'bool', create_state_setter('border')) mp.observe_property('ab-loop-a', 'number', create_state_setter('ab_loop_a'))