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.
- 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
|