reterm 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +131 -0
  4. data/designer/main.rb +10 -0
  5. data/designer/src/ComponentParams.rb +120 -0
  6. data/designer/src/ComponentsList.rb +90 -0
  7. data/designer/src/CreatedList.rb +156 -0
  8. data/designer/src/Designer.rb +114 -0
  9. data/designer/src/ToggleArea.rb +67 -0
  10. data/designer/src/common.rb +1 -0
  11. data/designer/src/component_factory.rb +30 -0
  12. data/designer/src/component_map.rb +116 -0
  13. data/designer/src/glade/ComponentParams.glade +96 -0
  14. data/designer/src/glade/Designer.glade +184 -0
  15. data/designer/src/images/add.png +0 -0
  16. data/designer/src/images/asciitext.png +0 -0
  17. data/designer/src/images/blank.png +0 -0
  18. data/designer/src/images/blank_sm.png +0 -0
  19. data/designer/src/images/button.png +0 -0
  20. data/designer/src/images/dial.png +0 -0
  21. data/designer/src/images/entry.png +0 -0
  22. data/designer/src/images/hslider.png +0 -0
  23. data/designer/src/images/label.png +0 -0
  24. data/designer/src/images/matrix.png +0 -0
  25. data/designer/src/images/orig/Check.png +0 -0
  26. data/designer/src/images/orig/ascii_text.png +0 -0
  27. data/designer/src/images/orig/button.png +0 -0
  28. data/designer/src/images/orig/dial.png +0 -0
  29. data/designer/src/images/orig/entry.png +0 -0
  30. data/designer/src/images/orig/hslider.png +0 -0
  31. data/designer/src/images/orig/label.png +0 -0
  32. data/designer/src/images/orig/matrix.png +0 -0
  33. data/designer/src/images/orig/radio.png +0 -0
  34. data/designer/src/images/orig/rocker.png +0 -0
  35. data/designer/src/images/orig/slist.png +0 -0
  36. data/designer/src/images/orig/vslider.png +0 -0
  37. data/designer/src/images/radio.png +0 -0
  38. data/designer/src/images/remove.png +0 -0
  39. data/designer/src/images/rocker.png +0 -0
  40. data/designer/src/images/slist.png +0 -0
  41. data/designer/src/images/vslider.png +0 -0
  42. data/lib/reterm.rb +22 -0
  43. data/lib/reterm/color_pair.rb +66 -0
  44. data/lib/reterm/component.rb +40 -0
  45. data/lib/reterm/components.rb +32 -0
  46. data/lib/reterm/components/ascii_text.rb +46 -0
  47. data/lib/reterm/components/button.rb +31 -0
  48. data/lib/reterm/components/dial.rb +95 -0
  49. data/lib/reterm/components/dialog.rb +34 -0
  50. data/lib/reterm/components/entry.rb +38 -0
  51. data/lib/reterm/components/hslider.rb +32 -0
  52. data/lib/reterm/components/image.rb +55 -0
  53. data/lib/reterm/components/label.rb +20 -0
  54. data/lib/reterm/components/matrix.rb +43 -0
  55. data/lib/reterm/components/radio.rb +33 -0
  56. data/lib/reterm/components/rocker.rb +71 -0
  57. data/lib/reterm/components/slist.rb +32 -0
  58. data/lib/reterm/components/template.rb +23 -0
  59. data/lib/reterm/components/vslider.rb +61 -0
  60. data/lib/reterm/init.rb +95 -0
  61. data/lib/reterm/layout.rb +63 -0
  62. data/lib/reterm/layouts.rb +16 -0
  63. data/lib/reterm/layouts/horizontal.rb +19 -0
  64. data/lib/reterm/layouts/vertical.rb +19 -0
  65. data/lib/reterm/loader.rb +126 -0
  66. data/lib/reterm/menu.rb +81 -0
  67. data/lib/reterm/mixins/cdk_component.rb +45 -0
  68. data/lib/reterm/mixins/component_input.rb +37 -0
  69. data/lib/reterm/mixins/event_dispatcher.rb +20 -0
  70. data/lib/reterm/mixins/nav_input.rb +126 -0
  71. data/lib/reterm/panel.rb +37 -0
  72. data/lib/reterm/resize.rb +27 -0
  73. data/lib/reterm/terminal.rb +33 -0
  74. data/lib/reterm/version.rb +3 -0
  75. data/lib/reterm/window.rb +260 -0
  76. metadata +155 -0
