class RubyText::Window
Reopening: Wrapper for curses windows
Wrapper for a curses window
Reopening: Coordinate handling (1-based!)
Constants
- Horiz
- ScreenStack
Attributes
Public Class Methods
# File lib/output.rb, line 103 def self.clear(win) # delete this? num = win.maxx * win.maxy - 1 win.setpos(0, 0) win.addstr(' '*num) win.setpos(0, 0) win.refresh end
Set up a window with fg/bg
# File lib/color.rb, line 45 def self.colorize!(cwin, fg, bg) cp = RubyText::Color.pair(fg, bg) cwin.color_set(cp) num = cwin.maxx * cwin.maxy cwin.setpos 0,0 cwin.addstr(' '*num) cwin.setpos 0,0 cwin.refresh rescue => err File.open("/tmp/#{__method__}.out", "w") do |f| f.puts err f.puts err.backtrace end end
# File lib/window.rb, line 46 def self.main(fg: White, bg: Blue, scroll: false) debug "Starting #main..." main_win = Curses.init_screen Curses.start_color self.colorize!(main_win, fg, bg) rows, cols = main_win.maxy, main_win.maxx win = self.make(main_win, rows, cols, 0, 0, border: false, fg: fg, bg: bg, scroll: scroll) debug "...started #main" win rescue => err File.open("/tmp/main.out", "w") {|f| f.puts err.inspect; f.puts err.backtrace } end
FIXME try again to inline this
# File lib/window.rb, line 62 def self.make(cwin, high, wide, r0, c0, border: true, fg: White, bg: Black, scroll: false) obj = self.allocate obj.instance_eval do @outer = @cwin = cwin @wide, @high, @r0, @c0 = wide, high, r0, c0 @fg, @bg = fg, bg @border = border @rows, @cols = high, wide @width, @height = @cols + 2, @rows + 2 if @border end obj.scrolling(scroll) obj end
Better to use Window.window IRL
# File lib/window.rb, line 24 def initialize(high=nil, wide=nil, r0=0, c0=0, border=false, fg=White, bg=Blue, scroll=false) @wide, @high, @r0, @c0 = wide, high, r0, c0 @border, @fg, @bg = border, fg, bg @cwin = Curses::Window.new(high, wide, r0, c0) colorize!(fg, bg) if @border @cwin.box(Vert, Horiz) @outer = @cwin @outer.refresh @cwin = Curses::Window.new(high-2, wide-2, r0+1, c0+1) colorize!(fg, bg) else @outer = @cwin end @rows, @cols = @cwin.maxy, @cwin.maxx # unnecessary really... @width, @height = @cols + 2, @rows + 2 if @border @scrolling = scroll @cwin.scrollok(scroll) @cwin.refresh end
Public Instance Methods
def go(r0, c0)
r, c = coords(r0, c0) save = self.rc goto r, c if block_given? yield goto *save end
end
# File lib/output.rb, line 138 def [](r, c) r0, c0 = self.rc @cwin.setpos(r, c) ch = @cwin.inch @cwin.setpos(r0, c0) ch.chr end
# File lib/output.rb, line 146 def []=(r, c, char) r0, c0 = self.rc @cwin.setpos(r, c) @cwin.addch(char[0].ord|Curses::A_NORMAL) @cwin.setpos(r0, c0) @cwin.refresh end
# File lib/output.rb, line 63 def _putch(ch) @cwin.addch(ch) end
# File lib/window.rb, line 76 def add_title(str, align = :center) raise "No border" unless @border len = str.length # What if it's too long? start = case align when :left; 1 when :center; (@outer.maxx - len)/2 when :right; @outer.maxx - len - 1 end @outer.setpos 0, start @outer.addstr str @outer.refresh end
# File lib/window.rb, line 124 def background(high=STDSCR.rows, wide=STDSCR.cols, r=0, c=0) saveback(high, wide, r, c) yield restback(high, wide, r, c) end
# File lib/window.rb, line 173 def beep Curses.beep end
Set background color
# File lib/color.rb, line 85 def bg=(sym) set_colors(@fg, sym) end
Move cursor to bottom of window
# File lib/navigation.rb, line 89 def bottom r, c = rc rmax = self.rows - 1 go rmax, c end
# File lib/output.rb, line 154 def boxme @outer.box(Vert, Horiz) @outer.refresh end
# File lib/output.rb, line 8 def center(str) r, c = self.rc n = @cwin.maxx - str.length go r, n/2 self.puts str end
# File lib/output.rb, line 111 def clear cwin.setpos(cwin.maxx, cwin.maxy) cwin.addstr(' ') num = cwin.maxx * cwin.maxy - 1 cwin.addstr(' '*num) cwin.setpos(0, 0) cwin.refresh end
Set up a window with fg/bg
# File lib/color.rb, line 69 def colorize!(fg, bg) set_colors(fg, bg) num = @cwin.maxx * @cwin.maxy self.home self.go(0, 0) { @cwin.addstr(' '*num) } @cwin.refresh end
Handle special coordinate names (symbols)
# File lib/navigation.rb, line 7 def coords(r, c) r = case when r == :center self.rows / 2 when r == :top 0 when r == :bottom self.rows - 1 else r end c = case when c == :center self.cols / 2 when c == :left 0 when c == :right self.cols - 1 else c end [r, c] end
# File lib/output.rb, line 85 def crlf # Technically not output... r, c = rc if @scrolling if r == @rows - 1 # bottom row scroll left! else go r+1, 0 end else if r == @rows - 1 # bottom row left! else go r+1, 0 end end end
# File lib/output.rb, line 21 def delegate_output(sym, *args) self.cwin.attrset(0) args = [""] if args.empty? args += ["\n"] if sym == :puts set_colors(@fg, @bg) meth = sym == :p ? :inspect : :to_s args.map! {|x| effect?(x) ? x : x.send(meth) } args.each do |arg| if arg.is_a?(RubyText::Effects) arg.set(self) else arg.effect.set(self) if arg.respond_to? :effect arg.each_char {|ch| ch == "\n" ? crlf : @cwin.addch(ch) } @cwin.refresh end end crlf if sym == :p # no implicit newline set_colors(@fg, @bg) @cwin.refresh end
Move cursor down
# File lib/navigation.rb, line 61 def down(n=1) r, c = rc go r+n, c end
Move cursor to bottom of window
# File lib/navigation.rb, line 103 def down! bottom end
FIXME Please refactor the Hal out of this.
# File lib/output.rb, line 17 def effect?(arg) arg.is_a?(RubyText::Effects) end
Set foreground color
# File lib/color.rb, line 79 def fg=(sym) set_colors(sym, @bg) end
# File lib/window.rb, line 177 def flash Curses.flash end
# File lib/output.rb, line 266 def gets(history: [], limit: nil, tab: [], default: "", capture: []) # needs improvement # echo assumed to be OFF, keypad ON @history = history gs = GetString.new(self, default, history: history, limit: limit, tab: tab, capture: capture) count = 0 loop do count += 1 # Escape and 'capture' chars have special meaning if first char ch = self.getch case ch when *capture return ch if count == 1 gs.add(ch) when Escape return Escape if count == 1 gs.enter break when CtlD return CtlD if count == 1 gs.enter break when Enter gs.enter break when BS, DEL, 63 # backspace, del, ^H (huh?) gs.backspace when Tab gs.complete when Left gs.left_arrow when Right gs.right_arrow when Up next if @history.nil? # move this? gs.history_prev when Down next if @history.nil? # move this? gs.history_next when Integer Curses.beep else gs.add(ch) end end gs.value rescue => err str = err.to_s + "\n" + err.backtrace.join("\n") raise str end
Go to specified row/column in current window,
execute block, and return cursor
# File lib/navigation.rb, line 42 def go(r0, c0) r, c = coords(r0, c0) save = self.rc goto r, c if block_given? yield goto *save end end
Go to specified row/column in current window
# File lib/navigation.rb, line 33 def goto(r, c) # only accepts numbers! @cwin.setpos(r, c) @cwin.refresh end
Move cursor to home (upper left)
# File lib/navigation.rb, line 124 def home go 0, 0 end
Move cursor left
# File lib/navigation.rb, line 68 def left(n=1) r, c = rc go r, c-n end
Move cursor to far left of window
# File lib/navigation.rb, line 109 def left! r, c = rc go r, 0 end
# File lib/output.rb, line 120 def output(&block) $stdscr = self block.call $stdscr = STDSCR end
# File lib/output.rb, line 50 def p(*args) delegate_output(:p, *args) end
# File lib/output.rb, line 46 def print(*args) delegate_output(:print, *args) end
# File lib/output.rb, line 67 def putch(ch, r: nil, c: nil, fx: nil) debug("putch: #{[ch, r, c, fx].inspect}") if r.nil? && c.nil? && fx.nil? _putch(ch) else r0, c0 = self.rc r ||= r0 c ||= c0 go(r, c) do fx.set(self) if fx val = fx.value rescue 0 @cwin.addch(ch.ord|val) end fx.reset(self) if fx end @cwin.refresh end
# File lib/output.rb, line 42 def puts(*args) delegate_output(:puts, *args) end
Return current row/column
# File lib/navigation.rb, line 130 def rc [@cwin.cury, @cwin.curx] end
# File lib/output.rb, line 54 def rcprint(r, c, *args) self.go(r, c) { self.print *args } end
# File lib/output.rb, line 58 def rcprint!(r, c, *args) self.go(r, c) # Cursor isn't restored self.print *args end
# File lib/output.rb, line 159 def refresh @cwin.refresh end
# File lib/window.rb, line 144 def restback(high=STDSCR.rows, wide=STDSCR.cols, r=0, c=0) save = ScreenStack.pop pos = save.shift 0.upto(high-1) do |h| line = "" 0.upto(wide-1) {|w| line << save.shift } row, col = h+r-1, c-1 row += 1 if self == STDSCR # wtf? col += 1 if self == STDSCR self.go row, col self.print line end self.go *pos @cwin.refresh rescue => err puts "Error!" puts err puts err.backtrace.join("\n") sleep 8 end
Move cursor right
# File lib/navigation.rb, line 75 def right(n=1) r, c = rc go r, c+n end
Move cursor to far left of window
# File lib/navigation.rb, line 116 def right! r, c = rc cmax = self.cols - 1 go r, cmax end
# File lib/window.rb, line 130 def saveback(high=STDSCR.rows, wide=STDSCR.cols, r=0, c=0) debug "saveback: #{[high, wide, r, c].inspect}" save = [self.rc] 0.upto(high-1) do |h| 0.upto(wide-1) do |w| row, col = h+r-1, w+c-1 row += 1 if self == STDSCR # wtf? col += 1 if self == STDSCR save << self[row, col] end end ScreenStack.push save end
# File lib/window.rb, line 111 def screen_text(file = nil) # rename? lines = [] 0.upto(self.rows-1) do |r| line = "" 0.upto(self.cols-1) do |c| line << self[r, c] end lines << line end File.open(file, "w") {|f| f.puts lines } if file lines end
# File lib/window.rb, line 96 def scroll(n=1) if n < 0 @cwin.scrl(n) (-n).times {|i| rcprint i, 0, (' '*@cols) } else n.times do |i| @cwin.scroll scrolling(false) rcprint @rows-1, 0, (' '*@cols) scrolling end end @cwin.refresh end
Assign color pair to curses window
# File lib/color.rb, line 62 def set_colors(fg, bg) cp = RubyText::Color.pair(fg, bg) @cwin.color_set(cp) end
Move cursor to top of window
# File lib/navigation.rb, line 82 def top r, c = rc go 0, c end
Move cursor up
# File lib/navigation.rb, line 54 def up(n=1) r, c = rc go r-n, c end
Move cursor to top of window
# File lib/navigation.rb, line 97 def up! top end
Simple yes/no decision
# File lib/menu.rb, line 191 def yesno # TODO: Accept YyNn r, c = STDSCR.rc num, str = STDSCR.menu(r: r, c: c+6, items: ["yes", "no"]) num == 0 end