reterm 0.4.2 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/designer/src/ComponentParams.rb +89 -42
- data/designer/src/ComponentsList.rb +26 -25
- data/designer/src/CreatedList.rb +9 -0
- data/designer/src/Designer.rb +23 -1
- data/designer/src/ToggleArea.rb +20 -3
- data/designer/src/about.rb +35 -0
- data/designer/src/component_factory.rb +18 -0
- data/designer/src/component_map.rb +7 -0
- data/designer/src/file_chooser.rb +25 -0
- data/designer/src/glade/Designer.glade +1 -19
- data/designer/src/images/{slist.png → scroll_list.png} +0 -0
- data/lib/reterm.rb +10 -1
- data/lib/reterm/color_pair.rb +166 -10
- data/lib/reterm/component.rb +89 -2
- data/lib/reterm/components.rb +16 -1
- data/lib/reterm/components/alphalist.rb +45 -0
- data/lib/reterm/components/ascii_text.rb +14 -1
- data/lib/reterm/components/asciimator.rb +1 -0
- data/lib/reterm/components/button.rb +35 -5
- data/lib/reterm/components/button_box.rb +108 -0
- data/lib/reterm/components/close_button.rb +22 -0
- data/lib/reterm/components/cmd_output.rb +69 -0
- data/lib/reterm/components/dial.rb +11 -2
- data/lib/reterm/components/dialog.rb +41 -1
- data/lib/reterm/components/drop_down_menu.rb +140 -0
- data/lib/reterm/components/entry.rb +42 -14
- data/lib/reterm/components/histogram.rb +55 -0
- data/lib/reterm/components/hslider.rb +9 -0
- data/lib/reterm/components/image.rb +9 -0
- data/lib/reterm/components/isometric.rb +38 -0
- data/lib/reterm/components/label.rb +20 -3
- data/lib/reterm/components/matrix.rb +9 -0
- data/lib/reterm/components/multi_line_entry.rb +54 -0
- data/lib/reterm/components/password_entry.rb +15 -0
- data/lib/reterm/components/radio.rb +10 -0
- data/lib/reterm/components/revealing_label.rb +126 -0
- data/lib/reterm/components/rocker.rb +21 -11
- data/lib/reterm/components/scroll_list.rb +96 -0
- data/lib/reterm/components/scrolling_area.rb +50 -0
- data/lib/reterm/components/select_list.rb +67 -0
- data/lib/reterm/components/splash.rb +85 -0
- data/lib/reterm/components/template.rb +12 -1
- data/lib/reterm/components/treeview.rb +1 -0
- data/lib/reterm/components/vslider.rb +11 -2
- data/lib/reterm/components/youtube.rb +20 -0
- data/lib/reterm/config.rb +22 -0
- data/lib/reterm/init.rb +131 -6
- data/lib/reterm/layout.rb +147 -11
- data/lib/reterm/layouts.rb +1 -0
- data/lib/reterm/layouts/grid.rb +69 -0
- data/lib/reterm/layouts/horizontal.rb +25 -4
- data/lib/reterm/layouts/vertical.rb +26 -5
- data/lib/reterm/loader.rb +2 -2
- data/lib/reterm/mixins/button_helpers.rb +7 -0
- data/lib/reterm/mixins/cdk_component.rb +66 -2
- data/lib/reterm/mixins/common_controls.rb +15 -0
- data/lib/reterm/mixins/common_keys.rb +20 -0
- data/lib/reterm/mixins/component_input.rb +62 -14
- data/lib/reterm/mixins/event_dispatcher.rb +2 -0
- data/lib/reterm/mixins/item_helpers.rb +8 -0
- data/lib/reterm/mixins/key_bindings.rb +23 -0
- data/lib/reterm/mixins/log_helpers.rb +13 -0
- data/lib/reterm/mixins/mouse_input.rb +58 -0
- data/lib/reterm/mixins/nav_controls.rb +33 -0
- data/lib/reterm/mixins/nav_input.rb +161 -69
- data/lib/reterm/terminal.rb +6 -2
- data/lib/reterm/util.rb +121 -0
- data/lib/reterm/version.rb +1 -1
- data/lib/reterm/window.rb +295 -29
- metadata +33 -17
- data/designer/src/images/orig/Check.png +0 -0
- data/designer/src/images/orig/ascii_text.png +0 -0
- data/designer/src/images/orig/button.png +0 -0
- data/designer/src/images/orig/dial.png +0 -0
- data/designer/src/images/orig/entry.png +0 -0
- data/designer/src/images/orig/hslider.png +0 -0
- data/designer/src/images/orig/label.png +0 -0
- data/designer/src/images/orig/matrix.png +0 -0
- data/designer/src/images/orig/radio.png +0 -0
- data/designer/src/images/orig/rocker.png +0 -0
- data/designer/src/images/orig/slist.png +0 -0
- data/designer/src/images/orig/vslider.png +0 -0
- data/lib/reterm/components/slist.rb +0 -32
- 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
|
-
|
9
|
-
|
10
|
-
QUIT_CONTROLS = [27, 'q'.ord, 'Q'.ord] # 27 = ESC
|
8
|
+
include MouseInput
|
9
|
+
include NavControls
|
11
10
|
|
12
|
-
#
|
13
|
-
|
11
|
+
# Used internally to specify component
|
12
|
+
# which we should navigate to
|
13
|
+
attr_accessor :nav_select
|
14
14
|
|
15
|
-
#
|
16
|
-
|
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.
|
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
|
-
|
40
|
+
# focus on first component
|
41
|
+
ch = handle_focused unless nav_select
|
45
42
|
|
46
|
-
|
47
|
-
|
48
|
-
focused.activate!
|
43
|
+
# Repeat until quit
|
44
|
+
until quit_nav?(ch)
|
49
45
|
|
50
|
-
|
51
|
-
|
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
|
-
|
54
|
-
|
55
|
-
|
52
|
+
# specified component is not a descendent,
|
53
|
+
else
|
54
|
+
nav_to_parent
|
55
|
+
return nil
|
56
|
+
end
|
56
57
|
|
57
|
-
|
58
|
+
elsif ENTER_CONTROLS.include?(ch)
|
59
|
+
focused.activate!
|
58
60
|
|
59
|
-
elsif
|
60
|
-
|
61
|
+
elsif MOVEMENT_CONTROLS.include?(ch)
|
62
|
+
handle_movement(ch, from_parent)
|
61
63
|
|
62
|
-
|
63
|
-
|
64
|
-
!window.parent.children.index(window) != 0
|
64
|
+
elsif mev = process_mouse(ch)
|
65
|
+
handle_mouse(mev)
|
65
66
|
|
66
|
-
|
67
|
+
else
|
68
|
+
dispatch(:entry, ch)
|
67
69
|
|
68
|
-
|
69
|
-
focused.no_border!
|
70
|
+
end
|
70
71
|
|
71
|
-
|
72
|
-
|
73
|
-
|
72
|
+
return ch unless sanitize_focus!(from_parent)
|
73
|
+
ch = handle_focused unless nav_select ||
|
74
|
+
shutdown? ||
|
75
|
+
deactivate?
|
76
|
+
end
|
74
77
|
|
75
|
-
|
78
|
+
ch
|
79
|
+
end
|
76
80
|
|
77
|
-
|
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
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
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
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
90
|
+
def next_focus(ch)
|
91
|
+
if UP_CONTROLS.include?(ch) ||
|
92
|
+
LEFT_CONTROLS.include?(ch)
|
93
|
+
@focus - 1
|
96
94
|
|
97
|
-
|
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.
|
113
|
-
|
114
|
-
window.draw!
|
121
|
+
focused.dispatch :focused
|
122
|
+
update_focus
|
115
123
|
|
116
|
-
if focused.
|
117
|
-
|
124
|
+
if focused.activate_focus?
|
125
|
+
focused.activate!
|
118
126
|
|
119
|
-
|
120
|
-
ch =
|
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
|
data/lib/reterm/terminal.rb
CHANGED
@@ -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
|
36
|
+
end # class Terminal
|
37
|
+
end # module RETerm
|
data/lib/reterm/util.rb
ADDED
@@ -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
|