reterm 0.4.2 → 0.5.0

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 (85) hide show
  1. checksums.yaml +4 -4
  2. data/designer/src/ComponentParams.rb +89 -42
  3. data/designer/src/ComponentsList.rb +26 -25
  4. data/designer/src/CreatedList.rb +9 -0
  5. data/designer/src/Designer.rb +23 -1
  6. data/designer/src/ToggleArea.rb +20 -3
  7. data/designer/src/about.rb +35 -0
  8. data/designer/src/component_factory.rb +18 -0
  9. data/designer/src/component_map.rb +7 -0
  10. data/designer/src/file_chooser.rb +25 -0
  11. data/designer/src/glade/Designer.glade +1 -19
  12. data/designer/src/images/{slist.png → scroll_list.png} +0 -0
  13. data/lib/reterm.rb +10 -1
  14. data/lib/reterm/color_pair.rb +166 -10
  15. data/lib/reterm/component.rb +89 -2
  16. data/lib/reterm/components.rb +16 -1
  17. data/lib/reterm/components/alphalist.rb +45 -0
  18. data/lib/reterm/components/ascii_text.rb +14 -1
  19. data/lib/reterm/components/asciimator.rb +1 -0
  20. data/lib/reterm/components/button.rb +35 -5
  21. data/lib/reterm/components/button_box.rb +108 -0
  22. data/lib/reterm/components/close_button.rb +22 -0
  23. data/lib/reterm/components/cmd_output.rb +69 -0
  24. data/lib/reterm/components/dial.rb +11 -2
  25. data/lib/reterm/components/dialog.rb +41 -1
  26. data/lib/reterm/components/drop_down_menu.rb +140 -0
  27. data/lib/reterm/components/entry.rb +42 -14
  28. data/lib/reterm/components/histogram.rb +55 -0
  29. data/lib/reterm/components/hslider.rb +9 -0
  30. data/lib/reterm/components/image.rb +9 -0
  31. data/lib/reterm/components/isometric.rb +38 -0
  32. data/lib/reterm/components/label.rb +20 -3
  33. data/lib/reterm/components/matrix.rb +9 -0
  34. data/lib/reterm/components/multi_line_entry.rb +54 -0
  35. data/lib/reterm/components/password_entry.rb +15 -0
  36. data/lib/reterm/components/radio.rb +10 -0
  37. data/lib/reterm/components/revealing_label.rb +126 -0
  38. data/lib/reterm/components/rocker.rb +21 -11
  39. data/lib/reterm/components/scroll_list.rb +96 -0
  40. data/lib/reterm/components/scrolling_area.rb +50 -0
  41. data/lib/reterm/components/select_list.rb +67 -0
  42. data/lib/reterm/components/splash.rb +85 -0
  43. data/lib/reterm/components/template.rb +12 -1
  44. data/lib/reterm/components/treeview.rb +1 -0
  45. data/lib/reterm/components/vslider.rb +11 -2
  46. data/lib/reterm/components/youtube.rb +20 -0
  47. data/lib/reterm/config.rb +22 -0
  48. data/lib/reterm/init.rb +131 -6
  49. data/lib/reterm/layout.rb +147 -11
  50. data/lib/reterm/layouts.rb +1 -0
  51. data/lib/reterm/layouts/grid.rb +69 -0
  52. data/lib/reterm/layouts/horizontal.rb +25 -4
  53. data/lib/reterm/layouts/vertical.rb +26 -5
  54. data/lib/reterm/loader.rb +2 -2
  55. data/lib/reterm/mixins/button_helpers.rb +7 -0
  56. data/lib/reterm/mixins/cdk_component.rb +66 -2
  57. data/lib/reterm/mixins/common_controls.rb +15 -0
  58. data/lib/reterm/mixins/common_keys.rb +20 -0
  59. data/lib/reterm/mixins/component_input.rb +62 -14
  60. data/lib/reterm/mixins/event_dispatcher.rb +2 -0
  61. data/lib/reterm/mixins/item_helpers.rb +8 -0
  62. data/lib/reterm/mixins/key_bindings.rb +23 -0
  63. data/lib/reterm/mixins/log_helpers.rb +13 -0
  64. data/lib/reterm/mixins/mouse_input.rb +58 -0
  65. data/lib/reterm/mixins/nav_controls.rb +33 -0
  66. data/lib/reterm/mixins/nav_input.rb +161 -69
  67. data/lib/reterm/terminal.rb +6 -2
  68. data/lib/reterm/util.rb +121 -0
  69. data/lib/reterm/version.rb +1 -1
  70. data/lib/reterm/window.rb +295 -29
  71. metadata +33 -17
  72. data/designer/src/images/orig/Check.png +0 -0
  73. data/designer/src/images/orig/ascii_text.png +0 -0
  74. data/designer/src/images/orig/button.png +0 -0
  75. data/designer/src/images/orig/dial.png +0 -0
  76. data/designer/src/images/orig/entry.png +0 -0
  77. data/designer/src/images/orig/hslider.png +0 -0
  78. data/designer/src/images/orig/label.png +0 -0
  79. data/designer/src/images/orig/matrix.png +0 -0
  80. data/designer/src/images/orig/radio.png +0 -0
  81. data/designer/src/images/orig/rocker.png +0 -0
  82. data/designer/src/images/orig/slist.png +0 -0
  83. data/designer/src/images/orig/vslider.png +0 -0
  84. data/lib/reterm/components/slist.rb +0 -32
  85. data/lib/reterm/menu.rb +0 -81
