module Ruco class Window OFFSET = 5 attr_accessor :lines, :columns, :left attr_reader :cursor, :top def initialize(lines, columns, options={}) @options = options @options[:line_scroll_threshold] ||= 1 @options[:line_scroll_offset] ||= 1 @options[:column_scroll_threshold] ||= 1 @options[:column_scroll_offset] ||= 5 @lines = lines @columns = columns @top = 0 @left = 0 @cursor = Position.new(0,0) end def position=(x) set_position(x) end def set_position(position, options={}) scroll_line_into_view position.line, (options[:max_lines] || 9999) scroll_column_into_view position.column @cursor = Position.new(position.line - @top, position.column - @left) end def crop(lines) lines_to_display = lines[visible_lines] || [] lines_to_display[@lines-1] ||= nil lines_to_display.map do |line| line ||= '' line[visible_columns] || '' end end def scroll_line_into_view(line, total_lines) result = adjustment(line, visible_lines, @options[:line_scroll_threshold], @options[:line_scroll_offset]) set_top result, total_lines if result end def scroll_column_into_view(column) result = adjustment(column, visible_columns, @options[:column_scroll_threshold], @options[:column_scroll_offset]) self.left = result if result end def style_map(selection) map = StyleMap.new(lines) if selection add_selection_styles(map, selection) else map end end def add_selection_styles(map, selection) lines.times do |line| visible = visible_area(line) next unless selection.overlap?(visible) first = [selection.first, visible.first].max first = first[1] - left last = [selection.last, visible.last].min last = last[1] - left map.add(:reverse, line, first...last) end map end def left=(x) @left = [x,0].max end def set_top(line, total_lines) max_top = total_lines - lines + 1 + @options[:line_scroll_offset] @top = [[line, max_top].min, 0].max end def visible_lines @top..(@top+@lines-1) end def visible_columns @left..(@left+@columns-1) end private def adjustment(current, allowed, threshold, offset) if current < (allowed.first + threshold) current - offset elsif current > (allowed.last - threshold) size = allowed.last - allowed.first + 1 current - size + 1 + offset end end def visible_area(line) line += @top start_of_line = [line, @left] last_visible_column = @left + @columns end_of_line = [line, last_visible_column] start_of_line..end_of_line end end end