Project

rubytext

0.01
Low commit activity in last 3 years
A long-lived project that still receives updates
Uses the curses gem and extends functionality and ease of use in Ruby.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Runtime

~> 1.4.5
 Project Readme

RubyText

RubyText is a curses wrapper that is in the early experimental stages. Unlike some similar projects, it is a "thick" wrapper; I am adding functionality that does not always correspond directly to the curses world.

Install the rubytext gem and then run demo code or the examples or whatever you want:

  $ gem install rubytext
  $ rubytext demo      # 2-minute demo
  $ rubytext tutorial  # (On OS/X) opens tutorial.html
  $ rubytext slides    # Longer "slideshow" (run all examples)

You can also use the example subcommand:

  $ rubytext example           # list all examples
  $ rubytext example progname  # run 'progname' example

When you specify the example name, there is no need to use the .rb extension.

Getting started

Here's the obligatory hello-world program.

require 'rubytext'

RubyText.start

puts "Hello, world!"

getch

You invoke RubyText.start to initialize the curses environment with default settings. If you then use puts, it will write to the standard screen (called STDSCR in this library).

The getch (get character) simply waits for keyboard input before proceeding. Without it, the program might terminate too fast for you to see anything.

The "slideshow"

Here are some of the programs from the rubytext slides set of demos. See the examples directory and the showme.rb script.

A simple window:

require 'rubytext'
win = RubyText.window(6, 25, r: 2, c: 34, 
                      # 6 rows, 25 cols; upper left at 2,4
                      fg: Blue, bg: White) # foreground, background

win.puts "This is a window..."

How output works. This name may change.

require 'rubytext'
win = RubyText.window(9, 36, r: 2, c: 6, fg: White, bg: Red) 

win.output do
  puts "Because this code uses #output,"
  puts "it doesn't have to specify the"
  puts "window as a receiver each time."
end

Windows are bordered by default.

require 'rubytext'
win = RubyText.window(9, 35, r: 3, c: 7, border: false, fg: Black, bg: Green)

win.puts "A window doesn't have to"
win.puts "have a border."

Using puts will wrap around the window automagically.

require 'rubytext'
win = RubyText.window(8, 39, r: 4, c: 9, fg: Black, bg: Blue)

win.puts "If your text is longer than " +
         "the width of the window, by default it will " +
         "wrap around."

Scrolling is not enabled by default for a window (except STDSCR).

require 'rubytext'
win = RubyText.window(10, 70, r: 2, c: 14, fg: Yellow, bg: Black)

win.output do
  puts "Without scrolling, this is what happens when your window fills up..."
  puts "This behavior will probably change later."
  sleep 1
  puts "Let's print 10 more lines now:"
  sleep 1
  
  10.times {|i| puts "Printing line #{i}..."; sleep 0.2 }
end

You can use print and p as well as puts.

require 'rubytext'
win = RubyText.window(10, 60, r: 2, c: 14, fg: Blue, bg: Black)

win.output do
  puts "The #print and #p methods also act as you expect."

  print "This will all "
  print "go on a single "
  puts "line."

  puts
  array = [1, 2, 3]
  p array
end

You can still use puts (etc.) with files, but watch for STDOUT and STDERR.

require 'rubytext'
win = RubyText.window(10, 50, r: 0, c: 5, fg: Yellow, bg: Blue)

win.output do
  puts "Of course, #puts and #print are unaffected \nfor other receivers."

  out = File.new("/tmp/junk", "w")
  out.puts "Nothing to see here."
  sleep 2

  print "\nHowever, if you print to STDOUT or STDERR \nwithout redirection, "
  STDOUT.print "you will have some "
  STDERR.print "unexpected/undefined results "
  puts " in more ways than one."
end

Use []= to stuff single characters into a window (like an array).

require 'rubytext'
win = RubyText.window(11, 50, r: 0, c: 5, fg: Yellow, bg: Blue)

win.puts "We can use the []= method (0-based)"
win.puts "to address individual window locations"
win.puts "and place characters there.\n "

sleep 2
win[4,22] = "X"; win.refresh

