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 disabling the shadow of selectable items, and drawing a
single rectangle below the items.

We could also calculate the height of each item to make them perfectly
adjacent by rendering each one with compute_bounds, but that takes 15ms
per render even on a new CPU, so 20 lines would take 300ms, which is too
slow.

The rectangle is as wide as the longest item. Even using compute_bounds
once on the longest item on each render() slows it to the point of
making updating the highlight while moving the mouse slower than with
outline-and-shadow, especially on old hardware. So cache the width of
the longest item when opening the console and update it when
osd-dimensions change. The calculation is only done with background-box
to not slow down outline-and-shadow unnecessarily, though it would be
fine since it's only calculated on open, and therefore we also observe
osd-border-style for the unlikely event that it changes during
selection.

Also increase the items' height to opts.font_size * 1.1 because it looks
better with some margin between the items. It is possible to do it now
because selectable items are drawn in separate ASS events in all border
styles.
  • Loading branch information
guidocella committed Jan 30, 2025
1 parent 5e9dbe4 commit 341173f
Showing 1 changed file with 63 additions and 33 deletions.
96 changes: 63 additions & 33 deletions player/lua/console.lua
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ local matches = {}
local selected_match = 1
local first_match_to_print = 1
local default_item
local item_positions = {}
local longest_width = 0

local complete
local cycle_through_completions
Expand Down Expand Up @@ -280,6 +282,10 @@ local function get_scaled_osd_dimensions()
return dims.w / scale, dims.h /scale
end

local function get_line_height()
return selectable_items and opts.font_size * 1.1 or opts.font_size
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,12 +298,33 @@ 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)
end

local function calculate_longest_width()
if not selectable_items or mp.get_property('osd-border-style') ~= 'background-box' then
return
end

local longest_item = selectable_items[1]
for _, item in pairs(selectable_items) do
if #item > #longest_item then
longest_item = item
end
end

local width_overlay = mp.create_osd_overlay('ass-events')
width_overlay.compute_bounds = true
width_overlay.hidden = true
width_overlay.res_x, width_overlay.res_y = get_scaled_osd_dimensions()
width_overlay.data = '{\\b1}' .. longest_item
local result = width_overlay:update()
longest_width = result.x1 - result.x0
end

local function should_highlight_completion(i)
return i == selected_completion_index or
(i == 1 and selected_completion_index == 0 and input_caller == nil)
Expand Down Expand Up @@ -627,21 +654,36 @@ 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 = {}

-- Disable background-box for selectable items to not draw separate
-- rectangles with different widths, and draw a single rectangle instead.
if selectable_items and mp.get_property('osd-border-style') == 'background-box' then
style = style .. '{\\4aff}'

ass:new_event()
ass:an(1)
ass:pos(x, y)
ass:append('{\\1aff}')
ass:draw_start()
ass:rect_cw(0, 0, longest_width, (1.5 + #matches) * line_height)
ass:draw_stop()
end

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
Expand Down Expand Up @@ -920,27 +962,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 Expand Up @@ -1079,6 +1106,7 @@ local function search_history()
selectable_items[i] = history[#history + 1 - i]
end

calculate_longest_width()
handle_edit()
bind_mouse()
end
Expand Down Expand Up @@ -1886,6 +1914,7 @@ mp.register_script_message('get-input', function (script_name, args)
selectable_items[i] = item:gsub("[\r\n].*", ""):sub(1, 300)
end
default_item = args.default_item
calculate_longest_width()
handle_edit()
bind_mouse()
end
Expand Down Expand Up @@ -1953,11 +1982,12 @@ mp.register_script_message('complete', function(list, start_pos)
render()
end)

-- Redraw the REPL when the OSD size changes. This is needed because the
-- PlayRes of the OSD will need to be adjusted.
mp.observe_property('osd-width', 'native', render)
mp.observe_property('osd-height', 'native', render)
mp.observe_property('display-hidpi-scale', 'native', render)
for _, property in pairs({'osd-width', 'osd-height', 'display-hidpi-scale', 'osd-border-style'}) do
mp.observe_property(property, 'native', function ()
calculate_longest_width()
render()
end)
end
mp.observe_property('focused', 'native', render)

mp.observe_property("user-data/osc/margins", "native", function(_, val)
Expand Down

0 comments on commit 341173f

Please sign in to comment.