nirish

config files for niri window manager
Log | Files | Refs | README | LICENSE

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 }