ass.lua (6797B)
1 -------------------------------------------------------------------------------------------------------- 2 -----------------------------------------List Formatting------------------------------------------------ 3 -------------------------------------------------------------------------------------------------------- 4 -------------------------------------------------------------------------------------------------------- 5 6 local g = require 'modules.globals' 7 local o = require 'modules.options' 8 local fb_utils = require 'modules.utils' 9 10 local state = g.state 11 local style = g.style 12 local ass = g.ass 13 14 local function draw() 15 ass:update() 16 end 17 18 local function remove() 19 ass:remove() 20 end 21 22 ---@type string[] 23 local string_buffer = {} 24 25 ---appends the entered text to the overlay 26 ---@param ... string 27 local function append(...) 28 for i = 1, select("#", ...) do 29 table.insert(string_buffer, select(i, ...) or '' ) 30 end 31 end 32 33 --appends a newline character to the osd 34 local function newline() 35 table.insert(string_buffer, '\\N') 36 end 37 38 local function flush_buffer() 39 ass.data = table.concat(string_buffer, '') 40 string_buffer = {} 41 end 42 43 ---detects whether or not to highlight the given entry as being played 44 ---@param v Item 45 ---@return boolean 46 local function highlight_entry(v) 47 if g.current_file.path == nil then return false end 48 local full_path = fb_utils.get_full_path(v) 49 local alt_path = v.name and g.state.directory..v.name or nil 50 51 if fb_utils.parseable_item(v) then 52 return ( 53 string.find(g.current_file.directory, full_path, 1, true) 54 or (alt_path and string.find(g.current_file.directory, alt_path, 1, true)) 55 ) ~= nil 56 else 57 return g.current_file.path == full_path 58 or (alt_path and g.current_file.path == alt_path) 59 end 60 end 61 62 ---escape ass values and replace newlines 63 ---@param str string 64 ---@param style_reset string? 65 ---@return string 66 local function ass_escape(str, style_reset) 67 return fb_utils.ass_escape(str, style_reset and style.warning..'␊'..style_reset or true) 68 end 69 70 local header_overrides = {['^'] = style.header} 71 72 ---@return number start 73 ---@return number finish 74 ---@return boolean is_overflowing 75 local function calculate_view_window() 76 ---@type number 77 local start = 1 78 ---@type number 79 local finish = start+o.num_entries-1 80 81 --handling cursor positioning 82 local mid = math.ceil(o.num_entries/2)+1 83 if state.selected+mid > finish then 84 ---@type number 85 local offset = state.selected - finish + mid 86 87 --if we've overshot the end of the list then undo some of the offset 88 if finish + offset > #state.list then 89 offset = offset - ((finish+offset) - #state.list) 90 end 91 92 start = start + offset 93 finish = finish + offset 94 end 95 96 --making sure that we don't overstep the boundaries 97 if start < 1 then start = 1 end 98 local overflow = finish < #state.list 99 --this is necessary when the number of items in the dir is less than the max 100 if not overflow then finish = #state.list end 101 102 return start, finish, overflow 103 end 104 105 ---@param i number index 106 ---@return string 107 local function calculate_item_style(i) 108 local is_playing_file = highlight_entry(state.list[i]) 109 110 --sets the selection colour scheme 111 local multiselected = state.selection[i] 112 113 --sets the colour for the item 114 local item_style = style.body 115 116 if multiselected then item_style = item_style..style.multiselect 117 elseif i == state.selected then item_style = item_style..style.selected end 118 119 if is_playing_file then item_style = item_style..(multiselected and style.playing_selected or style.playing) end 120 121 return item_style 122 end 123 124 local function draw_header() 125 append(style.header) 126 append(fb_utils.substitute_codes(o.format_string_header, header_overrides, nil, nil, function(str, code) 127 if code == '^' then return str end 128 return ass_escape(str, style.header) 129 end)) 130 newline() 131 end 132 133 ---@param wrapper_overrides ReplacerTable 134 local function draw_top_wrapper(wrapper_overrides) 135 --adding a header to show there are items above in the list 136 append(style.footer_header) 137 append(fb_utils.substitute_codes(o.format_string_topwrapper, wrapper_overrides, nil, nil, function(str) 138 return ass_escape(str) 139 end)) 140 newline() 141 end 142 143 ---@param wrapper_overrides ReplacerTable 144 local function draw_bottom_wrapper(wrapper_overrides) 145 append(style.footer_header) 146 append(fb_utils.substitute_codes(o.format_string_bottomwrapper, wrapper_overrides, nil, nil, function(str) 147 return ass_escape(str) 148 end)) 149 end 150 151 ---@param i number index 152 ---@param cursor string 153 local function draw_cursor(i, cursor) 154 --handles custom styles for different entries 155 if i == state.selected or i == state.multiselect_start then 156 if not (i == state.selected) then append(style.selection_marker) end 157 158 if not state.multiselect_start then append(style.cursor) 159 else 160 if state.selection[state.multiselect_start] then append(style.cursor_select) 161 else append(style.cursor_deselect) end 162 end 163 else 164 append(g.style.indent) 165 end 166 append(cursor, '\\h', style.body) 167 end 168 169 --refreshes the ass text using the contents of the list 170 local function update_ass() 171 if state.hidden then state.flag_update = true ; return end 172 173 append(style.global) 174 draw_header() 175 176 if #state.list < 1 then 177 append(state.empty_text) 178 flush_buffer() 179 draw() 180 return 181 end 182 183 local start, finish, overflow = calculate_view_window() 184 185 -- these are the number values to place into the wrappers 186 local wrapper_overrides = {['<'] = tostring(start-1), ['>'] = tostring(#state.list-finish)} 187 if o.format_string_topwrapper ~= '' and start > 1 then 188 draw_top_wrapper(wrapper_overrides) 189 end 190 191 for i=start, finish do 192 local v = state.list[i] 193 append(style.body) 194 if g.ALIGN_X ~= 'right' then draw_cursor(i, o.cursor_icon) end 195 196 local item_style = calculate_item_style(i) 197 append(item_style) 198 199 --sets the folder icon 200 if v.type == 'dir' then 201 append(style.folder, o.folder_icon, "\\h", style.body) 202 append(item_style) 203 end 204 205 --adds the actual name of the item 206 append(v.ass or ass_escape(v.label or v.name, item_style), '\\h') 207 if g.ALIGN_X == 'right' then draw_cursor(i, o.cursor_icon_flipped) end 208 newline() 209 end 210 211 if o.format_string_bottomwrapper ~= '' and overflow then 212 draw_bottom_wrapper(wrapper_overrides) 213 end 214 215 flush_buffer() 216 draw() 217 end 218 219 ---@class ass 220 return { 221 update_ass = update_ass, 222 highlight_entry = highlight_entry, 223 draw = draw, 224 remove = remove, 225 }