summaryrefslogtreecommitdiffstats
path: root/.config/mpv/scripts/uosc_shared/elements/TopBar.lua
blob: 977d5b85db919791f9ae4825ccb82fe73f6660f6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
local Element = require('uosc_shared/elements/Element')

---@alias TopBarButtonProps {icon: string; background: string; anchor_id?: string; command: string|fun()}

---@class TopBarButton : Element
local TopBarButton = class(Element)

---@param id string
---@param props TopBarButtonProps
function TopBarButton:new(id, props) return Class.new(self, id, props) --[[@as TopBarButton]] end
function TopBarButton:init(id, props)
	Element.init(self, id, props)
	self.anchor_id = 'top_bar'
	self.icon = props.icon
	self.background = props.background
	self.command = props.command
end

function TopBarButton:on_mbtn_left_down()
	mp.command(type(self.command) == 'function' and self.command() or self.command)
end

function TopBarButton:render()
	local visibility = self:get_visibility()
	if visibility <= 0 then return end
	local ass = assdraw.ass_new()

	-- Background on hover
	if self.proximity_raw == 0 then
		ass:rect(self.ax, self.ay, self.bx, self.by, {color = self.background, opacity = visibility})
	end

	local width, height = self.bx - self.ax, self.by - self.ay
	local icon_size = math.min(width, height) * 0.5
	ass:icon(self.ax + width / 2, self.ay + height / 2, icon_size, self.icon, {
		opacity = visibility, border = options.text_border,
	})

	return ass
end

--[[ TopBar ]]

---@class TopBar : Element
local TopBar = class(Element)

function TopBar:new() return Class.new(self) --[[@as TopBar]] end
function TopBar:init()
	Element.init(self, 'top_bar')
	self.pressed = false
	self.size, self.size_max, self.size_min = 0, 0, 0
	self.icon_size, self.spacing, self.font_size, self.title_bx = 1, 1, 1, 1
	self.size_min_override = options.timeline_start_hidden and 0 or nil
	self.top_border = options.timeline_border

	local function decide_maximized_command()
		return state.border
			and (state.fullscreen and 'set fullscreen no;cycle window-maximized' or 'cycle window-maximized')
			or 'set window-maximized no;cycle fullscreen'
	end

	-- Order aligns from right to left
	self.buttons = {
		TopBarButton:new('tb_close', {icon = 'close', background = '2311e8', command = 'quit'}),
		TopBarButton:new('tb_max', {icon = 'crop_square', background = '222222', command = decide_maximized_command}),
		TopBarButton:new('tb_min', {icon = 'minimize', background = '222222', command = 'cycle window-minimized'}),
	}
end

function TopBar:decide_enabled()
	if options.top_bar == 'no-border' then
		self.enabled = not state.border or state.fullscreen
	else
		self.enabled = options.top_bar == 'always'
	end
	self.enabled = self.enabled and (options.top_bar_controls or options.top_bar_title)
	for _, element in ipairs(self.buttons) do
		element.enabled = self.enabled and options.top_bar_controls
	end
end

function TopBar:update_dimensions()
	self.size = state.fullormaxed and options.top_bar_size_fullscreen or options.top_bar_size
	self.icon_size = round(self.size * 0.5)
	self.spacing = math.ceil(self.size * 0.25)
	self.font_size = math.floor((self.size - (self.spacing * 2)) * options.font_scale)
	self.button_width = round(self.size * 1.15)
	self.ay = Elements.window_border.size
	self.bx = display.width - Elements.window_border.size
	self.by = self.size + Elements.window_border.size
	self.title_bx = self.bx - (options.top_bar_controls and (self.button_width * 3) or 0)
	self.ax = options.top_bar_title and Elements.window_border.size or self.title_bx

	local button_bx = self.bx
	for _, element in pairs(self.buttons) do
		element.ax, element.bx = button_bx - self.button_width, button_bx
		element.ay, element.by = self.ay, self.by
		button_bx = button_bx - self.button_width
	end
end

function TopBar:on_prop_border()
	self:decide_enabled()
	self:update_dimensions()
end

function TopBar:on_prop_fullscreen()
	self:decide_enabled()
	self:update_dimensions()
end

function TopBar:on_prop_maximized()
	self:decide_enabled()
	self:update_dimensions()
end

function TopBar:on_display() self:update_dimensions() end

function TopBar:render()
	local visibility = self:get_visibility()
	if visibility <= 0 then return end
	local ass = assdraw.ass_new()

	-- Window title
	if options.top_bar_title and (state.title or state.has_playlist) then
		local bg_margin = math.floor((self.size - self.font_size) / 4)
		local padding = self.font_size / 2
		local title_ax = self.ax + bg_margin
		local title_ay = self.ay + bg_margin
		local max_bx = self.title_bx - self.spacing

		-- Playlist position
		if state.has_playlist then
			local text = state.playlist_pos .. '' .. state.playlist_count
			local formatted_text = '{\\b1}' .. state.playlist_pos .. '{\\b0\\fs' .. self.font_size * 0.9 .. '}/'
				.. state.playlist_count
			local opts = {size = self.font_size, wrap = 2, color = fgt, opacity = visibility}
			local bx = round(title_ax + text_width(text, opts) + padding * 2)
			ass:rect(title_ax, title_ay, bx, self.by - bg_margin, {color = fg, opacity = visibility, radius = 2})
			ass:txt(title_ax + (bx - title_ax) / 2, self.ay + (self.size / 2), 5, formatted_text, opts)
			title_ax = bx + bg_margin
		end

		-- Title
		local text = state.title
		if max_bx - title_ax > self.font_size * 3 and text and text ~= '' then
			local opts = {
				size = self.font_size, wrap = 2, color = bgt, border = 1, border_color = bg, opacity = visibility,
				clip = string.format('\\clip(%d, %d, %d, %d)', self.ax, self.ay, max_bx, self.by),
			}
			local bx = math.min(max_bx, title_ax + text_width(text, opts) + padding * 2)
			local by = self.by - bg_margin
			ass:rect(title_ax, title_ay, bx, by, {
				color = bg, opacity = visibility * options.top_bar_title_opacity, radius = 2,
			})
			ass:txt(title_ax + padding, self.ay + (self.size / 2), 4, text, opts)
			title_ay = by + 1
		end

		-- Subtitle: current chapter
		if state.current_chapter and max_bx - title_ax > self.font_size * 3 then
			local font_size = self.font_size * 0.8
			local height = font_size * 1.5
			local text = '└ ' .. state.current_chapter.index .. ': ' .. state.current_chapter.title
			local by = title_ay + height
			local opts = {
				size = font_size, italic = true, wrap = 2, color = bgt,
				border = 1, border_color = bg, opacity = visibility * 0.8,
			}
			local bx = math.min(max_bx, title_ax + text_width(text, opts) + padding * 2)
			opts.clip = string.format('\\clip(%d, %d, %d, %d)', title_ax, title_ay, bx, by)
			ass:rect(title_ax, title_ay, bx, by, {
				color = bg, opacity = visibility * options.top_bar_title_opacity, radius = 2,
			})
			ass:txt(title_ax + padding, title_ay + height / 2, 4, text, opts)
		end
	end

	return ass
end

return TopBar