Refactor flashing, add speed_flash, tweak options

This commit is contained in:
Tomas Sardyha
2020-04-19 21:08:25 +02:00
parent cf55e74a25
commit 87173dd01a
3 changed files with 288 additions and 280 deletions

View File

@@ -49,73 +49,63 @@ timeline_size_min_fullscreen=0
timeline_size_max_fullscreen=60
# timeline opacity
timeline_opacity=0.8
# adds a top border of background color to help visually separate elapsed bar
# from video of similar color
# in no border windowed mode bottom border is added as well to separate from
# whatever is behind the current window
# this might be unwanted if you are using unique/rare colors with low overlap
# chance, so you can disable it by setting to 0
# top (and bottom in no-border mode) border of background color to help visually
# separate elapsed bar from a video of similar color or desktop background
timeline_border=1
# display seekable buffered ranges for streaming videos, syntax `color:opacity`,
# color is an BBGGRR hex code, set to empty or `no` to disable
timeline_cached_ranges=345433:0.5
# when video position is changed externally (e.g. hotkeys), flash the timeline
# for this amount of time, set to 0 to disable
timeline_flash_duration=300
# briefly show timeline on external changes (e.g. seeking with a hotkey)
timeline_flash=yes
# timeline chapters indicator style: dots, lines, lines-top, lines-bottom
# timeline chapters style: dots, lines, lines-top, lines-bottom
# set to empty to disable
chapters=dots
# timeline chapters indicator opacity
chapters_opacity=0.3
# where to display volume controls, set to empty to disable
# where to display volume controls (`left` or `right`), set to empty to disable
volume=right
# volume control horizontal size
volume_size=40
# same as ^ but when in fullscreen
volume_size_fullscreen=40
# volume controls opacity
volume_opacity=0.8
# thin border around volume slider
volume_border=1
# when clicking or dragging volume slider, volume will snap only to increments
# of this value
volume_snap_to=1
# when volume is changed externally (e.g. hotkeys), flash the volume controls
# for this amount of time, set to 0 to disable
volume_flash_duration=300
# when dragging the slider, volume will round to increments of this value
volume_rounding=1
# briefly show volume slider on external changes (e.g. changed by a hotkey)
volume_flash=yes
# playback speed widget: drag left-right to change, click to reset
speed=no
speed_size=35
speed_size_fullscreen=50
speed_opacity=1
# when dragging the slider, speed will round to increments of this value
speed_rounding=0.1
# briefly show speed slider on external changes (e.g. changed by a hotkey)
speed_flash=yes
# controls all menus, such as context menu, subtitle loader/selector, etc
menu_item_height=40
menu_item_height_fullscreen=50
menu_item_height=30
menu_item_height_fullscreen=45
menu_opacity=0.8
# playback speed control widget: drag to change, click to reset
speed=no
speed_size=40
speed_size_fullscreen=60
speed_opacity=1
speed_step=0.1
# pause video on clicks shorter than this number of milliseconds
# enables you to use left mouse button for both dragging and pausing the video
# I recommend a duration of 120, leave at 0 to disable
# pause video on clicks shorter than this number of milliseconds, 0 to disable
pause_on_click_shorter_than=0
# proximity below which elements are fully faded in/expanded
proximity_min=40
# proximity above which elements are fully faded out/retracted
proximity_max=120
# BBGGRR - BLUE GREEN RED hex codes
# for how long in milliseconds to show elements they're it's being flashed
flash_duration=400
# distances in pixels below which elements are fully faded in/out
proximity_in=40
proximity_out=120
# BBGGRR - BLUE GREEN RED hex color codes
color_foreground=ffffff
color_foreground_text=000000
color_background=000000
color_background_text=ffffff
# hide proximity based elements when mpv autohides the cursor
# hide UI when mpv autohides the cursor
autohide=no
# display window title (filename) in top window controls bar in no-border mode
title=no
# load first file when calling next on last file in a directory and vice versa
# load first file when calling next on a last file in a directory and vice versa
directory_navigation_loops=no
# file types to display in file explorer when navigating media files
media_types=3gp,avi,bmp,flac,flv,gif,h264,h265,jpeg,jpg,m4a,m4v,mid,midi,mkv,mov,mp3,mp4,mp4a,mp4v,mpeg,mpg,oga,ogg,ogm,ogv,opus,png,rmvb,svg,tif,tiff,wav,weba,webm,webp,wma,wmv
@@ -126,9 +116,7 @@ subtitle_types=aqt,gsub,jss,sub,ttxt,pjs,psb,rt,smi,slt,ssf,srt,ssa,ass,usf,idx,
# try bumping this up
font_height_to_letter_width_ratio=0.5
# `chapter_ranges` lets you transform chapter indicators into range indicators
# with custom color and opacity by creating a chapter range definition that
# matches chapter titles.
# `chapter_ranges` lets you transform chapter indicators into range indicators.
#
# Chapter range definition syntax:
# ```

