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_size_max_fullscreen=60
# timeline opacity # timeline opacity
timeline_opacity=0.8 timeline_opacity=0.8
# adds a top border of background color to help visually separate elapsed bar # top (and bottom in no-border mode) border of background color to help visually
# from video of similar color # separate elapsed bar from a video of similar color or desktop background
# 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
timeline_border=1 timeline_border=1
# display seekable buffered ranges for streaming videos, syntax `color:opacity`, # display seekable buffered ranges for streaming videos, syntax `color:opacity`,
# color is an BBGGRR hex code, set to empty or `no` to disable # color is an BBGGRR hex code, set to empty or `no` to disable
timeline_cached_ranges=345433:0.5 timeline_cached_ranges=345433:0.5
# when video position is changed externally (e.g. hotkeys), flash the timeline # briefly show timeline on external changes (e.g. seeking with a hotkey)
# for this amount of time, set to 0 to disable timeline_flash=yes
timeline_flash_duration=300
# timeline chapters indicator style: dots, lines, lines-top, lines-bottom # timeline chapters style: dots, lines, lines-top, lines-bottom
# set to empty to disable # set to empty to disable
chapters=dots chapters=dots
# timeline chapters indicator opacity
chapters_opacity=0.3 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=right
# volume control horizontal size
volume_size=40 volume_size=40
# same as ^ but when in fullscreen
volume_size_fullscreen=40 volume_size_fullscreen=40
# volume controls opacity
volume_opacity=0.8 volume_opacity=0.8
# thin border around volume slider
volume_border=1 volume_border=1
# when clicking or dragging volume slider, volume will snap only to increments # when dragging the slider, volume will round to increments of this value
# of this value volume_rounding=1
volume_snap_to=1 # briefly show volume slider on external changes (e.g. changed by a hotkey)
# when volume is changed externally (e.g. hotkeys), flash the volume controls volume_flash=yes
# for this amount of time, set to 0 to disable
volume_flash_duration=300 # 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 # controls all menus, such as context menu, subtitle loader/selector, etc
menu_item_height=40 menu_item_height=30
menu_item_height_fullscreen=50 menu_item_height_fullscreen=45
menu_opacity=0.8 menu_opacity=0.8
# playback speed control widget: drag to change, click to reset # pause video on clicks shorter than this number of milliseconds, 0 to disable
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_on_click_shorter_than=0 pause_on_click_shorter_than=0
# proximity below which elements are fully faded in/expanded # for how long in milliseconds to show elements they're it's being flashed
proximity_min=40 flash_duration=400
# proximity above which elements are fully faded out/retracted # distances in pixels below which elements are fully faded in/out
proximity_max=120 proximity_in=40
# BBGGRR - BLUE GREEN RED hex codes proximity_out=120
# BBGGRR - BLUE GREEN RED hex color codes
color_foreground=ffffff color_foreground=ffffff
color_foreground_text=000000 color_foreground_text=000000
color_background=000000 color_background=000000
color_background_text=ffffff color_background_text=ffffff
# hide proximity based elements when mpv autohides the cursor # hide UI when mpv autohides the cursor
autohide=no autohide=no
# display window title (filename) in top window controls bar in no-border mode # display window title (filename) in top window controls bar in no-border mode
title=no 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 directory_navigation_loops=no
# file types to display in file explorer when navigating media files # 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 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 # try bumping this up
font_height_to_letter_width_ratio=0.5 font_height_to_letter_width_ratio=0.5
# `chapter_ranges` lets you transform chapter indicators into range indicators # `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 range definition syntax: # Chapter range definition syntax:
# ``` # ```

View File

