diff --git a/scripts/uosc/elements/Timeline.lua b/scripts/uosc/elements/Timeline.lua index 68ae159..7cb3ad5 100644 --- a/scripts/uosc/elements/Timeline.lua +++ b/scripts/uosc/elements/Timeline.lua @@ -16,11 +16,6 @@ function Timeline:init() self.top_border = options.timeline_border self.is_hovered = false self.has_thumbnail = false - self.pixels_per_frame = 1 - - -- Delayed seeking timer - self.seek_timer = mp.add_timeout(0.05, function() self:set_from_cursor() end) - self.seek_timer:kill() -- Release any dragging when file gets unloaded mp.register_event('end-file', function() self.pressed = false end) @@ -53,13 +48,6 @@ end function Timeline:get_is_hovered() return self.enabled and self.is_hovered end -function Timeline:update_pixels_per_frame() - if state.is_video and state.framerate and state.duration then - self.pixels_per_frame = self.width / (state.duration * state.framerate) - else - self.pixels_per_frame = 1 - end -end function Timeline:update_dimensions() if state.fullormaxed then self.size_min = options.timeline_size_min_fullscreen @@ -82,7 +70,6 @@ function Timeline:update_dimensions() if Elements.top_bar.enabled then available_space = available_space - Elements.top_bar.size end self.obstructed = available_space < self.size_max + 10 self:decide_enabled() - self:update_pixels_per_frame() end function Timeline:get_time_at_x(x) @@ -116,14 +103,12 @@ function Timeline:handle_cursor_down() self:set_from_cursor() cursor.on_primary_up = function() self:handle_cursor_up() end end -function Timeline:on_prop_duration() self:decide_enabled() self:update_pixels_per_frame() end -function Timeline:on_prop_framerate() self:update_pixels_per_frame() 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:handle_cursor_up() - self.seek_timer:kill() if self.pressed then mp.set_property_native('pause', self.pressed.pause) self.pressed = false @@ -137,10 +122,8 @@ function Timeline:on_global_mouse_move() if self.pressed then self.pressed.distance = self.pressed.distance + get_point_to_point_proximity(self.pressed.last, cursor) self.pressed.last.x, self.pressed.last.y = cursor.x, cursor.y - if not state.is_video or math.abs(cursor.velocity.x) > 80 * math.max(1, self.pixels_per_frame) then + if state.is_video and math.abs(cursor.get_velocity().x) / self.width * state.duration > 30 then self:set_from_cursor(true) - self.seek_timer:kill() - self.seek_timer:resume() else self:set_from_cursor() end end end diff --git a/scripts/uosc/lib/std.lua b/scripts/uosc/lib/std.lua index c72ccb1..b560890 100644 --- a/scripts/uosc/lib/std.lua +++ b/scripts/uosc/lib/std.lua @@ -202,3 +202,59 @@ function Class:init() end function Class:destroy() end function class(parent) return setmetatable({}, {__index = parent or Class}) end + +---@class CircularBuffer : Class +CircularBuffer = class() + +function CircularBuffer:new(max_size) return Class.new(self, max_size) --[[@as CircularBuffer]] end +function CircularBuffer:init(max_size) + self.max_size = max_size + self.size = 0 + self.pos = 0 + self.data = {} +end + +function CircularBuffer:insert(item) + self.pos = self.pos % self.max_size + 1 + self.data[self.pos] = item + if self.size < self.max_size then self.size = self.size + 1 end +end + +function CircularBuffer:get(i) + return i <= self.size and self.data[(self.pos + i - 1) % self.size + 1] or nil +end + +local function iter(self, i) + if i == self.size then return nil end + i = i + 1 + return i, self:get(i) +end + +function CircularBuffer:iter() + return iter, self, 0 +end + +local function iter_rev(self, i) + if i == 1 then return nil end + i = i - 1 + return i, self:get(i) +end + +function CircularBuffer:iter_rev() + return iter_rev, self, self.size + 1 +end + +function CircularBuffer:head() + return self.data[self.pos] +end + +function CircularBuffer:tail() + if self.size < 1 then return nil end + return self.data[self.pos % self.size + 1] +end + +function CircularBuffer:clear() + for i = self.size, 1, -1 do self.data[i] = nil end + self.size = 0 + self.pos = 0 +end diff --git a/scripts/uosc/main.lua b/scripts/uosc/main.lua index e3238a7..80a0151 100644 --- a/scripts/uosc/main.lua +++ b/scripts/uosc/main.lua @@ -316,10 +316,7 @@ cursor = { on_wheel_down = nil, on_wheel_up = nil, allow_dragging = false, - ---@type {x: number, y: number, time: number}[] - history = {}, - history_size = 10, - velocity = {x = 0, y = 0}, + history = CircularBuffer:new(10), -- Called at the beginning of each render reset_handlers = function() cursor.on_primary_down, cursor.on_primary_up = nil, nil @@ -342,6 +339,26 @@ cursor = { cursor.wheel_enabled = enable_wheel end end, + find_history_sample = function() + local time = mp.get_time() + for _, e in cursor.history:iter_rev() do + if time - e.time > 0.1 then + return e + end + end + return cursor.history:tail() + end, + get_velocity = function() + local snap = cursor.find_history_sample() + if snap then + local x, y, time = cursor.x - snap.x, cursor.y - snap.y, mp.get_time() + local time_diff = time - snap.time + if time_diff > 0.001 then + return { x = x / time_diff, y = y / time_diff } + end + end + return { x = 0, y = 0 } + end, move = function(x, y) local old_x, old_y = cursor.x, cursor.y @@ -360,8 +377,8 @@ cursor = { Elements:update_proximities() if cursor.x == INFINITY or cursor.y == INFINITY then - cursor.hidden, cursor.history = true, {} - cursor.velocity.x, cursor.velocity.y = 0, 0 + cursor.hidden = true + cursor.history:clear() -- Slowly fadeout elements that are currently visible for _, element_name in ipairs({'timeline', 'volume', 'top_bar'}) do @@ -375,28 +392,12 @@ cursor = { Elements:trigger('global_mouse_leave') elseif cursor.hidden then - cursor.hidden, cursor.history = false, {} - cursor.velocity.x, cursor.velocity.y = 0, 0 + cursor.hidden = false + cursor.history:clear() Elements:trigger('global_mouse_enter') else -- Update history - local new_index = #cursor.history + 1 - local last = {x = x, y = y, time = mp.get_time()} - if #cursor.history >= cursor.history_size then - new_index = #cursor.history - for i = 1, #cursor.history, 1 do cursor.history[i] = cursor.history[i + 1] end - end - cursor.history[new_index] = last - - -- Update velocity - if #cursor.history > 5 then - local first = cursor.history[1] - local time_delta = (last.time - first.time) - cursor.velocity.x = (last.x - first.x) / time_delta - cursor.velocity.y = (last.y - first.y) / time_delta - else - cursor.velocity.x, cursor.velocity.y = 0, 0 - end + cursor.history:insert({x = cursor.x, y = cursor.y, time = mp.get_time()}) end Elements:proximity_trigger('mouse_move') @@ -424,11 +425,8 @@ cursor = { -- Calculates distance in which cursor reaches rectangle if it continues moving on the same path. -- Returns `nil` if cursor is not moving towards the rectangle. direction_to_rectangle_distance = function(rect) - if cursor.hidden or #cursor.history < 10 then - return false - end - - local prev = cursor.history[#cursor.history - 9] + local prev = cursor.find_history_sample() + if not prev then return false end local end_x, end_y = cursor.x + (cursor.x - prev.x) * 1e10, cursor.y + (cursor.y - prev.y) * 1e10 return get_ray_to_rectangle_distance(cursor.x, cursor.y, end_x, end_y, rect) end @@ -455,7 +453,6 @@ state = { duration = nil, -- current media duration time_human = nil, -- current playback time in human format destination_time_human = nil, -- depends on options.destination_time - framerate = 1, pause = mp.get_property_native('pause'), chapters = {}, current_chapter = nil, @@ -829,7 +826,6 @@ end) mp.observe_property('display-hidpi-scale', 'native', create_state_setter('hidpi_scale', update_display_dimensions)) mp.observe_property('cache', 'string', create_state_setter('cache')) mp.observe_property('cache-buffering-state', 'number', create_state_setter('cache_buffering')) -mp.observe_property('demuxer-rawvideo-fps', 'number', create_state_setter('framerate')) mp.observe_property('demuxer-via-network', 'native', create_state_setter('is_stream', function() Elements:trigger('dispositions') end))