module RubyText

The top-level module

Skeleton… Can't put classes at top because of initalize

Constants

ESCDELAY
Path
VERSION

Public Class Methods

beep() click to toggle source
# File lib/settings.rb, line 77
def self.beep
  Curses.beep
end
flash() click to toggle source
# File lib/settings.rb, line 81
def self.flash
  Curses.flash
end
hide_cursor() click to toggle source
# File lib/settings.rb, line 137
def self.hide_cursor    # remove later?
  Curses.curs_set(0)
end
method_missing(name, *args) click to toggle source

For passing through arbitrary method calls to the lower level…

# File lib/settings.rb, line 128
def self.method_missing(name, *args)
  debug "method_missing: #{name}  #{args.inspect}"
  if name[0] == '_'
    Curses.send(name[1..-1], *args)
  else
    raise "#{name} #{args.inspect}" # NoMethodError
  end
end
selector(win: STDSCR, r: 0, c: 0, rows: 10, cols: 20, items:, fg: White, bg: Blue, win2:, callback:, enter: nil, quit: "q") click to toggle source

Two-paned widget with menu on left, informtional area on right

# File lib/menu.rb, line 275
def self.selector(win: STDSCR, r: 0, c: 0, rows: 10, cols: 20, 
                  items:, fg: White, bg: Blue,
                  win2:, callback:, enter: nil, quit: "q")
  high = rows
  wide = cols
  mwin = RubyText.window(high, wide, r: r, c: c, fg: fg, bg: bg)
  handler = callback
  Curses.stdscr.keypad(true)
  RubyText.hide_cursor
  sel = 0
  max = items.size - 1
  handler.call(sel, items[sel], win2)
  loop do
    mwin.home
    items.each.with_index do |item, row|
      mwin.crlf
      style = (sel == row) ? :reverse : :normal
      mwin.print fx(" #{item}", style)
    end
    ch = getch
    case ch
      when Up
        if sel > 0
          sel -= 1
          handler.call(sel, items[sel], win2)
        end
      when Down
        if sel < max
          sel += 1
          handler.call(sel, items[sel], win2)
        end
      when Enter
        if enter
          del = enter.call(sel, items[sel], win2)
          if del
            items -= [items[sel]]
            raise 
          end
        end
      when Tab
        Curses.flash
      when quit  # parameter
        exit
      else Curses.beep    # all else is trash
    end
  end
rescue
  retry
end
show_cursor() click to toggle source
# File lib/settings.rb, line 141
def self.show_cursor    # remove later?
  Curses.curs_set(1)
end
show_cursor!() click to toggle source
# File lib/settings.rb, line 145
def self.show_cursor!
  Curses.curs_set(2)  # Doesn't work? Device-dependent?
end
spinner(label: "", win: STDSCR, &block) click to toggle source
# File lib/widgets.rb, line 16
def self.spinner(label: "", win: STDSCR, &block) # TODO delay, etc.
  chars = "-\\|/"
  RubyText.hide_cursor
  t0 = Time.now.to_i
  thread = Thread.new do
    i=0
    loop do 
      t1 = Time.now.to_i
      elapsed = "0:%02d" % (t1-t0)   # FIXME breaks at 60 sec
      i = (i+1) % 4
      win.print " #{label} #{chars[i]}  #{elapsed}"
      win.left!
      sleep 0.04
    end
  end
  ret = block.call
  win.puts
  Thread.kill(thread)
  RubyText.show_cursor
  ret
end
splash(msg) click to toggle source
# File lib/widgets.rb, line 38
def self.splash(msg)
  lines = msg.split("\n")
  high = lines.size + 4
  wide = lines.map {|x| x.length }.max + 4
  r0 = (STDSCR.rows - high)/2
  c0 = (STDSCR.cols - wide)/2
  STDSCR.saveback(high, wide, r0, c0)
  win = RubyText.window(high, wide, r: r0, c: c0, 
                        fg: White, bg: Red, title: "[Press any key]")
  win.print "\n "
  win.puts msg
  getch
  STDSCR.restback(high, wide, r0, c0)
end
start(*args, log: "/tmp/rubytext.log", fg: White, bg: Blue, scroll: false) click to toggle source

FIXME refactor save/restore, etc. - rep as binary vector?