@@ -0,0 +1,63 @@
1
+ module RETerm
2
+ # Provides mechanism to orgranize on screen components according
3
+ # to rules defined by subclasses.
4
+ #
5
+ # Layouts themselves are specialized types of components as they
6
+ # are intended to be associated with windows in which to be rendered
7
+ class Layout < Component
8
+ include NavInput
9
+
10
+ # TODO padding / margin support
11
+
12
+ attr_accessor :children
13
+
14
+ def initialize(args={})
15
+ @children ||= []
16
+ end
17
+
18
+ # Subclasses should override this method returning
19
+ # boolean if child window exceeds bounds of layout
20
+ def exceeds_bounds?(child)
21
+ raise "NotImplemented"
22
+ end
23
+
24
+ # Return boolean indicating if any child component
25
+ # is activatable
26
+ def activatable?
27
+ children.any? { |c| c.component.activatable? }
28
+ end
29
+
30
+ # Create new child window and add it to layout
31
+ def add_child(h={})
32
+ child = window.create_child(h)
33
+
34
+ if child.win.nil?
35
+ raise ArgumentError, "could not create child window"
36
+ end
37
+
38
+ if exceeds_bounds?(child)
39
+ window.del_child(child) unless child.win.nil?
40
+ raise ArgumentError, "child exceeds bounds"
41
+ end
42
+
43
+ children << child
44
+ update_reterm
45
+ child
46
+ end
47
+
48
+ # Draw all layout children
49
+ def draw!
50
+ children.each { |c| c.draw! }
51
+ end
52
+
53
+ # Activates layout by dispatching to navigation
54
+ # input system.
55
+ #
56
+ # @see NavInput
57
+ def activate!
58
+ draw!
59
+ update_reterm
60
+ handle_input
61
+ end
62
+ end # class Layout
63
+ end # module RETerm
@@ -0,0 +1,16 @@
1
+ module RETerm
2
+ # Encapsulates all Layouts currently implemented
3
+ module Layouts
4
+ def self.all
5
+ @a ||= Layouts.constants
6
+ end
7
+
8
+ def self.names
9
+ @n ||= all.collect { |l| l.to_s }
10
+ end
11
+ end
12
+ end
13
+
14
+ require 'reterm/layout'
15
+ require 'reterm/layouts/horizontal'
16
+ require 'reterm/layouts/vertical'
@@ -0,0 +1,19 @@
1
+ module RETerm
2
+ module Layouts
3
+ # Layout which arrainges items horizontally across screen cols
4
+ class Horizontal < Layout
5
+ def current_cols
6
+ children.sum { |c| c.cols } + 1
7
+ end
8
+
9
+ def exceeds_bounds?(child)
10
+ child.rows > window.rows || (current_cols + child.cols) > window.cols
11
+ end
12
+
13
+ def add_child(h={})
14
+ # set x/y to next appropriate location
15
+ super(h.merge(:y => 1, :x => current_cols))
16
+ end
17
+ end # class Horizontal
18
+ end # module Layouts
19
+ end # module RETerm
@@ -0,0 +1,19 @@
1
+ module RETerm
2
+ module Layouts
3
+ # Layout which arrainges items vertically down screen rows
4
+ class Vertical < Layout
5
+ def current_rows
6
+ children.sum { |c| c.rows } + 1
7
+ end
8
+
9
+ def exceeds_bounds?(child)
10
+ child.cols > window.cols || (current_rows + child.rows) > window.rows
11
+ end
12
+
13
+ def add_child(h={})
14
+ # set x/y to next appropriate location
15
+ super(h.merge(:y => current_rows, :x => 1))
16
+ end
17
+ end # class Horizontal
18
+ end # module Layouts
19
+ end # module RETerm
@@ -0,0 +1,126 @@
1
+ module RETerm
2
+ # JSON schema loader, instantiates RETerm subsystem from schema
3
+ # stored in a JSON string. Currently this schema does not have a
4
+ # formal definition, but see examples/ for what can currently be
5
+ # done.
6
+ class Loader
7
+ # Initialie new loader from JSON string
8
+ def initialize(str)
9
+ require 'json'
10
+ parse_doc(str)
11
+ end
12
+
13
+ # Return top level window in loader
14
+ def window
15
+ @win
16
+ end
17
+
18
+ private
19
+
20
+ def parse_doc(str)
21
+ @j = JSON.parse(str)
22
+
23
+ raise ArgumentError unless @j.is_a?(Hash)
24
+ raise ArgumentError unless @j.key?("window")
25
+
26
+ parse_colors(@j['colors']) if @j.key?('colors')
27
+
28
+ @win = parse_win(@j['window'])
29
+ update_reterm
30
+ end
31
+
32
+ def parse_colors(colors)
33
+ raise ArgumentError unless colors.is_a?(Array)
34
+ colors.each { |c|
35
+ raise ArgumentError unless c.is_a?(Array) && c.size >= 3
36
+ ColorPair.register(*c)
37
+ }
38
+ end
39
+
40
+ def parse_win(win)
41
+ raise ArgumentError unless win.is_a?(Hash)
42
+
43
+ args = {}
44
+
45
+ args[:rows] = win['rows'] if win.key?('rows')
46
+ args[:cols] = win['cols'] if win.key?('cols')
47
+
48
+ w = Window.new args
49
+ parse_child(w, win)
50
+
51
+ w.border! if win.key?('border') && !!win['border']
52
+ w.colors = ColorPair.for(win['colors']) if win.key?('colors')
53
+
54
+ w
55
+ end
56
+
57
+ def parse_child(win, parent)
58
+ raise ArgumentError if parent.key?('component') && parent.key?('layout')
59
+ raise ArgumentError if !parent.key?('component') && !parent.key?('layout')
60
+
61
+ if parent.key?('component')
62
+ parse_component win, parent['component']
63
+
64
+ else # if parent.key?('layout')
65
+ parse_layout win, parent['layout']
66
+ end
67
+ end
68
+
69
+ def prep_init(init)
70
+ raise ArgumentError unless init.is_a?(Hash)
71
+ init = Hash[init] # make copy
72
+
73
+ init.keys.each { |k|
74
+ init[k.intern] = init[k] # XXX unsafe intern
75
+ init.delete(k)
76
+ }
77
+
78
+ init
79
+ end
80
+
81
+ def parse_component(win, c)
82
+ raise ArgumentError unless c.is_a?(Hash)
83
+ raise ArgumentError unless c.key?("type") ||
84
+ !Components.names.include?(c["type"])
85
+
86
+ init = c.key?("init") ? prep_init(c["init"]) : {}
87
+ c = RETerm::Components.const_get(c["type"]).new(init)
88
+ win.component = c
89
+ c
90
+ end
91
+
92
+ def parse_layout(win, l)
93
+ raise ArgumentError unless l.is_a?(Hash)
94
+ raise ArgumentError unless l.key?("type") ||
95
+ !Layouts.names.include?(l["type"])
96
+
97
+ init = l.key?("init") ? prep_init(l["init"]) : {}
98
+ lc = RETerm::Layouts.const_get(l["type"]).new(init)
99
+ win.component = lc
100
+ parse_layout_children(lc, l["children"]) if l.key?("children")
101
+ lc
102
+ end
103
+
104
+ def parse_layout_children(l, children)
105
+ raise ArgumentError unless children.is_a?(Array)
106
+
107
+ children.each { |child|
108
+ raise ArgumentError unless child.is_a?(Hash)
109
+ rows = child['rows'] if child.key?('rows')
110
+ cols = child['cols'] if child.key?('cols')
111
+
112
+ cw = l.add_child :rows => rows,
113
+ :cols => cols
114
+
115
+ cw.border! if child.key?('border') && !!child['border']
116
+ cw.colors = ColorPair.for(child['colors']) if child.key?('colors')
117
+
118
+ cw.component = parse_child(cw, child)
119
+ }
120
+ end
121
+ end # class Loader
122
+
123
+ def load_reterm(str)
124
+ Loader.new(str).window
125
+ end
126
+ end # module RETerm
@@ -0,0 +1,81 @@
1
+ module RETerm
2
+ # Graphical menu rendered on screen
3
+ #
4
+ # TODO: look into working this into std component/activation subsystem
5
+ class Menu
6
+ include EventDispatcher
7
+
8
+ attr_accessor :window
9
+
10
+ # Menus should be instantiated with a hash of label/value
11
+ # pairs to assign to menu
12
+ def initialize(items={})
13
+ @items = items
14
+ @menu = Ncurses::Menu.new_menu(items.collect { |k,v|
15
+ Ncurses::Menu.new_item(k.to_s, v.to_s)
16
+ })
17
+
18
+ @menu.menu_opts_off(Ncurses::Menu::O_SHOWDESC)
19
+ end
20
+
21
+ # Return number of rows in menu
22
+ def rows
23
+ @items.size
24
+ end
25
+
26
+ # Return number of cols in menu
27
+ def cols
28
+ @cols ||= @items.keys.max { |k| k.size }.size + 3
29
+ end
30
+
31
+ # Attach menu to specified window
32
+ def attach_to(win)
33
+ @menu.set_menu_win(win.win)
34
+ win.component = self
35
+
36
+ @child_win = win.create_child :rows => rows,
37
+ :cols => cols,
38
+ :x => 1, :y => 1
39
+ @menu.set_menu_sub(@child_win.win)
40
+ @menu.post_menu
41
+
42
+ self
43
+ end
44
+
45
+ # Destroy menu subsystem
46
+ def detach
47
+ @menu.unpost_menu
48
+ end
49
+
50
+ def finalize!
51
+ end
52
+
53
+ # Static map of menu drivers to internal representations.
54
+ #
55
+ # XXX defined here (as opposed to a const) since these
56
+ # consts won't be available until after init_scr
57
+ def driver_map
58
+ @driver_map ||= {
59
+ :left => Ncurses::Menu::REQ_LEFT_ITEM,
60
+ :right => Ncurses::Menu::REQ_RIGHT_ITEM,
61
+ :up => Ncurses::Menu::REQ_UP_ITEM,
62
+ :down => Ncurses::Menu::REQ_DOWN_ITEM,
63
+ :scr_up_line => Ncurses::Menu::REQ_SCR_ULINE,
64
+ :scr_down_line => Ncurses::Menu::REQ_SCR_DLINE,
65
+ :scr_down_page => Ncurses::Menu::REQ_SCR_DPAGE,
66
+ :scr_up_page => Ncurses::Menu::REQ_SCR_UPAGE,
67
+ :first => Ncurses::Menu::REQ_FIRST_ITEM,
68
+ :last => Ncurses::Menu::REQ_LAST_ITEM,
69
+ :next => Ncurses::Menu::REQ_NEXT_ITEM,
70
+ :prev => Ncurses::Menu::REQ_PREV_ITEM,
71
+ :toggle => Ncurses::Menu::REQ_TOGGLE_ITEM
72
+ }
73
+ end
74
+
75
+ # Drive menu from input
76
+ def drive(i)
77
+ i = driver_map[i] if i.is_a?(Symbol)
78
+ Ncurses::Menu.menu_driver(@menu, i)
79
+ end
80
+ end # class Menu
81
+ end # module RETerm
@@ -0,0 +1,45 @@
1
+ module RETerm
2
+ # Mixin used by CDK based component defining cdk-specific helpers
3
+ module CDKComponent
4
+ # Should be implemented in subclass to initialize component
5
+ def _component
6
+ raise "NotImplemented"
7
+ end
8
+
9
+ # Return completely initialized CDK component
10
+ def component
11
+ enable_cdk!
12
+ @component ||= begin
13
+ c = _component
14
+ c.setBackgroundColor("</#{@colors.id}>") if colored?
15
+ c
16
+ end
17
+ end
18
+
19
+ # Assign {ColorPair} to component
20
+ def colors=(c)
21
+ super
22
+ component.setBackgroundColor("</#{@colors.id}>")
23
+ end
24
+
25
+ # Invoke CDK draw routine
26
+ def draw!
27
+ component.draw([])
28
+ end
29
+
30
+ # CDK components may be activated
31
+ def activatable?
32
+ true
33
+ end
34
+
35
+ # Invoke CDK activation routine
36
+ def activate!
37
+ component.activate([])
38
+ end
39
+
40
+ # Return stored value of cdk component
41
+ def value
42
+ component.getValue
43
+ end
44
+ end # module CDKComponent
45
+ end # module RETerm
@@ -0,0 +1,37 @@
1
+ module RETerm
2
+ # Helper mixin defining standard input controls for custom
3
+ # components. 'In House' components included in the project
4
+ # may used this to standarding their usage.
5
+ module ComponentInput
6
+ # Key which if pressed cause the component to
7
+ # lose focus / become deactivated
8
+ QUIT_CONTROLS = [10, 27] # 10 = enter, 27 = ESC
9
+
10
+ # Keys if pressed invoked the increment operation
11
+ INC_CONTROLS = ['+'.ord, Ncurses::KEY_UP, Ncurses::KEY_RIGHT]
12
+
13
+ # Keys if pressed invoked the decrement operation
14
+ DEC_CONTROLS = ['-'.ord, Ncurses::KEY_DOWN, Ncurses::KEY_LEFT]
15
+
16
+ # May be overrideen in subclass, invoked when the user requests
17
+ # an 'increment'
18
+ def on_inc
19
+ end
20
+
21
+ # May be overrideen in subclass, invoked when the user
22
+ # requests a decrement
23
+ def on_dec
24
+ end
25
+
26
+ # Helper to be internally invoked by component on activation
27
+ def handle_input
28
+ while(!QUIT_CONTROLS.include?((ch = window.getch)))
29
+ if INC_CONTROLS.include?(ch)
30
+ on_inc
31
+ elsif DEC_CONTROLS.include?(ch)
32
+ on_dec
33
+ end
34
+ end
35
+ end
36
+ end # module ComponentInput
37
+ end # module RETerm
@@ -0,0 +1,20 @@
1
+ module RETerm
2
+ # Helper mixin defining methods to register event
3
+ # handlers and dispatch events. Events are simply
4
+ # composed parameters which are passed to the
5
+ # callback blocks when dispatched
6
+ module EventDispatcher
7
+ def dispatch(e, h={})
8
+ @event_handlers ||= {}
9
+ @event_handlers[e].each { |eh|
10
+ eh.call h
11
+ } if @event_handlers.key?(e)
12
+ end
13
+
14
+ def handle(e, &bl)
15
+ @event_handlers ||= {}
16
+ @event_handlers[e] ||= []
17
+ @event_handlers[e] << bl
18
+ end
19
+ end # module EventDispatcher
20
+ end # module RETerm
@@ -0,0 +1,126 @@
1
+ module RETerm
2
+ # Helper mixin defining standard navigation controls.
3
+ # Used by layouts and top level components this tightly
4
+ # defines keyboard navigation and allows the user to
5
+ # seemlessly move between and activate/decativate
6
+ # components.
7
+ module NavInput
8
+ # Key which if pressed causes the navigation component
9
+ # to lose focus / become deactivated
10
+ QUIT_CONTROLS = [27, 'q'.ord, 'Q'.ord] # 27 = ESC
11
+
12
+ # Key if pressed focuses on / activates a component
13
+ ENTER_CONTROLS = [10] # 10 = enter , space
14
+
15
+ # Up navigation keys
16
+ UP_CONTROLS = ['k'.ord, 'K'.ord, Ncurses::KEY_UP]
17
+
18
+ # Down navigation keys
19
+ DOWN_CONTROLS = ['j'.ord, 'J'.ord, Ncurses::KEY_DOWN]
20
+
21
+ # Left navigation keys
22
+ LEFT_CONTROLS = ['h'.ord, 'H'.ord, Ncurses::KEY_BACKSPACE,
23
+ Ncurses::KEY_BTAB,
24
+ Ncurses::KEY_LEFT]
25
+
26
+ # Right navigation keys
27
+ RIGHT_CONTROLS = ['l'.ord, 'L'.ord, "\t".ord, Ncurses::KEY_RIGHT]
28
+
29
+ # Return children which are focusabled/activable
30
+ def focusable
31
+ children.select { |c| c.component.activatable? }
32
+ end
33
+
34
+ # Return boolean indicating if any children are focusable
35
+ def focusable?
36
+ !focusable.empty?
37
+ end
38
+
39
+ # Helper to be internally invoked by navigation component
40
+ # on activation
41
+ def handle_input(from_parent=false)
42
+ @focus ||= 0
43
+
44
+ ch = handle_focused
45
+
46
+ while(!QUIT_CONTROLS.include?(ch))
47
+ if ENTER_CONTROLS.include?(ch)
48
+ focused.activate!
49
+
50
+ elsif UP_CONTROLS.include?(ch)
51
+ focused.no_border!
52
+
53
+ return ch if window.component.is_a?(Layouts::Horizontal) &&
54
+ from_parent &&
55
+ !window.parent.children.index(window) != 0
56
+
57
+ @focus -= 1
58
+
59
+ elsif LEFT_CONTROLS.include?(ch)
60
+ focused.no_border!
61
+
62
+ return ch if window.component.is_a?(Layouts::Vertical) &&
63
+ from_parent &&
64
+ !window.parent.children.index(window) != 0
65
+
66
+ @focus -= 1
67
+
68
+ elsif DOWN_CONTROLS.include?(ch)
69
+ focused.no_border!
70
+
71
+ return ch if window.component.is_a?(Layouts::Horizontal) &&
72
+ from_parent &&
73
+ !window.parent.children.index(window) != (window.parent.children.size - 1)
74
+
75
+ @focus += 1
76
+
77
+ elsif RIGHT_CONTROLS.include?(ch)
78
+ focused.no_border!
79
+
80
+ return ch if window.component.is_a?(Layouts::Vertical) &&
81
+ from_parent &&
82
+ !window.parent.children.index(window) != (window.parent.children.size - 1)
83
+
84
+ @focus += 1
85
+ end
86
+
87
+ if @focus >= focusable.size
88
+ @focus = focusable.size - 1
89
+ return ch if from_parent
90
+ end
91
+
92
+ if @focus < 0
93
+ @focus = 0
94
+ return ch if from_parent
95
+ end
96
+
97
+ ch = handle_focused
98
+ end
99
+
100
+ ch
101
+ end
102
+
103
+ private
104
+
105
+ def focused
106
+ focusable[@focus]
107
+ end
108
+
109
+ def handle_focused
110
+ ch = nil
111
+
112
+ focused.border! unless focused.component.kind_of?(Layout)
113
+ update_reterm
114
+ window.draw!
115
+
116
+ if focused.component.kind_of?(Layout)
117
+ ch = focused.component.handle_input(true)
118
+
119
+ else
120
+ ch = window.getch
121
+ end
122
+
123
+ ch
124
+ end
125
+ end # module NavInput
126
+ end # module RETerm