From f635df18f718ceb818e4a13c32c0a463cd89071a Mon Sep 17 00:00:00 2001 From: Tomas Klaen <47283320+tomasklaen@users.noreply.github.com> Date: Wed, 29 Mar 2023 09:08:18 +0200 Subject: [PATCH] refactor: cursor events consolidated into one interface (#483) `mbtn_left` and `wheel` events are now only fired on a `cursor.on_{event}` object, which resets at the beginning of each frame, so elements need to bind these listeners in their render functions. These properties are overwritable which allows elements to take over cursor event handling from their parents if necessary. --- scripts/uosc.lua | 43 +++++++++++- scripts/uosc_shared/elements/Button.lua | 10 +-- scripts/uosc_shared/elements/Elements.lua | 45 +------------ scripts/uosc_shared/elements/Menu.lua | 19 ++++-- scripts/uosc_shared/elements/Speed.lua | 33 ++++++---- scripts/uosc_shared/elements/Timeline.lua | 79 +++++++++++------------ scripts/uosc_shared/elements/TopBar.lua | 13 ++-- scripts/uosc_shared/elements/Volume.lua | 27 +++++--- scripts/uosc_shared/lib/utils.lua | 9 +++ 9 files changed, 155 insertions(+), 123 deletions(-) diff --git a/scripts/uosc.lua b/scripts/uosc.lua index 894067b..8c64443 100644 --- a/scripts/uosc.lua +++ b/scripts/uosc.lua @@ -296,7 +296,31 @@ end --[[ STATE ]] display = {width = 1280, height = 720, scale_x = 1, scale_y = 1, initialized = false} -cursor = {hidden = true, hover_raw = false, x = 0, y = 0} +cursor = { + x = 0, + y = 0, + hidden = true, + hover_raw = false, + -- Event handlers that are only fired on cursor, bound during render loop. Guidelines: + -- - element activations (clicks) go to `mbtn_left_down` handler + -- - `mbtn_button_up` is only for clearing dragging/swiping + on_primary_down = nil, + on_primary_up = nil, + on_wheel_down = nil, + on_wheel_up = nil, + -- Called at the beginning of each render + reset_handlers = function() + cursor.on_primary_down, cursor.on_primary_up = nil, nil + cursor.on_wheel_down, cursor.on_wheel_up = nil, nil + end, + -- Enables pointer key group captures needed by handlers (called at the end of each render) + decide_keybinds = function() + local mbtn_left_decision = (cursor.on_primary_down or cursor.on_primary_up) and 'enable' or 'disable' + local wheel_decision = (cursor.on_wheel_down or cursor.on_wheel_up) and 'enable' or 'disable' + mp[mbtn_left_decision .. '_key_bindings']('mbtn_left') + mp[wheel_decision .. '_key_bindings']('wheel') + end +} state = { os = (function() if os.getenv('windir') ~= nil then return 'windows' end @@ -771,6 +795,23 @@ mp.observe_property('core-idle', 'native', create_state_setter('core_idle')) --[[ KEY BINDS ]] +-- Pointer related binding groups +mp.set_key_bindings({ + { + 'mbtn_left', + function(...) call_maybe(cursor.on_primary_up, ...) end, + function(...) + update_mouse_pos(nil, mp.get_property_native('mouse-pos')) + call_maybe(cursor.on_primary_down, ...) + end, + }, + {'mbtn_left_dbl', 'ignore'}, +}, 'mbtn_left', 'force') +mp.set_key_bindings({ + {'wheel_up', function(...) call_maybe(cursor.on_wheel_up, ...) end}, + {'wheel_down', function(...) call_maybe(cursor.on_wheel_down, ...) end}, +}, 'wheel', 'force') + -- Adds a key binding that respects rerouting set by `key_binding_overwrites` table. ---@param name string ---@param callback fun(event: table) diff --git a/scripts/uosc_shared/elements/Button.lua b/scripts/uosc_shared/elements/Button.lua index 8d0f725..e57d614 100644 --- a/scripts/uosc_shared/elements/Button.lua +++ b/scripts/uosc_shared/elements/Button.lua @@ -23,19 +23,20 @@ function Button:init(id, props) end function Button:on_coordinates() self.font_size = round((self.by - self.ay) * 0.7) end -function Button:on_mbtn_left_down() - -- Don't accept clicks while hidden. - if self:get_visibility() <= 0 then return end +function Button:handle_cursor_down() -- We delay the callback to next tick, otherwise we are risking race -- conditions as we are in the middle of event dispatching. -- For example, handler might add a menu to the end of the element stack, and that - -- than picks up this click even we are in right now, and instantly closes itself. + -- than picks up this click event we are in right now, and instantly closes itself. mp.add_timeout(0.01, self.on_click) end function Button:render() local visibility = self:get_visibility() if visibility <= 0 then return end + if self.proximity_raw == 0 then + cursor.on_primary_down = function() self:handle_cursor_down() end + end local ass = assdraw.ass_new() local is_hover = self.proximity_raw == 0 @@ -54,7 +55,6 @@ function Button:render() -- Tooltip on hover if is_hover and self.tooltip then ass:tooltip(self, self.tooltip) end - -- Badge local icon_clip if self.badge then diff --git a/scripts/uosc_shared/elements/Elements.lua b/scripts/uosc_shared/elements/Elements.lua index f6fba01..186b9e9 100644 --- a/scripts/uosc_shared/elements/Elements.lua +++ b/scripts/uosc_shared/elements/Elements.lua @@ -29,8 +29,6 @@ function Elements:remove(idOrElement) end function Elements:update_proximities() - local capture_mbtn_left = false - local capture_wheel = false local menu_only = Elements.menu ~= nil local mouse_leave_elements = {} local mouse_enter_elements = {} @@ -42,26 +40,13 @@ function Elements:update_proximities() -- If menu is open, all other elements have to be disabled if menu_only then - if element.ignores_menu then - capture_mbtn_left = true - capture_wheel = true - element:update_proximity() - else - element:reset_proximity() - end + if element.ignores_menu then element:update_proximity() + else element:reset_proximity() end else element:update_proximity() end - -- Element has global forced key listeners - if element.on_global_mbtn_left_down then capture_mbtn_left = true end - if element.on_global_wheel_up or element.on_global_wheel_down then capture_wheel = true end - if element.proximity_raw == 0 then - -- Element has local forced key listeners - if element.on_mbtn_left_down then capture_mbtn_left = true end - if element.on_wheel_up or element.on_wheel_up then capture_wheel = true end - -- Mouse entered element area if previous_proximity_raw ~= 0 then mouse_enter_elements[#mouse_enter_elements + 1] = element @@ -75,10 +60,6 @@ function Elements:update_proximities() end end - -- Enable key group captures requested by elements - mp[capture_mbtn_left and 'enable_key_bindings' or 'disable_key_bindings']('mbtn_left') - mp[capture_wheel and 'enable_key_bindings' or 'disable_key_bindings']('wheel') - -- Trigger `mouse_leave` and `mouse_enter` events for _, element in ipairs(mouse_leave_elements) do element:trigger('mouse_leave') end for _, element in ipairs(mouse_enter_elements) do element:trigger('mouse_enter') end @@ -142,26 +123,4 @@ end function Elements:has(id) return self[id] ~= nil end function Elements:ipairs() return ipairs(self.itable) end ----@param name string Event name. -function Elements:create_proximity_dispatcher(name) - return function(...) self:proximity_trigger(name, ...) end -end - -mp.set_key_bindings({ - { - 'mbtn_left', - Elements:create_proximity_dispatcher('mbtn_left_up'), - function(...) - update_mouse_pos(nil, mp.get_property_native('mouse-pos')) - Elements:proximity_trigger('mbtn_left_down', ...) - end, - }, - {'mbtn_left_dbl', 'ignore'}, -}, 'mbtn_left', 'force') - -mp.set_key_bindings({ - {'wheel_up', Elements:create_proximity_dispatcher('wheel_up')}, - {'wheel_down', Elements:create_proximity_dispatcher('wheel_down')}, -}, 'wheel', 'force') - return Elements diff --git a/scripts/uosc_shared/elements/Menu.lua b/scripts/uosc_shared/elements/Menu.lua index 82f20dd..fee7b8d 100644 --- a/scripts/uosc_shared/elements/Menu.lua +++ b/scripts/uosc_shared/elements/Menu.lua @@ -518,7 +518,7 @@ end function Menu:on_display() self:update_dimensions() end function Menu:on_prop_fullormaxed() self:update_content_dimensions() end -function Menu:on_global_mbtn_left_down() +function Menu:handle_cursor_down() if self.proximity_raw == 0 then self.drag_data = {{y = cursor.y, time = mp.get_time()}} self.current.fling = nil @@ -538,7 +538,7 @@ function Menu:fling_distance() return #self.drag_data < 2 and 0 or ((first.y - last.y) / ((first.time - last.time) / 0.03)) * 10 end -function Menu:on_global_mbtn_left_up() +function Menu:handle_cursor_up() if self.proximity_raw == 0 and self.drag_data and not self.is_dragging then self:select_item_below_cursor() self:open_selected_item({preselect_submenu_item = false, keep_open = self.modifiers and self.modifiers.shift}) @@ -570,8 +570,8 @@ function Menu:on_global_mouse_move() request_render() end -function Menu:on_wheel_up() self:scroll_by(self.scroll_step * -3, nil, {update_cursor = true}) end -function Menu:on_wheel_down() self:scroll_by(self.scroll_step * 3, nil, {update_cursor = true}) end +function Menu:handle_wheel_up() self:scroll_by(self.scroll_step * -3, nil, {update_cursor = true}) end +function Menu:handle_wheel_down() self:scroll_by(self.scroll_step * 3, nil, {update_cursor = true}) end function Menu:on_pgup() local menu = self.current @@ -647,8 +647,8 @@ function Menu:create_modified_mbtn_left_handler(modifiers) return function() self.mouse_nav = true self.modifiers = modifiers - self:on_global_mbtn_left_down() - self:on_global_mbtn_left_up() + self:handle_cursor_down() + self:handle_cursor_up() self.modifiers = nil end end @@ -677,6 +677,13 @@ function Menu:render() end if update_cursor then self:select_item_below_cursor() end + cursor.on_primary_down = function() self:handle_cursor_down() end + cursor.on_primary_up = function() self:handle_cursor_up() end + if self.proximity_raw == 0 then + cursor.on_wheel_down = function() self:handle_wheel_down() end + cursor.on_wheel_up = function() self:handle_wheel_up() end + end + local ass = assdraw.ass_new() local opacity = options.menu_opacity * self.opacity local spacing = self.item_padding diff --git a/scripts/uosc_shared/elements/Speed.lua b/scripts/uosc_shared/elements/Speed.lua index 9681ccf..6ea5097 100644 --- a/scripts/uosc_shared/elements/Speed.lua +++ b/scripts/uosc_shared/elements/Speed.lua @@ -44,9 +44,7 @@ function Speed:speed_step(speed, up) end end -function Speed:on_mbtn_left_down() - -- Don't accept clicks while hidden. - if self:get_visibility() <= 0 then return end +function Speed:handle_cursor_down() self:tween_stop() -- Stop and cleanup possible ongoing animations self.dragging = { start_time = mp.get_time(), @@ -87,14 +85,13 @@ function Speed:on_global_mouse_move() end end -function Speed:on_mbtn_left_up() - -- Reset speed on short clicks - if self.dragging and math.abs(self.dragging.distance) < 6 and mp.get_time() - self.dragging.start_time < 0.15 then - mp.set_property_native('speed', 1) +function Speed:handle_cursor_up() + if self.proximity_raw == 0 then + -- Reset speed on short clicks + if self.dragging and math.abs(self.dragging.distance) < 6 and mp.get_time() - self.dragging.start_time < 0.15 then + mp.set_property_native('speed', 1) + end end -end - -function Speed:on_global_mbtn_left_up() self.dragging = nil request_render() end @@ -104,8 +101,8 @@ function Speed:on_global_mouse_leave() request_render() end -function Speed:on_wheel_up() mp.set_property_native('speed', self:speed_step(state.speed, true)) end -function Speed:on_wheel_down() mp.set_property_native('speed', self:speed_step(state.speed, false)) end +function Speed:handle_wheel_up() mp.set_property_native('speed', self:speed_step(state.speed, true)) end +function Speed:handle_wheel_down() mp.set_property_native('speed', self:speed_step(state.speed, false)) end function Speed:render() local visibility = self:get_visibility() @@ -113,6 +110,18 @@ function Speed:render() if opacity <= 0 then return end + if self.proximity_raw == 0 then + cursor.on_primary_down = function() + self:handle_cursor_down() + cursor.on_primary_up = function() self:handle_cursor_up() end + end + cursor.on_wheel_down = function() self:handle_wheel_down() end + cursor.on_wheel_up = function() self:handle_wheel_up() end + end + if self.dragging then + cursor.on_primary_up = function() self:handle_cursor_up() end + end + local ass = assdraw.ass_new() -- Background diff --git a/scripts/uosc_shared/elements/Timeline.lua b/scripts/uosc_shared/elements/Timeline.lua index 490e7e1..cdca43b 100644 --- a/scripts/uosc_shared/elements/Timeline.lua +++ b/scripts/uosc_shared/elements/Timeline.lua @@ -6,6 +6,7 @@ local Timeline = class(Element) function Timeline:new() return Class.new(self) --[[@as Timeline]] end function Timeline:init() Element.init(self, 'timeline') + ---@type false|{pause: boolean} self.pressed = false self.obstructed = false self.size_max = 0 @@ -13,7 +14,8 @@ function Timeline:init() self.size_min_override = options.timeline_start_hidden and 0 or nil self.font_size = 0 self.top_border = options.timeline_border - self.hovered_chapter = nil + self.is_hovered = false + self.has_thumbnail = false -- Release any dragging when file gets unloaded mp.register_event('end-file', function() self.pressed = false end) @@ -44,7 +46,7 @@ function Timeline:get_effective_line_width() return state.fullormaxed and options.timeline_line_width_fullscreen or options.timeline_line_width end -function Timeline:get_is_hovered() return self.enabled and (self.proximity_raw == 0 or self.hovered_chapter ~= nil) end +function Timeline:get_is_hovered() return self.enabled and self.is_hovered end function Timeline:update_dimensions() if state.fullormaxed then @@ -91,41 +93,20 @@ function Timeline:set_from_cursor(fast) end function Timeline:clear_thumbnail() mp.commandv('script-message-to', 'thumbfast', 'clear') end -function Timeline:determine_chapter_click_handler() - if self.hovered_chapter then - if not self.on_global_mbtn_left_down then - self.on_global_mbtn_left_down = function() - if self.hovered_chapter then mp.commandv('seek', self.hovered_chapter.time, 'absolute+exact') end - end - end - else - if self.on_global_mbtn_left_down then - self.on_global_mbtn_left_down = nil - if self.proximity_raw ~= 0 then self:clear_thumbnail() end - end - end -end - -function Timeline:on_mbtn_left_down() - -- `self.on_global_mbtn_left_down` has precedent - if self.on_global_mbtn_left_down then return end - - self.pressed = true - self.pressed_pause = state.pause +function Timeline:handle_cursor_down() + self.pressed = {pause = state.pause} mp.set_property_native('pause', true) self:set_from_cursor() + cursor.on_primary_up = function() self:handle_cursor_up() end end function Timeline:on_prop_duration() self:decide_enabled() end function Timeline:on_prop_time() self:decide_enabled() end function Timeline:on_prop_border() self:update_dimensions() end function Timeline:on_prop_fullormaxed() self:update_dimensions() end function Timeline:on_display() self:update_dimensions() end -function Timeline:on_mouse_leave() - if not self.hovered_chapter then self:clear_thumbnail() end -end -function Timeline:on_global_mbtn_left_up() +function Timeline:handle_cursor_up() if self.pressed then - mp.set_property_native('pause', self.pressed_pause) + mp.set_property_native('pause', self.pressed.pause) self.pressed = false end self:clear_thumbnail() @@ -144,11 +125,12 @@ function Timeline:on_global_mouse_move() self.seek_timer:kill() self.seek_timer:resume() else self:set_from_cursor() end + elseif self.has_thumbnail and self.proximity_raw > 0 then + self:clear_thumbnail() end - self:determine_chapter_click_handler() end -function Timeline:on_wheel_up() mp.commandv('seek', options.timeline_step) end -function Timeline:on_wheel_down() mp.commandv('seek', -options.timeline_step) end +function Timeline:handle_wheel_up() mp.commandv('seek', options.timeline_step) end +function Timeline:handle_wheel_down() mp.commandv('seek', -options.timeline_step) end function Timeline:render() if self.size_max == 0 then return end @@ -156,8 +138,18 @@ function Timeline:render() local size_min = self:get_effective_size_min() local size = self:get_effective_size() local visibility = self:get_visibility() + self.is_hovered = false if size < 1 then return end + if self.proximity_raw == 0 then + self.is_hovered = true + cursor.on_primary_down = function() self:handle_cursor_down() end + cursor.on_wheel_down = function() self:handle_wheel_down() end + cursor.on_wheel_up = function() self:handle_wheel_up() end + end + if self.pressed then + cursor.on_primary_up = function() self:handle_cursor_up() end + end local ass = assdraw.ass_new() @@ -248,7 +240,7 @@ function Timeline:render() end -- Chapters - self.hovered_chapter = nil + local hovered_chapter = nil if (options.timeline_chapters_opacity > 0 and (#state.chapters > 0 or state.ab_loop_a or state.ab_loop_b) ) then @@ -274,7 +266,7 @@ function Timeline:render() if #state.chapters > 0 then -- Find hovered chapter indicator - local hovered_chapter, closest_delta = nil, INFINITY + local closest_delta = INFINITY if self.proximity_raw < diamond_radius_hovered then for i, chapter in ipairs(state.chapters) do @@ -282,6 +274,10 @@ function Timeline:render() local cursor_chapter_delta = math.sqrt((cursor.x - chapter_x) ^ 2 + (cursor.y - chapter_y) ^ 2) if cursor_chapter_delta <= diamond_radius_hovered and cursor_chapter_delta < closest_delta then hovered_chapter, closest_delta = chapter, cursor_chapter_delta + self.is_hovered = true + cursor.on_primary_down = function() + mp.commandv('seek', hovered_chapter.time, 'absolute+exact') + end end end end @@ -291,11 +287,7 @@ function Timeline:render() end -- Render hovered chapter above others - if hovered_chapter then - draw_chapter(hovered_chapter.time, diamond_radius_hovered) - self.hovered_chapter = hovered_chapter - self:determine_chapter_click_handler() - end + if hovered_chapter then draw_chapter(hovered_chapter.time, diamond_radius_hovered) end end -- A-B loop indicators @@ -363,10 +355,10 @@ function Timeline:render() end -- Hovered time and chapter - if (self.proximity_raw == 0 or self.pressed or self.hovered_chapter) and + if (self.proximity_raw == 0 or self.pressed or hovered_chapter) and not (Elements.speed and Elements.speed.dragging) then - local cursor_x = self.hovered_chapter and t2x(self.hovered_chapter.time) or cursor.x - local hovered_seconds = self.hovered_chapter and self.hovered_chapter.time or self:get_time_at_x(cursor.x) + local cursor_x = hovered_chapter and t2x(hovered_chapter.time) or cursor.x + local hovered_seconds = hovered_chapter and hovered_chapter.time or self:get_time_at_x(cursor.x) -- Cursor line -- 0.5 to switch when the pixel is half filled in @@ -397,7 +389,10 @@ function Timeline:render() local ax, ay = (thumb_x - border) / scale_x, (thumb_y - border) / scale_y local bx, by = (thumb_x + thumb_width + border) / scale_x, (thumb_y + thumb_height + border) / scale_y ass:rect(ax, ay, bx, by, {color = bg, border = 1, border_color = fg, border_opacity = 0.08, radius = 2}) - mp.commandv('script-message-to', 'thumbfast', 'thumb', hovered_seconds, thumb_x, thumb_y) + if not self.pressed then + mp.commandv('script-message-to', 'thumbfast', 'thumb', hovered_seconds, thumb_x, thumb_y) + self.has_thumbnail = true + end tooltip_anchor.ax, tooltip_anchor.bx, tooltip_anchor.ay = ax, bx, ay end diff --git a/scripts/uosc_shared/elements/TopBar.lua b/scripts/uosc_shared/elements/TopBar.lua index daf208a..5feff4c 100644 --- a/scripts/uosc_shared/elements/TopBar.lua +++ b/scripts/uosc_shared/elements/TopBar.lua @@ -16,7 +16,7 @@ function TopBarButton:init(id, props) self.command = props.command end -function TopBarButton:on_mbtn_left_down() +function TopBarButton:handle_cursor_down() mp.command(type(self.command) == 'function' and self.command() or self.command) end @@ -28,6 +28,7 @@ function TopBarButton:render() -- Background on hover if self.proximity_raw == 0 then ass:rect(self.ax, self.ay, self.bx, self.by, {color = self.background, opacity = visibility}) + cursor.on_primary_down = function() self:handle_cursor_down() end end local width, height = self.bx - self.ax, self.by - self.ay @@ -151,10 +152,6 @@ function TopBar:on_prop_maximized() self:update_dimensions() end -function TopBar:on_mbtn_left_down() - if cursor.x < self.title_bx then self:toggle_title() end -end - function TopBar:on_display() self:update_dimensions() end function TopBar:render() @@ -193,6 +190,12 @@ function TopBar:render() } local bx = math.min(max_bx, title_ax + text_width(main_title, opts) + padding * 2) local by = self.by - bg_margin + local rect = {ax = title_ax, ay = self.ay, bx = bx, by = self.by} + + if get_point_to_rectangle_proximity(cursor, rect) == 0 then + cursor.on_primary_down = function() self:toggle_title() end + end + ass:rect(title_ax, title_ay, bx, by, { color = bg, opacity = visibility * options.top_bar_title_opacity, radius = 2, }) diff --git a/scripts/uosc_shared/elements/Volume.lua b/scripts/uosc_shared/elements/Volume.lua index be2d852..2f591b6 100644 --- a/scripts/uosc_shared/elements/Volume.lua +++ b/scripts/uosc_shared/elements/Volume.lua @@ -7,10 +7,12 @@ local MuteButton = class(Element) ---@param props? ElementProps function MuteButton:new(props) return Class.new(self, 'volume_mute', props) --[[@as MuteButton]] end function MuteButton:get_visibility() return Elements.volume:get_visibility(self) end -function MuteButton:on_mbtn_left_down() mp.commandv('cycle', 'mute') end function MuteButton:render() local visibility = self:get_visibility() if visibility <= 0 then return end + if self.proximity_raw == 0 then + cursor.on_primary_down = function() mp.commandv('cycle', 'mute') end + end local ass = assdraw.ass_new() local icon_name = state.mute and 'volume_off' or 'volume_up' local width = self.bx - self.ax @@ -58,17 +60,11 @@ function VolumeSlider:on_coordinates() self.spacing = round(width * 0.2) self.radius = math.max(2, (self.bx - self.ax) / 10) end -function VolumeSlider:on_mbtn_left_down() - self.pressed = true - self:set_from_cursor() -end -function VolumeSlider:on_global_mbtn_left_up() self.pressed = false end -function VolumeSlider:on_global_mouse_leave() self.pressed = false end function VolumeSlider:on_global_mouse_move() if self.pressed then self:set_from_cursor() end end -function VolumeSlider:on_wheel_up() self:set_volume(state.volume + options.volume_step) end -function VolumeSlider:on_wheel_down() self:set_volume(state.volume - options.volume_step) end +function VolumeSlider:handle_wheel_up() self:set_volume(state.volume + options.volume_step) end +function VolumeSlider:handle_wheel_down() self:set_volume(state.volume - options.volume_step) end function VolumeSlider:render() local visibility = self:get_visibility() @@ -77,6 +73,19 @@ function VolumeSlider:render() if width <= 0 or height <= 0 or visibility <= 0 then return end + if self.proximity_raw == 0 then + cursor.on_primary_down = function() + self.pressed = true + self:set_from_cursor() + cursor.on_primary_up = function() self.pressed = false end + end + cursor.on_wheel_down = function() self:handle_wheel_down() end + cursor.on_wheel_up = function() self:handle_wheel_up() end + end + if self.pressed then cursor.on_primary_up = function() + self.pressed = false end + end + local ass = assdraw.ass_new() local nudge_y, nudge_size = self.draw_nudge and self.nudge_y or -INFINITY, self.nudge_size local volume_y = self.ay + options.volume_border + diff --git a/scripts/uosc_shared/lib/utils.lua b/scripts/uosc_shared/lib/utils.lua index 2569fd6..be257e9 100644 --- a/scripts/uosc_shared/lib/utils.lua +++ b/scripts/uosc_shared/lib/utils.lua @@ -93,6 +93,11 @@ function get_point_to_rectangle_proximity(point, rect) return math.sqrt(dx * dx + dy * dy) 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 @@ -553,6 +558,8 @@ function render() if not display.initialized then return end state.render_last_time = mp.get_time() + cursor.reset_handlers() + -- Actual rendering local ass = assdraw.ass_new() @@ -566,6 +573,8 @@ function render() end end + cursor.decide_keybinds() + -- submit if osd.res_x == display.width and osd.res_y == display.height and osd.data == ass.text then return