feat(api): added search_submenus
prop to menus (#655)
`search_submenus` makes uosc's internal search handler (when no `on_search` callback is defined) look into submenus as well, effectively flattening the menu for the duration of the search.
This commit is contained in:
@@ -465,6 +465,8 @@ While the menu is open this value will be available in `user-data/uosc/menu/type
|
|||||||
|
|
||||||
`search_debounce` controls how soon the search happens after the last character was entered in milliseconds. Entering new character resets the timer. Defaults to `300`. It can also have a special value `'submit'`, which triggers a search only after `ctrl+enter` was pressed.
|
`search_debounce` controls how soon the search happens after the last character was entered in milliseconds. Entering new character resets the timer. Defaults to `300`. It can also have a special value `'submit'`, which triggers a search only after `ctrl+enter` was pressed.
|
||||||
|
|
||||||
|
`search_submenus` makes uosc's internal search handler (when no `on_search` callback is defined) look into submenus as well, effectively flattening the menu for the duration of the search.
|
||||||
|
|
||||||
`search_suggestion` fills menu search with initial query string. Useful for example when you want to implement something like subtitle downloader, you'd set it to current file name.
|
`search_suggestion` fills menu search with initial query string. Useful for example when you want to implement something like subtitle downloader, you'd set it to current file name.
|
||||||
|
|
||||||
`item.icon` property accepts icon names. You can pick one from here: [Google Material Icons](https://fonts.google.com/icons)\
|
`item.icon` property accepts icon names. You can pick one from here: [Google Material Icons](https://fonts.google.com/icons)\
|
||||||
|
@@ -1,13 +1,13 @@
|
|||||||
local Element = require('elements/Element')
|
local Element = require('elements/Element')
|
||||||
|
|
||||||
-- Menu data structure accepted by `Menu:open(menu)`.
|
-- Menu data structure accepted by `Menu:open(menu)`.
|
||||||
---@alias MenuData {id?: string; type?: string; title?: string; hint?: string; palette?: boolean; keep_open?: boolean; separator?: boolean; items?: MenuDataItem[]; selected_index?: integer; on_search?: string|string[]|fun(search_text: string), search_debounce?: number|string; search_suggestion?: string}
|
---@alias MenuData {id?: string; type?: string; title?: string; hint?: string; palette?: boolean; keep_open?: boolean; separator?: boolean; items?: MenuDataItem[]; selected_index?: integer; on_search?: string|string[]|fun(search_text: string); search_debounce?: number|string; search_submenus?: boolean; search_suggestion?: string}
|
||||||
---@alias MenuDataItem MenuDataValue|MenuData
|
---@alias MenuDataItem MenuDataValue|MenuData
|
||||||
---@alias MenuDataValue {title?: string; hint?: string; icon?: string; value: any; bold?: boolean; italic?: boolean; muted?: boolean; active?: boolean; keep_open?: boolean; separator?: boolean; selectable?: boolean; align?: 'left'|'center'|'right'}
|
---@alias MenuDataValue {title?: string; hint?: string; icon?: string; value: any; bold?: boolean; italic?: boolean; muted?: boolean; active?: boolean; keep_open?: boolean; separator?: boolean; selectable?: boolean; align?: 'left'|'center'|'right'}
|
||||||
---@alias MenuOptions {mouse_nav?: boolean; on_open?: fun(); on_close?: fun(); on_back?: fun(); on_move_item?: fun(from_index: integer, to_index: integer, submenu_path: integer[]); on_delete_item?: fun(index: integer, submenu_path: integer[])}
|
---@alias MenuOptions {mouse_nav?: boolean; on_open?: fun(); on_close?: fun(); on_back?: fun(); on_move_item?: fun(from_index: integer, to_index: integer, submenu_path: integer[]); on_delete_item?: fun(index: integer, submenu_path: integer[])}
|
||||||
|
|
||||||
-- Internal data structure created from `Menu`.
|
-- Internal data structure created from `Menu`.
|
||||||
---@alias MenuStack {id?: string; type?: string; title?: string; hint?: string; palette?: boolean, selected_index?: number; keep_open?: boolean; separator?: boolean; items: MenuStackItem[]; on_search?: string|string[]|fun(search_text: string); search_debounce?: number|string; search_suggestion?: string; parent_menu?: MenuStack; submenu_path: integer[]; active?: boolean; width: number; height: number; top: number; scroll_y: number; scroll_height: number; title_width: number; hint_width: number; max_width: number; is_root?: boolean; fling?: Fling, search?: Search, ass_safe_title?: string}
|
---@alias MenuStack {id?: string; type?: string; title?: string; hint?: string; palette?: boolean, selected_index?: number; keep_open?: boolean; separator?: boolean; items: MenuStackItem[]; on_search?: string|string[]|fun(search_text: string); search_debounce?: number|string; search_submenus?: boolean; search_suggestion?: string; parent_menu?: MenuStack; submenu_path: integer[]; active?: boolean; width: number; height: number; top: number; scroll_y: number; scroll_height: number; title_width: number; hint_width: number; max_width: number; is_root?: boolean; fling?: Fling, search?: Search, ass_safe_title?: string}
|
||||||
---@alias MenuStackItem MenuStackValue|MenuStack
|
---@alias MenuStackItem MenuStackValue|MenuStack
|
||||||
---@alias MenuStackValue {title?: string; hint?: string; icon?: string; value: any; active?: boolean; bold?: boolean; italic?: boolean; muted?: boolean; keep_open?: boolean; separator?: boolean; selectable?: boolean; align?: 'left'|'center'|'right'; title_width: number; hint_width: number}
|
---@alias MenuStackValue {title?: string; hint?: string; icon?: string; value: any; active?: boolean; bold?: boolean; italic?: boolean; muted?: boolean; keep_open?: boolean; separator?: boolean; selectable?: boolean; align?: 'left'|'center'|'right'; title_width: number; hint_width: number}
|
||||||
---@alias Fling {y: number, distance: number, time: number, easing: fun(x: number), duration: number, update_cursor?: boolean}
|
---@alias Fling {y: number, distance: number, time: number, easing: fun(x: number), duration: number, update_cursor?: boolean}
|
||||||
@@ -144,7 +144,9 @@ function Menu:update(data)
|
|||||||
local new_by_id = {}
|
local new_by_id = {}
|
||||||
local menus_to_serialize = {{new_root, data}}
|
local menus_to_serialize = {{new_root, data}}
|
||||||
local old_current_id = self.current and self.current.id
|
local old_current_id = self.current and self.current.id
|
||||||
local menu_props_to_copy = {'title', 'hint', 'keep_open', 'palette', 'on_search', 'search_suggestion'}
|
local menu_props_to_copy = {
|
||||||
|
'title', 'hint', 'keep_open', 'palette', 'on_search', 'search_submenus', 'search_suggestion'
|
||||||
|
}
|
||||||
local item_props_to_copy = itable_join(menu_props_to_copy, {
|
local item_props_to_copy = itable_join(menu_props_to_copy, {
|
||||||
'icon', 'active', 'bold', 'italic', 'muted', 'value', 'separator', 'selectable', 'align'
|
'icon', 'active', 'bold', 'italic', 'muted', 'value', 'separator', 'selectable', 'align'
|
||||||
})
|
})
|
||||||
@@ -670,14 +672,7 @@ function Menu:search_internal(menu)
|
|||||||
-- Reset menu state to what it was before search
|
-- Reset menu state to what it was before search
|
||||||
for key, value in pairs(menu.search.source) do menu[key] = value end
|
for key, value in pairs(menu.search.source) do menu[key] = value end
|
||||||
else
|
else
|
||||||
menu.items = itable_filter(menu.search.source.items, function(item)
|
menu.items = search_items(menu.search.source.items, query, menu.search_submenus)
|
||||||
if item.selectable == false then return false end
|
|
||||||
local title = item.title and item.title:lower()
|
|
||||||
local hint = item.hint and item.hint:lower()
|
|
||||||
return title and title:find(query, 1, true) or hint and hint:find(query, 1, true) or
|
|
||||||
title and table.concat(initials(title)):find(query, 1, true) or
|
|
||||||
hint and table.concat(initials(hint)):find(query, 1, true)
|
|
||||||
end)
|
|
||||||
-- Select 1st item in search results
|
-- Select 1st item in search results
|
||||||
menu.scroll_y = 0
|
menu.scroll_y = 0
|
||||||
self:select_index(1, menu)
|
self:select_index(1, menu)
|
||||||
@@ -685,6 +680,34 @@ function Menu:search_internal(menu)
|
|||||||
self:update_content_dimensions()
|
self:update_content_dimensions()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@param items MenuStackItem[]
|
||||||
|
---@param query string
|
||||||
|
---@param recursive? boolean
|
||||||
|
---@param prefix? string
|
||||||
|
---@return MenuStackItem[]
|
||||||
|
function search_items(items, query, recursive, prefix)
|
||||||
|
local result = {}
|
||||||
|
for _, item in ipairs(items) do
|
||||||
|
if item.selectable ~= false then
|
||||||
|
local prefixed_title = prefix and prefix .. ' / ' .. (item.title or '') or item.title
|
||||||
|
if item.items and recursive then
|
||||||
|
itable_append(result, search_items(item.items, query, recursive, prefixed_title))
|
||||||
|
else
|
||||||
|
local title = item.title and item.title:lower()
|
||||||
|
local hint = item.hint and item.hint:lower()
|
||||||
|
if title and title:find(query, 1, true) or hint and hint:find(query, 1, true) or
|
||||||
|
title and table.concat(initials(title)):find(query, 1, true) or
|
||||||
|
hint and table.concat(initials(hint)):find(query, 1, true) then
|
||||||
|
item = table_shallow_copy(item)
|
||||||
|
item.title = prefixed_title
|
||||||
|
result[#result + 1] = item
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
---@param menu? MenuStack
|
---@param menu? MenuStack
|
||||||
function Menu:search_submit(menu)
|
function Menu:search_submit(menu)
|
||||||
menu = menu or self.current
|
menu = menu or self.current
|
||||||
|
@@ -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}, opts) end
|
else open_command_menu({type = 'menu', items = config.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[])}
|
||||||
|
Reference in New Issue
Block a user