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,22 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module RETerm
|
4
|
+
class Config
|
5
|
+
CONFIG_FILE = File.expand_path("~/.reterm")
|
6
|
+
|
7
|
+
def self.get
|
8
|
+
@config ||=
|
9
|
+
begin
|
10
|
+
return {} unless File.exist?(CONFIG_FILE)
|
11
|
+
JSON.parse(File.read(CONFIG_FILE))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.load_plugins
|
16
|
+
return unless get.key?("plugins")
|
17
|
+
get["plugins"].each { |p|
|
18
|
+
require p
|
19
|
+
}
|
20
|
+
end
|
21
|
+
end # class Config
|
22
|
+
end # module RETerm
|
data/lib/reterm/init.rb
CHANGED
@@ -2,6 +2,10 @@ require 'ncursesw'
|
|
2
2
|
require_relative './resize'
|
3
3
|
|
4
4
|
module RETerm
|
5
|
+
SYNC_TIMEOUT = 150 # in milliseconds if enabled
|
6
|
+
|
7
|
+
###############################
|
8
|
+
|
5
9
|
# Initializes the RETerm subsystem before invoking block.
|
6
10
|
# After block is finished regardless of execution state
|
7
11
|
# (return, exception, etc) this will cleanup the terminal
|
@@ -17,6 +21,11 @@ module RETerm
|
|
17
21
|
# }
|
18
22
|
#
|
19
23
|
def init_reterm(opts={}, &bl)
|
24
|
+
@@shutdown = false
|
25
|
+
@@reterm_opts = opts
|
26
|
+
|
27
|
+
err = nil
|
28
|
+
|
20
29
|
begin
|
21
30
|
Terminal.load # XXX: not sure why but loading terminal
|
22
31
|
# info after changing terminal settings via
|
@@ -25,25 +34,43 @@ module RETerm
|
|
25
34
|
# without a 'reset' (see below).
|
26
35
|
# So just preload terminal info here.
|
27
36
|
|
37
|
+
# shorten up the esc delay time
|
38
|
+
ENV["ESCDELAY"] = 100.to_s
|
39
|
+
|
28
40
|
stdscr = Ncurses::initscr
|
29
41
|
Ncurses::start_color
|
30
42
|
Ncurses::noecho
|
31
43
|
Ncurses::cbreak
|
32
|
-
|
44
|
+
disable_cursor! unless !!opts[:cursor]
|
33
45
|
Ncurses::keypad(stdscr, true)
|
34
46
|
|
47
|
+
no_mouse = opts[:nomouse] || (opts.key?(:mouse) && !opts[:mouse])
|
48
|
+
Ncurses::mousemask(MouseInput::ALL_EVENTS, []) unless no_mouse
|
49
|
+
|
35
50
|
track_resize if opts[:resize]
|
36
51
|
|
37
52
|
bl.call
|
38
53
|
|
39
|
-
|
54
|
+
rescue => e
|
55
|
+
err = e
|
56
|
+
|
57
|
+
ensure
|
40
58
|
stop_track_resize
|
41
59
|
Ncurses.curs_set(1)
|
42
|
-
Window.
|
60
|
+
Window.top.each { |w| w.finalize! }
|
43
61
|
CDK::SCREEN.endCDK if cdk_enabled?
|
44
|
-
Ncurses.endwin
|
62
|
+
Ncurses.endwin
|
45
63
|
#`reset -Q` # XXX only way to guarantee a full reset (see above)
|
46
64
|
end
|
65
|
+
|
66
|
+
if err
|
67
|
+
puts "Unhandled error:"
|
68
|
+
puts " " + err.to_s
|
69
|
+
puts err.backtrace
|
70
|
+
.collect { |b|
|
71
|
+
" " + b
|
72
|
+
}.join("\n")
|
73
|
+
end
|
47
74
|
end
|
48
75
|
|
49
76
|
# This method resyncs the visible terminal with the internal
|
@@ -58,18 +85,74 @@ module RETerm
|
|
58
85
|
# win.getch # wait for input
|
59
86
|
# }
|
60
87
|
#
|
61
|
-
def update_reterm
|
62
|
-
|
88
|
+
def update_reterm(force_refresh=false)
|
89
|
+
if force_refresh
|
90
|
+
Window.top.each { |w|
|
91
|
+
w.erase
|
92
|
+
w.draw!
|
93
|
+
}
|
94
|
+
else
|
95
|
+
Window.top.each { |w| w.noutrefresh }
|
96
|
+
end
|
97
|
+
|
63
98
|
Ncurses::Panel.update_panels
|
64
99
|
Ncurses.doupdate
|
65
100
|
end
|
66
101
|
|
102
|
+
# Return copy of options specified to init_reterm
|
103
|
+
def reterm_opts
|
104
|
+
@@_reterm_opts ||= Hash[@@reterm_opts]
|
105
|
+
end
|
106
|
+
|
67
107
|
# XXX Ncurses is exposed so that users
|
68
108
|
# may employ any constants if desired.
|
69
109
|
# This should not be needed though and
|
70
110
|
# is discouraged to maintain portability
|
71
111
|
NC = Ncurses
|
72
112
|
|
113
|
+
###
|
114
|
+
|
115
|
+
# Cursor
|
116
|
+
|
117
|
+
def cursor?
|
118
|
+
!(@@cursor_disabled ||= false)
|
119
|
+
end
|
120
|
+
|
121
|
+
def disable_cursor!
|
122
|
+
# store cursor state
|
123
|
+
o = cursor?
|
124
|
+
|
125
|
+
# set cursor state
|
126
|
+
@@cursor_disabled = true
|
127
|
+
Ncurses::curs_set(0)
|
128
|
+
|
129
|
+
# invoke block if given
|
130
|
+
return unless block_given?
|
131
|
+
yield
|
132
|
+
|
133
|
+
# restore cursor state after block
|
134
|
+
enable_cursor! if o
|
135
|
+
end
|
136
|
+
|
137
|
+
def enable_cursor!
|
138
|
+
# store cursor state
|
139
|
+
o = cursor?
|
140
|
+
|
141
|
+
# set cursor state
|
142
|
+
@@cursor_disabled = false
|
143
|
+
Ncurses::curs_set(1)
|
144
|
+
|
145
|
+
# invoke block if given
|
146
|
+
return unless block_given?
|
147
|
+
yield
|
148
|
+
|
149
|
+
# restore cursor state after block
|
150
|
+
disable_cursor! unless o
|
151
|
+
end
|
152
|
+
|
153
|
+
|
154
|
+
###############################
|
155
|
+
|
73
156
|
# Enable the CDK subsystem. Used by CDKbased components
|
74
157
|
#
|
75
158
|
# @see #cdk_enabled?
|
@@ -92,4 +175,46 @@ module RETerm
|
|
92
175
|
def cdk_enabled?
|
93
176
|
!!@cdk_enabled
|
94
177
|
end
|
178
|
+
|
179
|
+
###############################
|
180
|
+
|
181
|
+
# Enables the input timeout and component syncronization.
|
182
|
+
#
|
183
|
+
# Used internally by components that need to periodically
|
184
|
+
# be updated outside of user input.
|
185
|
+
def activate_sync!
|
186
|
+
@@sync_activated = true
|
187
|
+
Window.all.each { |w|
|
188
|
+
w.win.timeout(SYNC_TIMEOUT)
|
189
|
+
}
|
190
|
+
end
|
191
|
+
|
192
|
+
# Boonean indicating if component syncronization is
|
193
|
+
# enabled
|
194
|
+
def sync_enabled?
|
195
|
+
defined?(@@sync_activated) && !!@@sync_activated
|
196
|
+
end
|
197
|
+
|
198
|
+
# Run the sync process, used internally
|
199
|
+
def run_sync!
|
200
|
+
return unless sync_enabled?
|
201
|
+
|
202
|
+
Window.all.each { |w|
|
203
|
+
w.sync!
|
204
|
+
}
|
205
|
+
|
206
|
+
update_reterm
|
207
|
+
end
|
208
|
+
|
209
|
+
###############################
|
210
|
+
|
211
|
+
# Use to halt all operation and cleanup.
|
212
|
+
def shutdown!
|
213
|
+
@@shutdown = true
|
214
|
+
end
|
215
|
+
|
216
|
+
# Boolean indicating if app in being halted
|
217
|
+
def shutdown?
|
218
|
+
!!@@shutdown
|
219
|
+
end
|
95
220
|
end # module RETerm
|
data/lib/reterm/layout.rb
CHANGED
@@ -7,57 +7,193 @@ module RETerm
|
|
7
7
|
class Layout < Component
|
8
8
|
include NavInput
|
9
9
|
|
10
|
-
#
|
10
|
+
# Flag indicating if layout should expand
|
11
|
+
# to be able to contain childen (if false
|
12
|
+
# error will be throw if trying to add
|
13
|
+
# child to layout that cannot contain it)
|
14
|
+
attr_accessor :expand
|
15
|
+
|
16
|
+
def expandable?
|
17
|
+
(!defined?(@expand) || !!@expand) &&
|
18
|
+
(!parent? || parent.expandable?)
|
19
|
+
end
|
20
|
+
|
21
|
+
def children
|
22
|
+
window.children.collect { |w| w.component }
|
23
|
+
end
|
24
|
+
|
25
|
+
def empty?
|
26
|
+
return true if window.nil?
|
27
|
+
window.children.empty?
|
28
|
+
end
|
29
|
+
|
30
|
+
def child_windows
|
31
|
+
window.children
|
32
|
+
end
|
33
|
+
|
34
|
+
def parent?
|
35
|
+
window.parent?
|
36
|
+
end
|
37
|
+
|
38
|
+
def parent
|
39
|
+
window.parent.component
|
40
|
+
end
|
11
41
|
|
12
|
-
|
42
|
+
# TODO padding / margin support
|
13
43
|
|
14
|
-
|
15
|
-
|
44
|
+
# Subclasses should override this method returning
|
45
|
+
# current rows in layout
|
46
|
+
def current_rows
|
47
|
+
raise "NotImplemented"
|
16
48
|
end
|
17
49
|
|
18
50
|
# Subclasses should override this method returning
|
19
|
-
#
|
20
|
-
def
|
51
|
+
# current cols in layout
|
52
|
+
def current_cols
|
21
53
|
raise "NotImplemented"
|
22
54
|
end
|
23
55
|
|
56
|
+
# Subclasses should overrid this method returning
|
57
|
+
# boolean indicating if boundries will be
|
58
|
+
# exceeded when child is added
|
59
|
+
def exceeds_bounds_with?(child)
|
60
|
+
raise "NotImplemented"
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns boolean indicating if current layout
|
64
|
+
# exceeds bounds of window
|
65
|
+
def exceeds_bounds?
|
66
|
+
current_cols > window.cols ||
|
67
|
+
current_rows > window.rows
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
# Return layout containing component
|
72
|
+
# If coordinates are contained in a child in current layout
|
73
|
+
def layout_containing(component)
|
74
|
+
return self if children.include?(component)
|
75
|
+
|
76
|
+
found = nil
|
77
|
+
children.each { |c|
|
78
|
+
next if found
|
79
|
+
|
80
|
+
if c.kind_of?(Layout)
|
81
|
+
found = c unless c.layout_containing(component).nil?
|
82
|
+
end
|
83
|
+
}
|
84
|
+
|
85
|
+
found
|
86
|
+
end
|
87
|
+
|
88
|
+
# Return boolean indicating if layout contains specified child
|
89
|
+
def contains?(child)
|
90
|
+
children.any? { |c|
|
91
|
+
(c.kind_of?(Layout) && c.contains?(child)) || c == child
|
92
|
+
}
|
93
|
+
end
|
94
|
+
|
24
95
|
# Return boolean indicating if any child component
|
25
96
|
# is activatable
|
26
97
|
def activatable?
|
27
|
-
|
98
|
+
return false if empty?
|
99
|
+
children.any? { |c| c.activatable? }
|
100
|
+
end
|
101
|
+
|
102
|
+
def highlight_focus?
|
103
|
+
false
|
28
104
|
end
|
29
105
|
|
30
106
|
# Create new child window and add it to layout
|
31
107
|
def add_child(h={})
|
108
|
+
c = nil
|
109
|
+
|
110
|
+
if h.key?(:component)
|
111
|
+
c = h[:component]
|
112
|
+
|
113
|
+
h = {:rows => c.requested_rows + c.extra_padding,
|
114
|
+
:cols => c.requested_cols + c.extra_padding}.merge(h)
|
115
|
+
end
|
116
|
+
|
117
|
+
raise ArgumentError, "must specify x/y" unless h.key?(:x) &&
|
118
|
+
h.key?(:y)
|
119
|
+
|
120
|
+
raise ArgumentError, "must specify rows/cols" unless h.key?(:rows) &&
|
121
|
+
h.key?(:cols)
|
122
|
+
|
123
|
+
h[:rows], h[:cols] = *Window.adjust_proportional(window, h[:rows], h[:cols])
|
124
|
+
h[:x], h[:y] = *Window.align(window, h[:x], h[:y], h[:rows], h[:cols])
|
125
|
+
h[:rows], h[:cols] = *Window.fill_parent(parent? ? parent.window : Terminal,
|
126
|
+
h[:x], h[:y],
|
127
|
+
h[:rows], h[:cols]) if h[:fill]
|
128
|
+
|
129
|
+
if exceeds_bounds_with?(h)
|
130
|
+
if expandable? # ... && can_expand_to?(h)
|
131
|
+
expand(h)
|
132
|
+
|
133
|
+
else
|
134
|
+
raise ArgumentError, "child exceeds bounds"
|
135
|
+
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
32
139
|
child = window.create_child(h)
|
33
140
|
|
141
|
+
# TODO need to reverse expansion if operation fails at any
|
142
|
+
# point on, or verify expandable before create_child but
|
143
|
+
# do not expand until after
|
144
|
+
|
34
145
|
if child.win.nil?
|
35
146
|
raise ArgumentError, "could not create child window"
|
36
147
|
end
|
37
148
|
|
38
|
-
if exceeds_bounds?
|
149
|
+
if exceeds_bounds?
|
39
150
|
window.del_child(child) unless child.win.nil?
|
40
151
|
raise ArgumentError, "child exceeds bounds"
|
41
152
|
end
|
42
153
|
|
43
|
-
|
154
|
+
child.component = c unless c.nil?
|
155
|
+
|
44
156
|
update_reterm
|
45
157
|
child
|
46
158
|
end
|
47
159
|
|
160
|
+
def expand(h)
|
161
|
+
nr = [current_rows, h[:y] + h[:rows]].max
|
162
|
+
nc = [current_cols, h[:x] + h[:cols]].max
|
163
|
+
|
164
|
+
# verify parent can contain expanded area and is expandable
|
165
|
+
if parent?
|
166
|
+
if parent.exceeds_bounds_with?(h)
|
167
|
+
raise ArgumentError, "cannot expand parent" unless parent.expandable?
|
168
|
+
|
169
|
+
# expand parent
|
170
|
+
parent.expand(h)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# perform actual window expansion
|
175
|
+
window.resize(nr+1, nc+1)
|
176
|
+
|
177
|
+
# FIXME need to expand children who are marked as 'fill'
|
178
|
+
end
|
179
|
+
|
48
180
|
# Draw all layout children
|
49
181
|
def draw!
|
50
182
|
children.each { |c| c.draw! }
|
183
|
+
draw_focus!
|
51
184
|
end
|
52
185
|
|
53
186
|
# Activates layout by dispatching to navigation
|
54
187
|
# input system.
|
55
188
|
#
|
56
189
|
# @see NavInput
|
57
|
-
def activate!
|
190
|
+
def activate!(*input)
|
191
|
+
reactivate!
|
192
|
+
|
58
193
|
draw!
|
59
194
|
update_reterm
|
60
|
-
handle_input
|
195
|
+
handle_input(*input)
|
196
|
+
deactivate!
|
61
197
|
end
|
62
198
|
end # class Layout
|
63
199
|
end # module RETerm
|
data/lib/reterm/layouts.rb
CHANGED
@@ -0,0 +1,69 @@
|
|
1
|
+
module RETerm
|
2
|
+
module Layouts
|
3
|
+
# Layout which permits the user to arrainge items anywhere on screen
|
4
|
+
class Grid < Layout
|
5
|
+
def current_rows
|
6
|
+
return 1 if empty?
|
7
|
+
child_windows.max { |c1, c2| c1.rows <=> c2.rows }.rows + 1
|
8
|
+
end
|
9
|
+
|
10
|
+
def current_cols
|
11
|
+
return 1 if empty?
|
12
|
+
child_windows.max { |c1, c2| c1.cols <=> c2.cols }.cols + 1
|
13
|
+
end
|
14
|
+
|
15
|
+
def exceeds_bounds_with?(child)
|
16
|
+
x1 = child.is_a?(Hash) ? child[:x] : child.x
|
17
|
+
y1 = child.is_a?(Hash) ? child[:y] : child.y
|
18
|
+
|
19
|
+
x2 = child.is_a?(Hash) ?
|
20
|
+
[current_cols, x1 + child[:cols]].compact.max :
|
21
|
+
[current_cols, x1 + child.cols].max
|
22
|
+
|
23
|
+
y2 = child.is_a?(Hash) ?
|
24
|
+
[current_rows, y1 + child[:rows]].compact.max :
|
25
|
+
[current_rows, y1 + child.rows].max
|
26
|
+
|
27
|
+
x1 < window.x || y1 < window.y ||
|
28
|
+
x2 > window.cols || y2 > window.rows
|
29
|
+
end
|
30
|
+
|
31
|
+
def valid_input?(ch, from_parent)
|
32
|
+
if UP_CONTROLS.include?(ch)
|
33
|
+
return focusable.any? { |f| f.window.y < focused.window.y }
|
34
|
+
|
35
|
+
elsif DOWN_CONTROLS.include?(ch)
|
36
|
+
return focusable.any? { |f| f.window.y > focused.window.y }
|
37
|
+
|
38
|
+
elsif LEFT_CONTROLS.include?(ch)
|
39
|
+
focusable.any? { |f| f.window.x < focused.window.x }
|
40
|
+
|
41
|
+
elsif RIGHT_CONTROLS.include?(ch)
|
42
|
+
focusable.any? { |f| f.window.x > focused.window.x }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Cycle through components child position on grid
|
47
|
+
def next_focus(ch)
|
48
|
+
f = nil
|
49
|
+
if UP_CONTROLS.include?(ch)
|
50
|
+
f = focusable.select { |f| f.window.y < focused.window.y }.first
|
51
|
+
|
52
|
+
elsif DOWN_CONTROLS.include?(ch)
|
53
|
+
f = focusable.select { |f| f.window.y > focused.window.y }.first
|
54
|
+
|
55
|
+
elsif LEFT_CONTROLS.include?(ch)
|
56
|
+
f = focusable.select { |f| f.window.x < focused.window.x }.first
|
57
|
+
|
58
|
+
elsif RIGHT_CONTROLS.include?(ch)
|
59
|
+
f = focusable.select { |f| f.window.x > focused.window.x }.first
|
60
|
+
|
61
|
+
else
|
62
|
+
return super
|
63
|
+
end
|
64
|
+
|
65
|
+
focusable.index(f)
|
66
|
+
end
|
67
|
+
end # class Grid
|
68
|
+
end # module Layouts
|
69
|
+
end # module RETerm
|