module Redwood
+## extends ScrollMode to have a line-based cursor.
class LineCursorMode < ScrollMode
register_keymap do |k|
## overwrite scrollmode binding on arrow keys for cursor movement
attr_reader :curpos
- def initialize cursor_top=0, opts={}
- @cursor_top = cursor_top
- @curpos = cursor_top
+ def initialize opts={}
+ @cursor_top = @curpos = opts.delete(:skip_top_rows) || 0
+ @load_more_callbacks = []
+ @load_more_callbacks_m = Mutex.new
+ @load_more_callbacks_active = false
super opts
end
protected
+ ## callbacks when the cursor is asked to go beyond the bottom
+ def to_load_more &b
+ @load_more_callbacks << b
+ end
+
def draw_line ln, opts={}
if ln == @curpos
super ln, :highlight => true, :debug => opts[:debug]
buffer.mark_dirty
end
+ ## override search behavior to be cursor-based. this is a stupid
+ ## implementation and should be made better. TODO: improve.
+ def search_goto_line line
+ page_down while line >= botline
+ page_up while line < topline
+ set_cursor_pos line
+ end
+
+ def search_start_line; @curpos end
+
def line_down # overwrite scrollmode
super
+ call_load_more_callbacks([topline + buffer.content_height - lines, 10].max) if topline + buffer.content_height > lines
set_cursor_pos topline if @curpos < topline
end
end
def cursor_down
+ call_load_more_callbacks buffer.content_height if @curpos == lines - 1
return false unless @curpos < lines - 1
+
if @curpos >= botline - 1
page_down
- set_cursor_pos [topline + 1, botline].min
+ set_cursor_pos topline
else
@curpos += 1
unless buffer.dirty?
def cursor_up
return false unless @curpos > @cursor_top
if @curpos == topline
+ old_topline = topline
page_up
- set_cursor_pos [botline - 2, topline].max
-# raise "cursor position now #@curpos, topline #{topline} botline #{botline}"
+ set_cursor_pos [old_topline - 1, topline].max
else
@curpos -= 1
unless buffer.dirty?
end
end
+ ## more complicated than one might think. three behaviors.
def page_down
- if topline >= lines - buffer.content_height
+ ## if we're on the last page, and it's not a full page, just move
+ ## the cursor down to the bottom and assume we can't load anything
+ ## else via the callbacks.
+ if topline > lines - buffer.content_height
set_cursor_pos(lines - 1)
+
+ ## if we're on the last page, and it's a full page, try and load
+ ## more lines via the callbacks and then shift the page down
+ elsif topline == lines - buffer.content_height
+ call_load_more_callbacks buffer.content_height
+ super
+
+ ## otherwise, just move down
else
relpos = @curpos - topline
super
end
end
- def jump_to_home
+ def jump_to_start
super
set_cursor_pos @cursor_top
end
private
def set_status
- @status = "line #{@curpos + 1} of #{lines}"
+ l = lines
+ @status = l > 0 ? "line #{@curpos + 1} of #{l}" : ""
end
+ def call_load_more_callbacks size
+ go =
+ @load_more_callbacks_m.synchronize do
+ if @load_more_callbacks_active
+ false
+ else
+ @load_more_callbacks_active = true
+ end
+ end
+
+ return unless go
+
+ @load_more_callbacks.each { |c| c.call size }
+ @load_more_callbacks_active = false
+ end
+
end
end