reterm 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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