Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improved escape and backspace behavior in menus #719

Merged
merged 1 commit into from
Oct 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ These bindings are active when any **uosc** menu is open (main menu, playlist, l
- `pgup`, `pgdwn`, `home`, `end` - Self explanatory.
- `ctrl+f` or `\` - In case `menu_type_to_search` is disabled, these two trigger the menu search instead.
- `ctrl+enter` - Submits a search in menus without instant search.
- `ctrl+backspace` - Delete search query by word.
- `shift+backspace` - Clear search query.
- `ctrl+up/down` - Move selected item in menus that support it (playlist).
- `del` - Delete selected item in menus that support it (playlist).
- `shift+enter`, `shift+right` - Activate item without closing the menu.
Expand Down
63 changes: 39 additions & 24 deletions scripts/uosc/elements/Menu.lua
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ function Menu:init(data, callback, opts)
self.opts = opts or {}
self.offset_x = 0 -- Used for submenu transition animation.
self.mouse_nav = self.opts.mouse_nav -- Stops pre-selecting items
---@type Modifiers|nil
self.modifiers = nil
---@type Modifiers
self.modifiers = {}
self.item_height = nil
self.min_width = nil
self.item_spacing = 1
Expand Down Expand Up @@ -813,13 +813,26 @@ function Menu:search_query_update(query, menu)
end

---@param event? string
function Menu:search_backspace(event)
---@param word_mode? boolean Delete by words.
function Menu:search_backspace(event, word_mode)
local pos, old_query, is_palette = #self.current.search.query, self.current.search.query, self.current.palette
-- The while loop is for skipping utf8 continuation bytes
while pos > 1 and old_query:byte(pos) >= 0x80 and old_query:byte(pos) <= 0xbf do
if word_mode then
local word_pat, other_pat = '[^%c%s%p]+$', '[%c%s%p]+$'
local init_pat = old_query:sub(#old_query):match(word_pat) and word_pat or other_pat
-- First we match all same type consecutive chars at the end
local tail = old_query:match(init_pat)
-- If there's only one, we extend the tail with opposite type chars
if tail and #tail == 1 then
tail = tail .. old_query:sub(1, #old_query - #tail):match(init_pat == word_pat and other_pat or word_pat)
end
pos = pos - #tail
Comment on lines +820 to +828
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have expected ctrl+backspace to always delete back to the beginning of the last word. From a quick test at least KDE things seem to consistently behave that way.

Suggested change
local word_pat, other_pat = '[^%c%s%p]+$', '[%c%s%p]+$'
local init_pat = old_query:sub(#old_query):match(word_pat) and word_pat or other_pat
-- First we match all same type consecutive chars at the end
local tail = old_query:match(init_pat)
-- If there's only one, we extend the tail with opposite type chars
if tail and #tail == 1 then
tail = tail .. old_query:sub(1, #old_query - #tail):match(init_pat == word_pat and other_pat or word_pat)
end
pos = pos - #tail
pos = old_query:find('[^%c%s%p]*[%c%s%p]*$') - 1

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently that's not the case for vscode, it behaves like your original code. It doesn't really matter i guess.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I've tried making it like vscode, which is how I expected it to work from the beginning.

For example, if I have a string like:

abc.--

I'd want ctrl+backspace to end before c, not delete the whole string.

vscode has a bit more conditional behaviors, but I didn't want to make it that complex.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have expected ctrl+backspace to always delete back to the beginning of the last word.

This behavior is iconsistent across applications, unfortunately. Sublime Text, for example, deletes until the next boundary between word characters, punctuation, or whitespace.

else
-- The while loop is for skipping utf8 continuation bytes
while pos > 1 and old_query:byte(pos) >= 0x80 and old_query:byte(pos) <= 0xbf do
pos = pos - 1
end
pos = pos - 1
end
pos = pos - 1
local new_query = old_query:sub(1, pos)
if new_query ~= old_query and (is_palette or not self.type_to_search or pos > 0) then
self:search_query_update(new_query)
Expand Down Expand Up @@ -888,26 +901,24 @@ function Menu:search_start(menu)
self:update_dimensions()
end

function Menu:key_esc()
if self.current.search then
if self.current.palette then
if self.current.search.query == '' then
self:close()
else
self:search_query_update('')
end
else
self:search_stop()
end
---@param menu? MenuStack
function Menu:search_clear_query(menu)
menu = menu or self.current
if not self.current.palette and self.type_to_search then
self:search_stop(menu)
else
self:close()
self:search_query_update('', menu)
end
end

function Menu:key_bs(info)
if info.event ~= 'up' then
if self.current.search then
self:search_backspace(info.event)
if self.modifiers.shift then
self:search_clear_query()
else
self:search_backspace(info.event, self.modifiers.ctrl)
end
elseif info.event ~= 'repeat' then
self:back()
end
Expand Down Expand Up @@ -989,6 +1000,10 @@ function Menu:enable_key_bindings()
self:add_key_binding('alt+mbtn_left', 'menu-select4', self:create_modified_mbtn_left_handler({alt = true}))
self:add_key_binding('mbtn_back', 'menu-back-alt3', self:create_key_action('back'))
self:add_key_binding('bs', 'menu-back-alt4', self:create_key_action('key_bs'), {repeatable = true, complex = true})
self:add_key_binding('shift+bs', 'menu-clear-query', self:create_key_action('key_bs', {shift = true}),
{repeatable = true, complex = true})
self:add_key_binding('ctrl+bs', 'menu-delete-word', self:create_key_action('key_bs', {ctrl = true}),
{repeatable = true, complex = true})
self:add_key_binding('enter', 'menu-select-alt3', self:create_key_action('open_selected_item_preselect'))
self:add_key_binding('kp_enter', 'menu-select-alt4', self:create_key_action('open_selected_item_preselect'))
self:add_key_binding('ctrl+enter', 'menu-select-ctrl1', self:create_key_action('key_ctrl_enter', {ctrl = true}))
Expand All @@ -1002,7 +1017,7 @@ function Menu:enable_key_bindings()
self:create_key_action('open_selected_item_soft', {shift = true}))
self:add_key_binding('shift+kp_enter', 'menu-select-alt6',
self:create_key_action('open_selected_item_soft', {shift = true}))
self:add_key_binding('esc', 'menu-close', self:create_key_action('key_esc'))
self:add_key_binding('esc', 'menu-close', self:create_key_action('close'))
self:add_key_binding('pgup', 'menu-page-up', self:create_key_action('on_pgup'), 'repeatable')
self:add_key_binding('pgdwn', 'menu-page-down', self:create_key_action('on_pgdwn'), 'repeatable')
self:add_key_binding('home', 'menu-home', self:create_key_action('on_home'))
Expand Down Expand Up @@ -1037,10 +1052,10 @@ end
function Menu:create_modified_mbtn_left_handler(modifiers)
return self:create_action(function()
self.mouse_nav = true
self.modifiers = modifiers
self.modifiers = modifiers or {}
self:handle_cursor_down()
self:handle_cursor_up()
self.modifiers = nil
self.modifiers = {}
end)
end

Expand All @@ -1049,9 +1064,9 @@ end
function Menu:create_key_action(name, modifiers)
return self:create_action(function(...)
self.mouse_nav = false
self.modifiers = modifiers
self.modifiers = modifiers or {}
self:maybe(name, ...)
self.modifiers = nil
self.modifiers = {}
end)
end

Expand Down