@@ -0,0 +1,33 @@
1
+ module RETerm
2
+ module NavControls
3
+ # Key which if pressed causes the navigation component
4
+ # to lose focus / become deactivated
5
+ QUIT_CONTROLS = [27] # 27 = ESC
6
+
7
+ # Key if pressed focuses on / activates a component
8
+ ENTER_CONTROLS = [10, Ncurses::KEY_ENTER] # 10 = enter , space
9
+
10
+ # Up navigation keys
11
+ UP_CONTROLS = ['k'.ord, 'K'.ord, Ncurses::KEY_UP]
12
+
13
+ # Down navigation keys
14
+ DOWN_CONTROLS = ['j'.ord, 'J'.ord, Ncurses::KEY_DOWN]
15
+
16
+ # Left navigation keys
17
+ LEFT_CONTROLS = ['h'.ord, 'H'.ord, Ncurses::KEY_BACKSPACE,
18
+ Ncurses::KEY_BTAB,
19
+ Ncurses::KEY_LEFT]
20
+
21
+ # Right navigation keys
22
+ RIGHT_CONTROLS = ['l'.ord, 'L'.ord, "\t".ord, Ncurses::KEY_RIGHT]
23
+
24
+ # All movement keys
25
+ MOVEMENT_CONTROLS = UP_CONTROLS + DOWN_CONTROLS +
26
+ LEFT_CONTROLS + RIGHT_CONTROLS
27
+
28
+ # Quit when quit-sequence detected or app-shutdown
29
+ def quit_nav?(ch=nil)
30
+ (!ch.nil? && QUIT_CONTROLS.include?(ch) || shutdown? || deactivate?)
31
+ end
32
+ end # module NavControls
33
+ end # module RETerm
@@ -5,30 +5,20 @@ module RETerm
5
5
  # seemlessly move between and activate/decativate
6
6
  # components.
7
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
8
+ include MouseInput
9
+ include NavControls
11
10
 
12
- # Key if pressed focuses on / activates a component
13
- ENTER_CONTROLS = [10] # 10 = enter , space
11
+ # Used internally to specify component
12
+ # which we should navigate to
13
+ attr_accessor :nav_select
14
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]
15
+ # Used internally to specify which movement
16
+ # command we should follow
17
+ attr_accessor :ch_select
28
18
 
