From 7d1d1cfcba4fa250e64fc2379efaf1a955199ab9 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Tue, 4 Feb 2025 20:36:20 +0100 Subject: [PATCH] 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 8b0b5590b2..fc52e134f7 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