sirens 0.0.1
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 +7 -0
- data/lib/components/component.rb +46 -0
- data/lib/components/component_behaviour.rb +160 -0
- data/lib/components/containers/splitter.rb +98 -0
- data/lib/components/containers/stack.rb +42 -0
- data/lib/components/containers/tabs.rb +24 -0
- data/lib/components/containers/window.rb +20 -0
- data/lib/components/primitive_component.rb +155 -0
- data/lib/components/widgets/button.rb +16 -0
- data/lib/components/widgets/checkbox.rb +39 -0
- data/lib/components/widgets/column_props.rb +23 -0
- data/lib/components/widgets/input_text.rb +27 -0
- data/lib/components/widgets/list.rb +68 -0
- data/lib/components/widgets/list_choice.rb +89 -0
- data/lib/components/widgets/radio_button.rb +33 -0
- data/lib/components/widgets/text.rb +31 -0
- data/lib/components/widgets/tree_choice.rb +82 -0
- data/lib/layouts/columns_builder.rb +63 -0
- data/lib/layouts/layout_builder.rb +203 -0
- data/lib/layouts/radio_button_group_builder.rb +28 -0
- data/lib/models/choice_model.rb +41 -0
- data/lib/models/list_model.rb +173 -0
- data/lib/models/tree_choice_model.rb +102 -0
- data/lib/models/value_model.rb +56 -0
- data/lib/models/virtual_tree_model.rb +150 -0
- data/lib/sirens/browsers/module_browser.rb +67 -0
- data/lib/sirens/browsers/object_browser.rb +103 -0
- data/lib/sirens/components/ancestors_list.rb +22 -0
- data/lib/sirens/components/class_browser.rb +38 -0
- data/lib/sirens/components/constants_list.rb +22 -0
- data/lib/sirens/components/method_source_code.rb +22 -0
- data/lib/sirens/components/methods_list.rb +63 -0
- data/lib/sirens/components/modules_list.rb +23 -0
- data/lib/sirens/components/namespaces_list.rb +21 -0
- data/lib/sirens/models/constant_model.rb +21 -0
- data/lib/sirens/models/method_model.rb +111 -0
- data/lib/sirens/models/module_browser_model.rb +225 -0
- data/lib/sirens/models/object_browser_model.rb +125 -0
- data/lib/sirens.rb +90 -0
- data/lib/views/button_view.rb +56 -0
- data/lib/views/check_button_view.rb +65 -0
- data/lib/views/entry_view.rb +25 -0
- data/lib/views/list_view.rb +230 -0
- data/lib/views/menu_view.rb +46 -0
- data/lib/views/notebook_view.rb +27 -0
- data/lib/views/paned_view.rb +26 -0
- data/lib/views/radio_button_view.rb +67 -0
- data/lib/views/stack_view.rb +26 -0
- data/lib/views/text_view.rb +112 -0
- data/lib/views/tree_view.rb +314 -0
- data/lib/views/view.rb +206 -0
- data/lib/views/window_view.rb +51 -0
- metadata +110 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
module Sirens
|
|
2
|
+
class TextView < View
|
|
3
|
+
# Class methods
|
|
4
|
+
|
|
5
|
+
class << self
|
|
6
|
+
##
|
|
7
|
+
# Answer the styles accepted by this view.
|
|
8
|
+
#
|
|
9
|
+
def view_accepted_styles()
|
|
10
|
+
super() + [:wrap_mode].freeze
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Initializing
|
|
15
|
+
|
|
16
|
+
def initialize_handles()
|
|
17
|
+
@text_view = Gtk::TextView.new
|
|
18
|
+
|
|
19
|
+
@main_handle = Gtk::ScrolledWindow.new
|
|
20
|
+
@main_handle.set_policy(:automatic, :always)
|
|
21
|
+
|
|
22
|
+
@main_handle.add(@text_view)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Hooking GUI signals
|
|
26
|
+
|
|
27
|
+
def subscribe_to_ui_events()
|
|
28
|
+
text_view.signal_connect('populate-popup') do |text_view, menu|
|
|
29
|
+
menu_view = MenuView.new(menu_handle: menu)
|
|
30
|
+
|
|
31
|
+
@populate_popup_menu_block.call(menu: menu_view)
|
|
32
|
+
|
|
33
|
+
menu.show_all
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Styles
|
|
38
|
+
|
|
39
|
+
def wrap_mode=(value)
|
|
40
|
+
text_view.wrap_mode = value
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def wrap_mode()
|
|
44
|
+
text_view.wrap_mode
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def background_color=(value)
|
|
48
|
+
state_colors_from(value).each_pair do |state, value|
|
|
49
|
+
next if value.nil?
|
|
50
|
+
|
|
51
|
+
text_view.override_background_color( state, Gdk::RGBA.parse(value) )
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def foreground_color=(value, state: :normal)
|
|
56
|
+
state_colors_from(value).each_pair do |state, value|
|
|
57
|
+
next if value.nil?
|
|
58
|
+
|
|
59
|
+
text_view.override_color( state, Gdk::RGBA.parse(value) )
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def state_colors_from(value)
|
|
64
|
+
colors = Hash[
|
|
65
|
+
normal: nil,
|
|
66
|
+
active: nil,
|
|
67
|
+
prelight: nil,
|
|
68
|
+
selected: nil,
|
|
69
|
+
insensitive: nil,
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
if value.kind_of?(Hash)
|
|
73
|
+
value.each_pair do |state, value|
|
|
74
|
+
colors[state] = value
|
|
75
|
+
end
|
|
76
|
+
else
|
|
77
|
+
colors[:normal] = value
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
colors
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Accessing
|
|
84
|
+
|
|
85
|
+
def text_view()
|
|
86
|
+
@text_view
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def text()
|
|
90
|
+
text_view.buffer.text
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def set_text(text)
|
|
94
|
+
text = '' if text.nil?
|
|
95
|
+
|
|
96
|
+
text_view.buffer.text = text
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Querying
|
|
100
|
+
|
|
101
|
+
##
|
|
102
|
+
# Returns the selected text of nil if no text is selected.
|
|
103
|
+
#
|
|
104
|
+
def selected_text()
|
|
105
|
+
selection_bounds = text_view.buffer.selection_bounds
|
|
106
|
+
|
|
107
|
+
return if selection_bounds.nil?
|
|
108
|
+
|
|
109
|
+
text_view.buffer.get_text(selection_bounds[0], selection_bounds[1], false)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
module Sirens
|
|
2
|
+
class TreeView < View
|
|
3
|
+
|
|
4
|
+
# Class methods
|
|
5
|
+
|
|
6
|
+
class << self
|
|
7
|
+
##
|
|
8
|
+
# Answer the laze children placeholder.
|
|
9
|
+
#
|
|
10
|
+
def placeholder()
|
|
11
|
+
@placeholder ||= '__placeholder__'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
##
|
|
15
|
+
# Answer the styles accepted by this view.
|
|
16
|
+
#
|
|
17
|
+
def view_accepted_styles()
|
|
18
|
+
super() + [:show_headers, :clickable_headers, :on_selection_action ].freeze
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Initializing
|
|
23
|
+
|
|
24
|
+
def initialize_handles()
|
|
25
|
+
@tree_view = Gtk::TreeView.new
|
|
26
|
+
@tree_view.set_model(Gtk::TreeStore.new(String))
|
|
27
|
+
|
|
28
|
+
@main_handle = Gtk::ScrolledWindow.new
|
|
29
|
+
@main_handle.add(tree_view)
|
|
30
|
+
@main_handle.set_policy(:automatic, :automatic)
|
|
31
|
+
|
|
32
|
+
@current_selection_indices = []
|
|
33
|
+
|
|
34
|
+
@columns_props = []
|
|
35
|
+
|
|
36
|
+
@on_selection_changed_block = nil
|
|
37
|
+
@get_item_block = nil
|
|
38
|
+
@get_children_block = nil
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Configuring callbacks
|
|
42
|
+
|
|
43
|
+
def on_selection_changed_block(&block)
|
|
44
|
+
@on_selection_changed_block = block
|
|
45
|
+
|
|
46
|
+
self
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def get_item_block(&block)
|
|
50
|
+
@get_item_block = block
|
|
51
|
+
|
|
52
|
+
self
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def get_children_block(&block)
|
|
56
|
+
@get_children_block = block
|
|
57
|
+
|
|
58
|
+
self
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Building columns
|
|
62
|
+
|
|
63
|
+
def define_columns(columns_props_array)
|
|
64
|
+
@columns_props = columns_props_array
|
|
65
|
+
|
|
66
|
+
tree_store_types = @columns_props
|
|
67
|
+
.collect { |each_column_props| each_column_props.fetch(:type, :text) }
|
|
68
|
+
.collect { |type| String }
|
|
69
|
+
|
|
70
|
+
tree_view.set_model(Gtk::TreeStore.new(*tree_store_types))
|
|
71
|
+
|
|
72
|
+
@columns_props.each do |each_column_props|
|
|
73
|
+
add_column_with_props(each_column_props)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def add_column_with_props(props)
|
|
78
|
+
column_type = props.fetch(:type, :text)
|
|
79
|
+
column_index = tree_view.columns.size
|
|
80
|
+
|
|
81
|
+
renderer = Gtk::CellRendererText.new
|
|
82
|
+
|
|
83
|
+
col = Gtk::TreeViewColumn.new(props[:label], renderer, column_type => column_index)
|
|
84
|
+
|
|
85
|
+
tree_view.append_column(col)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Hooking GUI signals
|
|
89
|
+
|
|
90
|
+
def subscribe_to_ui_events()
|
|
91
|
+
tree_view.selection.signal_connect('changed') { |tree_selection|
|
|
92
|
+
on_selection_changed(tree_selection)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
tree_view.signal_connect('row-activated') { |tree_view, tree_path, tree_column|
|
|
96
|
+
on_selection_action(tree_path: tree_path, tree_column: tree_column)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
tree_view.signal_connect('row-expanded') { |tree_view, iter, tree_path|
|
|
100
|
+
on_row_expanded(iter: iter, tree_path: tree_path)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
tree_view.signal_connect('button_press_event') do |tree_view, event|
|
|
104
|
+
show_popup_menu(button: event.button, time: event.time) if (event.button == 3)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Accessing
|
|
109
|
+
|
|
110
|
+
def tree_store()
|
|
111
|
+
tree_view.model
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def tree_view()
|
|
115
|
+
@tree_view
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
##
|
|
119
|
+
# Returns the rows contents of the tree.
|
|
120
|
+
# For testing and debugging only.
|
|
121
|
+
#
|
|
122
|
+
def rows()
|
|
123
|
+
tree_store.collect { |store, path, iter|
|
|
124
|
+
iter[0]
|
|
125
|
+
}
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def get_item_at(path:)
|
|
129
|
+
@get_item_block.call(path: path)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def get_children_at(path:)
|
|
133
|
+
@get_children_block.call(path: path)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Styles
|
|
137
|
+
|
|
138
|
+
def show_headers=(boolean)
|
|
139
|
+
tree_view.headers_visible = boolean
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def show_headers?()
|
|
143
|
+
tree_view.headers_visible?
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def clickable_headers=(boolean)
|
|
147
|
+
tree_view.headers_clickable = boolean
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def clickable_headers?()
|
|
151
|
+
tree_view.headers_clickable?
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def on_selection_action=(block)
|
|
155
|
+
@on_selection_action_block = block
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Handlers
|
|
159
|
+
|
|
160
|
+
def on_selection_changed(tree_selection)
|
|
161
|
+
indices = []
|
|
162
|
+
items = []
|
|
163
|
+
|
|
164
|
+
tree_selection.each do |tree_store, tree_path, iter|
|
|
165
|
+
indices_path = tree_path.indices
|
|
166
|
+
|
|
167
|
+
indices << indices_path
|
|
168
|
+
items << get_item_at(path: indices_path)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
@on_selection_changed_block.call(
|
|
172
|
+
selection_items: items,
|
|
173
|
+
selection_paths: indices
|
|
174
|
+
)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def on_selection_action(tree_path:, tree_column:)
|
|
178
|
+
return if @on_selection_action_block.nil?
|
|
179
|
+
|
|
180
|
+
index_path = tree_path.indices
|
|
181
|
+
|
|
182
|
+
item = get_item_at(path: index_path)
|
|
183
|
+
|
|
184
|
+
@on_selection_action_block.call(index_path: index_path, item: item)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
##
|
|
188
|
+
# To emulate a virtual tree elements are added with a child placeholder, the class placeholder constant.
|
|
189
|
+
# The first time a node is expanded if it has the child placeholder it is replaced by the actual node
|
|
190
|
+
# children.
|
|
191
|
+
# The actual children are get using the get_children_block.
|
|
192
|
+
#
|
|
193
|
+
def on_row_expanded(iter:, tree_path:)
|
|
194
|
+
child_iter = iter.first_child
|
|
195
|
+
|
|
196
|
+
return if tree_store.get_value(child_iter, 0) != self.class.placeholder
|
|
197
|
+
|
|
198
|
+
indices_path = tree_path.indices
|
|
199
|
+
|
|
200
|
+
children = get_children_at(path: indices_path)
|
|
201
|
+
|
|
202
|
+
tree_store.remove(child_iter)
|
|
203
|
+
|
|
204
|
+
add_items(items: children, parent_iter: iter, index: 0)
|
|
205
|
+
|
|
206
|
+
tree_view.expand_row(tree_path, false)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Actions
|
|
210
|
+
|
|
211
|
+
def clear_items()
|
|
212
|
+
tree_store.clear
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# Adding
|
|
216
|
+
|
|
217
|
+
def add_items(items:, parent_iter:, index:)
|
|
218
|
+
items.each_with_index do |each_item, i|
|
|
219
|
+
add_item(item: each_item, parent_iter: parent_iter, index: index + i)
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def add_item(item:, parent_iter:, index:)
|
|
224
|
+
iter = tree_store.insert(parent_iter, index)
|
|
225
|
+
|
|
226
|
+
set_item_column_values(item: item, iter: iter)
|
|
227
|
+
|
|
228
|
+
if parent_iter.nil?
|
|
229
|
+
indices_path = [index]
|
|
230
|
+
else
|
|
231
|
+
indices_path = parent_iter.path.indices + [index]
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
children_count = get_children_at(path: indices_path).size
|
|
235
|
+
|
|
236
|
+
if children_count > 0
|
|
237
|
+
placeholder = tree_store.insert(iter, 0)
|
|
238
|
+
|
|
239
|
+
placeholder[0] = self.class.placeholder
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# Updating
|
|
244
|
+
|
|
245
|
+
def update_items(items:, indices:)
|
|
246
|
+
items.each_with_index do |each_item, i|
|
|
247
|
+
update_item(item: each_item, index: indices[i])
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def update_item(item:, index:)
|
|
252
|
+
iter = tree_store.get_iter(index.to_s)
|
|
253
|
+
|
|
254
|
+
set_item_column_values(item: item, iter: iter)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# Removing
|
|
258
|
+
|
|
259
|
+
def remove_items(items:, indices:)
|
|
260
|
+
items.each_with_index do |each_item, i|
|
|
261
|
+
remove_item(item: each_item, index: indices[i])
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def remove_item(item:, index:)
|
|
266
|
+
iter = tree_store.get_iter(index.to_s)
|
|
267
|
+
|
|
268
|
+
tree_store.remove(iter)
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def set_item_column_values(item:, iter:)
|
|
272
|
+
@columns_props.each_with_index { |column, column_index|
|
|
273
|
+
iter[column_index] = column.display_text_of(item)
|
|
274
|
+
}
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# Querying
|
|
278
|
+
|
|
279
|
+
def selection_indices()
|
|
280
|
+
indices = []
|
|
281
|
+
|
|
282
|
+
tree_view.selection.each { |tree, path, iter|
|
|
283
|
+
indices << path.to_s.to_i
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
indices
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def set_selection_indices(indices)
|
|
290
|
+
if indices.empty?
|
|
291
|
+
tree_view.unselect_all
|
|
292
|
+
return
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
tree_path = Gtk::TreePath.new(
|
|
296
|
+
indices.join(':')
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
expand(path: indices[0..-2]) if indices.size > 1
|
|
300
|
+
|
|
301
|
+
tree_view.selection.select_path(tree_path)
|
|
302
|
+
|
|
303
|
+
tree_view.scroll_to_cell(tree_path, nil, false, 0.0, 0.0)
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def expand(path:)
|
|
307
|
+
tree_path = Gtk::TreePath.new(
|
|
308
|
+
path.join(':')
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
tree_view.expand_row(tree_path, false)
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
end
|
data/lib/views/view.rb
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
require 'set'
|
|
2
|
+
|
|
3
|
+
module Sirens
|
|
4
|
+
##
|
|
5
|
+
# A View is the library binding to a GUI interface handle.
|
|
6
|
+
# It is not a Component but is wrapped by a PrimitiveComponent.
|
|
7
|
+
# a View takes care of handling the internals of the GUI objects such as handles, events, default initialization,
|
|
8
|
+
# etc.
|
|
9
|
+
#
|
|
10
|
+
# By separating the View from the PrimitiveComponent that wraps it makes the PrimitiveComponents responsibilities
|
|
11
|
+
# more consistent with regular Components and it makes it easier to switch between GUI libraries
|
|
12
|
+
# (say, from Gtk to Qt).
|
|
13
|
+
#
|
|
14
|
+
class View
|
|
15
|
+
|
|
16
|
+
# Class methods
|
|
17
|
+
|
|
18
|
+
class << self
|
|
19
|
+
##
|
|
20
|
+
# Answer the styles accepted by this view.
|
|
21
|
+
#
|
|
22
|
+
def accepted_styles()
|
|
23
|
+
@accepted_styles ||= Set.new(view_accepted_styles)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
##
|
|
27
|
+
# Answer the styles accepted by this view.
|
|
28
|
+
#
|
|
29
|
+
def view_accepted_styles()
|
|
30
|
+
[ :width, :height, :background_color, :foreground_color ].freeze
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Initializing
|
|
35
|
+
|
|
36
|
+
##
|
|
37
|
+
# Initializes this View handles
|
|
38
|
+
#
|
|
39
|
+
def initialize()
|
|
40
|
+
@event_handles = Hash[]
|
|
41
|
+
|
|
42
|
+
@main_handle = nil
|
|
43
|
+
|
|
44
|
+
initialize_handles
|
|
45
|
+
|
|
46
|
+
subscribe_to_ui_events
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
## Instantiates this view handles.
|
|
50
|
+
# A View usually has a single handle to the GUI library, but in same cases it may have more than one.
|
|
51
|
+
# For instance when adding a Scroll decorator to the actual widget.
|
|
52
|
+
#
|
|
53
|
+
def initialize_handles()
|
|
54
|
+
raise RuntimeError.new("Subclass #{self.class.name} must implement the method ::initialize_handles().")
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
##
|
|
58
|
+
# Subscribes this View to the events/signals emitted by the GUI handle(s) of interest.
|
|
59
|
+
# When an event/signal is received calls the proper event_handler provided by the PrimitiveComponent,
|
|
60
|
+
# if one was given.
|
|
61
|
+
#
|
|
62
|
+
# This mechanism of event handler callbacks is more simple and lightweight than making this View to announce
|
|
63
|
+
# events using the Observer pattern, and since there is only one PrimitiveComponent wrapping each View
|
|
64
|
+
# using the Observer pattern would be unnecessary complex.
|
|
65
|
+
#
|
|
66
|
+
def subscribe_to_ui_events()
|
|
67
|
+
raise RuntimeError.new("Subclass #{self.class.name} must implement the method ::subscribe_to_ui_events().")
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Accessing
|
|
71
|
+
|
|
72
|
+
##
|
|
73
|
+
# Returns the main handle of this View.
|
|
74
|
+
# The main handle is the one that this View parent add as its child.
|
|
75
|
+
# Also, it is the handle that receives the style props and events by default.
|
|
76
|
+
#
|
|
77
|
+
def main_handle()
|
|
78
|
+
@main_handle
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Styling
|
|
82
|
+
|
|
83
|
+
##
|
|
84
|
+
# Answer the styles accepted by this view.
|
|
85
|
+
#
|
|
86
|
+
def accepted_styles()
|
|
87
|
+
self.class.accepted_styles
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
##
|
|
91
|
+
# Applies each prop in props to the actual GUI object.
|
|
92
|
+
#
|
|
93
|
+
def apply_props(props)
|
|
94
|
+
accepted_styles = self.accepted_styles
|
|
95
|
+
|
|
96
|
+
props.each_pair { |prop, value|
|
|
97
|
+
apply_prop(prop, value) if accepted_styles.include?(prop)
|
|
98
|
+
}
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
##
|
|
102
|
+
# Apply the prop to the actual GUI object.
|
|
103
|
+
#
|
|
104
|
+
def apply_prop(prop, value)
|
|
105
|
+
setter = prop.to_s + '='
|
|
106
|
+
|
|
107
|
+
send(setter, value)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Styles
|
|
111
|
+
|
|
112
|
+
def width=(value)
|
|
113
|
+
main_handle.width_request = value
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def width()
|
|
117
|
+
main_handle.width_request
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def height=(value)
|
|
121
|
+
main_handle.height_request = value
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def height()
|
|
125
|
+
main_handle.height_request
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def background_color=(value)
|
|
129
|
+
state_colors_from(value).each_pair do |state, value|
|
|
130
|
+
next if value.nil?
|
|
131
|
+
|
|
132
|
+
main_handle.override_background_color( state, Gdk::RGBA.parse(value) )
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def foreground_color=(value, state: :normal)
|
|
137
|
+
state_colors_from(value).each_pair do |state, value|
|
|
138
|
+
next if value.nil?
|
|
139
|
+
|
|
140
|
+
main_handle.override_color( state, Gdk::RGBA.parse(value) )
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def state_colors_from(value)
|
|
145
|
+
colors = Hash[
|
|
146
|
+
normal: nil,
|
|
147
|
+
active: nil,
|
|
148
|
+
prelight: nil,
|
|
149
|
+
selected: nil,
|
|
150
|
+
insensitive: nil,
|
|
151
|
+
]
|
|
152
|
+
|
|
153
|
+
if value.kind_of?(Hash)
|
|
154
|
+
value.each_pair do |state, value|
|
|
155
|
+
colors[state] = value
|
|
156
|
+
end
|
|
157
|
+
else
|
|
158
|
+
colors[:normal] = value
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
colors
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Showing/hiding
|
|
165
|
+
|
|
166
|
+
##
|
|
167
|
+
# Makes this component visible.
|
|
168
|
+
#
|
|
169
|
+
def show()
|
|
170
|
+
main_handle.show_all
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Child views
|
|
174
|
+
|
|
175
|
+
##
|
|
176
|
+
# Adds a child_view.
|
|
177
|
+
#
|
|
178
|
+
def add_view(child_view)
|
|
179
|
+
main_handle.add(child_view.main_handle)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
##
|
|
183
|
+
# Removes a child view.
|
|
184
|
+
#
|
|
185
|
+
def remove_view(child_view)
|
|
186
|
+
main_handle.remove(child_view.main_handle)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Popup menu
|
|
190
|
+
|
|
191
|
+
def populate_popup_menu_block=(populate_popup_menu_block)
|
|
192
|
+
@populate_popup_menu_block = populate_popup_menu_block
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
##
|
|
196
|
+
# Create and show a popup menu
|
|
197
|
+
#
|
|
198
|
+
def show_popup_menu(props)
|
|
199
|
+
menu = MenuView.new
|
|
200
|
+
|
|
201
|
+
@populate_popup_menu_block.call(menu: menu)
|
|
202
|
+
|
|
203
|
+
menu.open(props) unless menu.empty?
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module Sirens
|
|
2
|
+
class WindowView < View
|
|
3
|
+
|
|
4
|
+
# Class methods
|
|
5
|
+
|
|
6
|
+
class << self
|
|
7
|
+
##
|
|
8
|
+
# Answer the styles accepted by this view.
|
|
9
|
+
#
|
|
10
|
+
def view_accepted_styles()
|
|
11
|
+
super() + [:title].freeze
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Initializing
|
|
16
|
+
|
|
17
|
+
def initialize_handles()
|
|
18
|
+
@main_handle = Gtk::Window.new()
|
|
19
|
+
|
|
20
|
+
Sirens.register_window
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def subscribe_to_ui_events()
|
|
24
|
+
main_handle.signal_connect("delete_event") {
|
|
25
|
+
false
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
main_handle.signal_connect("destroy") {
|
|
29
|
+
Sirens.unregister_window
|
|
30
|
+
}
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Styles
|
|
34
|
+
|
|
35
|
+
def width=(value)
|
|
36
|
+
main_handle.default_width = value
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def height=(value)
|
|
40
|
+
main_handle.default_height = value
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def title=(value)
|
|
44
|
+
main_handle.title = value
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def title()
|
|
48
|
+
main_handle.title
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|