29
19
  # Return children which are focusabled/activable
30
20
  def focusable
31
- children.select { |c| c.component.activatable? }
21
+ children.select { |c| c.activatable? }
32
22
  end
33
23
 
34
24
  # Return boolean indicating if any children are focusable
@@ -36,91 +26,193 @@ module RETerm
36
26
  !focusable.empty?
37
27
  end
38
28
 
29
+ # May be overridden by subclass to indicate if the
30
+ # specified input / context is valid
31
+ def valid_input?(ch, from_parent)
32
+ true
33
+ end
34
+
39
35
  # Helper to be internally invoked by navigation component
40
36
  # on activation
41
37
  def handle_input(from_parent=false)
42
38
  @focus ||= 0
43
39
 
44
- ch = handle_focused
40
+ # focus on first component
41
+ ch = handle_focused unless nav_select
45
42
 
46
- while(!QUIT_CONTROLS.include?(ch))
47
- if ENTER_CONTROLS.include?(ch)
48
- focused.activate!
43
+ # Repeat until quit
44
+ until quit_nav?(ch)
49
45
 
50
- elsif UP_CONTROLS.include?(ch)
51
- focused.no_border!
46
+ # Navigate to the specified component (nav_select)
47
+ if self.nav_select
48
+ # it is a descendent of this one
49
+ if self.contains?(self.nav_select)
50
+ nav_to_selected
52
51
 
53
- return ch if window.component.is_a?(Layouts::Horizontal) &&
54
- from_parent &&
55
- !window.parent.children.index(window) != 0
52
+ # specified component is not a descendent,
53
+ else
54
+ nav_to_parent
55
+ return nil
56
+ end
56
57
 
57
- @focus -= 1
58
+ elsif ENTER_CONTROLS.include?(ch)
59
+ focused.activate!
58
60
 
59
- elsif LEFT_CONTROLS.include?(ch)
60
- focused.no_border!
61
+ elsif MOVEMENT_CONTROLS.include?(ch)
62
+ handle_movement(ch, from_parent)
61
63
 
62
- return ch if window.component.is_a?(Layouts::Vertical) &&
63
- from_parent &&
64
- !window.parent.children.index(window) != 0
64
+ elsif mev = process_mouse(ch)
65
+ handle_mouse(mev)
65
66
 
66
- @focus -= 1
67
+ else
68
+ dispatch(:entry, ch)
67
69
 
68
- elsif DOWN_CONTROLS.include?(ch)
69
- focused.no_border!
70
+ end
70
71
 
71
- return ch if window.component.is_a?(Layouts::Horizontal) &&
72
- from_parent &&
73
- !window.parent.children.index(window) != (window.parent.children.size - 1)
72
+ return ch unless sanitize_focus!(from_parent)
73
+ ch = handle_focused unless nav_select ||
74
+ shutdown? ||
75
+ deactivate?
76
+ end
74
77
 
75
- @focus += 1
78
+ ch
79
+ end
76
80
 
77
- elsif RIGHT_CONTROLS.include?(ch)
78
- focused.no_border!
81
+ private
79
82
 
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
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
84
+ def handle_movement(ch, from_parent)
85
+ remove_focus
86
+ return ch unless valid_input?(ch, from_parent)
87
+ @focus = next_focus(ch)
88
+ end
91
89
 
92
- if @focus < 0
93
- @focus = 0
94
- return ch if from_parent
95
- end
90
+ def next_focus(ch)
91
+ if UP_CONTROLS.include?(ch) ||
92
+ LEFT_CONTROLS.include?(ch)
93
+ @focus - 1
96
94
 
97
- ch = handle_focused
95
+ elsif DOWN_CONTROLS.include?(ch) ||
96
+ RIGHT_CONTROLS.include?(ch)
97
+ @focus + 1
98
98
  end
99
-
100
- ch
101
99
  end
102
100
 
103
- private
104
-
105
101
  def focused
