sup

A curses threads-with-tags style email client

sup.git

git clone https://supmua.dev/git/sup/

lib/sup/modes/line_cursor_mode.rb (4970B) - raw

      1 module Redwood
      2 
      3 ## extends ScrollMode to have a line-based cursor.
      4 class LineCursorMode < ScrollMode
      5   register_keymap do |k|
      6     ## overwrite scrollmode binding on arrow keys for cursor movement
      7     ## but j and k still scroll!
      8     k.add :cursor_down, "Move cursor down one line", :down, 'j'
      9     k.add :cursor_up, "Move cursor up one line", :up, 'k'
     10     k.add :select, "Select this item", :enter
     11   end
     12 
     13   attr_reader :curpos
     14 
     15   def initialize opts={}
     16     @cursor_top = @curpos = opts.delete(:skip_top_rows) || 0
     17     @load_more_callbacks = []
     18     @load_more_q = Queue.new
     19     @load_more_thread = ::Thread.new do
     20       while true
     21         e = @load_more_q.pop
     22         @load_more_callbacks.each { |c| c.call e }
     23         hysteresis = $config[:load_more_threads_hysteresis] || 0.5
     24         if hysteresis > 0 then
     25           sleep hysteresis
     26           @load_more_q.pop until @load_more_q.empty?
     27         end
     28       end
     29     end
     30 
     31     super opts
     32   end
     33 
     34   def spawned; call_load_more_callbacks buffer.content_height + 1; end
     35 
     36   def cleanup
     37     @load_more_thread.kill
     38     super
     39   end
     40 
     41   def draw
     42     super
     43     set_status
     44   end
     45 
     46 protected
     47 
     48   ## callbacks when the cursor is asked to go beyond the bottom
     49   def to_load_more &b
     50     @load_more_callbacks << b
     51   end
     52 
     53   def draw_line ln, opts={}
     54     if ln == @curpos
     55       super ln, :highlight => true, :debug => opts[:debug], :color => :text_color
     56     else
     57       super ln, :color => :text_color
     58     end
     59   end
     60 
     61   def ensure_mode_validity
     62     super
     63     raise @curpos.inspect unless @curpos.is_a?(Integer)
     64     c = @curpos.clamp topline, botline - 1
     65     c = @cursor_top if c < @cursor_top
     66     buffer.mark_dirty unless c == @curpos
     67     @curpos = c
     68   end
     69 
     70   def set_cursor_pos p
     71     return if @curpos == p
     72     @curpos = p.clamp @cursor_top, lines
     73     buffer.mark_dirty if buffer # not sure why the buffer is gone
     74     set_status
     75   end
     76 
     77   ## override search behavior to be cursor-based. this is a stupid
     78   ## implementation and should be made better. TODO: improve.
     79   def search_goto_line line
     80     page_down while line >= botline
     81     page_up while line < topline
     82     set_cursor_pos line
     83   end
     84 
     85   def search_start_line; @curpos end
     86 
     87   def line_down # overwrite scrollmode
     88     super
     89     call_load_more_callbacks([topline + buffer.content_height - lines, 10].max) if topline + buffer.content_height > lines
     90     set_cursor_pos topline if @curpos < topline
     91   end
     92 
     93   def line_up # overwrite scrollmode
     94     super
     95     set_cursor_pos botline - 1 if @curpos > botline - 1
     96   end
     97 
     98   def cursor_down
     99     call_load_more_callbacks buffer.content_height if @curpos >= lines - [buffer.content_height/2,1].max
    100     return false unless @curpos < lines - 1
    101 
    102     if $config[:continuous_scroll] and (@curpos == botline - 3 and @curpos < lines - 3)
    103       # load more lines, one at a time.
    104       jump_to_line topline + 1
    105       @curpos += 1
    106       unless buffer.dirty?
    107         draw_line @curpos - 1
    108         draw_line @curpos
    109         set_status
    110         buffer.commit
    111       end
    112     elsif @curpos >= botline - 1
    113       page_down
    114       set_cursor_pos topline
    115     else
    116       @curpos += 1
    117       unless buffer.dirty?
    118         draw_line @curpos - 1
    119         draw_line @curpos
    120         set_status
    121         buffer.commit
    122       end
    123     end
    124     true
    125   end
    126 
    127   def cursor_up
    128     return false unless @curpos > @cursor_top
    129 
    130     if $config[:continuous_scroll] and (@curpos == topline + 2)
    131       jump_to_line topline - 1
    132       @curpos -= 1
    133       unless buffer.dirty?
    134         draw_line @curpos + 1
    135         draw_line @curpos
    136         set_status
    137         buffer.commit
    138       end
    139     elsif @curpos == topline
    140       old_topline = topline
    141       page_up
    142       set_cursor_pos [old_topline - 1, topline].max
    143     else
    144       @curpos -= 1
    145       unless buffer.dirty?
    146         draw_line @curpos + 1
    147         draw_line @curpos
    148         set_status
    149         buffer.commit
    150       end
    151     end
    152     true
    153   end
    154 
    155   def page_up # overwrite
    156     if topline <= @cursor_top
    157       set_cursor_pos @cursor_top
    158     else
    159       relpos = @curpos - topline
    160       super
    161       set_cursor_pos topline + relpos
    162     end
    163   end
    164 
    165   def half_page_up
    166     super
    167     set_cursor_pos [botline, @curpos].min
    168   end
    169 
    170   def page_down
    171     relpos = @curpos - topline
    172     super
    173     set_cursor_pos [topline + relpos, lines - 1].min
    174     call_load_more_callbacks buffer.content_height if lines < topline + buffer.content_height
    175   end
    176 
    177   def half_page_down
    178     super
    179     set_cursor_pos [topline, @curpos].max
    180     call_load_more_callbacks buffer.content_height if lines < topline + buffer.content_height
    181   end
    182 
    183   def jump_to_start
    184     super
    185     set_cursor_pos @cursor_top
    186   end
    187 
    188   def jump_to_end
    189     super if topline < (lines - buffer.content_height)
    190     set_cursor_pos(lines - 1)
    191     call_load_more_callbacks buffer.content_height
    192   end
    193 
    194 private
    195 
    196   def set_status
    197     l = lines
    198     @status = l > 0 ? "line #{@curpos + 1} of #{l}" : ""
    199   end
    200 
    201   def call_load_more_callbacks size
    202     @load_more_q.push size if $config[:load_more_threads_when_scrolling]
    203   end
    204 end
    205 end