Skip to content

Commit

Permalink
feat: prevent cursor movement from deselecting menu items
Browse files Browse the repository at this point in the history
Also prevents small cursor movements after inactivity from selecting items as well.

ref #964
  • Loading branch information
tomasklaen committed Sep 2, 2024
1 parent f6159ad commit 5790169
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 30 deletions.
49 changes: 20 additions & 29 deletions src/uosc/elements/Menu.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1223,8 +1223,7 @@ function Menu:render()
bx = bx,
by = by + self.padding,
}
local blur_selected_index = is_current and self.mouse_nav
local blur_action_index = is_current and self.mouse_nav
local cursor_is_moving = self.mouse_nav and cursor.distance > 10

-- Background
ass:rect(menu_rect.ax, menu_rect.ay, menu_rect.bx, menu_rect.by, {
Expand Down Expand Up @@ -1341,6 +1340,7 @@ function Menu:render()
local title_clip_bx = content_bx

-- Actions
local item_can_blur_action_index = false
local actions_rect
if is_selected and actions and #actions > 0 and not item.items then
local place = item.actions_place or menu.item_actions_place
Expand Down Expand Up @@ -1383,14 +1383,17 @@ function Menu:render()
})

-- Select action on cursor hover
if self.mouse_nav and get_point_to_rectangle_proximity(cursor, rect) == 0 then
cursor:zone('primary_click', rect, self:create_action(function(shortcut)
self:activate_selected_item(shortcut)
end))
blur_action_index = false
if not is_active then
menu.action_index = action_index
request_render()
if cursor_is_moving then
item_can_blur_action_index = menu.action_index ~= nil
if get_point_to_rectangle_proximity(cursor, rect) == 0 then
cursor:zone('primary_click', rect, self:create_action(function(shortcut)
self:activate_selected_item(shortcut)
end))
item_can_blur_action_index = false
if not is_active then
menu.action_index = action_index
request_render()
end
end
end
end
Expand Down Expand Up @@ -1478,17 +1481,13 @@ function Menu:render()
end

-- Select hovered item
if is_current and self.mouse_nav and item.selectable ~= false then
if submenu_rect and cursor:direction_to_rectangle_distance(submenu_rect)
or actions_rect and actions_rect.is_outside and cursor:direction_to_rectangle_distance(actions_rect) then
blur_selected_index = false
else
if submenu_is_hovered or get_point_to_rectangle_proximity(cursor, item_rect_hitbox) == 0 then
blur_selected_index = false
menu.selected_index = index
if not is_selected then request_render() end
end
end
if is_current and cursor_is_moving and item.selectable ~= false
-- Do not select items if cursor is moving towards a submenu
and (not submenu_rect or not cursor:direction_to_rectangle_distance(submenu_rect))
and (submenu_is_hovered or get_point_to_rectangle_proximity(cursor, item_rect_hitbox) == 0) then
menu.selected_index = index
if not is_selected or item_can_blur_action_index and menu.action_index then request_render() end
if item_can_blur_action_index then menu.action_index = nil end
end
end

Expand Down Expand Up @@ -1599,14 +1598,6 @@ function Menu:render()
ass:rect(sax, thumb_y, sbx, thumb_y + thumb_height, {color = fg, opacity = menu_opacity * 0.8})
end

-- We are in mouse nav and cursor isn't hovering any item
if blur_selected_index then
menu.selected_index = nil
end
if blur_action_index then
menu.action_index = nil
end

return menu_rect
end

Expand Down
19 changes: 18 additions & 1 deletion src/uosc/lib/cursor.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ local cursor = {
x = math.huge,
y = math.huge,
hidden = true,
distance = 0, -- Distance traveled during current move. Reset by `cursor.distance_reset_timer`.
hover_raw = false,
-- Event handlers that are only fired on zones defined during render loop.
---@type {event: string, hitbox: Hitbox; handler: CursorEventHandler}[]
Expand Down Expand Up @@ -70,6 +71,12 @@ mp.observe_property('cursor-autohide', 'number', function(_, val)
cursor.autohide_timer.timeout = (val or 1000) / 1000
end)

cursor.distance_reset_timer = mp.add_timeout(0.2, function()
cursor.distance = 0
request_render()
end)
cursor.distance_reset_timer:kill()

-- Called at the beginning of each render
function cursor:clear_zones()
itable_clear(self.zones)
Expand Down Expand Up @@ -259,7 +266,7 @@ function cursor:_find_history_sample()
return self.history:tail()
end

-- Returns a table with current velocities in in pixels per second.
-- Returns the current velocity vector in pixels per second.
---@return Point
function cursor:get_velocity()
local snap = self:_find_history_sample()
Expand Down Expand Up @@ -323,6 +330,16 @@ function cursor:move(x, y)
Elements:trigger('global_mouse_enter')
end

-- Update current move travel distance
-- `mp.get_time() - last.time < 0.5` check is there to ignore first event after long inactivity to
-- filter out big jumps due to window being repositioned/rescaled (e.g. opening a different file).
local last = self.last_event.move
if last and last.x < math.huge and last.y < math.huge and mp.get_time() - last.time < 0.5 then
self.distance = self.distance + get_point_to_point_proximity(cursor, last)
cursor.distance_reset_timer:kill()
cursor.distance_reset_timer:resume()
end

Elements:update_proximities()
-- Update history
self.history:insert({x = self.x, y = self.y, time = mp.get_time()})
Expand Down

0 comments on commit 5790169

Please sign in to comment.