module Umbra

—————————————————————————– #

       File: box.rb
Description: a box or border around a container
     Author: j kepler  http://github.com/mare-imbrium/canis/
       Date: 2018-04-07
    License: MIT
Last update: 2018-06-03 14:50

—————————————————————————– #

box.rb  Copyright (C) 2018 j kepler

—————————————————————————– #

       File: checkbox.rb
Description: 
     Author: j kepler  http://github.com/mare-imbrium/canis/
       Date: 2018-04-01 - 16:08
    License: MIT
Last update: 2018-05-28 12:06

—————————————————————————– #

checkbox.rb  Copyright (C) 2012-2018 j kepler

—————————————————————————– #

       File: field.rb
Description: text input field or widget
     Author: j kepler  http://github.com/mare-imbrium/
       Date: 2018-03
    License: MIT
Last update: 2018-06-02 10:26

—————————————————————————– #

field.rb  Copyright (C) 2012-2018 j kepler

Text edit field

Todo :

NOTE: width is the length of the display whereas maxlen is the maximum size that the value can take. Thus, maxlen can exceed width. Currently, maxlen defaults to width which defaults to 20. NOTE: Use +text=(val)+ to set value of field, and +text()+ to retrieve value.

Example

f = Field.new  text: "Some value", row: 10, col: 2

Field introduces an event :CHANGE which is fired for each character deleted or inserted.

## Use the :CHANGED event handler for custom validations and throw a FieldValidationException if ## the validation fails. This is called in the on_leave. You may pass a block to the command method

for the same functionality.

Manages the controls/widgets on a screen. Manages traversal, rendering and events of all widgets that are associated with it via the add_widget method.

Passes keys pressed by user to the current field. Any keys that are not handled by the current field, are handled by the form if the application has bound the key via bind_key.

NOTE : 2018-03-08 - now using @focusables instead of @widgets in traversal.

      active_index is now index into focusables.
 Events: RESIZE (allows listener to reposition objects that have variable widths or heights)
NOTE: active_index: 2018-05-17 - is being set to 0 even though no field is active. Thus, first time 
  on_enter does not fire. It should only be set after a field is focused and its on_enter has succeeded.

—————————————————————————– #

       File: keymappinghandler.rb
Description: methods for mapping methods or blocks to keys
     Author: j kepler  http://github.com/mare-imbrium/canis/
       Date: 2018-04-05 - 08:34
    License: MIT
Last update: 2018-04-22 23:01

—————————————————————————– #

keymappinghandler.rb  Copyright (C) 2018 j kepler

—————————————————————————– #

       File: listbox.rb
Description: list widget that displays a scrollable list of items that is selectable.
     Author: j kepler  http://github.com/mare-imbrium/umbra
       Date: 2018-03-19 
    License: MIT
Last update: 2018-06-07 09:35

—————————————————————————– #

listbox.rb  Copyright (C) 2012-2018 j kepler
== TODO 
currently only do single selection, we may do multiple at a later date. TODO
insert/delete a row ??
Other selection functions: select_all, select(n), deselect(n), selected?(n), select(m,n,o...), select(String),
   select(Range). Same with deselect.
----------------
----------------------------------------------------------------------------- #
        File: multiline.rb
 Description: A base class for lists and textboxes and tables, i.e. components
              having multiple lines that are scrollable.
      Author: j kepler  http://github.com/mare-imbrium/canis/
        Date: 2018-05-08 - 11:54
     License: MIT
 Last update: 2018-06-10 10:50
----------------------------------------------------------------------------- #
 multiline.rb Copyright (C) 2012-2018 j kepler

  TODO allow setting of current_index programmatically
  TODO is a row visible. visible? row. and make a row visible. programmatically
  TODO insert delete a row (if editable)

/

—————————————————————————– #

       File: radiobutton.rb
Description: a member of a group of buttons, only one can be selected at a time.
     Author: j kepler  http://github.com/mare-imbrium/
       Date: 2018-04-02 - 10:37
    License: MIT
Last update: 2018-06-03 11:27

—————————————————————————– #

radiobutton.rb  Copyright (C) 2012-2018 j kepler

A simple tabular data generator. Given table data in arrays and a column heading row in arrays, it quickely generates tabular data. It only takes left and right alignment of columns into account.

You may specify individual column widths. Else it will take the widths of the column names you supply

in the startup array. You are encouraged to supply column widths.

If no columns are specified, and no widths are given, it take the widths of the first row

as a model to determine column widths.

—————————————————————————– #

       File: togglebutton.rb
Description: a button that has two states, on and off
     Author: j kepler  http://github.com/mare-imbrium/umbra/
       Date: 2018-03-17 - 22:50
    License: MIT
Last update: 2018-06-03 09:33

—————————————————————————– #