View File

@@ -7,73 +7,63 @@ timeline_size_min_fullscreen=0
timeline_size_max_fullscreen=60
# timeline opacity
timeline_opacity=0.8
# adds a top border of background color to help visually separate elapsed bar
# from video of similar color
# in no border windowed mode bottom border is added as well to separate from
# whatever is behind the current window
# this might be unwanted if you are using unique/rare colors with low overlap
# chance, so you can disable it by setting to 0
# top (and bottom in no-border mode) border of background color to help visually
# separate elapsed bar from a video of similar color or desktop background
timeline_border=1
# display seekable buffered ranges for streaming videos, syntax `color:opacity`,
# color is an BBGGRR hex code, set to empty or `no` to disable
timeline_cached_ranges=345433:0.5
# when video position is changed externally (e.g. hotkeys), flash the timeline
# for this amount of time, set to 0 to disable
timeline_flash_duration=300
# briefly show timeline on external changes (e.g. seeking with a hotkey)
timeline_flash=yes
# timeline chapters indicator style: dots, lines, lines-top, lines-bottom
# timeline chapters style: dots, lines, lines-top, lines-bottom
# set to empty to disable
chapters=dots
# timeline chapters indicator opacity
chapters_opacity=0.3
# where to display volume controls, set to empty to disable
# where to display volume controls (`left` or `right`), set to empty to disable
volume=right
# volume control horizontal size
volume_size=40
# same as ^ but when in fullscreen
volume_size_fullscreen=40
# volume controls opacity
volume_opacity=0.8
# thin border around volume slider
volume_border=1
# when clicking or dragging volume slider, volume will snap only to increments
# of this value
volume_snap_to=1
# when volume is changed externally (e.g. hotkeys), flash the volume controls
# for this amount of time, set to 0 to disable
volume_flash_duration=300
# when dragging the slider, volume will round to increments of this value
volume_rounding=1
# briefly show volume slider on external changes (e.g. changed by a hotkey)
volume_flash=yes
# playback speed widget: drag left-right to change, click to reset
speed=no
speed_size=35
speed_size_fullscreen=50
speed_opacity=1
# when dragging the slider, speed will round to increments of this value
speed_rounding=0.1
# briefly show speed slider on external changes (e.g. changed by a hotkey)
speed_flash=yes
# controls all menus, such as context menu, subtitle loader/selector, etc
menu_item_height=40
menu_item_height_fullscreen=50
menu_item_height=30
menu_item_height_fullscreen=45
menu_opacity=0.8
# playback speed control widget: drag to change, click to reset
speed=no
speed_size=40
speed_size_fullscreen=60
speed_opacity=1
speed_step=0.1
# pause video on clicks shorter than this number of milliseconds
# enables you to use left mouse button for both dragging and pausing the video
# I recommend a duration of 120, leave at 0 to disable
# pause video on clicks shorter than this number of milliseconds, 0 to disable
pause_on_click_shorter_than=0
# proximity below which elements are fully faded in/expanded
proximity_min=40
# proximity above which elements are fully faded out/retracted
proximity_max=120
# BBGGRR - BLUE GREEN RED hex codes
# for how long in milliseconds to show elements they're it's being flashed
flash_duration=400
# distances in pixels below which elements are fully faded in/out
proximity_in=40
proximity_out=120
# BBGGRR - BLUE GREEN RED hex color codes
color_foreground=ffffff
color_foreground_text=000000
color_background=000000
color_background_text=ffffff
# hide proximity based elements when mpv autohides the cursor
# hide UI when mpv autohides the cursor
autohide=no
# display window title (filename) in top window controls bar in no-border mode
title=no
# load first file when calling next on last file in a directory and vice versa
# load first file when calling next on a last file in a directory and vice versa
directory_navigation_loops=no
# file types to display in file explorer when navigating media files
media_types=3gp,avi,bmp,flac,flv,gif,h264,h265,jpeg,jpg,m4a,m4v,mid,midi,mkv,mov,mp3,mp4,mp4a,mp4v,mpeg,mpg,oga,ogg,ogm,ogv,opus,png,rmvb,svg,tif,tiff,wav,weba,webm,webp,wma,wmv
@@ -84,9 +74,7 @@ subtitle_types=aqt,gsub,jss,sub,ttxt,pjs,psb,rt,smi,slt,ssf,srt,ssa,ass,usf,idx,
# try bumping this up
font_height_to_letter_width_ratio=0.5
# `chapter_ranges` lets you transform chapter indicators into range indicators
# with custom color and opacity by creating a chapter range definition that
# matches chapter titles.
# `chapter_ranges` lets you transform chapter indicators into range indicators.
#
# Chapter range definition syntax:
# ```

