Skip to content

Commit

Permalink
fix: cursor events not exposing modifiers
Browse files Browse the repository at this point in the history
  • Loading branch information
tomasklaen committed Aug 30, 2024
1 parent 1fc68f2 commit 06f6eb7
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 54 deletions.
15 changes: 9 additions & 6 deletions src/uosc/elements/Menu.lua
Original file line number Diff line number Diff line change
Expand Up @@ -683,9 +683,10 @@ function Menu:handle_cursor_down()
end
end

function Menu:handle_cursor_up()
---@param shortcut? Shortcut
function Menu:handle_cursor_up(shortcut)
if self.proximity_raw == 0 and self.drag_last_y and not self.is_dragging then
self:activate_selected_item()
self:activate_selected_item(shortcut)
end
if self.is_dragging then
local distance = cursor:get_velocity().y / -3
Expand Down Expand Up @@ -1186,7 +1187,7 @@ function Menu:render()

local display_rect = {ax = 0, ay = 0, bx = display.width, by = display.height}
cursor:zone('primary_down', display_rect, self:create_action(function() self:handle_cursor_down() end))
cursor:zone('primary_up', display_rect, self:create_action(function() self:handle_cursor_up() end))
cursor:zone('primary_up', display_rect, self:create_action(function(shortcut) self:handle_cursor_up(shortcut) end))
cursor:zone('wheel_down', self, function() self:handle_wheel_down() end)
cursor:zone('wheel_up', self, function() self:handle_wheel_up() end)

Expand Down Expand Up @@ -1239,7 +1240,9 @@ function Menu:render()
local submenu_is_hovered = false
if current_item and current_item.items then
submenu_rect = draw_menu(current_item --[[@as MenuStack]], menu_rect.bx + self.gap, 1)
cursor:zone('primary_down', submenu_rect, self:create_action(function() self:activate_selected_item() end))
cursor:zone('primary_down', submenu_rect, self:create_action(function(shortcut)
self:activate_selected_item(shortcut)
end))
end

for index = start_index, end_index, 1 do
Expand Down Expand Up @@ -1349,8 +1352,8 @@ 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_down', rect, self:create_action(function()
self:activate_selected_item()
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
Expand Down
58 changes: 37 additions & 21 deletions src/uosc/lib/cursor.lua
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
---@alias CursorEventHandler fun(shortcut: Shortcut)