togglebutton.rb Copyright (C) 2018 j kepler

NOTE: conflict between KEY_RESIZE and wtimeout or halfdelay.

Setting a timeout or delay allows an application to respond to updates without a keypress,
 such as continuous updates.
 However, this means that KEY_RESIZE will not work unless a key is pressed.

If resizing is important, then caller may set $ncurses_timeout to -1.

Constants

BOLD

attribute constants, use them to specify an attrib for a widget or window.

COLOR_BLACK

color constants, use these when creating a color

COLOR_BLUE
COLOR_CYAN
COLOR_GREEN
COLOR_MAGENTA
COLOR_RED
COLOR_WHITE
CP_BLACK

In case, the init_pairs are changed, then update these as well, so that the programs use the correct pairs.

CP_BLUE
CP_CYAN
CP_GREEN
CP_MAGENTA
CP_RED
CP_WHITE
CP_YELLOW
KEY_ENTER

key constants

KEY_RETURN
NORMAL
REVERSE
UNDERLINE
VERSION

Public Instance Methods

alert(str, config={}) click to toggle source
# File lib/umbra.rb, line 12
def alert str, config={}
  require 'umbra/dialog'

  config[:text]              ||= str
  config[:title]             ||= "Alert"
  config[:window_color_pair] ||=  create_color_pair( COLOR_BLUE, COLOR_WHITE )
  config[:window_attr]       ||= NORMAL
  config[:buttons]           ||= ["Ok"]

  #m = Dialog.new text: str, title: title, window_color_pair: cp, window_attr: attr
  m = Dialog.new config
  m.run
end
confirm(str, config={}) click to toggle source

confirmation dialog which prompts message with Ok and Cancel and returns true or false.

# File lib/umbra.rb, line 27
def confirm str, config={}
  require 'umbra/dialog'

  config[:text]              ||= str
  config[:title]             ||= "Confirm"
  config[:window_color_pair] ||=  create_color_pair( COLOR_BLUE, COLOR_WHITE )
  config[:window_attr]       ||= NORMAL
  config[:buttons]           ||= ["Ok", "Cancel"]

  m = Dialog.new config
  ret = m.run
  return ret == 0
end
create_centered_window(height, width, color_pair=0, attrib=REVERSE) click to toggle source

create a centered window. # {{{ NOTE: this should probably go into window class, or some util class.

# File lib/umbra/dialog.rb, line 169
def create_centered_window height, width, color_pair=0, attrib=REVERSE
  row = ((FFI::NCurses.LINES-height)/2).floor
  col = ((FFI::NCurses.COLS-width)/2).floor
  win = Window.new  height, width, row, col
  #FFI::NCurses.wbkgd(win.pointer, FFI::NCurses.COLOR_PAIR(0) | REVERSE); #  does not work on xterm-256color
  FFI::NCurses.wbkgd(win.pointer, FFI::NCurses.COLOR_PAIR(color_pair) | attrib); #  does not work on xterm-256color
  #FFI::NCurses.wbkgd(win.pointer, color_pair | attrib)
  return win
end
create_color_pair(bgcolor, fgcolor) click to toggle source

create and return a color_pair for a combination of bg and fg. This will always return the same color_pair so a duplicate one will not be created. @param bgcolor [Integer] color of background e.g., COLOR_BLACK @param fgcolor [Integer] color of foreground e.g., COLOR_WHITE @return [Integer] - color_pair which can be passed to printstring, or used directly as #COLOR_PAIR(int)

# File lib/umbra/window.rb, line 107
def create_color_pair(bgcolor, fgcolor)
  code = (bgcolor*10) + fgcolor
  FFI::NCurses.init_pair(code, fgcolor, bgcolor)
  return code
end
create_logger(path, config={}) click to toggle source

create a logger instance given a path, return the logger NOTE: Ideally would like to set $log here to this, but what if user creates multiple.

# File lib/umbra.rb, line 89
def create_logger path, config={}
  require 'logger'
  _path   = File.open(path, File::WRONLY|File::TRUNC|File::CREAT) 
  logg = Logger.new(_path)
  raise "Could not create logger: #{path}" unless logg
  ## if not set, will default to 0 which is debug. Other values are 1 - info, 2 - warn
  logg.level = config[:level] ||  ENV["UMBRA_LOG_LEVEL"].to_i
  #logg.info "START -- #{$0} log level: #{logg.level}. To change log level, increase UMBRA_LOG_LEVEL in your environment to 1 or 2 or 3."
  return logg
end
curses() click to toggle source
# File lib/umbra.rb, line 8
def curses
  FFI::NCurses