sleep 2
win[8,20] = "y"; win.refresh

sleep 2
win[6,38] = "Z"; win.refresh

Likewise use [] to retrieve characters from a window.

require 'rubytext'
win = RubyText.window(12, 60, r: 2, c: 5, fg: Yellow, bg: Blue)

win.puts "ABCDE    Method [] can retrieve characters "
win.puts "FGHIJ    from a window."
win.puts "KLMNO\nPQRST\nUVWZYZ"
win.puts

sleep 2
win.puts "(2,2) => '#{win[2,2]}'    (0,4)  => '#{win[0,4]}'"
win.puts "(6,7) => '#{win[6,7]}'    (0,15) => '#{win[0,15]}'"

You can write to STDSCR or to a subwindow.

require 'rubytext'
win = RubyText.window(6, 30, r: 2, c: 5, fg: Yellow, bg: Blue)

win.puts "You can write to a window..."

sleep 2
9.times { STDSCR.puts }
STDSCR.puts "...or you can write to STDSCR (standard screen)"

sleep 1
puts "STDSCR is the default receiver."

sleep 2
STDSCR.go 5, 0
puts "Nothing stops you from overwriting a window."

You can retrieve cursor position and window size.

require 'rubytext'
win = RubyText.window(12, 65, r: 1, c: 5, fg: Yellow, bg: Blue)

win.output do
  puts "You can detect the size and cursor position of any window."
  puts "\nSTDSCR is #{STDSCR.rows} rows by #{STDSCR.cols} columns"
  puts "win is     #{win.rows} rows by #{win.cols} columns"
  puts "\nSlightly Heisenbergian report of cursor position:"
  puts "  STDSCR.rc = #{STDSCR.rc.inspect}\n  win.rc    = #{win.rc.inspect}"
  puts "\nFor fun, I'll print \"ABC\" to STDSCR..."
  sleep 2
  STDSCR.print "ABC"
end

Move the cursor with go (and use a block to jump/return).

require 'rubytext'
win = RubyText.window(11, 65, r: 0, c: 15, fg: Blue, bg: Black)

win.puts "The #go method will move the cursor to a specific location."
win.go 2, 5
win.puts "x  <-- The x is at 2,5"

win.puts "\nWith a block, it will execute the block and then"
win.puts "return to its previous location."

win.print "\n   ABC..."
sleep 2
win.go(8, 20) { win.print "XYZ" }
sleep 2
win.print "DEF"

Use rcprint to print at specific coordinates.

require 'rubytext'
win = RubyText.window(13, 65, r: 0, c: 6, fg: Blue, bg: White)

win.puts "The #rcprint method will print at the specified"
win.puts "row/column, like go(r,c) followed by a print,"
win.puts "except that it does NOT move the cursor."

win.rcprint 4,8,  "Simplify,"
win.rcprint 6,12, "simplify,"
win.rcprint 8,16, "simplify!"

win.rcprint 10,0, "Later there will be other ways to do this kind of thing."

Window navigation: home, up, down, left, right

require 'rubytext'
win = RubyText.window(11, 65, r: 0, c: 6, fg: Blue, bg: White)

win.go 2,0
win.puts "   Method #home will home the cursor..."
win.puts "   and #putch will put a character at the current location."
sleep 2
win.home; win.putch "H"; sleep 2
win.rcprint 4,3, "We can also move up/down/left/right..."; sleep 2

win.go 7, 29;            win.putch("+"); sleep 1
win.go 7, 29; win.up;    win.putch("U"); sleep 1
win.go 7, 29; win.down;  win.putch("D"); sleep 1
win.go 7, 29; win.left;  win.putch("L"); sleep 1
win.go 7, 29; win.right; win.putch("R"); sleep 1

More navigation: up, down, left, right with a parameter.

require 'rubytext'
win = RubyText.window(11, 65, r: 1, c: 6, fg: Blue, bg: White)

win.puts "Methods up/down/left/right can also take an integer..."

