reterm 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +131 -0
- data/designer/main.rb +10 -0
- data/designer/src/ComponentParams.rb +120 -0
- data/designer/src/ComponentsList.rb +90 -0
- data/designer/src/CreatedList.rb +156 -0
- data/designer/src/Designer.rb +114 -0
- data/designer/src/ToggleArea.rb +67 -0
- data/designer/src/common.rb +1 -0
- data/designer/src/component_factory.rb +30 -0
- data/designer/src/component_map.rb +116 -0
- data/designer/src/glade/ComponentParams.glade +96 -0
- data/designer/src/glade/Designer.glade +184 -0
- data/designer/src/images/add.png +0 -0
- data/designer/src/images/asciitext.png +0 -0
- data/designer/src/images/blank.png +0 -0
- data/designer/src/images/blank_sm.png +0 -0
- data/designer/src/images/button.png +0 -0
- data/designer/src/images/dial.png +0 -0
- data/designer/src/images/entry.png +0 -0
- data/designer/src/images/hslider.png +0 -0
- data/designer/src/images/label.png +0 -0
- data/designer/src/images/matrix.png +0 -0
- 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/designer/src/images/radio.png +0 -0
- data/designer/src/images/remove.png +0 -0
- data/designer/src/images/rocker.png +0 -0
- data/designer/src/images/slist.png +0 -0
- data/designer/src/images/vslider.png +0 -0
- data/lib/reterm.rb +22 -0
- data/lib/reterm/color_pair.rb +66 -0
- data/lib/reterm/component.rb +40 -0
- data/lib/reterm/components.rb +32 -0
- data/lib/reterm/components/ascii_text.rb +46 -0
- data/lib/reterm/components/button.rb +31 -0
- data/lib/reterm/components/dial.rb +95 -0
- data/lib/reterm/components/dialog.rb +34 -0
- data/lib/reterm/components/entry.rb +38 -0
- data/lib/reterm/components/hslider.rb +32 -0
- data/lib/reterm/components/image.rb +55 -0
- data/lib/reterm/components/label.rb +20 -0
- data/lib/reterm/components/matrix.rb +43 -0
- data/lib/reterm/components/radio.rb +33 -0
- data/lib/reterm/components/rocker.rb +71 -0
- data/lib/reterm/components/slist.rb +32 -0
- data/lib/reterm/components/template.rb +23 -0
- data/lib/reterm/components/vslider.rb +61 -0
- data/lib/reterm/init.rb +95 -0
- data/lib/reterm/layout.rb +63 -0
- data/lib/reterm/layouts.rb +16 -0
- data/lib/reterm/layouts/horizontal.rb +19 -0
- data/lib/reterm/layouts/vertical.rb +19 -0
- data/lib/reterm/loader.rb +126 -0
- data/lib/reterm/menu.rb +81 -0
- data/lib/reterm/mixins/cdk_component.rb +45 -0
- data/lib/reterm/mixins/component_input.rb +37 -0
- data/lib/reterm/mixins/event_dispatcher.rb +20 -0
- data/lib/reterm/mixins/nav_input.rb +126 -0
- data/lib/reterm/panel.rb +37 -0
- data/lib/reterm/resize.rb +27 -0
- data/lib/reterm/terminal.rb +33 -0
- data/lib/reterm/version.rb +3 -0
- data/lib/reterm/window.rb +260 -0
- metadata +155 -0
checksums.yaml
ADDED
@@ -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
|
data/MIT-LICENSE
ADDED
@@ -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.
|
data/README.md
ADDED
@@ -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
|
data/designer/main.rb
ADDED
@@ -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
|