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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
|
--------------------------------------------------------------------------------------------------------
-----------------------------------------List Formatting------------------------------------------------
--------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------
local g = require 'modules.globals'
local o = require 'modules.options'
local fb_utils = require 'modules.utils'
local state = g.state
local style = g.style
local ass = g.ass
local function draw()
ass:update()
end
local function remove()
ass:remove()
end
---@type string[]
local string_buffer = {}
---appends the entered text to the overlay
---@param ... string
local function append(...)
for i = 1, select("#", ...) do
table.insert(string_buffer, select(i, ...) or '' )
end
end
--appends a newline character to the osd
local function newline()
table.insert(string_buffer, '\\N')
end
local function flush_buffer()
ass.data = table.concat(string_buffer, '')
string_buffer = {}
end
---detects whether or not to highlight the given entry as being played
---@param v Item
---@return boolean
local function highlight_entry(v)
if g.current_file.path == nil then return false end
local full_path = fb_utils.get_full_path(v)
local alt_path = v.name and g.state.directory..v.name or nil
if fb_utils.parseable_item(v) then
return (
string.find(g.current_file.directory, full_path, 1, true)
or (alt_path and string.find(g.current_file.directory, alt_path, 1, true))
) ~= nil
else
return g.current_file.path == full_path
or (alt_path and g.current_file.path == alt_path)
end
end
---escape ass values and replace newlines
---@param str string
---@param style_reset string?
---@return string
local function ass_escape(str, style_reset)
return fb_utils.ass_escape(str, style_reset and style.warning..'␊'..style_reset or true)
end
local header_overrides = {['^'] = style.header}
---@return number start
---@return number finish
---@return boolean is_overflowing
local function calculate_view_window()
---@type number
local start = 1
---@type number
local finish = start+o.num_entries-1
--handling cursor positioning
local mid = math.ceil(o.num_entries/2)+1
if state.selected+mid > finish then
---@type number
local offset = state.selected - finish + mid
--if we've overshot the end of the list then undo some of the offset
if finish + offset > #state.list then
offset = offset - ((finish+offset) - #state.list)
end
start = start + offset
finish = finish + offset
end
--making sure that we don't overstep the boundaries
if start < 1 then start = 1 end
local overflow = finish < #state.list
--this is necessary when the number of items in the dir is less than the max
if not overflow then finish = #state.list end
return start, finish, overflow
end
---@param i number index
---@return string
local function calculate_item_style(i)
local is_playing_file = highlight_entry(state.list[i])
--sets the selection colour scheme
local multiselected = state.selection[i]
--sets the colour for the item
local item_style = style.body
if multiselected then item_style = item_style..style.multiselect
elseif i == state.selected then item_style = item_style..style.selected end
if is_playing_file then item_style = item_style..(multiselected and style.playing_selected or style.playing) end
return item_style
end
local function draw_header()
append(style.header)
append(fb_utils.substitute_codes(o.format_string_header, header_overrides, nil, nil, function(str, code)
if code == '^' then return str end
return ass_escape(str, style.header)
end))
newline()
end
---@param wrapper_overrides ReplacerTable
local function draw_top_wrapper(wrapper_overrides)
--adding a header to show there are items above in the list
append(style.footer_header)
append(fb_utils.substitute_codes(o.format_string_topwrapper, wrapper_overrides, nil, nil, function(str)
return ass_escape(str)
end))
newline()
end
---@param wrapper_overrides ReplacerTable
local function draw_bottom_wrapper(wrapper_overrides)
append(style.footer_header)
append(fb_utils.substitute_codes(o.format_string_bottomwrapper, wrapper_overrides, nil, nil, function(str)
return ass_escape(str)
end))
end
---@param i number index
---@param cursor string
local function draw_cursor(i, cursor)
--handles custom styles for different entries
if i == state.selected or i == state.multiselect_start then
if not (i == state.selected) then append(style.selection_marker) end
if not state.multiselect_start then append(style.cursor)
else
if state.selection[state.multiselect_start] then append(style.cursor_select)
else append(style.cursor_deselect) end
end
else
append(g.style.indent)
end
append(cursor, '\\h', style.body)
end
--refreshes the ass text using the contents of the list
local function update_ass()
if state.hidden then state.flag_update = true ; return end
append(style.global)
draw_header()
if #state.list < 1 then
append(state.empty_text)
flush_buffer()
draw()
return
end
local start, finish, overflow = calculate_view_window()
-- these are the number values to place into the wrappers
local wrapper_overrides = {['<'] = tostring(start-1), ['>'] = tostring(#state.list-finish)}
if o.format_string_topwrapper ~= '' and start > 1 then
draw_top_wrapper(wrapper_overrides)
end
for i=start, finish do
local v = state.list[i]
append(style.body)
if g.ALIGN_X ~= 'right' then draw_cursor(i, o.cursor_icon) end
local item_style = calculate_item_style(i)
append(item_style)
--sets the folder icon
if v.type == 'dir' then
append(style.folder, o.folder_icon, "\\h", style.body)
append(item_style)
end
--adds the actual name of the item
append(v.ass or ass_escape(v.label or v.name, item_style), '\\h')
if g.ALIGN_X == 'right' then draw_cursor(i, o.cursor_icon_flipped) end
newline()
end
if o.format_string_bottomwrapper ~= '' and overflow then
draw_bottom_wrapper(wrapper_overrides)
end
flush_buffer()
draw()
end
---@class ass
return {
update_ass = update_ass,
highlight_entry = highlight_entry,
draw = draw,
remove = remove,
}
|