102
+ return nil unless !!@focus
106
103
  focusable[@focus]
107
104
  end
108
105
 
106
+ def draw_focus!
107
+ focused.window.border! if !!focused && focused.highlight_focus?
108
+ end
109
+
110
+ # Internal help, set the visual properties of the focused window
111
+ def update_focus
112
+ draw_focus!
113
+ update_reterm
114
+ window.root.draw!
115
+ end
116
+
117
+ # Internal helper, logic invoked when a component gains focus
109
118
  def handle_focused
110
119
  ch = nil
111
120
 
112
- focused.border! unless focused.component.kind_of?(Layout)
113
- update_reterm
114
- window.draw!
121
+ focused.dispatch :focused
122
+ update_focus
115
123
 
116
- if focused.component.kind_of?(Layout)
117
- ch = focused.component.handle_input(true)
124
+ if focused.activate_focus?
125
+ focused.activate!
118
126
 
119
- else
120
- ch = window.getch
127
+ elsif focused.kind_of?(Layout)
128
+ ch = focused.handle_input(true)
129
+
130
+ elsif !deactivate? && !nav_select
131
+ ch = sync_getch
132
+ end
133
+
134
+ if self.ch_select
135
+ ch = self.ch_select
136
+ self.ch_select = nil
121
137
  end
122
138
 
123
139
  ch
124
140
  end
141
+
142
+ # Internal helper, logic invoked when a component loses focus
143
+ def remove_focus
144
+ focused.window.no_border!
145
+ focused.dispatch :unfocused
146
+ end
147
+
148
+ # Internal helper, navigate to selected component under current
149
+ def nav_to_selected
150
+ # clear nav_select
151
+ ns = self.nav_select
152
+ self.nav_select = nil
153
+
154
+ # specified component is a direct child
155
+ if self.children.include?(ns)
156
+ remove_focus
157
+ @focus = focusable.index(ns)
158
+ #handle_focused
159
+ #update_focus
160
+ #focused.activate!
161
+
162
+ # not a direct child, navigate down to layout
163
+ # containing it
164
+ else
165
+ child = self.layout_containing(ns)
166
+ child.nav_select = ns
167
+ ch = child.handle_input(true)
168
+ end
169
+ end
170
+
171
+ # Internal helper, navigate to selected component up heirarchy
172
+ def nav_to_parent
173
+ # set selected component on the parent,
174
+ # clear it locally, and
175
+ # return to parent
176
+ remove_focus
177
+ ns = self.nav_select
178
+ self.nav_select = nil
179
+ self.parent.nav_select = ns
180
+ end
181
+
182
+ # Internal helper, sanitize the focus tracker.
183
+ # Return value indicates if sanity is preseved
184
+ # in the context of this component (else we will
185
+ # return to parent)
186
+ def sanitize_focus!(from_parent)
187
+ if @focus >= focusable.size
188
+ @focus = focusable.size - 1
189
+ return false if from_parent
190
+
191
+ elsif @focus < 0
192
+ @focus = 0
193
+ return false if from_parent
194
+ end
195
+
196
+ true
197
+ end
198
+
199
+ def handle_mouse(mev)
200
+ child = window.root.child_containing(mev.x, mev.y, mev.z)
201
+
202
+ if child
203
+ child = child.component
204
+
205
+ if child.activatable?
206
+ if self.contains?(child)
207
+ nav_to_selected
208
+
209
+ else
210
+ self.nav_select = child
211
+ nav_to_parent
212
+ return nil
213
+ end
214
+ end
215
+ end
216
+ end
125
217
  end # module NavInput
126
218
  end # module RETerm
@@ -24,10 +24,14 @@ module RETerm
24
24
  dimensions[0]
25
25
  end
26
26
 
27
+ def self.contains?(r, c)
28
+ r < rows && c < cols
29
+ end
30
+
27
31
  def self.resize!
28
32
  Window.top.each { |w|
29
33
  w.dispatch :resize
30
34
  }
