reterm 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +131 -0
  4. data/designer/main.rb +10 -0
  5. data/designer/src/ComponentParams.rb +120 -0
  6. data/designer/src/ComponentsList.rb +90 -0
  7. data/designer/src/CreatedList.rb +156 -0
  8. data/designer/src/Designer.rb +114 -0
  9. data/designer/src/ToggleArea.rb +67 -0
  10. data/designer/src/common.rb +1 -0
  11. data/designer/src/component_factory.rb +30 -0
  12. data/designer/src/component_map.rb +116 -0
  13. data/designer/src/glade/ComponentParams.glade +96 -0
  14. data/designer/src/glade/Designer.glade +184 -0
  15. data/designer/src/images/add.png +0 -0
  16. data/designer/src/images/asciitext.png +0 -0
  17. data/designer/src/images/blank.png +0 -0
  18. data/designer/src/images/blank_sm.png +0 -0
  19. data/designer/src/images/button.png +0 -0
  20. data/designer/src/images/dial.png +0 -0
  21. data/designer/src/images/entry.png +0 -0
  22. data/designer/src/images/hslider.png +0 -0
  23. data/designer/src/images/label.png +0 -0
  24. data/designer/src/images/matrix.png +0 -0
  25. data/designer/src/images/orig/Check.png +0 -0
  26. data/designer/src/images/orig/ascii_text.png +0 -0
  27. data/designer/src/images/orig/button.png +0 -0
  28. data/designer/src/images/orig/dial.png +0 -0
  29. data/designer/src/images/orig/entry.png +0 -0
  30. data/designer/src/images/orig/hslider.png +0 -0
  31. data/designer/src/images/orig/label.png +0 -0
  32. data/designer/src/images/orig/matrix.png +0 -0
  33. data/designer/src/images/orig/radio.png +0 -0
  34. data/designer/src/images/orig/rocker.png +0 -0
  35. data/designer/src/images/orig/slist.png +0 -0
  36. data/designer/src/images/orig/vslider.png +0 -0
  37. data/designer/src/images/radio.png +0 -0
  38. data/designer/src/images/remove.png +0 -0
  39. data/designer/src/images/rocker.png +0 -0
  40. data/designer/src/images/slist.png +0 -0
  41. data/designer/src/images/vslider.png +0 -0
  42. data/lib/reterm.rb +22 -0
  43. data/lib/reterm/color_pair.rb +66 -0
  44. data/lib/reterm/component.rb +40 -0
  45. data/lib/reterm/components.rb +32 -0
  46. data/lib/reterm/components/ascii_text.rb +46 -0
  47. data/lib/reterm/components/button.rb +31 -0
  48. data/lib/reterm/components/dial.rb +95 -0
  49. data/lib/reterm/components/dialog.rb +34 -0
  50. data/lib/reterm/components/entry.rb +38 -0
  51. data/lib/reterm/components/hslider.rb +32 -0
  52. data/lib/reterm/components/image.rb +55 -0
  53. data/lib/reterm/components/label.rb +20 -0
  54. data/lib/reterm/components/matrix.rb +43 -0
  55. data/lib/reterm/components/radio.rb +33 -0
  56. data/lib/reterm/components/rocker.rb +71 -0
  57. data/lib/reterm/components/slist.rb +32 -0
  58. data/lib/reterm/components/template.rb +23 -0
  59. data/lib/reterm/components/vslider.rb +61 -0
  60. data/lib/reterm/init.rb +95 -0
  61. data/lib/reterm/layout.rb +63 -0
  62. data/lib/reterm/layouts.rb +16 -0
  63. data/lib/reterm/layouts/horizontal.rb +19 -0
  64. data/lib/reterm/layouts/vertical.rb +19 -0
  65. data/lib/reterm/loader.rb +126 -0
  66. data/lib/reterm/menu.rb +81 -0
  67. data/lib/reterm/mixins/cdk_component.rb +45 -0
  68. data/lib/reterm/mixins/component_input.rb +37 -0
  69. data/lib/reterm/mixins/event_dispatcher.rb +20 -0
  70. data/lib/reterm/mixins/nav_input.rb +126 -0
  71. data/lib/reterm/panel.rb +37 -0
  72. data/lib/reterm/resize.rb +27 -0
  73. data/lib/reterm/terminal.rb +33 -0
  74. data/lib/reterm/version.rb +3 -0
  75. data/lib/reterm/window.rb +260 -0
  76. metadata +155 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b8eccbffc9e67c0b58c4ecce6689ed983192fdfc