end
get_string(label, config={}) click to toggle source
# File lib/umbra.rb, line 67
def get_string label, config={}
  require 'umbra/messagebox'
  config[:title]             ||= "Entry"
  config[:buttons]           ||= ["Ok", "Cancel"]
  fld = nil
  mb = MessageBox.new config do 
    add Label.new text: label, row: 2, col: 2
    fld = Field.new name: "field", row: 3, col: 2
    add fld
  end
  index = mb.run
  if index == 0
    return fld.text
  else
    return nil
  end
end
init_curses() click to toggle source

Initialize ncurses before any program. Reduce the value of $ncurses_timeout if you want a quicker response to Escape keys or continuous updates. I have set the default to 1000.

# File lib/umbra/window.rb, line 37
def init_curses
  FFI::NCurses.initscr
  FFI::NCurses.curs_set 1
  FFI::NCurses.raw
  FFI::NCurses.noecho
  FFI::NCurses.keypad FFI::NCurses.stdscr, true
  FFI::NCurses.scrollok FFI::NCurses.stdscr, true
  if FFI::NCurses.has_colors
    FFI::NCurses.start_color
    std_colors
  end

end
main_loop(form) { |ch| ... } click to toggle source

Trying out a key loop to simplify matters for user. NOT TESTED. @yield key pressed if block given, else calls form.handle_key(ch).

# File lib/umbra.rb, line 102
def main_loop form, &block
  win = form.window
  form.repaint
  win.wrefresh
  stopping = false
  while true
    catch :close do
      while( ch = win.getkey) != 999
        next if ch == -1      ## should we put this here ???
        begin
          if ch == curses::KEY_CTRL_Q
            stopping = true 
            break
          end
          if block_given?
            yield ch
          else
            form.handle_key ch
          end
        rescue => err
          if $log
            $log.debug( "loop in umbra.rb handle_key rescue reached ")
            $log.debug( err.to_s) 
            $log.debug(err.backtrace.join("\n")) 
          end
          textdialog [err.to_s, *err.backtrace], :title => "Exception"
        end
        win.wrefresh
      end
      #stopping = win.fire_close_handler  ## TODO next version
      #win.wrefresh
      break if stopping
    end
    break if stopping
  end
end
startup() click to toggle source
# File lib/umbra/pad.rb, line 301
def startup
  require 'logger'
  require 'date'

  path = File.join(ENV["LOGDIR"] || "./" ,"v.log")
  file   = File.open(path, File::WRONLY|File::TRUNC|File::CREAT) 
  $log = Logger.new(path)
  $log.level = Logger::DEBUG
  today = Time.now.to_s
  $log.info "Pad demo #{$0} started on #{today}"
end
std_colors() click to toggle source
# File lib/umbra/pad.rb, line 312
def std_colors
  FFI::NCurses.use_default_colors
  # 2018-03-17 - changing it to ncurses defaults
  FFI::NCurses.init_pair(0,  FFI::NCurses::BLACK,   -1)
  FFI::NCurses.init_pair(1,  FFI::NCurses::RED,   -1)
  FFI::NCurses.init_pair(2,  FFI::NCurses::GREEN,     -1)
  FFI::NCurses.init_pair(3,  FFI::NCurses::YELLOW,   -1)
  FFI::NCurses.init_pair(4,  FFI::NCurses::BLUE,    -1)
  FFI::NCurses.init_pair(5,  FFI::NCurses::MAGENTA,  -1)
  FFI::NCurses.init_pair(6,  FFI::NCurses::CYAN,    -1)
  FFI::NCurses.init_pair(7,  FFI::NCurses::WHITE,    -1)
  FFI::NCurses.init_pair(14,  FFI::NCurses::WHITE,    FFI::NCurses::CYAN)
end
textdialog(array, config={}) click to toggle source

Pop up a dialog with an array, such as an exception

# File lib/umbra.rb, line 42
def textdialog array, config={}
  require 'umbra/messagebox'
  config[:title]             ||= "Alert"
  config[:buttons]           ||= ["Ok"]

  mb = MessageBox.new config do
    text array
  end
  mb.run
end
view(array, config={}) click to toggle source

view an array in a popup window

# File lib/umbra.rb, line 54
def view array, config={}, &block
  require 'umbra/pad'
  config[:title] ||= "Viewer"
  config[:color_pair] ||= create_color_pair( COLOR_BLUE, COLOR_WHITE )
  config[:attrib]     ||= NORMAL
  config[:list]       = array
  config[:height]     ||= FFI::NCurses.LINES-2
  config[:width]      ||= FFI::NCurses.COLS-10
  #m = Pad.new list: array, height: FFI::NCurses.LINES-2, width: FFI::NCurses.COLS-10, title: title, color_pair: cp, attrib: attr
  m = Pad.new config, &block
  m.run
end

Private Instance Methods

print_border_mb(window, row, col, height, width, color, attr) click to toggle source