local cursor = {
x = math.huge,
y = math.huge,
hidden = true,
hover_raw = false,
-- Event handlers that are only fired on zones defined during render loop.
---@type {event: string, hitbox: Hitbox; handler: fun(...)}[]
---@type {event: string, hitbox: Hitbox; handler: CursorEventHandler}[]
zones = {},
handlers = {
primary_down = {},
Expand Down Expand Up @@ -105,14 +107,15 @@ end
-- - `move` event zones are ignored due to it being a high frequency event that is currently not needed as a zone.
---@param event string
---@param hitbox Hitbox
---@param callback fun(...)
---@param callback CursorEventHandler
function cursor:zone(event, hitbox, callback)
self.zones[#self.zones + 1] = {event = event, hitbox = hitbox, handler = callback}
end

-- Binds a permanent cursor event handler active until manually unbound using `cursor:off()`.
-- `_click` events are not available as permanent global events, only as zones.
---@param event string
---@param callback CursorEventHandler
---@return fun() disposer Unbinds the event.
function cursor:on(event, callback)
if self.handlers[event] and not itable_index_of(self.handlers[event], callback) then
Expand Down Expand Up @@ -146,16 +149,17 @@ end

-- Trigger the event.
---@param event string
function cursor:trigger(event, ...)
---@param shortcut? Shortcut
function cursor:trigger(event, shortcut)
local forward = true

-- Call raw event handlers.
local zone = self:find_zone(event)
local callbacks = self.handlers[event]
if zone or #callbacks > 0 then
forward = false
if zone then zone.handler(...) end
for _, callback in ipairs(callbacks) do callback(...) end
if zone and shortcut then zone.handler(shortcut) end
for _, callback in ipairs(callbacks) do callback(shortcut) end
end

-- Call compound/parent (click) event handlers if both start and end events are within `parent_zone.hitbox`.
Expand All @@ -166,8 +170,8 @@ function cursor:trigger(event, ...)
forward = false -- Canceled here so we don't forward down events if they can lead to a click.
if parent.is_end then
local last_start_event = self.last_event[parent.start_event]
if last_start_event and point_collides_with(last_start_event, parent_zone.hitbox) then
parent_zone.handler(...)
if last_start_event and point_collides_with(last_start_event, parent_zone.hitbox) and shortcut then
parent_zone.handler(create_shortcut('primary_click', shortcut.modifiers))
end
end
end
Expand Down Expand Up @@ -367,10 +371,13 @@ function cursor:direction_to_rectangle_distance(rect)
return get_ray_to_rectangle_distance(self.x, self.y, end_x, end_y, rect)
end

function cursor:create_handler(event, cb)
return function(...)
call_maybe(cb, ...)
self:trigger(event, ...)
---@param event string
---@param shortcut Shortcut
---@param cb? fun(shortcut: Shortcut)
function cursor:create_handler(event, shortcut, cb)
return function()
if cb then cb(shortcut) end
self:trigger(event, shortcut)
end
end

Expand All @@ -387,24 +394,33 @@ end
mp.observe_property('mouse-pos', 'native', handle_mouse_pos)

-- Key binding groups
mp.set_key_bindings({
{
'mbtn_left',
cursor:create_handler('primary_up'),
cursor:create_handler('primary_down', function(...)
local modifiers = {nil, 'alt', 'alt+ctrl', 'alt+shift', 'alt+ctrl+shift', 'ctrl', 'ctrl+shift', 'shift'}
local primary_bindings = {}
for i = 1, #modifiers do
local mods = modifiers[i]
local mp_name = (mods and mods .. '+' or '') .. 'mbtn_left'
primary_bindings[#primary_bindings + 1] = {
mp_name,
cursor:create_handler('primary_up', create_shortcut('primary_up', mods)),
cursor:create_handler('primary_down', create_shortcut('primary_down', mods), function(...)
handle_mouse_pos(nil, mp.get_property_native('mouse-pos'))
end),
},
}, 'mbtn_left', 'force')
}
end
mp.set_key_bindings(primary_bindings, 'mbtn_left', 'force')
mp.set_key_bindings({
{'mbtn_left_dbl', 'ignore'},
}, 'mbtn_left_dbl', 'force')
mp.set_key_bindings({
{'mbtn_right', cursor:create_handler('secondary_up'), cursor:create_handler('secondary_down')},
{
'mbtn_right',
cursor:create_handler('secondary_up', create_shortcut('secondary_up')),
cursor:create_handler('secondary_down', create_shortcut('secondary_down')),
},
}, 'mbtn_right', 'force')
mp.set_key_bindings({
{'wheel_up', cursor:create_handler('wheel_up')},
{'wheel_down', cursor:create_handler('wheel_down')},
{'wheel_up', cursor:create_handler('wheel_up', create_shortcut('wheel_up'))},
{'wheel_down', cursor:create_handler('wheel_down', create_shortcut('wheel_down'))},
}, 'wheel', 'force')

return cursor
22 changes: 22 additions & 0 deletions src/uosc/lib/std.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
--[[ Stateless utilities missing in lua standard library ]]

---@alias Shortcut {id: string; key: string; modifiers?: string; alt: boolean; ctrl: boolean; shift: boolean}

---@param number number
function round(number) return math.floor(number + 0.5) end

Expand Down Expand Up @@ -262,6 +264,26 @@ function serialize_key_value_list(input, value_sanitizer)
return result
end

---@param key string
---@param modifiers? string
---@return Shortcut
function create_shortcut(key, modifiers)
key = key:lower()

local id_parts, modifiers_set
if modifiers then
id_parts = split(modifiers:lower(), '+')
table.sort(id_parts, function(a, b) return a < b end)
modifiers_set = create_set(id_parts)
modifiers = table.concat(id_parts, '+')
else
id_parts, modifiers, modifiers_set = {}, nil, {}
end
id_parts[#id_parts + 1] = key

return table_assign({id = table.concat(id_parts, '+'), key = key, modifiers = modifiers}, modifiers_set)
end

--[[ EASING FUNCTIONS ]]

function ease_out_quart(x) return 1 - ((1 - x) ^ 4) end
Expand Down
26 changes: 0 additions & 26 deletions src/uosc/lib/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
---@alias Rect {ax: number, ay: number, bx: number, by: number, window_drag?: boolean}
---@alias Circle {point: Point, r: number, window_drag?: boolean}
---@alias Hitbox Rect|Circle
---@alias Shortcut {id: string; key: string; modifiers: string; alt: boolean; ctrl: boolean; shift: boolean}
---@alias ComplexBindingInfo {event: 'down' | 'repeat' | 'up' | 'press'; is_mouse: boolean; canceled: boolean; key_name?: string; key_text?: string;}

--- In place sorting of filenames
Expand Down Expand Up @@ -225,11 +224,6 @@ function get_ray_to_rectangle_distance(ax, ay, bx, by, rect)
return closest
end

-- Call function with args if it exists
function call_maybe(fn, ...)
if type(fn) == 'function' then fn(...) end
end

-- Extracts the properties used by property expansion of that string.
---@param str string
---@param res { [string] : boolean } | nil
Expand Down Expand Up @@ -400,26 +394,6 @@ function has_any_extension(path, extensions)
return false
end

---@param key string
---@param modifiers? string
---@return Shortcut
function create_shortcut(key, modifiers)
key = key:lower()

local id_parts, modifiers_set
if modifiers then
id_parts = split(modifiers:lower(), '+')
table.sort(id_parts, function(a, b) return a < b end)
modifiers_set = create_set(id_parts)
modifiers = table.concat(id_parts, '+')
else
id_parts, modifiers, modifiers_set = {}, '', {}
end
id_parts[#id_parts + 1] = key

return table_assign({id = table.concat(id_parts, '+'), key = key, modifiers = modifiers}, modifiers_set)
end

-- Executes mp command defined as a string or an itable, or does nothing if command is any other value.
-- Returns boolean specifying if command was executed or not.
---@param command string | string[] | nil | any
Expand Down
3 changes: 2 additions & 1 deletion src/uosc/main.lua
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,8 @@ end

function set_state(name, value)
state[name] = value
call_maybe(state['on_' .. name], value)
local state_event = state['on_' .. name]
if state_event then state_event(value) end
Elements:trigger('prop_' .. name, value)
end

Expand Down

0 comments on commit 06f6eb7

Please sign in to comment.