416
uosc.lua
View File

@@ -25,73 +25,63 @@ timeline_size_min_fullscreen=0
timeline_size_max_fullscreen=60
# timeline opacity
timeline_opacity=0.8
# adds a top border of background color to help visually separate elapsed bar
# from video of similar color
# in no border windowed mode bottom border is added as well to separate from
# whatever is behind the current window
# this might be unwanted if you are using unique/rare colors with low overlap
# chance, so you can disable it by setting to 0
# top (and bottom in no-border mode) border of background color to help visually
# separate elapsed bar from a video of similar color or desktop background
timeline_border=1
# display seekable buffered ranges for streaming videos, syntax `color:opacity`,
# color is an BBGGRR hex code, set to empty or `no` to disable
timeline_cached_ranges=345433:0.5
# when video position is changed externally (e.g. hotkeys), flash the timeline
# for this amount of time, set to 0 to disable
timeline_flash_duration=300
# briefly show timeline on external changes (e.g. seeking with a hotkey)
timeline_flash=yes
# timeline chapters indicator style: dots, lines, lines-top, lines-bottom
# timeline chapters style: dots, lines, lines-top, lines-bottom
# set to empty to disable
chapters=dots
# timeline chapters indicator opacity
chapters_opacity=0.3
# where to display volume controls, set to empty to disable
# where to display volume controls (`left` or `right`), set to empty to disable
volume=right
# volume control horizontal size
volume_size=40
# same as ^ but when in fullscreen
volume_size_fullscreen=40
# volume controls opacity
volume_opacity=0.8
# thin border around volume slider
volume_border=1
# when clicking or dragging volume slider, volume will snap only to increments
# of this value
volume_snap_to=1
# when volume is changed externally (e.g. hotkeys), flash the volume controls
# for this amount of time, set to 0 to disable
volume_flash_duration=300
# when dragging the slider, volume will round to increments of this value
volume_rounding=1
# briefly show volume slider on external changes (e.g. changed by a hotkey)
volume_flash=yes
# playback speed widget: drag left-right to change, click to reset
speed=no
speed_size=35
speed_size_fullscreen=50
speed_opacity=1
# when dragging the slider, speed will round to increments of this value
speed_rounding=0.1
# briefly show speed slider on external changes (e.g. changed by a hotkey)
speed_flash=yes
# controls all menus, such as context menu, subtitle loader/selector, etc
menu_item_height=40
menu_item_height_fullscreen=50
menu_item_height=30
menu_item_height_fullscreen=45
menu_opacity=0.8
# playback speed control widget: drag to change, click to reset
speed=no
speed_size=40
speed_size_fullscreen=60
speed_opacity=1
speed_step=0.1
# pause video on clicks shorter than this number of milliseconds
# enables you to use left mouse button for both dragging and pausing the video
# I recommend a duration of 120, leave at 0 to disable
# pause video on clicks shorter than this number of milliseconds, 0 to disable
pause_on_click_shorter_than=0
# proximity below which elements are fully faded in/expanded
proximity_min=40
# proximity above which elements are fully faded out/retracted
proximity_max=120
# BBGGRR - BLUE GREEN RED hex codes
# for how long in milliseconds to show elements they're it's being flashed
flash_duration=400
# distances in pixels below which elements are fully faded in/out
proximity_in=40
proximity_out=120
# BBGGRR - BLUE GREEN RED hex color codes
color_foreground=ffffff
color_foreground_text=000000
color_background=000000
color_background_text=ffffff
# hide proximity based elements when mpv autohides the cursor
# hide UI when mpv autohides the cursor
autohide=no
# display window title (filename) in top window controls bar in no-border mode
title=no
# load first file when calling next on last file in a directory and vice versa
# load first file when calling next on a last file in a directory and vice versa
directory_navigation_loops=no
# file types to display in file explorer when navigating media files
media_types=3gp,avi,bmp,flac,flv,gif,h264,h265,jpeg,jpg,m4a,m4v,mid,midi,mkv,mov,mp3,mp4,mp4a,mp4v,mpeg,mpg,oga,ogg,ogm,ogv,opus,png,rmvb,svg,tif,tiff,wav,weba,webm,webp,wma,wmv
@@ -102,9 +92,7 @@ subtitle_types=aqt,gsub,jss,sub,ttxt,pjs,psb,rt,smi,slt,ssf,srt,ssa,ass,usf,idx,
# try bumping this up
font_height_to_letter_width_ratio=0.5
# `chapter_ranges` lets you transform chapter indicators into range indicators
# with custom color and opacity by creating a chapter range definition that
# matches chapter titles.
# `chapter_ranges` lets you transform chapter indicators into range indicators.
#
# Chapter range definition syntax:
# ```
@@ -191,7 +179,7 @@ local options = {
timeline_opacity = 0.8,
timeline_border = 1,
timeline_cached_ranges = '345433:0.5',
timeline_flash_duration = 400,
timeline_flash = true,
chapters = 'dots',
chapters_opacity = 0.3,
@@ -201,23 +189,24 @@ local options = {
volume_size_fullscreen = 60,
volume_opacity = 0.8,
volume_border = 1,
volume_snap_to = 1,
volume_flash_duration = 400,
volume_rounding = 1,
volume_flash = true,
speed = false,
speed_size = 40,
speed_size_fullscreen = 60,
speed_size = 35,
speed_size_fullscreen = 50,
speed_opacity = 1,
speed_step = 0.1,
speed_rounding = 0.1,
speed_flash = true,
menu_item_height = 36,
menu_item_height_fullscreen = 50,
menu_opacity = 0.8,
pause_on_click_shorter_than = 0,
click_duration = 110,
proximity_min = 40,
proximity_max = 120,
flash_duration = 400,
proximity_in = 40,
proximity_out = 120,
color_foreground = 'ffffff',
color_foreground_text = '000000',
color_background = '000000',
@@ -371,9 +360,8 @@ end
-- Creates in-between frames to animate value from `from` to `to` numbers.
-- Returns function that terminates animation.
-- `to` can be a function that returns target value, useful for movable targets.
-- `on_end` callback is called only when animation arrives at the end.
-- `cleanup` callback is called either on_end, or when animation is canceled.
function tween(from, to, setter, on_end, cleanup)
-- `callback` is called either on animation end, or when animation is canceled
function tween(from, to, setter, callback)
local timeout
local getTo = type(to) == 'function' and to or function() return to end
local cutoff = math.abs(getTo() - from) * 0.01
@@ -383,8 +371,7 @@ function tween(from, to, setter, on_end, cleanup)
setter(is_end and getTo() or from)
request_render()
if is_end then
call_me_maybe(on_end)
call_me_maybe(cleanup)
call_me_maybe(callback)
else
timeout:resume()
end
@@ -393,22 +380,21 @@ function tween(from, to, setter, on_end, cleanup)
tick()
return function()
timeout:kill()
call_me_maybe(cleanup)
call_me_maybe(callback)
end
end
-- Kills ongoing animation if one is already running on this element.
-- Killed animation will not get its `on_end` called.
function tween_element(element, from, to, setter, on_end, cleanup)
function tween_element(element, from, to, setter, callback)
tween_element_stop(element)
element.stop_current_animation = tween(
from, to,
function(value) setter(element, value) end,
on_end,
function()
element.stop_current_animation = nil
call_me_maybe(cleanup, element)
call_me_maybe(callback, element)
end
)
end
@@ -424,8 +410,8 @@ function tween_element_stop(element)
end
-- Helper to automatically use an element property setter
function tween_element_property(element, prop, from, to, on_end, cleanup)
tween_element(element, from, to, function(_, value) element[prop] = value end, on_end, cleanup)
function tween_element_property(element, prop, from, to, callback)
tween_element(element, from, to, function(_, value) element[prop] = value end, callback)
end
function get_point_to_rectangle_proximity(point, rect)
@@ -603,7 +589,19 @@ Element.__index = Element
function Element.new(props)
local element = setmetatable(props, Element)
element._eventListeners = {}
-- Flash timer
element._flash_out_timer = mp.add_timeout(options.flash_duration / 1000, function()
local getTo = function() return state.interactive_proximity end
element:tween_property('forced_proximity', 1, getTo, function()
print('what')
element.forced_proximity = nil
end)
end)
element._flash_out_timer:kill()
element:init()
return element
end
@@ -642,6 +640,18 @@ function Element:trigger(name, ...)
for _, handler in ipairs(self._eventListeners[name]) do handler(...) end
end
-- Briefly flashes the element for `options.flash_duration` milliseconds.
-- Useful to visualize changes of volume and timeline when changed via hotkeys.
-- Implemented by briefly adding animated `forced_proximity` property to the element.
function Element:flash()
if options.flash_duration > 0 and (state.interactive_proximity < 1 or self._flash_out_timer:is_enabled()) then
self:tween_stop()
self.forced_proximity = 1
self._flash_out_timer:kill()
self._flash_out_timer:resume()
end
end
-- ELEMENTS
local Elements = {itable = {}}
@@ -794,11 +804,11 @@ function Menu:open(items, open_item, opts)
update_proximities()
end
end,
fadeout = function(this, callback)
tween_element(this, 1, 0, function(this, pos)
fadeout = function(this, on_end, cleanup)
this:tween(1, 0, function(this, pos)
this.opacity = pos
this:set_parent_opacity(pos * config.menu_parent_opacity)
end, callback)
end, on_end, cleanup)
end,
set_parent_opacity = function(this, opacity)
if this.parent_menu then
@@ -814,9 +824,12 @@ function Menu:open(items, open_item, opts)
request_render()
end,
select_item = function(this, index)
if index >= 1 and index <= #this.items then
if index and index >= 1 and index <= #this.items then
this.selected_item = index
this:center_selected_item()
else
this.selected_item = nil
request_render()
end
end,
center_selected_item = function(this)
@@ -985,6 +998,8 @@ function Menu:open(items, open_item, opts)
on_end = function(this) this:scroll_to(this.scroll_height) end,
render = render_menu,
}))
elements.menu:maybe('on_open')
end
function Menu:add_key_binding(key, name, fn, flags)
@@ -1037,6 +1052,7 @@ function Menu:close(immediate, callback)
if elements:has('menu') and not menu.is_closing then
function close()
elements.menu:maybe('on_close')
elements.menu:destroy()
elements:remove('menu')
menu.is_closing = false
@@ -1051,7 +1067,7 @@ function Menu:close(immediate, callback)
if immediate then
close()
else
elements.menu:fadeout(close)
elements.menu:fadeout(nil, close)
end
end
end
@@ -1171,9 +1187,9 @@ function update_element_cursor_proximity(element)
element.proximity_raw = infinity
element.proximity = 0
else
local range = options.proximity_max - options.proximity_min
local range = options.proximity_out - options.proximity_in
element.proximity_raw = get_point_to_rectangle_proximity(cursor, element)
element.proximity = menu:is_open() and 0 or 1 - (math.min(math.max(element.proximity_raw - options.proximity_min, 0), range) / range)
element.proximity = menu:is_open() and 0 or 1 - (math.min(math.max(element.proximity_raw - options.proximity_in, 0), range) / range)
end
end
@@ -1682,7 +1698,7 @@ function render_speed(this)
-- Notches
local speed_at_center = state.speed
if this.dragging then
speed_at_center = this.dragging.start_speed + ((-this.dragging.distance / this.step_distance) * options.speed_step)
speed_at_center = this.dragging.start_speed + ((-this.dragging.distance / this.step_distance) * options.speed_rounding)
speed_at_center = math.min(math.max(speed_at_center, 0.01), 100)
end
local nearest_notch_speed = round(speed_at_center / this.notch_every) * this.notch_every
@@ -1916,34 +1932,6 @@ end
-- STATIC ELEMENTS
-- Creates a function that, when called, briefly flashes passed element name.
-- Useful to visualize changes of volume and timeline when changed via hotkeys.
function create_flash_function_for(element_name)
local duration = options[element_name..'_flash_duration']
if not duration or duration < 1 then
return function() end
end
local flash_timer
flash_timer = mp.add_timeout(duration / 1000, function()
local getTo = function() return state.interactive_proximity end
elements[element_name]:tween_property('forced_proximity', 1, getTo, nil, function()
elements[element_name].forced_proximity = nil
end)
end)
flash_timer:kill()
return function()
if state.interactive_proximity < 1 or flash_timer:is_enabled() then
tween_element_stop(elements[element_name])
elements[element_name].forced_proximity = 1
flash_timer:kill()
flash_timer:resume()
end
request_render()
end, flash_timer
end
elements:add('timeline', Element.new({
interactive = true,
pressed = false,
@@ -1952,13 +1940,24 @@ elements:add('timeline', Element.new({
font_size = 0, -- calculated in on_display_resize
top_border = options.timeline_border,
bottom_border = 0, -- set dynamically in `border` property observer
flash = create_flash_function_for('timeline'),
init = function(this)
-- Toggle 1px bottom border for timeline in no-border mode
mp.observe_property('border', 'bool', function(_, border)
-- Sets 1px bottom border for timeline in no-border mode
this.bottom_border = not border and options.timeline_border or 0
request_render()
end)
-- Flash on external changes
if options.timeline_flash then
mp.register_event('seek', function()
local position = mp.get_property_native('playback-time')
if position and state.position then
local seek_length = math.abs(position - state.position)
-- Don't flash on video looping (seek to 0) or tiny seeks (frame-step)
if position > 0.5 and seek_length > 0.5 then this:flash() end
end
end)
end
end,
get_effective_proximity = function(this)
if this.pressed then
@@ -2056,7 +2055,21 @@ if itable_find({'left', 'right'}, options.volume) then
height = nil, -- set in `on_display_resize` handler based on `state.fullscreen`
margin = nil, -- set in `on_display_resize` handler based on `state.fullscreen`
font_size = nil, -- calculated in on_display_resize
flash = create_flash_function_for('volume'),
init = function(this)
-- FLash on external changes
if options.volume_flash then
local is_initial_volume_call = true
mp.observe_property('volume', 'number', function(_, value)
if not is_initial_volume_call then this:flash() end
is_initial_volume_call = false
end)
local is_initial_mute_call = true
mp.observe_property('mute', 'bool', function(_, value)
if not is_initial_mute_call then this:flash() end
is_initial_mute_call = false
end)
end
end,
on_display_resize = function(this)
local left = options.volume == 'left'
this.width = (state.fullscreen or state.maximized) and options.volume_size_fullscreen or options.volume_size
@@ -2112,7 +2125,7 @@ if itable_find({'left', 'right'}, options.volume) then
set_from_cursor = function(this)
local volume_fraction = (this.by - cursor.y - options.volume_border) / (this.height - options.volume_border)
local new_volume = math.min(math.max(volume_fraction, 0), 1) * state.volume_max
new_volume = round(new_volume / options.volume_snap_to) * options.volume_snap_to
new_volume = round(new_volume / options.volume_rounding) * options.volume_rounding
if state.volume ~= new_volume then mp.commandv('set', 'volume', new_volume) end
end,
on_mbtn_left_down = function(this)
@@ -2139,29 +2152,37 @@ if options.speed then
init = function(this)
-- Fade out/in on timeline mouse enter/leave
elements.timeline:on('mouse_enter', function()
this:tween_property('forced_proximity', 1, 0, nil, function(this)
this.forced_proximity = 0
end)
if not this.dragging then this:fadeout() end
end)
elements.timeline:on('mouse_leave', function()
local get_current_proximity = function() return state.interactive_proximity end
this:tween_property('forced_proximity', 0, get_current_proximity, nil, function(this)
this.forced_proximity = nil
end)
if not this.dragging then this:fadein() end
end)
-- Flash on external changes
mp.observe_property_native('speed', function()
if not this.dragging then
this:flash()
end
if options.speed_flash then
local initial_call = true
mp.observe_property('speed', 'number', function()
if not initial_call and not this.dragging then this:flash() end
initial_call = false
end)
end
end,
fadeout = function(this)
this:tween_property('forced_proximity', 1, 0, function(this)
this.forced_proximity = 0
end)
end,
fadein = function(this)
local get_current_proximity = function() return state.interactive_proximity end
this:tween_property('forced_proximity', 0, get_current_proximity, function(this)
this.forced_proximity = nil
end)
end,
on_display_resize = function(this)
this.height = (state.fullscreen or state.maximized) and options.speed_size_fullscreen or options.speed_size
this.width = this.height * 5
this.notch_spacing = this.width / this.notches
this.step_distance = this.notch_spacing * (options.speed_step / this.notch_every)
this.step_distance = this.notch_spacing * (options.speed_rounding / this.notch_every)
this.ax = (display.width - this.width) / 2
this.by = display.height - elements.timeline.size_max - (this.height / 3)
this.ay = this.by - this.height
@@ -2171,7 +2192,7 @@ if options.speed then
set_from_cursor = function(this)
local volume_fraction = (this.by - cursor.y - options.volume_border) / (this.height - options.volume_border)
local new_volume = math.min(math.max(volume_fraction, 0), 1) * state.volume_max
new_volume = round(new_volume / options.volume_snap_to) * options.volume_snap_to
new_volume = round(new_volume / options.volume_rounding) * options.volume_rounding
if state.volume ~= new_volume then mp.commandv('set', 'volume', new_volume) end
end,
on_mbtn_left_down = function(this)
@@ -2188,7 +2209,7 @@ if options.speed then
this.dragging.distance = cursor.x - this.dragging.start_x
local steps_dragged = round(-this.dragging.distance / this.step_distance)
local new_speed = this.dragging.start_speed + (steps_dragged * options.speed_step)
local new_speed = this.dragging.start_speed + (steps_dragged * options.speed_rounding)
mp.set_property_native('speed', round(new_speed * 100) / 100)
end,
on_mbtn_left_up = function(this)
@@ -2198,6 +2219,9 @@ if options.speed then
end
end,
on_global_mbtn_left_up = function(this)
if this.dragging and elements.timeline.proximity_raw == 0 then
this:fadeout()
end
this.dragging = nil
request_render()
end,
@@ -2535,10 +2559,14 @@ function create_select_tracklist_type_menu_opener(menu_title, track_type, track_
end
end
function open_file_navigation_menu(menu_type, directory, handle_select, allowed_types, selected_file)
-- `menu_options`:
-- **allowed_types** - table with file extensions to display
-- **selected_file** - full path of a file to preselect
-- Rest of the options are passed to `menu:open()`
function open_file_navigation_menu(directory, handle_select, menu_options)
directory = serialize_path(directory)
local directories, error = utils.readdir(directory.path, 'dirs')
local files, error = get_files_in_directory(directory.path, allowed_types)
local files, error = get_files_in_directory(directory.path, menu_options.allowed_types)
if not files or not directories then
msg.error('Retrieving files from '..directory..' failed: '..(error or ''))
@@ -2563,10 +2591,13 @@ function open_file_navigation_menu(menu_type, directory, handle_select, allowed_
items[#items + 1] = {
title = serialized.basename,
value = serialized.path,
selected = selected_file == file
selected = menu_options.selected_file == serialized.path
}
end
menu_options.title = directory.basename..'/'
menu_options.select_on_hover = false
menu:open(items, function(path)
local meta, error = utils.file_info(path)
@@ -2576,12 +2607,12 @@ function open_file_navigation_menu(menu_type, directory, handle_select, allowed_
end
if meta.is_dir then
open_file_navigation_menu(path, handle_select, allowed_types)
open_file_navigation_menu(path, handle_select, menu_options)
else
handle_select(path)
menu:close()
end
end, {type = menu_type, title = directory.basename..'/', select_on_hover = false})
end, menu_options)
end
-- VALUE SERIALIZATION/NORMALIZATION
@@ -2604,17 +2635,9 @@ mp.observe_property('window-maximized', 'bool', create_state_setter('maximized')
mp.observe_property('idle-active', 'bool', create_state_setter('idle'))
mp.observe_property('speed', 'number', create_state_setter('speed'))
mp.observe_property('pause', 'bool', create_state_setter('paused'))
mp.observe_property('volume', 'number', function(_, value)
local is_initial_call = state.volume == nil
state.volume = value
if not is_initial_call then elements.volume.flash() end
end)
mp.observe_property('volume', 'number', create_state_setter('volume'))
mp.observe_property('volume-max', 'number', create_state_setter('volume_max'))
mp.observe_property('mute', 'bool', function(_, value)
local is_initial_call = state.mute == nil
state.mute = value
if not is_initial_call then elements.volume.flash() end
end)
mp.observe_property('mute', 'bool', create_state_setter('mute'))
mp.observe_property('playback-time', 'number', function(name, val)
-- Ignore the initial call with nil value
if val == nil then return end
@@ -2640,50 +2663,6 @@ mp.observe_property('demuxer-cache-state', 'native', function(prop, cache_state)
state.cached_ranges = #cache_ranges > 0 and cache_ranges or nil
end)
mp.register_event('file-loaded', function()
-- Update selected file in playlist navigation menu
if menu:is_open('navigate-playlist') then
local index = mp.get_property_number('playlist-pos-1')
if index then elements.menu:select_item(index) end
end
-- Update selected file in directory navigation menu
if menu:is_open('navigate-directory') then
local path = mp.get_property_native('path')
if is_protocol(path) then return end
path = serialize_path(path)
local index = itable_find(elements.menu.items, function(_, item)
return item.value == path.path
end)
if index then elements.menu:select_item(index) end
end
end)
mp.register_event('seek', function()
-- Flash timeline
local position = mp.get_property_native('playback-time')
if position and state.position then
local seek_length = math.abs(position - state.position)
-- Don't flash on video looping (seek to 0) or tiny seeks (frame-step)
if position > 0.5 and seek_length > 0.5 then
elements.timeline.flash()
end
end
-- Update selected chapter in chaper navigation menu
if position and menu:is_open('navigate-chapters') then
local index = itable_find(elements.menu.items, function(_, item)
-- item.value = chapter.time
return item.value >= position
end)
if index then elements.menu:select_item(index) end
end
end)
-- CONTROLS
-- base keybinds
@@ -2757,7 +2736,10 @@ mp.add_key_binding(nil, 'load-subtitles', function()
open_file_navigation_menu(
serialize_path(path).dirname,
function(path) mp.commandv('sub-add', path) end,
options.subtitle_types
{
type = 'load-subtitles',
allowed_types = options.subtitle_types
}
)
end
end)
@@ -2778,34 +2760,64 @@ mp.add_key_binding(nil, 'navigate-playlist', function()
}
end
-- Update selected file in playlist navigation menu
function handle_file_loaded()
if menu:is_open('navigate-playlist') then
local index = mp.get_property_number('playlist-pos-1')
if index then elements.menu:select_item(index) end
end
end
menu:open(items, function(index)
mp.commandv('set', 'playlist-pos-1', tostring(index))
end, {type = 'navigate-playlist', title = 'Playlist', select_on_hover = false})
end, {
type = 'navigate-playlist',
title = 'Playlist',
select_on_hover = false,
on_open = function() mp.register_event('file-loaded', handle_file_loaded) end,
on_close = function() mp.unregister_event(handle_file_loaded) end,
})
end)
mp.add_key_binding(nil, 'navigate-chapters', function()
local items = {}
local chapters = get_normalized_chapters()
local selected_item = nil
for index, chapter in ipairs(chapters) do
-- Set as selected chapter if this is the first chapter with time lower
-- than current playing position (with 100ms leeway), or if this
-- chapters' time is the same as previously selected chapter (later
-- defined chapters are prioritized).
if state.position and (state.position + 0.1 > chapter.time or (selected_item and chapters[selected_item].time == chapter.time)) then
selected_item = index
end
items[#items + 1] = {
title = chapter.title or '',
hint = mp.format_time(chapter.time),
value = chapter.time
}
print('item', index, chapter.time, mp.format_time(chapter.time), chapter.title or '')
end
-- Select first chapter from the end with time lower
-- than current playing position (with 100ms leeway).
function get_selected_chapter_index()
local position = mp.get_property_native('playback-time')
if not position then return nil end
for index = #items, 1, -1 do
if position - 0.1 > items[index].value then return index end
end
end
-- Update selected chapter in chaper navigation menu
function seek_handler()
if menu:is_open('navigate-chapters') then
elements.menu:select_item(get_selected_chapter_index())
end
end
menu:open(items, function(time)
mp.commandv('seek', tostring(time), 'absolute')
end, {type = 'navigate-chapters', title = 'Chapters', select_on_hover = false, selected_item = selected_item})
end, {
type = 'navigate-chapters',
title = 'Chapters',
select_on_hover = false,
selected_item = get_selected_chapter_index(),
on_open = function() mp.register_event('seek', seek_handler) end,
on_close = function() mp.unregister_event(seek_handler) end
})
end)
mp.add_key_binding(nil, 'show-in-directory', function()
local path = mp.get_property_native('path')
@@ -2831,13 +2843,33 @@ end)
mp.add_key_binding(nil, 'navigate-directory', function()
local path = mp.get_property_native('path')
if not is_protocol(path) then
-- Update selected file in directory navigation menu
function handle_file_loaded()
if menu:is_open('navigate-directory') then
local path = mp.get_property_native('path')
if is_protocol(path) then return end
path = serialize_path(path)
local index = itable_find(elements.menu.items, function(_, item)
return item.value == path.path
end)
elements.menu:select_item(index)
end
end
path = serialize_path(path)
open_file_navigation_menu(
'navigate-directory',
path.dirname,
function(path) mp.commandv('loadfile', path) end,
options.media_types,
path.basename
{
type = 'navigate-directory',
allowed_types = options.media_types,
selected_file = path.path,
on_open = function() mp.register_event('file-loaded', handle_file_loaded) end,
on_close = function() mp.unregister_event(handle_file_loaded) end,
}
)
end
end)