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
# 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
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 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 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 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
# File lib/umbra.rb, line 8 def curses FFI::NCurses end
# 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
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
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
# 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
# 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
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 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
# File lib/umbra/dialog.rb, line 178 def print_border_mb window, row, col, height, width, color, attr # {{{ win = window.pointer #att = attr len = width len = FFI::NCurses.COLS if len == 0 space_char = " ".codepoints.first (row-1).upto(row+height-1) do |r| # this loop clears the screen, printing spaces does not work since ncurses does not do anything FFI::NCurses.mvwhline(win, r, col, space_char, len) end FFI::NCurses.mvwaddch win, row, col, FFI::NCurses::ACS_ULCORNER FFI::NCurses.mvwhline( win, row, col+1, FFI::NCurses::ACS_HLINE, width-6) FFI::NCurses.mvwaddch win, row, col+width-5, FFI::NCurses::ACS_URCORNER FFI::NCurses.mvwvline( win, row+1, col, FFI::NCurses::ACS_VLINE, height-4) FFI::NCurses.mvwaddch win, row+height-3, col, FFI::NCurses::ACS_LLCORNER FFI::NCurses.mvwhline(win, row+height-3, col+1, FFI::NCurses::ACS_HLINE, width-6) FFI::NCurses.mvwaddch win, row+height-3, col+width-5, FFI::NCurses::ACS_LRCORNER FFI::NCurses.mvwvline( win, row+1, col+width-5, FFI::NCurses::ACS_VLINE, height-4) end