# File lib/settings.rb, line 89
  def self.start(*args, log: "/tmp/rubytext.log", 
                 fg: White, bg: Blue, scroll: false)
    $debug ||= File.new(log, "w") if log   # FIXME remove global

    args.each {|arg| raise "#{arg} is not valid" unless Settings::ValidArgs.include?(arg) }
    raise RTError("#{fg} is not a color") unless ::Colors.include? fg
    raise RTError("#{bg} is not a color") unless ::Colors.include? bg

    @settings = Settings.new
    @settings.set(*args)            # override defaults

    main = RubyText::Window.main(fg: fg, bg: bg, scroll: scroll)
    Object.const_set(:STDSCR, main) unless defined? STDSCR
    $stdscr = STDSCR  # FIXME global needed?
    Object.include(WindowIO)
    Curses.ESCDELAY = 10
    @started = true
# rescue => err
#   puts(err.inspect)
#   puts(err.backtrace)
#   raise RTError("#{err}")
  end
started() click to toggle source
# File lib/settings.rb, line 69
def self.started   # remove later
  @started
end
started?() click to toggle source
# File lib/settings.rb, line 73
def self.started?
  @started
end
stop() click to toggle source
# File lib/settings.rb, line 112
def self.stop
  @started = false
  Curses.close_screen
end
ticker(row: STDSCR.rows-1, col: 0, width: STDSCR.cols, fg: White, bg: Blue, text:, delay: 0.1) click to toggle source
# File lib/widgets.rb, line 2
def self.ticker(row: STDSCR.rows-1, col: 0, width: STDSCR.cols, 
                fg: White, bg: Blue, text:, delay: 0.1)
  text = text.gsub("\n", " ") + " "
  win = RubyText.window(1, width, r: row, c: col, border: false, fg: fg, bg: bg)
  leader = " "*width + text
  leader = text.chars.cycle.each_cons(width)
  width.times { win.rcprint 0, 0, leader.next.join }
  repeat = text.chars.cycle.each_cons(width)
  loop do   # Warning: loops forever
    win.rcprint 0, 0, repeat.next.join
    sleep delay
  end
end
window(high, wide, r: nil, c: nil, border: true, fg: White, bg: Blue, scroll: false, title: nil) click to toggle source
# File lib/window.rb, line 3
def self.window(high, wide, r: nil, c: nil, border: true, 
                fg: White, bg: Blue, scroll: false, title: nil)
  r ||= (STDSCR.rows - high)/2
  c ||= (STDSCR.cols - wide)/2
  win = RubyText::Window.new(high, wide, r, c, border, fg, bg, scroll)
  win.add_title(title) if title
  0.upto(high) {|row| 0.upto(wide) {|col| win[row, col] = " " } }
  win
end

Public Instance Methods

checklist(r: :center, c: :center, items:, curr: 0, selected: [], title: nil, sel_fg: Yellow, fg: White, bg: Blue) click to toggle source

“Menu” for checklists

# File lib/menu.rb, line 327
def checklist(r: :center, c: :center, 
              items:, curr: 0, selected: [],
              title: nil, sel_fg: Yellow, fg: White, bg: Blue)
  RubyText.hide_cursor
  high = items.size + 2
  wide = items.map(&:length).max + 8
  tlen = title.length + 8 rescue 0
  wide = [wide, tlen].max
  row, col = self.coords(r, c)
  row = row - high/2 if r == :center
  col = col - wide/2 if c == :center
  r, c = row, col
  self.saveback(high, wide, r, c)
  mr, mc = r+self.r0, c+self.c0
  mwin = RubyText.window(high, wide, r: mr, c: mc, 
                         fg: fg, bg: bg, title: title)
  Curses.stdscr.keypad(true)
  sel = curr
  max = items.size - 1
  loop do
    RubyText.hide_cursor  # FIXME should be unnecessary
    items.each.with_index do |item, row|
      mwin.go row, 0
      style = (sel == row) ? :reverse : :normal
      color = selected.find {|x| x[0] == row } ? sel_fg : fg
      label = "[ ]" + item
      mwin.print fx(label, color, style)
    end
    ch = getch
    case ch
      when Up
        sel -= 1 if sel > 0
      when Down
        sel += 1 if sel < max
      when Esc
        self.restback(high, wide, r, c)
        RubyText.show_cursor
        return []
      when Enter
        self.restback(high, wide, r, c)
        RubyText.show_cursor
        return selected.map {|i| items[i] }
      when " "
        selected << [sel, items[sel]]
        sel += 1 if sel < max
    else Curses.beep
    end
    RubyText.show_cursor
  end
end
reset() click to toggle source
# File lib/settings.rb, line 121
def reset
  @settings.reset
end
set(*args) click to toggle source
# File lib/settings.rb, line 117
def set(*args)
  @settings.set(*args)
end