win.go 4, 29;               win.putch("+"); sleep 1
win.go 4, 29; win.up(2);    win.putch("2"); sleep 1
win.go 4, 29; win.down(3);  win.putch("3"); sleep 1
win.go 4, 29; win.left(4);  win.putch("4"); sleep 1
win.go 4, 29; win.right(5); win.putch("5"); sleep 1

Still more navigation: up!, down!, left!, right!

require 'rubytext'
win = RubyText.window(11, 65, r: 1, c: 6, fg: Blue, bg: White)

win.go 2,0
win.puts "We also have: up!, down!, left!, and right! which can" 
win.puts "Take us to the edges of the window."

win.go 5, 21;             win.putch("+"); sleep 1
win.go 5, 21; win.up!;    win.putch("U"); sleep 1
win.go 5, 21; win.down!;  win.putch("D"); sleep 1
win.go 5, 21; win.left!;  win.putch("L"); sleep 1
win.go 5, 21; win.right!; win.putch("R"); sleep 1

And finally, top and bottom.

require 'rubytext'
win = RubyText.window(11, 65, r: 1, c: 6, fg: Blue, bg: White)

win.go 2,0
win.puts "#top and #bottom are the same as #up! and #down!"

win.go 5, 21;             win.putch("+"); sleep 1
win.go 5, 21; win.top;    win.putch("T"); sleep 1
win.go 5, 21; win.bottom; win.putch("B"); sleep 1

Somewhat useless, but there is a center method.

require 'rubytext'
win = RubyText.window(15, 65, r: 1, c: 6, fg: Green, bg: Blue)

win.puts "#center will print text centered on the current row"
win.puts "and do an implicit CRLF at the end.\n "

stuff = ["I","can","never","imagine","good sets","of real words",
         "which can somehow", "produce tree shapes", "|", "|"]

stuff.each {|str| win.center(str) }

Changing colors during printing. This syntax will change.

require 'rubytext'
win = RubyText.window(12, 65, r: 0, c: 6, fg: Green, bg: Blue)

win.puts "This is EXPERIMENTAL."
win.puts "Use a color symbol to change text color temporarily:\n "

win.puts "This is", :yellow, " another color", :white, " and yet another."
win.puts "And this is normal again.\n "

win.puts "This does mean that you can't print a symbol that is"
win.puts "also a color name... you'd need a workaround.\n "

sym = :red
win.puts "The symbol is ", sym.inspect, " which works", sym, " just fine."

A simple menu (incomplete, still experimental).

require 'rubytext'
puts "This very crude menu is also EXPERIMENTAL."
puts "At the moment, it only works inside STDSCR!\n"
puts "It knows up, down, Enter, and Escape.\n "
puts "Press any key to display the menu..."
getch

days = %w[Monday Tuesday Wednesday Thursday Friday]
num, day = STDSCR.menu(c: 30, items: days)

puts
if day.nil?
  puts "You picked nothing!"
else
  puts "You picked item #{num} which is #{day.inspect}"
end

A marquee or ticker example. This uses threads and is kind of cool.

require 'rubytext'
msg = <<~EOS
 A "ticker" example that actually uses threads. Maybe Curses is not as slow as 
 you thought? PRESS ANY KEY TO EXIT...
EOS

w, h = STDSCR.cols, STDSCR.rows - 1
threads = []

r = RubyText
t1 = -> { r.ticker(text: msg, row: h-8, col: 20, width: w-40, delay: 0.02, fg: Red, bg: Black) }
t2 = -> { r.ticker(text: msg, row: h-6, col: 15, width: w-30, delay: 0.04) }
t3 = -> { r.ticker(text: msg, row: h-4, col: 10, width: w-20, delay: 0.06, fg: Black, bg: Green) }
t4 = -> { r.ticker(text: msg, row: h-2, col:  5, width: w-10, delay: 0.08, bg: Black) }
t5 = -> { r.ticker(text: msg) }  # All defaults -- goes at bottom

threads << Thread.new { t1.call } << Thread.new { t2.call } << Thread.new { t3.call } << 
           Thread.new { t4.call } << Thread.new { t5.call }

threads << Thread.new { getch; exit }   # quitter thread...
threads.each {|t| t.join } 

More later...

DESC

require 'rubytext'