31
35
  end
32
- end
33
- end # module Terminal
36
+ end # class Terminal
37
+ end # module RETerm
@@ -0,0 +1,121 @@
1
+ module RETerm
2
+ # Helper returning boolean indicating if specified process is alive
3
+ def process_alive?(pid)
4
+ begin
5
+ Process.getpgid( pid )
6
+ true
7
+ rescue Errno::ESRCH
8
+ false
9
+ end
10
+ end
11
+
12
+ ANSI_COLORS = {
13
+ 1 => :bold,
14
+ 30 => :black,
15
+ 31 => :red,
16
+ 32 => :green,
17
+ 33 => :yellow,
18
+ 34 => :blue,
19
+ 35 => :magenta,
20
+ 36 => :cyan,
21
+ 37 => :white,
22
+
23
+ # XXX techincally the 'bright' variations
24
+ 90 => :black,
25
+ 91 => :red,
26
+ 92 => :green,
27
+ 93 => :yellow,
28
+ 94 => :blue,
29
+ 95 => :magenta,
30
+ 96 => :cyan,
31
+ 97 => :white,
32
+ }
33
+
34
+ # Converts specified ansi string to array
35
+ # of character data & corresponding control
36
+ # logic
37
+ def parse_ansi(str)
38
+ require 'strscan'
39
+
40
+ r = []
41
+ f = []
42
+ t = []
43
+
44
+ a = nil
45
+ s = StringScanner.new(str)
46
+
47
+ while(!s.eos?)
48
+ # end of formatting
49
+ if s.scan(/(\e|\[)\[0m/)
50
+ t << f.pop
51
+ t.compact!
52
+ if f.empty?
53
+ r << [a, t]
54
+ t = []
55
+ a = nil
56
+ end
57
+
58
+ # basic formatter
59
+ elsif s.scan(/\e\[(3[0-7]|90|1)m/)
60
+ # FIXME need to register formatting for 'a'
61
+ # up to this point (and reset 'a') (and below)
62
+ f << ANSI_COLORS[s[1].to_i]
63
+
64
+ # sgr
65
+ # https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
66
+ elsif s.scan(/\e\[(([0-9]+;?)+)m/)
67
+ sgr = s[1].split(";").collect { |s| s.to_i }
68
+ f << if (30..37).include?(sgr[0])
69
+ ANSI_COLORS[sgr[1]]
70
+
71
+ elsif sgr[0] == 38
72
+ if sgr[1] == 5
73
+ if sgr[2] < 8
74
+ ANSI_COLORS[sgr[2]]
75
+
76
+ elsif sgr[2] < 16
77
+ ANSI_COLORS[sgr[2]]
78
+
79
+ elsif sgr[2] < 232
80
+ # TODO verify:
81
+ # https://stackoverflow.com/questions/12338015/converting-8-bit-color-into-rgb-value
82
+ re = (sgr[2] >> 5) * 32
83
+ gr = ((sgr[2] & 28) >> 2) * 32
84
+ bl = (sgr[2] & 3) * 64
85
+ [re, gr, bl]
86
+
87
+ else # if srg[2] < 256
88
+ # TODO
89
+ end
90
+
91
+ else # if sgr[1] == 2
92
+ # TODO
93
+ end
94
+
95
+ # TODO other sgr commands
96
+ end
97
+
98
+ else
99
+ a = "" if a.nil?
100
+ a += s.scan(/./m)
101
+
102
+ end
103
+ end
104
+
105
+
106
+ # handle remaining / lingering data
107
+ r << [a, (t + f).compact] unless f.empty?
108
+
109
+ r
110
+ end
111
+
112
+ # Helper appending string to debug file
113
+ def file_append(f, t)
114
+ File.open(f, "a") { |f| f.write t }
115
+ end
116
+
117
+ # Flushes all input queues
118
+ def flush_input
119
+ Ncurses.flushinp
120
+ end
121
+ end # module RETerm