@@ -7,73 +7,63 @@ timeline_size_min_fullscreen=0
timeline_size_max_fullscreen=60 timeline_size_max_fullscreen=60
# timeline opacity # timeline opacity
timeline_opacity=0.8 timeline_opacity=0.8
# adds a top border of background color to help visually separate elapsed bar # top (and bottom in no-border mode) border of background color to help visually
# from video of similar color # separate elapsed bar from a video of similar color or desktop background
# 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
timeline_border=1 timeline_border=1
# display seekable buffered ranges for streaming videos, syntax `color:opacity`, # display seekable buffered ranges for streaming videos, syntax `color:opacity`,
# color is an BBGGRR hex code, set to empty or `no` to disable # color is an BBGGRR hex code, set to empty or `no` to disable
timeline_cached_ranges=345433:0.5 timeline_cached_ranges=345433:0.5
# when video position is changed externally (e.g. hotkeys), flash the timeline # briefly show timeline on external changes (e.g. seeking with a hotkey)
# for this amount of time, set to 0 to disable timeline_flash=yes
timeline_flash_duration=300
# timeline chapters indicator style: dots, lines, lines-top, lines-bottom # timeline chapters style: dots, lines, lines-top, lines-bottom
# set to empty to disable # set to empty to disable
chapters=dots chapters=dots
# timeline chapters indicator opacity
chapters_opacity=0.3 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=right
# volume control horizontal size
volume_size=40 volume_size=40
# same as ^ but when in fullscreen
volume_size_fullscreen=40 volume_size_fullscreen=40
# volume controls opacity
volume_opacity=0.8 volume_opacity=0.8
# thin border around volume slider
volume_border=1 volume_border=1
# when clicking or dragging volume slider, volume will snap only to increments # when dragging the slider, volume will round to increments of this value
# of this value volume_rounding=1
volume_snap_to=1 # briefly show volume slider on external changes (e.g. changed by a hotkey)
# when volume is changed externally (e.g. hotkeys), flash the volume controls volume_flash=yes
# for this amount of time, set to 0 to disable
volume_flash_duration=300 # 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 # controls all menus, such as context menu, subtitle loader/selector, etc
menu_item_height=40 menu_item_height=30
menu_item_height_fullscreen=50 menu_item_height_fullscreen=45
menu_opacity=0.8 menu_opacity=0.8
# playback speed control widget: drag to change, click to reset # pause video on clicks shorter than this number of milliseconds, 0 to disable
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_on_click_shorter_than=0 pause_on_click_shorter_than=0
# proximity below which elements are fully faded in/expanded # for how long in milliseconds to show elements they're it's being flashed
proximity_min=40 flash_duration=400
# proximity above which elements are fully faded out/retracted # distances in pixels below which elements are fully faded in/out
proximity_max=120 proximity_in=40
# BBGGRR - BLUE GREEN RED hex codes proximity_out=120
# BBGGRR - BLUE GREEN RED hex color codes
color_foreground=ffffff color_foreground=ffffff
color_foreground_text=000000 color_foreground_text=000000
color_background=000000 color_background=000000
color_background_text=ffffff color_background_text=ffffff
# hide proximity based elements when mpv autohides the cursor # hide UI when mpv autohides the cursor
autohide=no autohide=no
# display window title (filename) in top window controls bar in no-border mode # display window title (filename) in top window controls bar in no-border mode
title=no 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 directory_navigation_loops=no
# file types to display in file explorer when navigating media files # 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 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 # try bumping this up
font_height_to_letter_width_ratio=0.5 font_height_to_letter_width_ratio=0.5
# `chapter_ranges` lets you transform chapter indicators into range indicators # `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 range definition syntax: # Chapter range definition syntax:
# ``` # ```

416
uosc.lua
View File

