feat: inputs
command to display a palette menu with all active keybindings (#665)
This commit is contained in:
@@ -221,6 +221,10 @@ Editions menu. Editions are different video cuts available in some mkv files.
|
|||||||
|
|
||||||
Switch stream quality. This is just a basic re-assignment of `ytdl-format` mpv property from predefined options (configurable with `stream_quality_options`) and video reload, there is no fetching of available formats going on.
|
Switch stream quality. This is just a basic re-assignment of `ytdl-format` mpv property from predefined options (configurable with `stream_quality_options`) and video reload, there is no fetching of available formats going on.
|
||||||
|
|
||||||
|
#### `inputs`
|
||||||
|
|
||||||
|
Displays a command palette menu with all inputs defined in your `input.conf` file. Useful to check what command is bound to what shortcut, etc.
|
||||||
|
|
||||||
#### `open-file`
|
#### `open-file`
|
||||||
|
|
||||||
Open file menu. Browsing starts in current file directory, or user directory when file not available. The explorer only displays file types defined in the `video_types`, `audio_types`, and `image_types` options.
|
Open file menu. Browsing starts in current file directory, or user directory when file not available. The explorer only displays file types defined in the `video_types`, `audio_types`, and `image_types` options.
|
||||||
@@ -369,6 +373,7 @@ o script-binding uosc/open-file #! Navigation > Open file
|
|||||||
# script-binding uosc/audio-device #! Utils > Audio devices
|
# script-binding uosc/audio-device #! Utils > Audio devices
|
||||||
# script-binding uosc/editions #! Utils > Editions
|
# script-binding uosc/editions #! Utils > Editions
|
||||||
ctrl+s async screenshot #! Utils > Screenshot
|
ctrl+s async screenshot #! Utils > Screenshot
|
||||||
|
alt+i script-binding uosc/inputs #! Utils > Inputs
|
||||||
O script-binding uosc/show-in-directory #! Utils > Show in directory
|
O script-binding uosc/show-in-directory #! Utils > Show in directory
|
||||||
# script-binding uosc/open-config-directory #! Utils > Open config directory
|
# script-binding uosc/open-config-directory #! Utils > Open config directory
|
||||||
esc quit #! Quit
|
esc quit #! Quit
|
||||||
|
@@ -23,7 +23,7 @@ end
|
|||||||
---@param opts? {submenu?: string; mouse_nav?: boolean; on_close?: string | string[]}
|
---@param opts? {submenu?: string; mouse_nav?: boolean; on_close?: string | string[]}
|
||||||
function toggle_menu_with_items(opts)
|
function toggle_menu_with_items(opts)
|
||||||
if Menu:is_open('menu') then Menu:close()
|
if Menu:is_open('menu') then Menu:close()
|
||||||
else open_command_menu({type = 'menu', items = config.menu_items, search_submenus = true}, opts) end
|
else open_command_menu({type = 'menu', items = get_menu_items(), search_submenus = true}, opts) end
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param options {type: string; title: string; list_prop: string; active_prop?: string; serializer: fun(list: any, active: any): MenuDataItem[]; on_select: fun(value: any); on_move_item?: fun(from_index: integer, to_index: integer, submenu_path: integer[]); on_delete_item?: fun(index: integer, submenu_path: integer[])}
|
---@param options {type: string; title: string; list_prop: string; active_prop?: string; serializer: fun(list: any, active: any): MenuDataItem[]; on_select: fun(value: any); on_move_item?: fun(from_index: integer, to_index: integer, submenu_path: integer[]); on_delete_item?: fun(index: integer, submenu_path: integer[])}
|
||||||
@@ -293,3 +293,113 @@ function open_drives_menu(handle_select, opts)
|
|||||||
handle_select
|
handle_select
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- On demand menu items loading
|
||||||
|
do
|
||||||
|
local items = nil
|
||||||
|
function get_menu_items()
|
||||||
|
if items then return items end
|
||||||
|
|
||||||
|
local input_conf_property = mp.get_property_native('input-conf')
|
||||||
|
local input_conf = input_conf_property == '' and '~~/input.conf' or input_conf_property
|
||||||
|
local input_conf_path = mp.command_native({'expand-path', input_conf})
|
||||||
|
local input_conf_meta, meta_error = utils.file_info(input_conf_path)
|
||||||
|
|
||||||
|
-- File doesn't exist
|
||||||
|
if not input_conf_meta or not input_conf_meta.is_file then
|
||||||
|
return create_default_menu_items()
|
||||||
|
end
|
||||||
|
|
||||||
|
local main_menu = {items = {}, items_by_command = {}}
|
||||||
|
local by_id = {}
|
||||||
|
|
||||||
|
for line in io.lines(input_conf_path) do
|
||||||
|
local key, command, comment = string.match(line, '%s*([%S]+)%s+(.-)%s+#%s*(.-)%s*$')
|
||||||
|
local title = ''
|
||||||
|
|
||||||
|
if comment then
|
||||||
|
local comments = split(comment, '#')
|
||||||
|
local titles = itable_filter(comments, function(v, i) return v:match('^!') or v:match('^menu:') end)
|
||||||
|
if titles and #titles > 0 then
|
||||||
|
title = titles[1]:match('^!%s*(.*)%s*') or titles[1]:match('^menu:%s*(.*)%s*')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if title ~= '' then
|
||||||
|
local is_dummy = key:sub(1, 1) == '#'
|
||||||
|
local submenu_id = ''
|
||||||
|
local target_menu = main_menu
|
||||||
|
local title_parts = split(title or '', ' *> *')
|
||||||
|
|
||||||
|
for index, title_part in ipairs(#title_parts > 0 and title_parts or {''}) do
|
||||||
|
if index < #title_parts then
|
||||||
|
submenu_id = submenu_id .. title_part
|
||||||
|
|
||||||
|
if not by_id[submenu_id] then
|
||||||
|
local items = {}
|
||||||
|
by_id[submenu_id] = {items = items, items_by_command = {}}
|
||||||
|
target_menu.items[#target_menu.items + 1] = {title = title_part, items = items}
|
||||||
|
end
|
||||||
|
|
||||||
|
target_menu = by_id[submenu_id]
|
||||||
|
else
|
||||||
|
if command == 'ignore' then break end
|
||||||
|
-- If command is already in menu, just append the key to it
|
||||||
|
if target_menu.items_by_command[command] then
|
||||||
|
local hint = target_menu.items_by_command[command].hint
|
||||||
|
target_menu.items_by_command[command].hint = hint and hint .. ', ' .. key or key
|
||||||
|
else
|
||||||
|
local item = {
|
||||||
|
title = title_part,
|
||||||
|
hint = not is_dummy and key or nil,
|
||||||
|
value = command,
|
||||||
|
}
|
||||||
|
target_menu.items_by_command[command] = item
|
||||||
|
target_menu.items[#target_menu.items + 1] = item
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
items = main_menu.items
|
||||||
|
return #items > 0 and items or create_default_menu_items()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Adapted from `stats.lua`
|
||||||
|
function get_input_items()
|
||||||
|
local bindings = mp.get_property_native("input-bindings", {})
|
||||||
|
local active = {} -- map: key-name -> bind-info
|
||||||
|
local items = {}
|
||||||
|
|
||||||
|
-- Find active keybinds
|
||||||
|
for _, bind in pairs(bindings) do
|
||||||
|
if bind.priority >= 0 and (
|
||||||
|
not active[bind.key]
|
||||||
|
or (active[bind.key].is_weak and not bind.is_weak)
|
||||||
|
or (bind.is_weak == active[bind.key].is_weak and bind.priority > active[bind.key].priority)
|
||||||
|
)
|
||||||
|
then
|
||||||
|
active[bind.key] = bind
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Convert to menu items
|
||||||
|
for _, bind in pairs(active) do
|
||||||
|
items[#items + 1] = {title = bind.cmd, hint = bind.key, value = bind.cmd}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Sort
|
||||||
|
table.sort(items, function(a, b) return a.title < b.title end)
|
||||||
|
|
||||||
|
return #items > 0 and items or {
|
||||||
|
{
|
||||||
|
title = t('%s are empty', '`input-bindings`'),
|
||||||
|
selectable = false,
|
||||||
|
align = 'center',
|
||||||
|
italic = true,
|
||||||
|
muted = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
@@ -142,38 +142,6 @@ t = intl.t
|
|||||||
|
|
||||||
--[[ CONFIG ]]
|
--[[ CONFIG ]]
|
||||||
|
|
||||||
function create_default_menu()
|
|
||||||
return {
|
|
||||||
{title = t('Subtitles'), value = 'script-binding uosc/subtitles'},
|
|
||||||
{title = t('Audio tracks'), value = 'script-binding uosc/audio'},
|
|
||||||
{title = t('Stream quality'), value = 'script-binding uosc/stream-quality'},
|
|
||||||
{title = t('Playlist'), value = 'script-binding uosc/items'},
|
|
||||||
{title = t('Chapters'), value = 'script-binding uosc/chapters'},
|
|
||||||
{title = t('Navigation'), items = {
|
|
||||||
{title = t('Next'), hint = t('playlist or file'), value = 'script-binding uosc/next'},
|
|
||||||
{title = t('Prev'), hint = t('playlist or file'), value = 'script-binding uosc/prev'},
|
|
||||||
{title = t('Delete file & Next'), value = 'script-binding uosc/delete-file-next'},
|
|
||||||
{title = t('Delete file & Prev'), value = 'script-binding uosc/delete-file-prev'},
|
|
||||||
{title = t('Delete file & Quit'), value = 'script-binding uosc/delete-file-quit'},
|
|
||||||
{title = t('Open file'), value = 'script-binding uosc/open-file'},
|
|
||||||
},},
|
|
||||||
{title = t('Utils'), items = {
|
|
||||||
{title = t('Aspect ratio'), items = {
|
|
||||||
{title = t('Default'), value = 'set video-aspect-override "-1"'},
|
|
||||||
{title = '16:9', value = 'set video-aspect-override "16:9"'},
|
|
||||||
{title = '4:3', value = 'set video-aspect-override "4:3"'},
|
|
||||||
{title = '2.35:1', value = 'set video-aspect-override "2.35:1"'},
|
|
||||||
},},
|
|
||||||
{title = t('Audio devices'), value = 'script-binding uosc/audio-device'},
|
|
||||||
{title = t('Editions'), value = 'script-binding uosc/editions'},
|
|
||||||
{title = t('Screenshot'), value = 'async screenshot'},
|
|
||||||
{title = t('Show in directory'), value = 'script-binding uosc/show-in-directory'},
|
|
||||||
{title = t('Open config folder'), value = 'script-binding uosc/open-config-directory'},
|
|
||||||
},},
|
|
||||||
{title = t('Quit'), value = 'quit'},
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
version = uosc_version,
|
version = uosc_version,
|
||||||
-- sets max rendering frequency in case the
|
-- sets max rendering frequency in case the
|
||||||
@@ -202,73 +170,6 @@ config = {
|
|||||||
},
|
},
|
||||||
stream_quality_options = split(options.stream_quality_options, ' *, *'),
|
stream_quality_options = split(options.stream_quality_options, ' *, *'),
|
||||||
top_bar_flash_on = split(options.top_bar_flash_on, ' *, *'),
|
top_bar_flash_on = split(options.top_bar_flash_on, ' *, *'),
|
||||||
menu_items = (function()
|
|
||||||
local input_conf_property = mp.get_property_native('input-conf')
|
|
||||||
local input_conf_path = mp.command_native({
|
|
||||||
'expand-path', input_conf_property == '' and '~~/input.conf' or input_conf_property,
|
|
||||||
})
|
|
||||||
local input_conf_meta, meta_error = utils.file_info(input_conf_path)
|
|
||||||
|
|
||||||
-- File doesn't exist
|
|
||||||
if not input_conf_meta or not input_conf_meta.is_file then return create_default_menu() end
|
|
||||||
|
|
||||||
local main_menu = {items = {}, items_by_command = {}}
|
|
||||||
local by_id = {}
|
|
||||||
|
|
||||||
for line in io.lines(input_conf_path) do
|
|
||||||
local key, command, comment = string.match(line, '%s*([%S]+)%s+(.-)%s+#%s*(.-)%s*$')
|
|
||||||
local title = ''
|
|
||||||
if comment then
|
|
||||||
local comments = split(comment, '#')
|
|
||||||
local titles = itable_filter(comments, function(v, i) return v:match('^!') or v:match('^menu:') end)
|
|
||||||
if titles and #titles > 0 then
|
|
||||||
title = titles[1]:match('^!%s*(.*)%s*') or titles[1]:match('^menu:%s*(.*)%s*')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if title ~= '' then
|
|
||||||
local is_dummy = key:sub(1, 1) == '#'
|
|
||||||
local submenu_id = ''
|
|
||||||
local target_menu = main_menu
|
|
||||||
local title_parts = split(title or '', ' *> *')
|
|
||||||
|
|
||||||
for index, title_part in ipairs(#title_parts > 0 and title_parts or {''}) do
|
|
||||||
if index < #title_parts then
|
|
||||||
submenu_id = submenu_id .. title_part
|
|
||||||
|
|
||||||
if not by_id[submenu_id] then
|
|
||||||
local items = {}
|
|
||||||
by_id[submenu_id] = {items = items, items_by_command = {}}
|
|
||||||
target_menu.items[#target_menu.items + 1] = {title = title_part, items = items}
|
|
||||||
end
|
|
||||||
|
|
||||||
target_menu = by_id[submenu_id]
|
|
||||||
else
|
|
||||||
if command == 'ignore' then break end
|
|
||||||
-- If command is already in menu, just append the key to it
|
|
||||||
if target_menu.items_by_command[command] then
|
|
||||||
local hint = target_menu.items_by_command[command].hint
|
|
||||||
target_menu.items_by_command[command].hint = hint and hint .. ', ' .. key or key
|
|
||||||
else
|
|
||||||
local item = {
|
|
||||||
title = title_part,
|
|
||||||
hint = not is_dummy and key or nil,
|
|
||||||
value = command,
|
|
||||||
}
|
|
||||||
target_menu.items_by_command[command] = item
|
|
||||||
target_menu.items[#target_menu.items + 1] = item
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if #main_menu.items > 0 then
|
|
||||||
return main_menu.items
|
|
||||||
else
|
|
||||||
-- Default context menu
|
|
||||||
return create_default_menu()
|
|
||||||
end
|
|
||||||
end)(),
|
|
||||||
chapter_ranges = (function()
|
chapter_ranges = (function()
|
||||||
---@type table<string, string[]> Alternative patterns.
|
---@type table<string, string[]> Alternative patterns.
|
||||||
local alt_patterns = {}
|
local alt_patterns = {}
|
||||||
@@ -308,6 +209,40 @@ for _, name in ipairs({'timeline', 'controls', 'volume', 'top_bar', 'speed'}) do
|
|||||||
config[option_name] = flags
|
config[option_name] = flags
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Default menu items
|
||||||
|
function create_default_menu_items()
|
||||||
|
return {
|
||||||
|
{title = t('Subtitles'), value = 'script-binding uosc/subtitles'},
|
||||||
|
{title = t('Audio tracks'), value = 'script-binding uosc/audio'},
|
||||||
|
{title = t('Stream quality'), value = 'script-binding uosc/stream-quality'},
|
||||||
|
{title = t('Playlist'), value = 'script-binding uosc/items'},
|
||||||
|
{title = t('Chapters'), value = 'script-binding uosc/chapters'},
|
||||||
|
{title = t('Navigation'), items = {
|
||||||
|
{title = t('Next'), hint = t('playlist or file'), value = 'script-binding uosc/next'},
|
||||||
|
{title = t('Prev'), hint = t('playlist or file'), value = 'script-binding uosc/prev'},
|
||||||
|
{title = t('Delete file & Next'), value = 'script-binding uosc/delete-file-next'},
|
||||||
|
{title = t('Delete file & Prev'), value = 'script-binding uosc/delete-file-prev'},
|
||||||
|
{title = t('Delete file & Quit'), value = 'script-binding uosc/delete-file-quit'},
|
||||||
|
{title = t('Open file'), value = 'script-binding uosc/open-file'},
|
||||||
|
},},
|
||||||
|
{title = t('Utils'), items = {
|
||||||
|
{title = t('Aspect ratio'), items = {
|
||||||
|
{title = t('Default'), value = 'set video-aspect-override "-1"'},
|
||||||
|
{title = '16:9', value = 'set video-aspect-override "16:9"'},
|
||||||
|
{title = '4:3', value = 'set video-aspect-override "4:3"'},
|
||||||
|
{title = '2.35:1', value = 'set video-aspect-override "2.35:1"'},
|
||||||
|
},},
|
||||||
|
{title = t('Audio devices'), value = 'script-binding uosc/audio-device'},
|
||||||
|
{title = t('Editions'), value = 'script-binding uosc/editions'},
|
||||||
|
{title = t('Screenshot'), value = 'async screenshot'},
|
||||||
|
{title = t('Inputs'), value = 'script-binding uosc/inputs'},
|
||||||
|
{title = t('Show in directory'), value = 'script-binding uosc/show-in-directory'},
|
||||||
|
{title = t('Open config folder'), value = 'script-binding uosc/open-config-directory'},
|
||||||
|
},},
|
||||||
|
{title = t('Quit'), value = 'quit'},
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
--[[ STATE ]]
|
--[[ STATE ]]
|
||||||
|
|
||||||
display = {width = 1280, height = 720, scale_x = 1, scale_y = 1, initialized = false}
|
display = {width = 1280, height = 720, scale_x = 1, scale_y = 1, initialized = false}
|
||||||
@@ -953,6 +888,10 @@ bind_command('toggle-title', function() Elements.top_bar:toggle_title() end)
|
|||||||
bind_command('decide-pause-indicator', function() Elements.pause_indicator:decide() end)
|
bind_command('decide-pause-indicator', function() Elements.pause_indicator:decide() end)
|
||||||
bind_command('menu', function() toggle_menu_with_items() end)
|
bind_command('menu', function() toggle_menu_with_items() end)
|
||||||
bind_command('menu-blurred', function() toggle_menu_with_items({mouse_nav = true}) end)
|
bind_command('menu-blurred', function() toggle_menu_with_items({mouse_nav = true}) end)
|
||||||
|
bind_command('inputs', function()
|
||||||
|
if Menu:is_open('inputs') then Menu:close()
|
||||||
|
else open_command_menu({type = 'inputs', items = get_input_items(), palette = true}) end
|
||||||
|
end)
|
||||||
local track_loaders = {
|
local track_loaders = {
|
||||||
{name = 'subtitles', prop = 'sub', allowed_types = itable_join(config.types.video, config.types.subtitle)},
|
{name = 'subtitles', prop = 'sub', allowed_types = itable_join(config.types.video, config.types.subtitle)},
|
||||||
{name = 'audio', prop = 'audio', allowed_types = itable_join(config.types.video, config.types.audio)},
|
{name = 'audio', prop = 'audio', allowed_types = itable_join(config.types.video, config.types.audio)},
|
||||||
|
Reference in New Issue
Block a user