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,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
- Ncurses::curs_set(0)
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
- #ensure
54
+ rescue => e
55
+ err = e
56
+
57
+ ensure
40
58
  stop_track_resize
41
59
  Ncurses.curs_set(1)
42
- Window.all.each { |w| w.finalize! }
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
- Window.top.each { |w| w.refresh }
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
- # TODO padding / margin support
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
- attr_accessor :children
42
+ # TODO padding / margin support
13
43
 
14
- def initialize(args={})
15
- @children ||= []
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
- # boolean if child window exceeds bounds of layout
20
- def exceeds_bounds?(child)
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
- children.any? { |c| c.component.activatable? }
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?(child)
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
- children << child
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
@@ -14,3 +14,4 @@ end
14
14
  require 'reterm/layout'
15
15
  require 'reterm/layouts/horizontal'
16
16
  require 'reterm/layouts/vertical'
17
+ require 'reterm/layouts/grid'
@@ -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