@@ -25,73 +25,63 @@ timeline_size_min_fullscreen=0
timeline_size_max_fullscreen=60 timeline_size_max_fullscreen=60
# timeline opacity # timeline opacity
timeline_opacity=0.8 timeline_opacity=0.8
# adds a top border of background color to help visually separate elapsed bar # top (and bottom in no-border mode) border of background color to help visually
# from video of similar color # separate elapsed bar from a video of similar color or desktop background
# 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
timeline_border=1 timeline_border=1
# display seekable buffered ranges for streaming videos, syntax `color:opacity`, # display seekable buffered ranges for streaming videos, syntax `color:opacity`,
# color is an BBGGRR hex code, set to empty or `no` to disable # color is an BBGGRR hex code, set to empty or `no` to disable
timeline_cached_ranges=345433:0.5 timeline_cached_ranges=345433:0.5
# when video position is changed externally (e.g. hotkeys), flash the timeline # briefly show timeline on external changes (e.g. seeking with a hotkey)
# for this amount of time, set to 0 to disable timeline_flash=yes
timeline_flash_duration=300
# timeline chapters indicator style: dots, lines, lines-top, lines-bottom # timeline chapters style: dots, lines, lines-top, lines-bottom
# set to empty to disable # set to empty to disable
chapters=dots chapters=dots
# timeline chapters indicator opacity
chapters_opacity=0.3 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=right
# volume control horizontal size
volume_size=40 volume_size=40
# same as ^ but when in fullscreen
volume_size_fullscreen=40 volume_size_fullscreen=40
# volume controls opacity
volume_opacity=0.8 volume_opacity=0.8
# thin border around volume slider
volume_border=1 volume_border=1
# when clicking or dragging volume slider, volume will snap only to increments # when dragging the slider, volume will round to increments of this value
# of this value volume_rounding=1
volume_snap_to=1 # briefly show volume slider on external changes (e.g. changed by a hotkey)
# when volume is changed externally (e.g. hotkeys), flash the volume controls volume_flash=yes
# for this amount of time, set to 0 to disable
volume_flash_duration=300 # 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 # controls all menus, such as context menu, subtitle loader/selector, etc
menu_item_height=40 menu_item_height=30
menu_item_height_fullscreen=50 menu_item_height_fullscreen=45
menu_opacity=0.8 menu_opacity=0.8
# playback speed control widget: drag to change, click to reset # pause video on clicks shorter than this number of milliseconds, 0 to disable
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_on_click_shorter_than=0 pause_on_click_shorter_than=0
# proximity below which elements are fully faded in/expanded # for how long in milliseconds to show elements they're it's being flashed
proximity_min=40 flash_duration=400
# proximity above which elements are fully faded out/retracted # distances in pixels below which elements are fully faded in/out
proximity_max=120 proximity_in=40
# BBGGRR - BLUE GREEN RED hex codes proximity_out=120
# BBGGRR - BLUE GREEN RED hex color codes
color_foreground=ffffff color_foreground=ffffff
color_foreground_text=000000 color_foreground_text=000000
color_background=000000 color_background=000000
color_background_text=ffffff color_background_text=ffffff
# hide proximity based elements when mpv autohides the cursor # hide UI when mpv autohides the cursor
autohide=no autohide=no
# display window title (filename) in top window controls bar in no-border mode # display window title (filename) in top window controls bar in no-border mode
title=no 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 directory_navigation_loops=no
# file types to display in file explorer when navigating media files # 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 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 # try bumping this up
font_height_to_letter_width_ratio=0.5 font_height_to_letter_width_ratio=0.5
# `chapter_ranges` lets you transform chapter indicators into range indicators # `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 range definition syntax: # Chapter range definition syntax:
# ``` # ```
@@ -191,7 +179,7 @@ local options = {
timeline_opacity = 0.8, timeline_opacity = 0.8,
timeline_border = 1, timeline_border = 1,
timeline_cached_ranges = '345433:0.5', timeline_cached_ranges = '345433:0.5',
timeline_flash_duration = 400, timeline_flash = true,
chapters = 'dots', chapters = 'dots',
chapters_opacity = 0.3, chapters_opacity = 0.3,
@@ -201,23 +189,24 @@ local options = {
volume_size_fullscreen = 60, volume_size_fullscreen = 60,
volume_opacity = 0.8, volume_opacity = 0.8,
volume_border = 1, volume_border = 1,
volume_snap_to = 1, volume_rounding = 1,
volume_flash_duration = 400, volume_flash = true,
speed = false, speed = false,
speed_size = 40, speed_size = 35,
speed_size_fullscreen = 60, speed_size_fullscreen = 50,
speed_opacity = 1, speed_opacity = 1,
speed_step = 0.1, speed_rounding = 0.1,
speed_flash = true,
menu_item_height = 36, menu_item_height = 36,
menu_item_height_fullscreen = 50, menu_item_height_fullscreen = 50,
menu_opacity = 0.8, menu_opacity = 0.8,
pause_on_click_shorter_than = 0, pause_on_click_shorter_than = 0,
click_duration = 110, flash_duration = 400,
proximity_min = 40, proximity_in = 40,
proximity_max = 120, proximity_out = 120,
color_foreground = 'ffffff', color_foreground = 'ffffff',
color_foreground_text = '000000', color_foreground_text = '000000',
color_background = '000000', color_background = '000000',
@@ -371,9 +360,8 @@ end
-- Creates in-between frames to animate value from `from` to `to` numbers. -- Creates in-between frames to animate value from `from` to `to` numbers.
-- Returns function that terminates animation. -- Returns function that terminates animation.
-- `to` can be a function that returns target value, useful for movable targets. -- `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. -- `callback` is called either on animation end, or when animation is canceled
-- `cleanup` callback is called either on_end, or when animation is canceled. function tween(from, to, setter, callback)
function tween(from, to, setter, on_end, cleanup)
local timeout local timeout
local getTo = type(to) == 'function' and to or function() return to end local getTo = type(to) == 'function' and to or function() return to end
local cutoff = math.abs(getTo() - from) * 0.01 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) setter(is_end and getTo() or from)
request_render() request_render()
if is_end then if is_end then
call_me_maybe(on_end) call_me_maybe(callback)
call_me_maybe(cleanup)
else else
timeout:resume() timeout:resume()
end end
@@ -393,22 +380,21 @@ function tween(from, to, setter, on_end, cleanup)
tick() tick()
return function() return function()
timeout:kill() timeout:kill()
call_me_maybe(cleanup) call_me_maybe(callback)
end end
end end
-- Kills ongoing animation if one is already running on this element. -- Kills ongoing animation if one is already running on this element.
-- Killed animation will not get its `on_end` called. -- 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) tween_element_stop(element)
element.stop_current_animation = tween( element.stop_current_animation = tween(
from, to, from, to,
function(value) setter(element, value) end, function(value) setter(element, value) end,
on_end,
function() function()
element.stop_current_animation = nil element.stop_current_animation = nil
call_me_maybe(cleanup, element) call_me_maybe(callback, element)
end end
) )
end end
@@ -424,8 +410,8 @@ function tween_element_stop(element)
end end
-- Helper to automatically use an element property setter -- Helper to automatically use an element property setter
function tween_element_property(element, prop, from, to, on_end, cleanup) function tween_element_property(element, prop, from, to, callback)
tween_element(element, from, to, function(_, value) element[prop] = value end, on_end, cleanup) tween_element(element, from, to, function(_, value) element[prop] = value end, callback)
end end
function get_point_to_rectangle_proximity(point, rect) function get_point_to_rectangle_proximity(point, rect)
@@ -603,7 +589,19 @@ Element.__index = Element
function Element.new(props) function Element.new(props)
local element = setmetatable(props, Element) local element = setmetatable(props, Element)
element._eventListeners = {} 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() element:init()
return element return element
end end
@@ -642,6 +640,18 @@ function Element:trigger(name, ...)
for _, handler in ipairs(self._eventListeners[name]) do handler(...) end for _, handler in ipairs(self._eventListeners[name]) do handler(...) end
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 -- ELEMENTS
local Elements = {itable = {}} local Elements = {itable = {}}
@@ -794,11 +804,11 @@ function Menu:open(items, open_item, opts)
update_proximities() update_proximities()
end end
end, end,
fadeout = function(this, callback) fadeout = function(this, on_end, cleanup)
tween_element(this, 1, 0, function(this, pos) this:tween(1, 0, function(this, pos)
this.opacity = pos this.opacity = pos
this:set_parent_opacity(pos * config.menu_parent_opacity) this:set_parent_opacity(pos * config.menu_parent_opacity)
end, callback) end, on_end, cleanup)
end, end,
set_parent_opacity = function(this, opacity) set_parent_opacity = function(this, opacity)
if this.parent_menu then if this.parent_menu then
@@ -814,9 +824,12 @@ function Menu:open(items, open_item, opts)
request_render() request_render()
end, end,
select_item = function(this, index) 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.selected_item = index
this:center_selected_item() this:center_selected_item()
else
this.selected_item = nil
request_render()
end end
end, end,
center_selected_item = function(this) 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, on_end = function(this) this:scroll_to(this.scroll_height) end,
render = render_menu, render = render_menu,
})) }))
elements.menu:maybe('on_open')
end end
function Menu:add_key_binding(key, name, fn, flags) 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 if elements:has('menu') and not menu.is_closing then
function close() function close()
elements.menu:maybe('on_close')
elements.menu:destroy() elements.menu:destroy()
elements:remove('menu') elements:remove('menu')
menu.is_closing = false menu.is_closing = false
@@ -1051,7 +1067,7 @@ function Menu:close(immediate, callback)
if immediate then if immediate then
close() close()
else else
elements.menu:fadeout(close) elements.menu:fadeout(nil, close)
end end
end end
end end
@@ -1171,9 +1187,9 @@ function update_element_cursor_proximity(element)
element.proximity_raw = infinity element.proximity_raw = infinity
element.proximity = 0 element.proximity = 0
else 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_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
end end
@@ -1682,7 +1698,7 @@ function render_speed(this)
-- Notches -- Notches
local speed_at_center = state.speed local speed_at_center = state.speed
if this.dragging then 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) speed_at_center = math.min(math.max(speed_at_center, 0.01), 100)
end end
local nearest_notch_speed = round(speed_at_center / this.notch_every) * this.notch_every local nearest_notch_speed = round(speed_at_center / this.notch_every) * this.notch_every
@@ -1916,34 +1932,6 @@ end
-- STATIC ELEMENTS -- 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({ elements:add('timeline', Element.new({
interactive = true, interactive = true,
pressed = false, pressed = false,
@@ -1952,13 +1940,24 @@ elements:add('timeline', Element.new({
font_size = 0, -- calculated in on_display_resize font_size = 0, -- calculated in on_display_resize
top_border = options.timeline_border, top_border = options.timeline_border,
bottom_border = 0, -- set dynamically in `border` property observer bottom_border = 0, -- set dynamically in `border` property observer
flash = create_flash_function_for('timeline'),
init = function(this) init = function(this)
-- Toggle 1px bottom border for timeline in no-border mode
mp.observe_property('border', 'bool', function(_, border) 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 this.bottom_border = not border and options.timeline_border or 0
request_render() request_render()
end) 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, end,
get_effective_proximity = function(this) get_effective_proximity = function(this)
if this.pressed then 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` height = nil, -- set in `on_display_resize` handler based on `state.fullscreen`
margin = 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 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) on_display_resize = function(this)
local left = options.volume == 'left' local left = options.volume == 'left'
this.width = (state.fullscreen or state.maximized) and options.volume_size_fullscreen or options.volume_size 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) set_from_cursor = function(this)
local volume_fraction = (this.by - cursor.y - options.volume_border) / (this.height - options.volume_border) 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 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 if state.volume ~= new_volume then mp.commandv('set', 'volume', new_volume) end
end, end,
on_mbtn_left_down = function(this) on_mbtn_left_down = function(this)
@@ -2139,29 +2152,37 @@ if options.speed then
init = function(this) init = function(this)
-- Fade out/in on timeline mouse enter/leave -- Fade out/in on timeline mouse enter/leave
elements.timeline:on('mouse_enter', function() elements.timeline:on('mouse_enter', function()
this:tween_property('forced_proximity', 1, 0, nil, function(this) if not this.dragging then this:fadeout() end
this.forced_proximity = 0
end)
end) end)
elements.timeline:on('mouse_leave', function() elements.timeline:on('mouse_leave', function()
local get_current_proximity = function() return state.interactive_proximity end if not this.dragging then this:fadein() end
this:tween_property('forced_proximity', 0, get_current_proximity, nil, function(this)
this.forced_proximity = nil
end)
end) end)
-- Flash on external changes -- Flash on external changes
mp.observe_property_native('speed', function() if options.speed_flash then
if not this.dragging then local initial_call = true
this:flash() mp.observe_property('speed', 'number', function()
end 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)
end, end,
on_display_resize = function(this) on_display_resize = function(this)
this.height = (state.fullscreen or state.maximized) and options.speed_size_fullscreen or options.speed_size this.height = (state.fullscreen or state.maximized) and options.speed_size_fullscreen or options.speed_size
this.width = this.height * 5 this.width = this.height * 5
this.notch_spacing = this.width / this.notches 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.ax = (display.width - this.width) / 2
this.by = display.height - elements.timeline.size_max - (this.height / 3) this.by = display.height - elements.timeline.size_max - (this.height / 3)
this.ay = this.by - this.height this.ay = this.by - this.height
@@ -2171,7 +2192,7 @@ if options.speed then
set_from_cursor = function(this) set_from_cursor = function(this)
local volume_fraction = (this.by - cursor.y - options.volume_border) / (this.height - options.volume_border) 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 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 if state.volume ~= new_volume then mp.commandv('set', 'volume', new_volume) end
end, end,
on_mbtn_left_down = function(this) on_mbtn_left_down = function(this)
@@ -2188,7 +2209,7 @@ if options.speed then
this.dragging.distance = cursor.x - this.dragging.start_x this.dragging.distance = cursor.x - this.dragging.start_x
local steps_dragged = round(-this.dragging.distance / this.step_distance) 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) mp.set_property_native('speed', round(new_speed * 100) / 100)
end, end,
on_mbtn_left_up = function(this) on_mbtn_left_up = function(this)
@@ -2198,6 +2219,9 @@ if options.speed then
end end
end, end,
on_global_mbtn_left_up = function(this) on_global_mbtn_left_up = function(this)
if this.dragging and elements.timeline.proximity_raw == 0 then
this:fadeout()
end
this.dragging = nil this.dragging = nil
request_render() request_render()
end, end,
@@ -2535,10 +2559,14 @@ function create_select_tracklist_type_menu_opener(menu_title, track_type, track_
end end
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) directory = serialize_path(directory)
local directories, error = utils.readdir(directory.path, 'dirs') 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 if not files or not directories then
msg.error('Retrieving files from '..directory..' failed: '..(error or '')) 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] = { items[#items + 1] = {
title = serialized.basename, title = serialized.basename,
value = serialized.path, value = serialized.path,
selected = selected_file == file selected = menu_options.selected_file == serialized.path
} }
end end
menu_options.title = directory.basename..'/'
menu_options.select_on_hover = false
menu:open(items, function(path) menu:open(items, function(path)
local meta, error = utils.file_info(path) local meta, error = utils.file_info(path)
@@ -2576,12 +2607,12 @@ function open_file_navigation_menu(menu_type, directory, handle_select, allowed_
end end
if meta.is_dir then if meta.is_dir then
open_file_navigation_menu(path, handle_select, allowed_types) open_file_navigation_menu(path, handle_select, menu_options)
else else
handle_select(path) handle_select(path)
menu:close() menu:close()
end end
end, {type = menu_type, title = directory.basename..'/', select_on_hover = false}) end, menu_options)
end end
-- VALUE SERIALIZATION/NORMALIZATION -- 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('idle-active', 'bool', create_state_setter('idle'))
mp.observe_property('speed', 'number', create_state_setter('speed')) mp.observe_property('speed', 'number', create_state_setter('speed'))
mp.observe_property('pause', 'bool', create_state_setter('paused')) mp.observe_property('pause', 'bool', create_state_setter('paused'))
mp.observe_property('volume', 'number', function(_, value) mp.observe_property('volume', 'number', create_state_setter('volume'))
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-max', 'number', create_state_setter('volume_max')) mp.observe_property('volume-max', 'number', create_state_setter('volume_max'))
mp.observe_property('mute', 'bool', function(_, value) mp.observe_property('mute', 'bool', create_state_setter('mute'))
local is_initial_call = state.mute == nil
state.mute = value
if not is_initial_call then elements.volume.flash() end
end)
mp.observe_property('playback-time', 'number', function(name, val) mp.observe_property('playback-time', 'number', function(name, val)
-- Ignore the initial call with nil value -- Ignore the initial call with nil value
if val == nil then return end 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 state.cached_ranges = #cache_ranges > 0 and cache_ranges or nil
end) 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 -- CONTROLS
-- base keybinds -- base keybinds
@@ -2757,7 +2736,10 @@ mp.add_key_binding(nil, 'load-subtitles', function()
open_file_navigation_menu( open_file_navigation_menu(
serialize_path(path).dirname, serialize_path(path).dirname,
function(path) mp.commandv('sub-add', path) end, function(path) mp.commandv('sub-add', path) end,
options.subtitle_types {
type = 'load-subtitles',
allowed_types = options.subtitle_types
}
) )
end end
end) end)
@@ -2778,34 +2760,64 @@ mp.add_key_binding(nil, 'navigate-playlist', function()
} }
end 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) menu:open(items, function(index)
mp.commandv('set', 'playlist-pos-1', tostring(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) end)
mp.add_key_binding(nil, 'navigate-chapters', function() mp.add_key_binding(nil, 'navigate-chapters', function()
local items = {} local items = {}
local chapters = get_normalized_chapters() local chapters = get_normalized_chapters()
local selected_item = nil
for index, chapter in ipairs(chapters) do 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] = { items[#items + 1] = {
title = chapter.title or '', title = chapter.title or '',
hint = mp.format_time(chapter.time), hint = mp.format_time(chapter.time),
value = 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 end
menu:open(items, function(time) menu:open(items, function(time)
mp.commandv('seek', tostring(time), 'absolute') 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) end)
mp.add_key_binding(nil, 'show-in-directory', function() mp.add_key_binding(nil, 'show-in-directory', function()
local path = mp.get_property_native('path') local path = mp.get_property_native('path')
@@ -2831,13 +2843,33 @@ end)
mp.add_key_binding(nil, 'navigate-directory', function() mp.add_key_binding(nil, 'navigate-directory', function()
local path = mp.get_property_native('path') local path = mp.get_property_native('path')
if not is_protocol(path) then 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) path = serialize_path(path)
open_file_navigation_menu( open_file_navigation_menu(
'navigate-directory',
path.dirname, path.dirname,
function(path) mp.commandv('loadfile', path) end, 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
end) end)