From 285c40b338ef7ebacc2403e3bb450cffb1135942 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sun, 19 Jan 2025 21:06:27 +0100 Subject: [PATCH 1/9] console.lua: update the max width calculation This should not divide by scale_factor() because osd_w is already scaled pixels. Also add a TODO to fix --osd-margin-x's scaling. --- player/lua/console.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/player/lua/console.lua b/player/lua/console.lua index 027d1f1869c83..6b8ca9f451d63 100644 --- a/player/lua/console.lua +++ b/player/lua/console.lua @@ -610,8 +610,10 @@ local function render() -- Even with bottom-left anchoring, -- libass/ass_render.c:ass_render_event() subtracts --osd-margin-x from -- the maximum text width twice. + -- TODO: --osd-margin-x should scale with osd-width and PlayResX to make + -- the calculation accurate. local width_max = math.floor( - (osd_w - x - mp.get_property_native('osd-margin-x') * 2 / scale_factor()) + (osd_w - x - mp.get_property_native('osd-margin-x') * 2) / opts.font_size * get_font_hw_ratio()) local completions, rows = format_grid(completion_buffer, width_max, max_lines) From f41f8a861208b63a87a8165e091c71f0698d17da Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Mon, 3 Feb 2025 17:26:54 +0100 Subject: [PATCH 2/9] 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 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. 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. --- player/lua/console.lua | 112 +++++++++++++++++++++++++++-------------- 1 file changed, 74 insertions(+), 38 deletions(-) diff --git a/player/lua/console.lua b/player/lua/console.lua index 6b8ca9f451d63..c32960a91e980 100644 --- a/player/lua/console.lua +++ b/player/lua/console.lua @@ -105,6 +105,8 @@ local matches = {} local selected_match = 1 local first_match_to_print = 1 local default_item +local item_positions = {} +local max_item_width = 0 local complete local cycle_through_completions @@ -271,6 +273,12 @@ local function scale_factor() return mp.get_property_native('display-hidpi-scale', 1) end +local function terminal_output() + -- Unlike vo-configured, current-vo doesn't become falsy while switching VO, + -- which would print the log to the OSD. + return not mp.get_property('current-vo') or not mp.get_property_native('video-osd') +end + local function get_scaled_osd_dimensions() local dims = mp.get_property_native('osd-dimensions') local scale = scale_factor() @@ -278,9 +286,12 @@ 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 + if terminal_output() then -- Subtract 1 for the input line and for each line in the status line. -- This does not detect wrapped lines. return mp.get_property_native('term-size/h', 24) - 2 - @@ -290,12 +301,36 @@ 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_max_item_width() + if not selectable_items or terminal_output() then + return + end + + local longest_item = prompt .. ('a'):rep(9) + 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') + local font = get_font() + width_overlay.compute_bounds = true + width_overlay.hidden = true + width_overlay.res_x, width_overlay.res_y = get_scaled_osd_dimensions() + width_overlay.data = '{\\fs' .. opts.font_size .. + (font and '\\fn' .. font or '') .. '\\b1\\q2}' .. + ass_escape(longest_item) + local result = width_overlay:update() + max_item_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) @@ -541,9 +576,7 @@ end local function render() pending_update = false - -- Unlike vo-configured, current-vo doesn't become falsy while switching VO, - -- which would print the log to the OSD. - if not mp.get_property('current-vo') or not mp.get_property_native('video-osd') then + if terminal_output() then print_to_terminal() return end @@ -625,21 +658,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 .. '{\\4a&Hff&}' + + ass:new_event() + ass:an(1) + ass:pos(x, y) + ass:append('{\\1a&Hff&}') + ass:draw_start() + ass:rect_cw(0, 0, max_item_width, (1.5 + math.min(#matches, max_lines)) * 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 @@ -918,27 +966,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 @@ -1077,6 +1110,7 @@ local function search_history() selectable_items[i] = history[#history + 1 - i] end + calculate_max_item_width() handle_edit() bind_mouse() end @@ -1865,6 +1899,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_max_item_width() handle_edit() bind_mouse() end @@ -1932,11 +1967,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'}) do + mp.observe_property(property, 'native', function () + calculate_max_item_width() + render() + end) +end mp.observe_property('focused', 'native', render) mp.observe_property("user-data/osc/margins", "native", function(_, val) From b3081790dad9f60ba92ec420a8760b038514adff Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Tue, 4 Feb 2025 11:50:01 +0100 Subject: [PATCH 3/9] console.lua: center the select menu in the window The newly added max width calculation allows positioning the select menu in the center of the window. The input line is moved above the items so that the selected item stays near the input line as you type instead of jumping from top to bottom as the matches decrease. Keep aligning to the bottom left to search the command history to not change the layout abruptly, the same reason the monospace font is kept. --- player/lua/console.lua | 52 +++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/player/lua/console.lua b/player/lua/console.lua index c32960a91e980..4094cbb99faaa 100644 --- a/player/lua/console.lua +++ b/player/lua/console.lua @@ -319,16 +319,18 @@ local function calculate_max_item_width() end end - local width_overlay = mp.create_osd_overlay('ass-events') + local osd_w, osd_h = get_scaled_osd_dimensions() local font = get_font() + 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.res_x = osd_w + width_overlay.res_y = osd_h width_overlay.data = '{\\fs' .. opts.font_size .. (font and '\\fn' .. font or '') .. '\\b1\\q2}' .. ass_escape(longest_item) local result = width_overlay:update() - max_item_width = result.x1 - result.x0 + max_item_width = math.min(result.x1 - result.x0, osd_w - get_margin_x() * 2) end local function should_highlight_completion(i) @@ -594,19 +596,29 @@ local function render() end local ass = assdraw.ass_new() - local osd_w, osd_h = get_scaled_osd_dimensions() + local line_height = get_line_height() + local max_lines = calculate_max_log_lines() - local x = get_margin_x() - local y = osd_h * (1 - global_margins.b) - get_margin_y() + local x, y, alignment, clipping_coordinates + if selectable_items and not searching_history then + x = (osd_w - max_item_width) / 2 + y = osd_h / 2 - (math.min(#selectable_items, max_lines) + 1.5) * line_height / 2 + alignment = 7 + clipping_coordinates = '0,0,' .. x + max_item_width .. ',' .. osd_h + else + x = get_margin_x() + y = osd_h * (1 - global_margins.b) - get_margin_y() + alignment = 1 + -- Avoid drawing below topbar OSC when there are wrapped lines. + local coordinate_top = math.floor(global_margins.t * osd_h + 0.5) + clipping_coordinates = '0,' .. coordinate_top .. ',' .. osd_w .. ',' .. osd_h + end local font = get_font() -- Use the same blur value as the rest of the OSD. 288 is the OSD's -- PlayResY. local blur = mp.get_property_native('osd-blur') * osd_h / 288 - local coordinate_top = math.floor(global_margins.t * osd_h + 0.5) - local clipping_coordinates = '0,' .. coordinate_top .. ',' .. - osd_w .. ',' .. osd_h local style = '{\\r' .. (font and '\\fn' .. font or '') .. @@ -636,7 +648,6 @@ local function render() -- Render log messages as ASS. -- This will render at most screeny / font_size - 1 messages. - local max_lines = calculate_max_log_lines() local completion_ass = '' if next(completion_buffer) then -- Estimate how many characters fit in one line @@ -658,7 +669,6 @@ local function render() local log_ass = '' local log_buffer = log_buffers[id] - local line_height = get_line_height() item_positions = {} -- Disable background-box for selectable items to not draw separate @@ -667,7 +677,7 @@ local function render() style = style .. '{\\4a&Hff&}' ass:new_event() - ass:an(1) + ass:an(alignment) ass:pos(x, y) ass:append('{\\1a&Hff&}') ass:draw_start() @@ -679,14 +689,17 @@ local function render() local log_item = style .. log_buffer[i].style .. ass_escape(log_buffer[i].text) if selectable_items then - local item_y = y - (1.5 + #log_buffer - i) * line_height + local item_y = alignment == 7 + and y + (1 + i) * line_height + or y - (1.5 + #log_buffer - i) * line_height ass:new_event() - ass:an(1) + ass:an(4) 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 } + item_positions[#item_positions + 1] = + { item_y - line_height / 2, item_y + line_height / 2 } end else log_ass = log_ass .. log_item .. '\\N' @@ -694,10 +707,11 @@ local function render() end ass:new_event() - ass:an(1) + ass:an(alignment) ass:pos(x, y) - ass:append(log_ass .. '\\N') - ass:append(completion_ass) + if not selectable_items then + ass:append(log_ass .. '\\N' .. completion_ass) + end ass:append(style .. ass_escape(prompt) .. ' ' .. before_cur) ass:append(cglyph) ass:append(style .. after_cur) @@ -705,7 +719,7 @@ local function render() -- Redraw the cursor with the REPL text invisible. This will make the -- cursor appear in front of the text. ass:new_event() - ass:an(1) + ass:an(alignment) ass:pos(x, y) ass:append(style .. '{\\alpha&HFF&}' .. ass_escape(prompt) .. ' ' .. before_cur) ass:append(cglyph) From 084b61179952e616f3a55f74a8b53a1344195192 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Tue, 4 Feb 2025 12:12:20 +0100 Subject: [PATCH 4/9] console.lua: close when clicking outside of items horizontally --- DOCS/man/select.rst | 2 +- player/lua/console.lua | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/DOCS/man/select.rst b/DOCS/man/select.rst index 8fbd0ce9fe6a2..0ecb2f9b96bb2 100644 --- a/DOCS/man/select.rst +++ b/DOCS/man/select.rst @@ -31,7 +31,7 @@ PGDN and Ctrl+f MBTN_LEFT Confirm the selection of the highlighted item, or close the console if - clicking above the first item or below the last item. + clicking outside of the menu rectangle. WHEEL_UP Scroll up. diff --git a/player/lua/console.lua b/player/lua/console.lua index 4094cbb99faaa..914ca1179d0f9 100644 --- a/player/lua/console.lua +++ b/player/lua/console.lua @@ -980,10 +980,19 @@ local function handle_enter() end local function determine_hovered_item() - local y = mp.get_property_native('mouse-pos').y / scale_factor() + local osd_w, _ = get_scaled_osd_dimensions() + local scale = scale_factor() + local mouse_pos = mp.get_property_native('mouse-pos') + local mouse_x = mouse_pos.x / scale + local mouse_y = mouse_pos.y / scale + local item_x0 = searching_history and get_margin_x() or (osd_w - max_item_width) / 2 + + if mouse_x < item_x0 or mouse_x > item_x0 + max_item_width then + return + end for i, positions in ipairs(item_positions) do - if y >= positions[1] and y <= positions[2] then + if mouse_y >= positions[1] and mouse_y <= positions[2] then return first_match_to_print - 1 + i end end From ac511d631b1fe73121cc020c329e5310d3abfee0 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Tue, 4 Feb 2025 20:36:20 +0100 Subject: [PATCH 5/9] console.lua: add a scrollbar Add a scrollbar to give a visual representation of the size and position in the items. It is not clickable. The counter is moved above the scrollbar to free up a line for an another item, simplify calculations, and prevent the selected item from jumping from the second to the first line as you type. In terminal output there is no easy way to draw a scrollbar to the right due to Unicode widths. This just moves the counter into the prompt. --- player/lua/console.lua | 99 ++++++++++++++++++++++++++---------------- 1 file changed, 61 insertions(+), 38 deletions(-) diff --git a/player/lua/console.lua b/player/lua/console.lua index 914ca1179d0f9..ce440d457c8e7 100644 --- a/player/lua/console.lua +++ b/player/lua/console.lua @@ -32,6 +32,7 @@ local opts = { font = "", font_size = 24, border_size = 1.65, + padding = 10, margin_x = -1, margin_y = -1, scale_with_window = "auto", @@ -54,7 +55,6 @@ local styles = { error = '{\\1c&H7a77f2&}', fatal = '{\\1c&H5791f9&}', completion = '{\\1c&Hcc99cc&}', - disabled = '{\\1c&Hcccccc&}', } for key, style in pairs(styles) do styles[key] = style .. '{\\3c&H111111&}' @@ -467,12 +467,6 @@ local function populate_log_with_matches() local log = log_buffers[id] local max_log_lines = calculate_max_log_lines() - local print_counter = false - - if #matches > max_log_lines then - print_counter = true - max_log_lines = max_log_lines - 1 - end if selected_match < first_match_to_print then first_match_to_print = selected_match @@ -483,18 +477,6 @@ local function populate_log_with_matches() local last_match_to_print = math.min(first_match_to_print + max_log_lines - 1, #matches) - if print_counter then - log[1] = { - text = '', - style = styles.disabled .. selected_match .. '/' .. #matches .. - ' {\\fs' .. opts.font_size * 0.75 .. '}[' .. - first_match_to_print .. '-' .. last_match_to_print .. ']', - terminal_style = terminal_styles.disabled .. selected_match .. '/' .. - #matches .. ' [' .. first_match_to_print .. '-' .. - last_match_to_print .. ']', - } - end - for i = first_match_to_print, last_match_to_print do local style = '' local terminal_style = '' @@ -562,6 +544,15 @@ local function print_to_terminal() completions = completions .. (i < #completion_buffer and '\t' or '\n') end + local counter = '' + if selectable_items and #selectable_items > calculate_max_log_lines() then + local digits = math.ceil(math.log(#selectable_items, 10)) + counter = terminal_styles.disabled .. + '[' .. string.format('%0' .. digits .. 'd', selected_match) .. + '/' .. string.format('%0' .. digits .. 'd', #matches) .. + ']\027[0m ' + end + local before_cur = line:sub(1, cursor - 1) local after_cur = line:sub(cursor) -- Ensure there is a character with inverted colors to print. @@ -569,8 +560,8 @@ local function print_to_terminal() after_cur = ' ' end - mp.osd_message(log .. completions .. prompt .. ' ' .. before_cur .. - '\027[7m' .. after_cur:sub(1, 1) .. '\027[0m' .. + mp.osd_message(log .. completions .. counter .. prompt .. ' ' .. before_cur + .. '\027[7m' .. after_cur:sub(1, 1) .. '\027[0m' .. after_cur:sub(2), 999) osd_msg_active = true end @@ -619,6 +610,7 @@ local function render() -- Use the same blur value as the rest of the OSD. 288 is the OSD's -- PlayResY. local blur = mp.get_property_native('osd-blur') * osd_h / 288 + local border_style = mp.get_property('osd-border-style') local style = '{\\r' .. (font and '\\fn' .. font or '') .. @@ -667,13 +659,9 @@ local function render() populate_log_with_matches() - local log_ass = '' - local log_buffer = log_buffers[id] - 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 + if selectable_items and border_style == 'background-box' then style = style .. '{\\4a&Hff&}' ass:new_event() @@ -685,6 +673,10 @@ local function render() ass:draw_stop() end + local log_ass = '' + local log_buffer = log_buffers[id] + 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) @@ -697,15 +689,46 @@ local function render() 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 / 2, item_y + line_height / 2 } - end + item_positions[#item_positions + 1] = + { item_y - line_height / 2, item_y + line_height / 2 } else log_ass = log_ass .. log_item .. '\\N' end end + -- Scrollbar + if selectable_items and #matches > max_lines then + ass:new_event() + ass:an(alignment + 2) + ass:pos(x + max_item_width, y) + if border_style == 'background-box' then + ass:append('{\\4a&Hff&}') + end + ass:append(selected_match .. '/' .. #matches) + + local start_percentage = (first_match_to_print - 1) / #matches + local end_percentage = (first_match_to_print - 1 + max_lines) / #matches + if end_percentage - start_percentage < 0.04 then + local diff = 0.04 - (end_percentage - start_percentage) + start_percentage = start_percentage * (1 - diff) + end_percentage = end_percentage + diff * (1 - end_percentage) + end + + local max_height = max_lines * line_height + local bar_y = alignment == 7 + and y + 1.5 * line_height + start_percentage * max_height + or y - 1.5 * line_height - max_height * (1 - end_percentage) + local height = max_height * (end_percentage - start_percentage) + + ass:new_event() + ass:an(alignment) + ass:append('{\\blur0\\4a&Hff&\\bord1}') + ass:pos(x + max_item_width + opts.padding - 1, bar_y) + ass:draw_start() + ass:rect_cw(0, 0, -opts.padding / 2, height) + ass:draw_stop() + end + ass:new_event() ass:an(alignment) ass:pos(x, y) @@ -798,8 +821,8 @@ local function handle_edit() selected_match = default_item local max_lines = calculate_max_log_lines() - first_match_to_print = math.max(1, selected_match - math.floor(max_lines / 2) + 1) - if first_match_to_print > #selectable_items - max_lines + 2 then + first_match_to_print = math.max(1, selected_match + 1 - math.ceil(max_lines / 2)) + if first_match_to_print > #selectable_items - max_lines + 1 then first_match_to_print = math.max(1, #selectable_items - max_lines + 1) end else @@ -1061,18 +1084,18 @@ local function move_history(amount, is_wheel) -- Update selected_match only if it's the first or last printed item and -- there are hidden items. if (amount > 0 and selected_match == first_match_to_print - and first_match_to_print + max_lines - 2 < #matches) - or (amount < 0 and selected_match == first_match_to_print + max_lines - 2 + and first_match_to_print - 1 + max_lines < #matches) + or (amount < 0 and selected_match == first_match_to_print - 1 + max_lines and first_match_to_print > 1) then selected_match = selected_match + amount end - if amount > 0 and first_match_to_print < #matches - max_lines + 2 + if amount > 0 and first_match_to_print < #matches - max_lines + 1 or amount < 0 and first_match_to_print > 1 then -- math.min and math.max would only be needed with amounts other than -- 1 and -1. first_match_to_print = math.min( - math.max(first_match_to_print + amount, 1), #matches - max_lines + 2) + math.max(first_match_to_print + amount, 1), #matches - max_lines + 1) end local item = determine_hovered_item() @@ -1101,7 +1124,7 @@ end -- Go to the first command in the command history (PgUp) local function handle_pgup() if selectable_items then - selected_match = math.max(selected_match - calculate_max_log_lines() + 2, 1) + selected_match = math.max(selected_match - calculate_max_log_lines() + 1, 1) render() return end @@ -1112,7 +1135,7 @@ end -- Stop browsing history and start editing a blank line (PgDown) local function handle_pgdown() if selectable_items then - selected_match = math.min(selected_match + calculate_max_log_lines() - 2, #matches) + selected_match = math.min(selected_match + calculate_max_log_lines() - 1, #matches) render() return end From 41c4353ac44cb304706e66622d5dfe920f921489 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Fri, 7 Feb 2025 13:34:01 +0100 Subject: [PATCH 6/9] console.lua: always draw a background behind items Draw a background behind selectable items even with outline-and-shadow. This makes sense for a menu because you want it to be readable, select something and move on. It doesn't stay open while watching like OSD messages and subtitles, so it can cover more of the video. In fact this was probably the only menu without a background by default. Also the scrollbar without a background looked weird, and the background shows the new horizontal hit box. --osd-outline-color determines the background color with outline-and-shadow, while --osd-back-color determines the background color with background-box. Some transparency is added because using pure black is not recommended because it causes eye strain; alternatively --osd-outline-color could default to #222222. Drawing the background ourselves also allows making the corners rounded. Free-form text mode keeps using only background-box backgrounds if configured as covering the whole screen while searching stats key bindings would be bad. Searching history also doesn't add a background to not change the layout abruptly. When searching history with background-box, it preserves the alpha component of --osd-back-color. --- DOCS/interface-changes/console-menu.txt | 1 + DOCS/man/console.rst | 26 +++++++++++++ DOCS/man/osc.rst | 2 +- player/lua/console.lua | 50 ++++++++++++++++++------- 4 files changed, 64 insertions(+), 15 deletions(-) create mode 100644 DOCS/interface-changes/console-menu.txt diff --git a/DOCS/interface-changes/console-menu.txt b/DOCS/interface-changes/console-menu.txt new file mode 100644 index 0000000000000..7a2c39e89c752 --- /dev/null +++ b/DOCS/interface-changes/console-menu.txt @@ -0,0 +1 @@ +add `console-background_alpha` `console-menu_outline_size` `console-menu_outline_color` `console-corner_radius` script-opts diff --git a/DOCS/man/console.rst b/DOCS/man/console.rst index d14762c0ba99c..9bc31619f1fcf 100644 --- a/DOCS/man/console.rst +++ b/DOCS/man/console.rst @@ -169,6 +169,32 @@ Configurable Options Set the font border size used for the REPL and the console. +``background_alpha`` + Default: 20 + + The transparency of the menu's background. Ranges from 0 (opaque) to 255 + (fully transparent). + +``padding`` + Default: 10 + + The padding of the menu. + +``menu_outline_size`` + Default: 0 + + The size of the menu's border. + +``menu_outline_color`` + Default: #FFFFFF + + The color of the menu's border. + +``corner_radius`` + Default: 8 + + The radius of the menu's corners. + ``margin_x`` Default: same as ``--osd-margin-x`` diff --git a/DOCS/man/osc.rst b/DOCS/man/osc.rst index ec8b265b80a64..f47e9c52e5a30 100644 --- a/DOCS/man/osc.rst +++ b/DOCS/man/osc.rst @@ -218,7 +218,7 @@ Configurable Options seekbar or separately if ``seekbarstyle`` is set to ``bar``. ``seekrangealpha`` - Default: 200 + Default: 20 Alpha of the seekable ranges, 0 (opaque) to 255 (fully transparent). diff --git a/player/lua/console.lua b/player/lua/console.lua index ce440d457c8e7..279b2a07efedd 100644 --- a/player/lua/console.lua +++ b/player/lua/console.lua @@ -32,7 +32,11 @@ local opts = { font = "", font_size = 24, border_size = 1.65, + background_alpha = 50, padding = 10, + menu_outline_size = 0, + menu_outline_color = '#FFFFFF', + corner_radius = 8, margin_x = -1, margin_y = -1, scale_with_window = "auto", @@ -300,7 +304,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()) + - get_margin_y() - (selectable_items and opts.padding * 2 or 0)) / get_line_height() -- Subtract 1 for the input line and 0.5 for the empty -- line between the log and the input line. @@ -330,7 +334,8 @@ local function calculate_max_item_width() (font and '\\fn' .. font or '') .. '\\b1\\q2}' .. ass_escape(longest_item) local result = width_overlay:update() - max_item_width = math.min(result.x1 - result.x0, osd_w - get_margin_x() * 2) + max_item_width = math.min(result.x1 - result.x0, + osd_w - get_margin_x() * 2 - opts.padding * 2) end local function should_highlight_completion(i) @@ -343,6 +348,10 @@ local function mpv_color_to_ass(color) string.format('%x', 255 - tonumber('0x' .. color:sub(2,3))) end +local function option_color_to_ass(color) + return color:sub(6,7) .. color:sub(4,5) .. color:sub(2,3) +end + local function get_selected_ass() local color, alpha = mpv_color_to_ass(mp.get_property('osd-selected-color')) local outline_color, outline_alpha = @@ -659,17 +668,32 @@ local function render() populate_log_with_matches() - -- Disable background-box for selectable items to not draw separate - -- rectangles with different widths, and draw a single rectangle instead. - if selectable_items and border_style == 'background-box' then - style = style .. '{\\4a&Hff&}' + -- Background + if selectable_items and + (not searching_history or border_style == 'background-box') then + style = style .. '{\\bord0\\blur0\\4a&Hff&}' + local back_color, back_alpha = mpv_color_to_ass(mp.get_property( + border_style == 'background-box' and 'osd-back-color' or 'osd-outline-color')) + if not searching_history then + back_alpha = string.format('%x', opts.background_alpha) + end ass:new_event() ass:an(alignment) ass:pos(x, y) - ass:append('{\\1a&Hff&}') + ass:append('{\\1c&H' .. back_color .. '&\\1a&H' .. back_alpha .. + '&\\bord' .. opts.menu_outline_size .. '\\3c&H' .. + option_color_to_ass(opts.menu_outline_color) .. '&}') + if border_style == 'background-box' then + ass:append('{\\4a&Hff&}') + end ass:draw_start() - ass:rect_cw(0, 0, max_item_width, (1.5 + math.min(#matches, max_lines)) * line_height) + ass:round_rect_cw(-opts.padding, + opts.padding * (alignment == 7 and -1 or 1), + max_item_width + opts.padding, + (1.5 + math.min(#matches, max_lines)) * line_height + + opts.padding * (alignment == 7 and 1 or 2), + opts.corner_radius, opts.corner_radius) ass:draw_stop() end @@ -701,10 +725,7 @@ local function render() ass:new_event() ass:an(alignment + 2) ass:pos(x + max_item_width, y) - if border_style == 'background-box' then - ass:append('{\\4a&Hff&}') - end - ass:append(selected_match .. '/' .. #matches) + ass:append('{\\bord0\\4a&Hff&\\blur0}' .. selected_match .. '/' .. #matches) local start_percentage = (first_match_to_print - 1) / #matches local end_percentage = (first_match_to_print - 1 + max_lines) / #matches @@ -1008,9 +1029,10 @@ local function determine_hovered_item() local mouse_pos = mp.get_property_native('mouse-pos') local mouse_x = mouse_pos.x / scale local mouse_y = mouse_pos.y / scale - local item_x0 = searching_history and get_margin_x() or (osd_w - max_item_width) / 2 + local item_x0 = (searching_history and get_margin_x() or (osd_w - max_item_width) / 2) + - opts.padding - if mouse_x < item_x0 or mouse_x > item_x0 + max_item_width then + if mouse_x < item_x0 or mouse_x > item_x0 + max_item_width + opts.padding * 2 then return end From 1167325eb77e324c219fdbc98e2cabd6e9be76ee Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 8 Feb 2025 08:24:27 +0100 Subject: [PATCH 7/9] Revert "console.lua: close with right click" This reverts commit ad0c29e5acaed727ca16e9df6fbbb4930c12a0da. This should be unnecessary now that the menu has a visible horizontal and vertical hitbox, and was not discoverable anyway. Right click can be useful even while the console is open to pause or open a context menu. Requested in https://github.com/mpv-player/mpv/pull/15145#issuecomment-2428032616. --- DOCS/man/console.rst | 3 --- player/lua/console.lua | 1 - 2 files changed, 4 deletions(-) diff --git a/DOCS/man/console.rst b/DOCS/man/console.rst index 9bc31619f1fcf..7281a95095bc1 100644 --- a/DOCS/man/console.rst +++ b/DOCS/man/console.rst @@ -100,9 +100,6 @@ Shift+TAB Ctrl+l Clear all log messages from the console. -MBTN_RIGHT - Hide the console. - MBTN_MID Paste text (uses the primary selection on X11 and Wayland). diff --git a/player/lua/console.lua b/player/lua/console.lua index 279b2a07efedd..342339531a565 100644 --- a/player/lua/console.lua +++ b/player/lua/console.lua @@ -1777,7 +1777,6 @@ local function get_bindings() { 'ins', handle_ins }, { 'shift+ins', function() paste(false) end }, { 'mbtn_mid', function() paste(false) end }, - { 'mbtn_right', function() set_active(false) end }, { 'left', function() prev_char() end }, { 'ctrl+b', function() page_up_or_prev_char() end }, { 'right', function() next_char() end }, From 2c05c038478ab0784087583fd5fd71ba2501554c Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 8 Feb 2025 08:44:21 +0100 Subject: [PATCH 8/9] console.lua: give a different background color to the selected item The default item also has the same background color but with transparency. Also stop bolding selections since inverted black and white backgrounds should be visible even with color blindness. It was annoying with proportional fonts because it misalignes similar strings. As mpv's default text colors are white on black border or background, --osd-selected-color's default of a bright yellow meant to be used with a black border becomes unreadable with the inverted white background. We could default to a dark --osd-selected-color and a a light --osd-selected-outline-color and use --osd-selected-outline-color as the selected back color. However in show-text commands having only the selected item with a different white border doesn't look good. This therefore adds indipendent selected_color and selected_back_color script-opts. --osd-selected-color is only used for completions and for the selected item when searching the command history with outline-and-shadow. --- DOCS/interface-changes/console-menu.txt | 2 +- DOCS/man/console.rst | 10 +++++++ player/lua/console.lua | 35 ++++++++++++++++++++----- 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/DOCS/interface-changes/console-menu.txt b/DOCS/interface-changes/console-menu.txt index 7a2c39e89c752..1539a61e406ae 100644 --- a/DOCS/interface-changes/console-menu.txt +++ b/DOCS/interface-changes/console-menu.txt @@ -1 +1 @@ -add `console-background_alpha` `console-menu_outline_size` `console-menu_outline_color` `console-corner_radius` script-opts +add `console-background_alpha` `console-menu_outline_size` `console-menu_outline_color` `console-corner_radius` `console-selected_color` and `console-selected_back_color` script-opts diff --git a/DOCS/man/console.rst b/DOCS/man/console.rst index 7281a95095bc1..d1f7aef7b3359 100644 --- a/DOCS/man/console.rst +++ b/DOCS/man/console.rst @@ -208,6 +208,16 @@ Configurable Options Whether to scale the console with the window height. Can be ``yes``, ``no``, or ``auto``, which follows the value of ``--osd-scale-by-window``. +``selected_color`` + Default: ``#222222`` + + The color of the selected item. + +``selected_back_color`` + Default: ``#FFFFFF`` + + The background color of the selected item. + ``case_sensitive`` Default: no on Windows, yes on other platforms. diff --git a/player/lua/console.lua b/player/lua/console.lua index 342339531a565..7b022e99c8020 100644 --- a/player/lua/console.lua +++ b/player/lua/console.lua @@ -40,6 +40,8 @@ local opts = { margin_x = -1, margin_y = -1, scale_with_window = "auto", + selected_color = '#222222', + selected_back_color = '#FFFFFF', case_sensitive = platform ~= 'windows' and true or false, history_dedup = true, font_hw_ratio = 'auto', @@ -331,7 +333,7 @@ local function calculate_max_item_width() width_overlay.res_x = osd_w width_overlay.res_y = osd_h width_overlay.data = '{\\fs' .. opts.font_size .. - (font and '\\fn' .. font or '') .. '\\b1\\q2}' .. + (font and '\\fn' .. font or '') .. '\\q2}' .. ass_escape(longest_item) local result = width_overlay:update() max_item_width = math.min(result.x1 - result.x0, @@ -356,7 +358,7 @@ local function get_selected_ass() local color, alpha = mpv_color_to_ass(mp.get_property('osd-selected-color')) local outline_color, outline_alpha = mpv_color_to_ass(mp.get_property('osd-selected-outline-color')) - return '{\\1c&H' .. color .. '&\\1a&H' .. alpha .. + return '{\\b1\\1c&H' .. color .. '&\\1a&H' .. alpha .. '&\\3c&H' .. outline_color .. '&\\3a&H' .. outline_alpha .. '&}' end @@ -442,7 +444,7 @@ local function format_grid(list, width_max, rows_max) columns[column] = ass_escape(string.format(format_string, list[i])) if should_highlight_completion(i) then - columns[column] = '{\\b1}' .. get_selected_ass() .. columns[column] .. + columns[column] = get_selected_ass() .. columns[column] .. '{\\b\\1a&\\3a&}' .. styles.completion end end @@ -490,14 +492,16 @@ local function populate_log_with_matches() local style = '' local terminal_style = '' - if i == selected_match or matches[i].index == default_item then - style = get_selected_ass() - end if matches[i].index == default_item then terminal_style = terminal_styles.default_item end if i == selected_match then - style = style .. '{\\b1}' + if searching_history and + mp.get_property('osd-border-style') == 'outline-and-shadow' then + style = get_selected_ass() + else + style = '{\\1c&H' .. option_color_to_ass(opts.selected_color) .. '&}' + end terminal_style = terminal_style .. terminal_styles.selected_completion end @@ -708,6 +712,23 @@ local function render() local item_y = alignment == 7 and y + (1 + i) * line_height or y - (1.5 + #log_buffer - i) * line_height + + if (first_match_to_print - 1 + i == selected_match or + matches[first_match_to_print - 1 + i].index == default_item) + and (not searching_history or border_style == 'background-box') then + ass:new_event() + ass:an(4) + ass:pos(x, item_y) + ass:append('{\\blur0\\bord0\\4aH&ff&\\1c&H' .. + option_color_to_ass(opts.selected_back_color) .. '&}') + if first_match_to_print - 1 + i ~= selected_match then + ass:append('{\\1aH&dd&}') + end + ass:draw_start() + ass:rect_cw(-opts.padding, 0, max_item_width + opts.padding, line_height) + ass:draw_stop() + end + ass:new_event() ass:an(4) ass:pos(x, item_y) From 25c730584befd956108a997fcdc57b14e8b15080 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 8 Feb 2025 09:18:24 +0100 Subject: [PATCH 9/9] DOCS/man/{mpv,osc}: say menu instead of selector It's fine to call them menus now that they actually look like ones. --- DOCS/man/mpv.rst | 6 +++--- DOCS/man/osc.rst | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/DOCS/man/mpv.rst b/DOCS/man/mpv.rst index 84af8dd43e6f9..2199090676f05 100644 --- a/DOCS/man/mpv.rst +++ b/DOCS/man/mpv.rst @@ -289,13 +289,13 @@ Alt+2 (and Command+2 on macOS) Command + f (macOS only) Toggle fullscreen (see also ``--fs``). -(The following keybindings open a selector in the console that lets you choose -from a list of items by typing part of the desired item, by clicking the desired +(The following keybindings open a menu in the console that lets you choose from +a list of items by typing part of the desired item, by clicking the desired item, or by navigating them with keybindings: ``Down`` and ``Ctrl+n`` go down, ``Up`` and ``Ctrl+p`` go up, ``Page down`` and ``Ctrl+f`` scroll down one page, and ``Page up`` and ``Ctrl+b`` scroll up one page.) -In track selectors, selecting the current tracks disables it. +In track menus, selecting the current tracks disables it. g-p Select a playlist entry. diff --git a/DOCS/man/osc.rst b/DOCS/man/osc.rst index f47e9c52e5a30..2568b42e088f2 100644 --- a/DOCS/man/osc.rst +++ b/DOCS/man/osc.rst @@ -38,7 +38,7 @@ pl prev left-click play previous file in playlist shift+L-click show the playlist middle-click show the playlist - right-click open the playlist selector + right-click open the playlist menu ============= ================================================ pl next @@ -46,7 +46,7 @@ pl next left-click play next file in playlist shift+L-click show the playlist middle-click show the playlist - right-click open the playlist selector + right-click open the playlist menu ============= ================================================ title @@ -57,7 +57,7 @@ title left-click show file and track info shift+L-click show the path middle-click show the path - right-click open the history selector + right-click open the history menu ============= ================================================ cache @@ -76,7 +76,7 @@ skip back left-click go to beginning of chapter / previous chapter shift+L-click show chapters middle-click show chapters - right-click open the chapter selector + right-click open the chapter menu ============= ================================================ skip frwd @@ -84,7 +84,7 @@ skip frwd left-click go to next chapter shift+L-click show chapters middle-click show chapters - right-click open the chapter selector + right-click open the chapter menu ============= ================================================ time elapsed @@ -117,14 +117,14 @@ audio and sub left-click cycle audio/sub tracks forward shift+L-click cycle audio/sub tracks backwards middle-click cycle audio/sub tracks backwards - right-click open the audio/sub track selector + right-click open the audio/sub track menu mouse wheel cycle audio/sub tracks forward/backwards ============= ================================================ vol ============= ================================================ left-click toggle mute - right-click open the audio device selector + right-click open the audio device menu mouse wheel volume up/down ============= ================================================