feat: improve volume slider visuals

I wanted to round the nudge as well, but thats just too much math and bezier curves.
This commit is contained in:
tomasklaen
2022-09-14 13:50:31 +02:00
parent 3d21df656b
commit 1f18b8b751
2 changed files with 84 additions and 63 deletions

View File

@@ -84,9 +84,9 @@ controls_persistency=
# Where to display volume controls: none, left, right # Where to display volume controls: none, left, right
volume=right volume=right
volume_size=40 volume_size=40
volume_size_fullscreen=60 volume_size_fullscreen=52
volume_persistency= volume_persistency=
volume_opacity=0.8 volume_opacity=0.9
volume_border=1 volume_border=1
volume_step=1 volume_step=1

View File

@@ -14,6 +14,7 @@ local utils = require('mp.utils')
local msg = require('mp.msg') local msg = require('mp.msg')
local osd = mp.create_osd_overlay('ass-events') local osd = mp.create_osd_overlay('ass-events')
local infinity = 1e309 local infinity = 1e309
local quarter_pi_sin = math.sin(math.pi/4)
--[[ BASE HELPERS ]] --[[ BASE HELPERS ]]
@@ -168,9 +169,9 @@ local options = {
volume = 'right', volume = 'right',
volume_size = 40, volume_size = 40,
volume_size_fullscreen = 60, volume_size_fullscreen = 52,
volume_persistency = '', volume_persistency = '',
volume_opacity = 0.8, volume_opacity = 0.9,
volume_border = 1, volume_border = 1,
volume_step = 1, volume_step = 1,
@@ -2589,8 +2590,8 @@ function Timeline:init()
end end
function Timeline:get_visibility() function Timeline:get_visibility()
return Elements.controls return Elements.controls and math.max(Elements.controls.proximity, Element.get_visibility(self))
and math.max(Elements.controls.proximity, Element.get_visibility(self)) or Element.get_visibility(self) or Element.get_visibility(self)
end end
function Timeline:decide_enabled() function Timeline:decide_enabled()
@@ -2768,7 +2769,7 @@ function Timeline:render()
clip = dots and '\\iclip(' .. foreground_coordinates .. ')' or nil, clip = dots and '\\iclip(' .. foreground_coordinates .. ')' or nil,
opacity = options.timeline_chapters_opacity, opacity = options.timeline_chapters_opacity,
} }
if dots then if dots then
local cx, dx = math.max(ax, fax), math.min(bx, fbx) local cx, dx = math.max(ax, fax), math.min(bx, fbx)
-- 0.5 because clipping coordinates are rounded -- 0.5 because clipping coordinates are rounded
@@ -3350,6 +3351,7 @@ function VolumeSlider:init(props)
self.nudge_size = 0 self.nudge_size = 0
self.draw_nudge = false self.draw_nudge = false
self.spacing = 0 self.spacing = 0
self.radius = 1
end end
function VolumeSlider:set_volume(volume) function VolumeSlider:set_volume(volume)
@@ -3370,6 +3372,7 @@ function VolumeSlider:on_coordinates()
self.nudge_size = round(width * 0.18) self.nudge_size = round(width * 0.18)
self.draw_nudge = self.ay < self.nudge_y self.draw_nudge = self.ay < self.nudge_y
self.spacing = round(width * 0.2) self.spacing = round(width * 0.2)
self.radius = math.max(2, (self.bx - self.ax) / 10)
end end
function VolumeSlider:on_mbtn_left_down() function VolumeSlider:on_mbtn_left_down()
self.pressed = true self.pressed = true
@@ -3385,66 +3388,82 @@ function VolumeSlider:on_wheel_down() self:set_volume(state.volume - options.vol
function VolumeSlider:render() function VolumeSlider:render()
local visibility = self:get_visibility() local visibility = self:get_visibility()
local width, height = self.bx - self.ax, self.by - self.ay local ax, ay, bx, by = self.ax, self.ay, self.bx, self.by
local width, height = bx - ax, by - ay
if width <= 0 or height <= 0 or visibility <= 0 then return end if width <= 0 or height <= 0 or visibility <= 0 then return end
local ass = assdraw.ass_new() local ass = assdraw.ass_new()
local nudge_y, nudge_size = self.draw_nudge and self.nudge_y or -infinity, self.nudge_size 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 +
-- Background bar coordinates ((height - (options.volume_border * 2)) * (1 - math.min(state.volume / state.volume_max, 1)))
local bax, bay, bbx, bby = self.ax, self.ay, self.bx, self.by
-- Foreground bar coordinates
local height_without_border = height - (options.volume_border * 2)
local fax = self.ax + options.volume_border
local fay = self.ay + (height_without_border * (1 - math.min(state.volume / state.volume_max, 1))) +
options.volume_border
local fbx = self.bx - options.volume_border
local fby = self.by - options.volume_border
-- Draws a rectangle with nudge at requested position -- Draws a rectangle with nudge at requested position
---@param ax number ---@param p number Padding from slider edges.
---@param ay number ---@param cy? number A y coordinate where to clip the path from the bottom.
---@param bx number function create_nudged_path(p, cy)
---@param by number cy = cy or ay + p
function make_nudged_path(ax, ay, bx, by) local ax, bx, by = ax + p, bx - p, by - p
local fg_path = assdraw.ass_new() local r = math.max(1, self.radius - p)
fg_path:move_to(bx, by) local d, rh = r * 2, r / 2
fg_path:line_to(ax, by) local nudge_size = ((quarter_pi_sin * (nudge_size - p)) + p) / quarter_pi_sin
local nudge_bottom_y = nudge_y + nudge_size local path = assdraw.ass_new()
if ay <= nudge_bottom_y then path:move_to(bx - r, by)
fg_path:line_to(ax, math.min(nudge_bottom_y)) path:line_to(ax + r, by)
if ay <= nudge_y then if cy > by - d then
fg_path:line_to((ax + nudge_size), nudge_y) local subtracted_radius = (d - (cy - (by - d))) / 2
local nudge_top_y = nudge_y - nudge_size local xbd = (r - subtracted_radius * 1.35) -- x bezier delta
if ay <= nudge_top_y then path:bezier_curve(ax + xbd, by, ax + xbd, cy, ax + r, cy)
fg_path:line_to(ax, nudge_top_y) path:line_to(bx - r, cy)
fg_path:line_to(ax, ay) path:bezier_curve(bx - xbd, cy, bx - xbd, by, bx - r, by)
fg_path:line_to(bx, ay)
fg_path:line_to(bx, nudge_top_y)
else
local triangle_side = ay - nudge_top_y
fg_path:line_to((ax + triangle_side), ay)
fg_path:line_to((bx - triangle_side), ay)
end
fg_path:line_to((bx - nudge_size), nudge_y)
else
local triangle_side = nudge_bottom_y - ay
fg_path:line_to((ax + triangle_side), ay)
fg_path:line_to((bx - triangle_side), ay)
end
fg_path:line_to(bx, nudge_bottom_y)
else else
fg_path:line_to(ax, ay) path:bezier_curve(ax + rh, by, ax, by - rh, ax, by - r)
fg_path:line_to(bx, ay) local nudge_bottom_y = nudge_y + nudge_size
if cy + rh <= nudge_bottom_y then
path:line_to(ax, nudge_bottom_y)
if cy <= nudge_y then
path:line_to((ax + nudge_size), nudge_y)
local nudge_top_y = nudge_y - nudge_size
if cy <= nudge_top_y then
local r, rh = r, rh
if cy > nudge_top_y - r then
r = nudge_top_y - cy
rh = r / 2
end
path:line_to(ax, nudge_top_y)
path:line_to(ax, cy + r)
path:bezier_curve(ax, cy + rh, ax + rh, cy, ax + r, cy)
path:line_to(bx - r, cy)
path:bezier_curve(bx - rh, cy, bx, cy + rh, bx, cy + r)
path:line_to(bx, nudge_top_y)
else
local triangle_side = cy - nudge_top_y
path:line_to((ax + triangle_side), cy)
path:line_to((bx - triangle_side), cy)
end
path:line_to((bx - nudge_size), nudge_y)
else
local triangle_side = nudge_bottom_y - cy
path:line_to((ax + triangle_side), cy)
path:line_to((bx - triangle_side), cy)
end
path:line_to(bx, nudge_bottom_y)
else
path:line_to(ax, cy + r)
path:bezier_curve(ax, cy + rh, ax + rh, cy, ax + r, cy)
path:line_to(bx - r, cy)
path:bezier_curve(bx - rh, cy, bx, cy + rh, bx, cy + r)
end
path:line_to(bx, by - r)
path:bezier_curve(bx, by - rh, bx - rh, by, bx - r, by)
end end
fg_path:line_to(bx, by) return path
return fg_path
end end
-- FG & BG paths -- BG & FG paths
local fg_path = make_nudged_path(fax, fay, fbx, fby) local bg_path = create_nudged_path(0)
local bg_path = make_nudged_path(bax, bay, bbx, bby) local fg_path = create_nudged_path(options.volume_border, volume_y)
-- Background -- Background
ass:new_event() ass:new_event()
@@ -3469,13 +3488,13 @@ function VolumeSlider:render()
local volume_string = tostring(round(state.volume * 10) / 10) local volume_string = tostring(round(state.volume * 10) / 10)
local font_size = round(((width * 0.6) - (#volume_string * (width / 20))) * options.font_scale) local font_size = round(((width * 0.6) - (#volume_string * (width / 20))) * options.font_scale)
local opacity = math.min(options.volume_opacity + 0.1, 1) * visibility local opacity = math.min(options.volume_opacity + 0.1, 1) * visibility
if fay < self.by - self.spacing then if volume_y < self.by - self.spacing then
ass:txt(self.ax + (width / 2), self.by - self.spacing, 2, volume_string, { ass:txt(self.ax + (width / 2), self.by - self.spacing, 2, volume_string, {
size = font_size, color = options.color_foreground_text, opacity = opacity, size = font_size, color = options.color_foreground_text, opacity = opacity,
clip = '\\clip(' .. fg_path.scale .. ', ' .. fg_path.text .. ')', clip = '\\clip(' .. fg_path.scale .. ', ' .. fg_path.text .. ')',
}) })
end end
if fay > self.by - self.spacing - font_size then if volume_y > self.by - self.spacing - font_size then
ass:txt(self.ax + (width / 2), self.by - self.spacing, 2, volume_string, { ass:txt(self.ax + (width / 2), self.by - self.spacing, 2, volume_string, {
size = font_size, color = options.color_background_text, opacity = opacity, size = font_size, color = options.color_background_text, opacity = opacity,
clip = '\\iclip(' .. fg_path.scale .. ', ' .. fg_path.text .. ')', clip = '\\iclip(' .. fg_path.scale .. ', ' .. fg_path.text .. ')',
@@ -3485,9 +3504,7 @@ function VolumeSlider:render()
-- Disabled stripes for no audio -- Disabled stripes for no audio
if not state.has_audio then if not state.has_audio then
-- Create 100 foreground clip path -- Create 100 foreground clip path
local f100ax, f100ay = self.ax + options.volume_border, self.ay + options.volume_border local fg_100_path = create_nudged_path(options.volume_border)
local f100bx, f100by = self.bx - options.volume_border, self.by - options.volume_border
local fg_100_path = make_nudged_path(f100ax, f100ay, f100bx, f100by)
-- Render stripes -- Render stripes
local stripe_height = 12 local stripe_height = 12
@@ -3531,6 +3548,10 @@ function Volume:init()
self.slider = VolumeSlider:new({anchor_id = 'volume'}) self.slider = VolumeSlider:new({anchor_id = 'volume'})
end end
function Volume:get_visibility()
return self.slider.pressed and 1 or Element.get_visibility(self)
end
function Volume:update_dimensions() function Volume:update_dimensions()
local width = state.fullormaxed and options.volume_size_fullscreen or options.volume_size local width = state.fullormaxed and options.volume_size_fullscreen or options.volume_size
local controls, timeline, top_bar = Elements.controls, Elements.timeline, Elements.top_bar local controls, timeline, top_bar = Elements.controls, Elements.timeline, Elements.top_bar