From 7bd0fda5bbaee6eb188006fe10bf427beea5d073 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sun, 19 Jan 2025 22:20:32 +0100 Subject: [PATCH] console.lua: improve the hovered item calculation with background-box Follow up to c438732b23 which improved the calculation with the default outline-and-shadow. We can make the calculation accurate for background-box too without making semitransparent rectangle backgrounds overlap by adding margin between items. We could calculate the height of each item to make them perfectly adjacent by rendering each one with compute_bounds, but that takes 15ms per render, so 20 lines would take 300ms, which is too slow. Instead add a fixed 0.1 * opts.font_size to each item's height. This ensures the backgrounds don't overlap with tall CJK text or track circles and --osd-shadow-offset=0. Add this to outline-and-shadow too since it looks better with some margin between the items. Then add the rectangle offset to the height depending on the border style. --- player/lua/console.lua | 68 +++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 30 deletions(-) diff --git a/player/lua/console.lua b/player/lua/console.lua index 0d6c54da045ee..369ebee5228ec 100644 --- a/player/lua/console.lua +++ b/player/lua/console.lua @@ -106,6 +106,7 @@ local matches = {} local selected_match = 1 local first_match_to_print = 1 local default_item +local item_positions = {} local complete local cycle_through_suggestions @@ -279,6 +280,24 @@ local function get_scaled_osd_dimensions() return dims.w / scale, dims.h /scale end +local function get_line_height() + if not selectable_items then + return opts.font_size + end + + local height = opts.font_size * 1.1 + + local style = mp.get_property('osd-border-style') + + if style == 'background-box' then + height = height + mp.get_property_native('osd-shadow-offset') * 2 + elseif style == 'opaque-box' then + height = height + mp.get_property_native('osd-outline-size') * 2 + end + + return height +end + local function calculate_max_log_lines() if not mp.get_property_native('vo-configured') or not mp.get_property_native('video-osd') then @@ -291,7 +310,7 @@ local function calculate_max_log_lines() return math.floor((select(2, get_scaled_osd_dimensions()) * (1 - global_margins.t - global_margins.b) - get_margin_y()) - / opts.font_size + / get_line_height() -- Subtract 1 for the input line and 0.5 for the empty -- line between the log and the input line. - 1.5) @@ -603,31 +622,35 @@ local function update() local log_ass = '' local log_buffer = log_buffers[id] - local box = mp.get_property('osd-border-style') == 'background-box' + local line_height = get_line_height() + item_positions = {} for i = #log_buffer - math.min(max_lines, #log_buffer) + 1, #log_buffer do local log_item = style .. log_buffer[i].style .. ass_escape(log_buffer[i].text) - -- Put every selectable item in its own event to prevent libass from - -- drawing them taller than opts.font_size with taller fonts, which - -- makes the hovered item calculation inaccurate and clips the counter. - -- But not with background-box, because it makes it look bad by - -- overlapping the semitransparent backgrounds of every line. - if selectable_items and not box then + if selectable_items then + local item_y = y - (1.5 + #log_buffer - i) * line_height ass:new_event() ass:an(1) - ass:pos(x, y - (1.5 + #log_buffer - i) * opts.font_size) + ass:pos(x, item_y) ass:append(log_item) + + if #matches <= max_lines or i > 1 then -- skip the counter + item_positions[#item_positions + 1] = { item_y - line_height, item_y } + end else log_ass = log_ass .. log_item .. '\\N' end end + if log_ass ~= '' then + log_ass = log_ass .. '\\N' + end + ass:new_event() ass:an(1) ass:pos(x, y) - ass:append(log_ass .. '\\N') - ass:append(suggestion_ass) + ass:append(log_ass .. suggestion_ass) ass:append(style .. ass_escape(prompt) .. ' ' .. before_cur) ass:append(cglyph) ass:append(style .. after_cur) @@ -895,27 +918,12 @@ local function handle_enter() end local function determine_hovered_item() - local height = select(2, get_scaled_osd_dimensions()) local y = mp.get_property_native('mouse-pos').y / scale_factor() - local log_bottom_pos = height * (1 - global_margins.b) - - get_margin_y() - - 1.5 * opts.font_size - - if y > log_bottom_pos then - return - end - local max_lines = calculate_max_log_lines() - -- Subtract 1 line for the position counter. - if #matches > max_lines then - max_lines = max_lines - 1 - end - local last = math.min(first_match_to_print - 1 + max_lines, #matches) - - local hovered_item = last - math.floor((log_bottom_pos - y) / opts.font_size) - - if hovered_item >= first_match_to_print then - return hovered_item + for i, positions in ipairs(item_positions) do + if y >= positions[1] and y <= positions[2] then + return first_match_to_print - 1 + i + end end end