class Dispel::Screen

Constants

COLOR_DIVIDE
COLOR_SOURCE_VALUES
COLOR_TARGET_VALUES
TERM_COLOR_BASE

Attributes

options[RW]

Public Class Methods

new(options) click to toggle source
# File lib/dispel/screen.rb, line 7
def initialize(options)
  @options = options
  @cache = []
end
open(options={}, &block) click to toggle source
# File lib/dispel/screen.rb, line 12
def self.open(options={}, &block)
  new(options).open(&block)
end

Private Class Methods

color_id(foreground, background) click to toggle source

create a new color from foreground+background or reuse old and return color-id

# File lib/dispel/screen.rb, line 137
def color_id(foreground, background)
  @color_ids ||= {}
  @color_ids[[foreground, background]] ||= begin
    # make a new pair with a unique id
    @@max_color_id ||= 0
    id = (@@max_color_id += 1)
    unless defined? RSpec # stops normal text-output, do not use in tests
      Curses::init_pair(id, foreground, background)
    end
    Curses.color_pair(id)
  end
end
curses_style(style, colors, options={}) click to toggle source
# File lib/dispel/screen.rb, line 108
def curses_style(style, colors, options={})
  if colors
    foreground = options[:foreground] || '#ffffff'
    background = options[:background] || '#000000'

    foreground, background = if style == :normal
      [foreground, background]
    elsif style == :reverse
      ['#000000', '#ffffff']
    else
      # :red or [:red, :blue]
      f,b = style
      [f || foreground, b || background]
    end

    foreground = html_to_terminal_color(foreground)
    background = html_to_terminal_color(background)
    color_id(foreground, background)
  else # no colors
    if style == :reverse
      Curses::A_REVERSE
    else
      Curses::A_NORMAL
    end
  end
end
html_to_terminal_color(html_color) click to toggle source
# File lib/dispel/screen.rb, line 155
def html_to_terminal_color(html_color)
  return unless html_color
  r = (html_color[1..2].to_i(16) / COLOR_DIVIDE) * 36
  g = (html_color[3..4].to_i(16) / COLOR_DIVIDE) * 6
  b = (html_color[5..6].to_i(16) / COLOR_DIVIDE) * 1
  TERM_COLOR_BASE + r + g + b
end

Public Instance Methods

color?() click to toggle source
# File lib/dispel/screen.rb, line 50
def color?
  @options[:colors] and Curses.has_colors?
end
columns() click to toggle source
# File lib/dispel/screen.rb, line 30
def columns
  Curses.stdscr.maxx
end
debug_key(key) click to toggle source
# File lib/dispel/screen.rb, line 44
def debug_key(key)
  @key_line ||= -1
  @key_line = (@key_line + 1) % lines
  write(@key_line, 0, "#{key.inspect}---")
end
draw(view, style_map=[], cursor=[0,0]) click to toggle source
# File lib/dispel/screen.rb, line 38
def draw(view, style_map=[], cursor=[0,0])
  @cache.clear if dimensions_changed?
  draw_view(view, style_map)
  Curses.setpos(*cursor) # cursor has to always be set or it ends in random position
end
lines() click to toggle source
# File lib/dispel/screen.rb, line 34
def lines
  Curses.stdscr.maxy
end
open() { |self| ... } click to toggle source
# File lib/dispel/screen.rb, line 16
def open(&block)
  Curses.noecho # do not show typed chars
  Curses.nonl # turn off newline translation
  Curses.stdscr.keypad(true) # enable arrow keys
  Curses.raw # give us all other keys
  Curses.stdscr.nodelay = 1 # do not block -> we can use timeouts
  Curses.init_screen
  Curses.start_color if color?
  yield self
ensure
  Curses.clear # needed to clear the menu/status bar on windows
  Curses.close_screen
end

Private Instance Methods

curses_style(style) click to toggle source
# File lib/dispel/screen.rb, line 102
def curses_style(style)
  @style ||= {}
  @style[style] ||= self.class.curses_style(style, color?, options)
end
dimensions_changed?() click to toggle source
# File lib/dispel/screen.rb, line 96
def dimensions_changed?
  current_dimensions = [columns, lines]
  @old_dimensions != current_dimensions
  @old_dimensions = current_dimensions
end
draw_view(view, style_map) click to toggle source
# File lib/dispel/screen.rb, line 61
def draw_view(view, style_map)
  lines = Tools.naive_split(view, "\n")
  style_map = style_map.flatten

  lines.each_with_index do |line, line_number|
    styles = style_map[line_number]

    # expand line with whitespace to overwrite previous content
    # and remove overflow to avoid display errors
    line = line.ljust(columns, " ").slice!(0, columns)

    # display tabs as single-space -> nothing breaks
    line.gsub!("\t",' ')

    if_line_changes line_number, [line, styles] do
      # position at start of line and draw
      Curses.setpos(line_number,0)
      Dispel::StyleMap.styled(line, styles).each do |style, part|
        Curses.attrset curses_style(style)
        Curses.addstr part
      end

      if @options[:debug_cache]
        write(line_number, 0, (rand(899)+100).to_s)
      end
    end
  end
end
if_line_changes(key, args) { || ... } click to toggle source
# File lib/dispel/screen.rb, line 90
def if_line_changes(key, args)
  return if @cache[key] == args # would not change the line -> nothing to do
  @cache[key] = args # store current line
  yield # render the line
end
write(line,row,text) click to toggle source
# File lib/dispel/screen.rb, line 56
def write(line,row,text)
  Curses.setpos(line,row)
  Curses.addstr(text);
end