4
+ data.tar.gz: fa932fa439231e95d1bb945c33a3e760da491f4a
5
+ SHA512:
6
+ metadata.gz: 45677b6521f05935d939bac79b2ab237508ce109cf177868a69136f89cfb0993fa3404202b87d0c90cc2734439250db8a00401c4509b5eb7ddabcb0128ddc282
7
+ data.tar.gz: 5800ed5af8c81a8909a3ce6f50a4ee7fba3db37bfd932a14d927fba956accb60d1b603cf196a2e5e9105ab748072f1d5d53b460a707f1e9c914ecccf08376c1f
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2017 Mo Morsi (mo@morsi.org)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,131 @@
1
+ # RETerm - Ruby Enhanced Terminal
2
+
3
+ RETerm is a Text Based User Interface Framework built ontop of [ncurses](https://en.wikipedia.org/wiki/Ncurses)
4
+
5
+ ## Worth a thousand words...
6
+
7
+ ![Screenshot1](examples/sc1.gif)
8
+
9
+ ## Installation
10
+
11
+ RETerm depends on the **ncursesw** rubygem, which in return depends on the '[ncursesw](https://www.gnu.org/software/ncurses/)' and terminfo libraries. These must be available for use locally via on means or another. Most Operating Systems and distributions have a binary / bundled version available, read the ncurses documentation for more information. The following packages should suffice to install on Linux:
12
+
13
+ ```
14
+ $ dnf install ruby-devel ncurses-devel (Fedora/RedHat)
15
+
16
+ $ apt-get install ruby-dev libncursesw5-dev libncurses5-dev libtinfo-dev (Ubuntu)
17
+ ```
18
+
19
+ To install the core RETerm framework and components, simply run:
20
+
21
+ ```
22
+ $ gem install reterm
23
+ ```
24
+
25
+ ## Optional Components
26
+
27
+ RETerm ships with components that depend on additional gem dependencies. These features are completely optional and will not be required unless the corresponding components are
28
+ instantiated. To install all optional RETerm dependencies run:
29
+
30
+ ```
31
+ $ gem install artii drawille chunky_png tawny-cdk
32
+ ```
33
+
34
+ ## Usage
35
+
36
+ See the **examples/** directory for demos of many common components.
37
+
38
+ The following will instantiate a simple "Hello World" interface:
39
+
40
+ ```ruby
41
+ require 'reterm'
42
+ include RETerm
43
+
44
+ init_reterm {
45
+ win = Window.new :rows => 10,
46
+ :cols => 30
47
+ win.border!
48
+ update_reterm
49
+
50
+ label = Components::Label.new :text => "Hello world"
51
+ win.component = label
52
+ label.draw!
53
+
54
+ sleep(3)
55
+ }
56
+ ```
57
+
58
+ For something more interesting, create and activate layout and components. After
59
+ we are done, reading the user input is a cinch!
60
+
61
+ ```ruby
62
+ results = {}
63
+
64
+ init_reterm {
65
+ win = Window.new
66
+ win.colors = :main
67
+ win.border!
68
+
69
+ layout1 = Layouts::Horizontal.new
70
+ win.component = layout1
71
+
72
+ child1 = layout1.add_child :rows => 3,
73
+ :cols => 20
74
+
75
+ dial = Components::Dial.new
76
+ child1.component = dial
77
+
78
+ child2 = layout1.add_child :rows => 15,
79
+ :cols => 40
80
+
81
+ entry = Components::Entry.new :title => "Enter: ", :label => "Text:"
82
+ child2.component = entry
83
+
84
+ ###
85
+
86
+ win.activate!
87
+
88
+ results[:dial] = dial.value
89
+ results[:entry] = entry.value
90
+ }
91
+
92
+ puts "Input results: "
93
+ puts results
94
+ ```
95
+
96
+ How about a generic JSON schema representing a terminal interface?
97
+
98
+ ```json
99
+ {
100
+ "window" : {
101
+ "rows" : 10,
102
+ "cols" : 50,
103
+ "border" : true,
104
+ "component" : {
105
+ "type" : "Entry",
106
+ "init" : {
107
+ "title" : "<C>Demo",
108
+ "label" : "Enter Text: "
109
+ }
110
+ }
111
+ }
112
+ }
113
+ ```
114
+
115
+ And here's how to load it:
116
+
117
+ ```ruby
118
+ init_reterm {
119
+ load_reterm(File.read('path/to/schema.json')).activate!
120
+ }
121
+ ```
122
+
123
+ ## Documentation
124
+
125
+ Full reference documentation is available via [yard](http://www.rubydoc.info/gems/reterm)
126
+
127
+ ## Legaleese
128
+
129
+ Copyright (C) 2017 Mo Morsi <mo@morsi.org>
130
+
131
+ Distributed under the MIT licesnse
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require "vrlib"
4
+ require_rel 'src/'
5
+
6
+ designer = Designer.new
7
+ designer.show_glade()
8
+
9
+ require 'pp'
10
+ pp designer.created_list.schema
@@ -0,0 +1,120 @@
1
+ # Popup window allowing us to set component parameters
2
+ class ComponentParams
3
+ include GladeGUI
4
+
5
+ def initialize(component, window)
6
+ super()
7
+ @component = component
8
+ @window = window
9
+ end
10
+
11
+ def submit_button__clicked(*args)
12
+ @window.create_component(@component, component_params)
13
+ @builder["window1"].destroy
14
+ end
15
+
16
+ def cancel_button__clicked(*args)
17
+ @builder["window1"].destroy
18
+ end
19
+
20
+ def before_show
21
+ @builder[:component_edit_title].text = "Edit \"#{@component}\" params"
22
+
23
+ COMPONENTS[@component][:params].each { |label, type|
24
+ layout = Gtk::HBox.new
25
+
26
+ glabel = Gtk::Label.new label
27
+ glabel.margin = 5
28
+ layout.add glabel
29
+ glabel.show
30
+
31
+ if type.is_a?(Array)
32
+ placeholder = Gtk::Image.new(:file => "#{IMG_DIR}/blank_sm.png")
33
+ layout.pack_end placeholder
34
+ placeholder.margin = 14
35
+ placeholder.show
36
+
37
+ add_img = Gtk::Image.new(:file => "#{IMG_DIR}/add.png")
38
+ add_btn = Gtk::Button.new
39
+ add_btn.margin = 5
40
+ add_btn.add(add_img)
41
+
42
+ layout.pack_end add_btn
43
+ add_btn.signal_connect("clicked") {
44
+ add_row(label, type)
45
+ }
46
+
47
+ add_img.show
48
+ add_btn.show
49
+ end
50
+
51
+ gedit = Gtk::Entry.new
52
+ layout.pack_end gedit
53
+ gedit.show
54
+
55
+
56
+ @builder[:component_edit_layout].add layout
57
+ layout.show
58
+ }
59
+ end
60
+
61
+ private
62
+
63
+ def component_params
64
+ @builder[:component_edit_layout].children.collect { |c|
65
+ p = c.children.first # label
66
+ .text
67
+
68
+ t = COMPONENTS[@component][:params][p]
69
+ t = t.first if t.is_a?(Array)
70
+
71
+ txt = c.children[1].text
72
+ if t == :int
73
+ txt.to_i
74
+ else # if t == :string
75
+ txt
76
+ end
77
+ }
78
+ end
79
+
80
+ def add_row(label, type)
81
+ layout = Gtk::HBox.new
82
+ glabel = Gtk::Label.new
83
+ glabel.margin = 5
84
+ layout.add glabel
85
+ glabel.show
86
+
87
+ rm_img = Gtk::Image.new(:file => "#{IMG_DIR}/remove.png")
88
+ rm_btn = Gtk::Button.new
89
+ rm_btn.add(rm_img)
90
+ rm_btn.margin = 5
91
+ layout.pack_end rm_btn
92
+ rm_btn.signal_connect("clicked") {
93
+ rm_row(layout)
94
+ }
95
+ rm_img.show
96
+ rm_btn.show
97
+
98
+ add_img = Gtk::Image.new(:file => "#{IMG_DIR}/add.png")
99
+ add_btn = Gtk::Button.new
100
+ add_btn.margin = 5
101
+ add_btn.add(add_img)
102
+ layout.pack_end add_btn
103
+ add_btn.signal_connect("clicked") {
104
+ add_row(label, type)
105
+ }
106
+ add_img.show
107
+ add_btn.show
108
+
109
+ gedit = Gtk::Entry.new
110
+ layout.pack_end gedit
111
+ gedit.show
112
+
113
+ @builder[:component_edit_layout].add layout
114
+ layout.show
115
+ end
116
+
117
+ def rm_row(layout)
118
+ @builder[:component_edit_layout].remove layout
119
+ end
120
+ end
@@ -0,0 +1,90 @@
1
+ # List of supported components
2
+ class ComponentsList < VR::ListView
3
+ attr_accessor :designer
4
+
5
+ def initialize(designer)
6
+ @designer = designer
7
+
8
+ @cols = {}
9
+ @cols[:pix] = GdkPixbuf::Pixbuf
10
+ @cols[:title] = String
11
+ @cols[:desc] = String
12
+ super(@cols)
13
+
14
+ parse_signals # XXX needed for row activation callback
15
+
16
+ col_title(:pix => "",
17
+ :title => "Component",
18
+ :desc => "Description")
19
+
20
+ self.visible = true
21
+
22
+ first = true
23
+ selection.signal_connect("changed") {
24
+ unless selection.selected.nil?
25
+ changed unless first
26
+ selection.unselect_all
27
+ first = false
28
+ end
29
+ }
30
+ end
31
+
32
+ def add_component(c)
33
+ row = model.append
34
+ row[id(:pix)] = icon_for(c)
35
+ row[id(:title)] = c
36
+ row[id(:desc)] = desc_for(c)
37
+ end
38
+
39
+ def self__row_activated(*args)
40
+ changed
41
+ end
42
+
43
+ private
44
+
45
+ def changed
46
+ unless designer.has_toggled_area?
47
+ dialog = Gtk::MessageDialog.new(:parent => designer.window,
48
+ :flags => :destroy_with_parent,
49
+ :type => :info,
50
+ :buttons => :close,
51
+ :message => "Must select area to add component")
52
+
53
+ dialog.signal_connect("response") { |*args|
54
+ dialog.destroy
55
+ }
56
+ dialog.show
57
+ return
58
+ end
59
+
60
+
61
+ c = selection.selected[1]
62
+ e = COMPONENTS[c.intern]
63
+ if e.key?(:params)
64
+ w = ComponentParams.new(c.intern, designer)
65
+ w.show_glade(designer)
66
+ else
67
+ designer.create_component(c.intern, [])
68
+ end
69
+ end
70
+
71
+ DEFAULT_IMG = GdkPixbuf::Pixbuf.new(:file => "#{IMG_DIR}/blank.png")
72
+
73
+ def desc_for(c)
74
+ COMPONENTS[c][:desc]
75
+ end
76
+
77
+ def icon_for(c)
78
+ if File.exist?("#{IMG_DIR}/#{c.to_s.downcase}.png")
79
+ GdkPixbuf::Pixbuf.new(:file => "#{IMG_DIR}/#{c.to_s.downcase}.png")
80
+ else
81
+ DEFAULT_IMG
82
+ end
83
+ end
84
+
85
+ public
86
+
87
+ def image_for(c)
88
+ Gtk::Image.new(icon_for(c))
89
+ end
90
+ end
@@ -0,0 +1,156 @@
1
+ # List of components created
2
+ class CreatedList < VR::TreeView
3
+ attr_accessor :designer, :schema
4
+
5
+ attr_accessor :rows_by_parent,
6
+ :rows_by_widget,
7
+ :rows_by_component,
8
+ :params_by_widget
9
+
10
+ def initialize(designer)
11
+ @designer = designer
12
+
13
+ @rows_by_parent = {}
14
+ @rows_by_widget = {}
15
+ @params_by_widget = {}
16
+ @rows_by_component = {}
17
+
18
+ @cols = {}
19
+ @cols[:title] = String
20
+ @cols[:rm] = GdkPixbuf::Pixbuf
21
+ super(@cols)
22
+
23
+ col_title(:rm => "Remove",
24
+ :title => "Component")
25
+
26
+ self.activate_on_single_click = true
27
+
28
+ self.signal_connect("row_activated") { |*args|
29
+ if args[2] == columns[1]
30
+ path = args[1]
31
+ iter = model.get_iter(path)
32
+ unregister(iter)
33
+ end
34
+
35
+ selection.unselect_all
36
+ }
37
+ end
38
+
39
+ def register(component, params, widget, parent)
40
+ img = GdkPixbuf::Pixbuf.new(:file => "#{IMG_DIR}/remove.png")
41
+
42
+ row = model.append row_for(parent)
43
+ row[id(:title)] = component.to_s
44
+ row[id(:rm)] = img
45
+
46
+ rows_by_parent[parent] ||= []
47
+ rows_by_parent[parent] << row
48
+
49
+ rows_by_component[component] = row
50
+ rows_by_widget[widget] = row
51
+ params_by_widget[widget] = params
52
+
53
+ if visible?
54
+ designer.builder["created_window"].visible = true
55
+ show
56
+ end
57
+
58
+ @schema = _schema
59
+ end
60
+
61
+ def visible?
62
+ rows_by_parent.any? { |k,v| v.size > 0 }
63
+ end
64
+
65
+ def parent_for(row)
66
+ rows_by_parent.keys.find { |k|
67
+ rows_by_parent[k].include?(row)
68
+ }
69
+ end
70
+
71
+ def row_for(widget)
72
+ rows_by_widget[widget]
73
+ end
74
+
75
+ def widget_for(row)
76
+ rows_by_widget.keys.find { |k|
77
+ rows_by_widget[k] == row
78
+ }
79
+ end
80
+
81
+ def component_for(row)
82
+ rows_by_component.keys.find { |k|
83
+ rows_by_component[k] == row
84
+ }
85
+ end
86
+
87
+ def component_for_widget(widget)
88
+ component_for(row_for(widget))
89
+ end
90
+
91
+ def unregister(row)
92
+ 0.upto(row.n_children-1) { |child|
93
+ unregister(row.nth_child(child))
94
+ }
95
+
96
+ parent = parent_for(row)
97
+ widget = widget_for(row)
98
+
99
+ rows_by_parent[parent].delete(row)
100
+ rows_by_widget.delete(widget)
101
+ model.remove(row)
102
+
103
+ designer.remove_component(widget)
104
+
105
+ unless visible?
106
+ designer.builder["created_window"].visible = false
107
+ hide
108
+ end
109
+
110
+ @schema = _schema
111
+ end
112
+
113
+ def _schema
114
+ win = {'window' => {}}
115
+
116
+ top = rows_by_parent[nil].first # there should be only one element here
117
+ tc = component_for(top)
118
+ wg = widget_for(top)
119
+
120
+ if wg.kind_of?(Gtk::Box)
121
+ win['window']["layout"] = layout_schema(wg)
122
+ else
123
+ win['window']["component"] = {"type" => tc,
124
+ "init" => params_for(wg)}
125
+ end
126
+
127
+ win
128
+ end
129
+
130
+ private
131
+
132
+ def params_for(w)
133
+ keys = COMPONENTS[component_for_widget(w)][:init_keys]
134
+ return [] unless keys
135
+ vals = params_by_widget[w]
136
+ Hash[keys.zip(vals)]
137
+ end
138
+
139
+ def layout_schema(l)
140
+ h = l.orientation == Gtk::Orientation::HORIZONTAL
141
+ s = {"type" => (h ? "Horizontal" : "Vertical"),
142
+ "children" => []}
143
+
144
+ l.children.each { |c|
145
+ next unless component_for_widget(c)
146
+ s["children"] << (c.kind_of?(Gtk::Box) ?
147
+ layout_schema(c) :
148
+ {"component" => {
149
+ "type" => component_for(row_for(c)),
150
+ "init" => params_for(c)
151
+ }})
152
+ }
153
+
154
+ s
155
+ end
156
+ end