Skip to content

Commit

Permalink
console.lua: improve the hovered item calculation with background-box
Browse files Browse the repository at this point in the history
Follow up to c438732 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.
  • Loading branch information
guidocella committed Jan 24, 2025
1 parent 6ddc22d commit dfe0518
Showing 1 changed file with 38 additions and 30 deletions.
68 changes: 38 additions & 30 deletions player/lua/console.lua
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ local matches = {}
local selected_match = 1
local first_match_to_print = 1
local default_item
local item_positions = {}

local complete
local cycle_through_completions
Expand Down Expand Up @@ -280,6 +281,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
Expand All @@ -292,7 +311,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)
Expand Down Expand Up @@ -620,31 +639,35 @@ local function render()

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(completion_ass)
ass:append(log_ass .. completion_ass)
ass:append(style .. ass_escape(prompt) .. ' ' .. before_cur)
ass:append(cglyph)
ass:append(style .. after_cur)
Expand Down Expand Up @@ -913,27 +936,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

Expand Down

0 comments on commit dfe0518

Please sign in to comment.