rugui 1.2.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.
- data/LICENSE +165 -0
- data/README +67 -0
- data/README.rdoc +67 -0
- data/Rakefile +56 -0
- data/VERSION.yml +4 -0
- data/bin/rugui +16 -0
- data/lib/rugui/base_controller.rb +194 -0
- data/lib/rugui/base_model.rb +22 -0
- data/lib/rugui/base_object.rb +73 -0
- data/lib/rugui/base_view.rb +302 -0
- data/lib/rugui/base_view_helper.rb +23 -0
- data/lib/rugui/configuration.rb +136 -0
- data/lib/rugui/framework_adapters/GTK.rb +233 -0
- data/lib/rugui/framework_adapters/Qt4.rb +171 -0
- data/lib/rugui/framework_adapters/base_framework_adapter.rb +90 -0
- data/lib/rugui/framework_adapters/framework_adapter_support.rb +35 -0
- data/lib/rugui/gem_builder.rb +21 -0
- data/lib/rugui/gem_dependency.rb +282 -0
- data/lib/rugui/initialize_hooks.rb +36 -0
- data/lib/rugui/initializer.rb +162 -0
- data/lib/rugui/log_support.rb +118 -0
- data/lib/rugui/observable_property_proxy.rb +73 -0
- data/lib/rugui/observable_property_support.rb +251 -0
- data/lib/rugui/plugin/loader.rb +77 -0
- data/lib/rugui/property_observer.rb +58 -0
- data/lib/rugui/signal_support.rb +57 -0
- data/lib/rugui/tasks/gems_application.rake +71 -0
- data/lib/rugui/tasks/rugui.rb +8 -0
- data/lib/rugui/tasks/rugui_framework.rb +4 -0
- data/lib/rugui/tasks/runner_application.rake +4 -0
- data/lib/rugui/tasks/spec_application.rake +64 -0
- data/lib/rugui/tasks/spec_framework.rake +27 -0
- data/lib/rugui/tasks/test_application.rake +77 -0
- data/lib/rugui/vendor_gem_source_index.rb +140 -0
- data/lib/rugui/version.rb +9 -0
- data/lib/rugui.rb +37 -0
- data/spec/framework/base_controller_spec.rb +48 -0
- data/spec/framework/base_model_spec.rb +13 -0
- data/spec/framework/base_view_helper_spec.rb +13 -0
- data/spec/framework/base_view_spec.rb +92 -0
- data/spec/framework/log_support_spec.rb +16 -0
- data/spec/framework/observable_property_proxy_spec.rb +67 -0
- data/spec/framework/observable_property_support_spec.rb +283 -0
- data/spec/framework/property_observer_spec.rb +88 -0
- data/spec/helpers/controllers.rb +29 -0
- data/spec/helpers/initialize_hooks_helper.rb +18 -0
- data/spec/helpers/models.rb +9 -0
- data/spec/helpers/observables.rb +210 -0
- data/spec/helpers/view_helpers.rb +9 -0
- data/spec/helpers/views.rb +72 -0
- data/spec/rcov.opts +1 -0
- data/spec/resource_files/my_other_view.glade +46 -0
- data/spec/resource_files/my_view.glade +46 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +15 -0
- metadata +149 -0
@@ -0,0 +1,302 @@
|
|
1
|
+
module RuGUI
|
2
|
+
# A base class for views.
|
3
|
+
#
|
4
|
+
# To use this class create a subclass and reimplement #setup_widgets, if you
|
5
|
+
# want to create your interface by hand. If you want to use a builder file
|
6
|
+
# just call #use_builder and, optionally, call #builder_file passing the
|
7
|
+
# filename to use.
|
8
|
+
#
|
9
|
+
# Each adapter may implement additional features, extending this class, look
|
10
|
+
# for it in adapter classes.
|
11
|
+
#
|
12
|
+
# The view may have ViewHelpers, which works as 'models' for views, i.e., they
|
13
|
+
# have observable properties that can be observed by the view. A default
|
14
|
+
# helper, named as <code>{view_name}Helper</code> is registered if it exists.
|
15
|
+
# For example, for a view named *MyView*, the default view helper should be
|
16
|
+
# named *MyViewHelper*. This helper can be accessed as a <code>helper</code>
|
17
|
+
# attribute. Other helpers may be registered if needed.
|
18
|
+
#
|
19
|
+
# Example (using GTK framework adapter):
|
20
|
+
# class MyGladeView < RuGUI::BaseView
|
21
|
+
# builder_file 'my_file.glade' # this is optional, if the glade file was called my_glade_view.glade this wouldn't be needed.
|
22
|
+
# root :top_window
|
23
|
+
# use_builder
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# class MyHandView < RuGUI::BaseView
|
27
|
+
# def setup_widgets
|
28
|
+
# # do your hand-made code here...
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
class BaseView < BaseObject
|
32
|
+
include RuGUI::LogSupport
|
33
|
+
include RuGUI::PropertyObserver
|
34
|
+
include RuGUI::SignalSupport
|
35
|
+
|
36
|
+
attr_accessor :controllers
|
37
|
+
attr_reader :widgets
|
38
|
+
attr_reader :unnamed_widgets
|
39
|
+
|
40
|
+
class_inheritable_accessor :configured_builder_file
|
41
|
+
class_inheritable_accessor :configured_builder_file_usage
|
42
|
+
class_inheritable_accessor :configured_builder_file_extension
|
43
|
+
class_inheritable_accessor :configured_root
|
44
|
+
class_inheritable_accessor :configured_display_root
|
45
|
+
|
46
|
+
def initialize
|
47
|
+
@controllers = {}
|
48
|
+
@helpers = {}
|
49
|
+
@unnamed_widgets = []
|
50
|
+
@widgets = {}
|
51
|
+
|
52
|
+
register_default_helper
|
53
|
+
setup_view_helpers
|
54
|
+
build_from_builder_file if use_builder?
|
55
|
+
setup_widgets
|
56
|
+
autoconnect_signals(self)
|
57
|
+
end
|
58
|
+
|
59
|
+
# This is included here so that the initialize method is properly updated.
|
60
|
+
include RuGUI::InitializeHooks
|
61
|
+
|
62
|
+
# Returns the framework_adapter for this class.
|
63
|
+
def framework_adapter
|
64
|
+
framework_adapter_for('BaseView')
|
65
|
+
end
|
66
|
+
|
67
|
+
# Reimplement this method to create widgets by hand.
|
68
|
+
def setup_widgets
|
69
|
+
end
|
70
|
+
|
71
|
+
# Reimplement this method to setup view helpers.
|
72
|
+
def setup_view_helpers
|
73
|
+
end
|
74
|
+
|
75
|
+
# Adds the given widget to a container widget.
|
76
|
+
def add_widget_to_container(widget, container_widget_or_name)
|
77
|
+
self.framework_adapter.add_widget_to_container(widget, from_widget_or_name(container_widget_or_name))
|
78
|
+
end
|
79
|
+
|
80
|
+
# Adds the given widget to a container widget.
|
81
|
+
def remove_widget_from_container(widget, container_widget_or_name)
|
82
|
+
self.framework_adapter.remove_widget_from_container(widget, from_widget_or_name(container_widget_or_name))
|
83
|
+
end
|
84
|
+
|
85
|
+
# Includes a view root widget inside the given container widget.
|
86
|
+
def include_view(container_widget_name, view)
|
87
|
+
raise RootWidgetNotSetForIncludedView, "You must set a root for views to be included." if view.root_widget.nil?
|
88
|
+
add_widget_to_container(view.root_widget, container_widget_name)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Removes a view root widget from the given container widget.
|
92
|
+
def remove_view(container_widget_name, view)
|
93
|
+
raise RootWidgetNotSetForIncludedView, "You must set a root for views to be removed." if view.root_widget.nil?
|
94
|
+
remove_widget_from_container(view.root_widget, container_widget_name)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Removes all children from the given container widget
|
98
|
+
def remove_all_children(container_widget)
|
99
|
+
self.framework_adapter.remove_all_children(container_widget)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Registers a controller as receiver of signals from the view widgets.
|
103
|
+
def register_controller(controller, name = nil)
|
104
|
+
name ||= controller.class.to_s.underscore
|
105
|
+
autoconnect_signals(controller)
|
106
|
+
@controllers[name.to_sym] = controller
|
107
|
+
end
|
108
|
+
|
109
|
+
# Registers a view helper for the view.
|
110
|
+
def register_helper(helper, name = nil)
|
111
|
+
helper = create_instance_if_possible(helper) if helper.is_a?(String) or helper.is_a?(Symbol)
|
112
|
+
unless helper.nil?()
|
113
|
+
name ||= helper.class.to_s.underscore
|
114
|
+
helper.register_observer(self, name)
|
115
|
+
@helpers[name.to_sym] = helper
|
116
|
+
create_attribute_reader(:helpers, name)
|
117
|
+
helper.post_registration(self)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Called after the view is registered in a controller.
|
122
|
+
def post_registration(controller)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Returns the root widget if one is set.
|
126
|
+
def root_widget
|
127
|
+
send(root.to_sym) if not root.nil?
|
128
|
+
end
|
129
|
+
|
130
|
+
# Returns the builder file.
|
131
|
+
def builder_file
|
132
|
+
self.configured_builder_file
|
133
|
+
end
|
134
|
+
|
135
|
+
# Returns the builder file extension.
|
136
|
+
def builder_file_extension
|
137
|
+
self.configured_builder_file_extension
|
138
|
+
end
|
139
|
+
|
140
|
+
# Returns true if builder file is being used for this view.
|
141
|
+
def use_builder?
|
142
|
+
self.configured_builder_file_usage
|
143
|
+
end
|
144
|
+
|
145
|
+
# Framework adapters should implement this if they support builder files.
|
146
|
+
def build_from_builder_file
|
147
|
+
filename = get_builder_file
|
148
|
+
raise BuilderFileNotFoundError, "Could not find builder file for view #{self.class.name}. UI file paths: #{RuGUI.configuration.builder_files_paths.join(', ')}." if filename.nil?
|
149
|
+
|
150
|
+
self.framework_adapter.build_widgets_from(filename)
|
151
|
+
self.framework_adapter.register_widgets
|
152
|
+
end
|
153
|
+
|
154
|
+
# Returns the name of the root widget for this view.
|
155
|
+
def root
|
156
|
+
self.configured_root.to_s unless self.configured_root.nil?
|
157
|
+
end
|
158
|
+
|
159
|
+
def display_root?
|
160
|
+
!!self.configured_display_root
|
161
|
+
end
|
162
|
+
|
163
|
+
class << self
|
164
|
+
# Sets the name of the root widget for this view.
|
165
|
+
#
|
166
|
+
# This is specially useful when more than one view uses the same glade
|
167
|
+
# file, but each one uses a diferent widget tree inside that glade file.
|
168
|
+
#
|
169
|
+
# Other use for this is when building a reusable widget, composed of the
|
170
|
+
# contents of a glade file. One could create a window, place a vertical
|
171
|
+
# box, and then place elements inside this vertical box. Later, this glade
|
172
|
+
# file is used to insert the contents of the vertical box inside another
|
173
|
+
# vertical box in other glade file.
|
174
|
+
def root(root_widget_name)
|
175
|
+
self.configured_root = root_widget_name
|
176
|
+
end
|
177
|
+
|
178
|
+
# Sets the builder file to use when creating this view.
|
179
|
+
def builder_file(file)
|
180
|
+
self.configured_builder_file = file
|
181
|
+
end
|
182
|
+
|
183
|
+
# Tells whether we should use a builder file when creating this view.
|
184
|
+
#
|
185
|
+
# By default the root widget will be displayed, but you can pass false to
|
186
|
+
# this method to prevent it for being displayed.
|
187
|
+
def use_builder(display_root = true)
|
188
|
+
self.configured_builder_file_usage = true
|
189
|
+
self.configured_display_root = display_root
|
190
|
+
|
191
|
+
self.configured_builder_file_extension = self.framework_adapter_class.builder_file_extension
|
192
|
+
default_builder_file_path = RuGUI.root.join('app', 'resources', "#{self.configured_builder_file_extension}")
|
193
|
+
RuGUI.configuration.builder_files_paths << default_builder_file_path unless RuGUI.configuration.builder_files_paths.include?(default_builder_file_path)
|
194
|
+
end
|
195
|
+
|
196
|
+
def framework_adapter_class
|
197
|
+
class_adapter_for('BaseView')
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
protected
|
202
|
+
# Builds a widget of the given type, possibly adding it to a parent
|
203
|
+
# widget, and display it.
|
204
|
+
#
|
205
|
+
# The *args are passed to the widget constructor.
|
206
|
+
def build_widget(widget_type, widget_name = nil, parent = nil, *args)
|
207
|
+
widget = widget_type.new(*args)
|
208
|
+
self.framework_adapter.set_widget_name(widget, widget_name)
|
209
|
+
add_widget(widget, widget_name)
|
210
|
+
add_widget_to_container(widget, parent) unless parent.nil?
|
211
|
+
widget.show
|
212
|
+
end
|
213
|
+
|
214
|
+
# Adds the widget to the view.
|
215
|
+
#
|
216
|
+
# If +widget_name+ is not given one is assumed the widget`s name will be
|
217
|
+
# used instead. If the widget doesn't have a name it will be added as an
|
218
|
+
# unnamed widget (accessible through #unnamed_widgets property.
|
219
|
+
def add_widget(widget, widget_name = nil)
|
220
|
+
widget_name ||= widget.name
|
221
|
+
widget_name = widget_name.to_s
|
222
|
+
|
223
|
+
unless widget_name.nil? || widget_name.empty?
|
224
|
+
create_attribute_for_widget(widget_name)
|
225
|
+
send("#{widget_name}=", widget)
|
226
|
+
@widgets[widget_name] = widget
|
227
|
+
else
|
228
|
+
@unnamed_widgets << widget
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
def from_widget_or_name(widget_or_name)
|
233
|
+
if widget_or_name.is_a?(String) || widget_or_name.is_a?(Symbol)
|
234
|
+
send(widget_or_name)
|
235
|
+
else
|
236
|
+
widget_or_name
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def autoconnect_signals(receiver)
|
241
|
+
receiver.autoconnect_declared_signals(self)
|
242
|
+
self.framework_adapter.autoconnect_signals(receiver)
|
243
|
+
end
|
244
|
+
|
245
|
+
private
|
246
|
+
def get_builder_file
|
247
|
+
filename = (not self.builder_file.nil?) ? self.builder_file : "#{self.class.to_s.underscore}.#{builder_file_extension}"
|
248
|
+
|
249
|
+
# The builder file given may already contain a full path to a glade file.
|
250
|
+
return filename if File.file?(filename)
|
251
|
+
|
252
|
+
filename = "#{filename}.#{builder_file_extension}" unless File.extname(filename) == ".#{builder_file_extension}"
|
253
|
+
|
254
|
+
paths = RuGUI.configuration.builder_files_paths.select do |path|
|
255
|
+
File.file?(File.join(path, filename))
|
256
|
+
end
|
257
|
+
File.join(paths.first, filename) unless paths.empty?
|
258
|
+
end
|
259
|
+
|
260
|
+
# Attempts to register the default helper for the view
|
261
|
+
def register_default_helper
|
262
|
+
register_helper("#{self.class.name}Helper", :helper)
|
263
|
+
end
|
264
|
+
|
265
|
+
def create_attribute_for_widget(widget_name)
|
266
|
+
self.instance_eval <<-instance_eval
|
267
|
+
def #{widget_name}
|
268
|
+
@#{widget_name}
|
269
|
+
end
|
270
|
+
|
271
|
+
def #{widget_name}=(widget)
|
272
|
+
@#{widget_name} = widget
|
273
|
+
end
|
274
|
+
instance_eval
|
275
|
+
end
|
276
|
+
|
277
|
+
# Creates an attribute reader for the some entity.
|
278
|
+
def create_attribute_reader(entity, name)
|
279
|
+
self.class.class_eval <<-class_eval
|
280
|
+
def #{name}
|
281
|
+
@#{entity}[:#{name}]
|
282
|
+
end
|
283
|
+
class_eval
|
284
|
+
end
|
285
|
+
|
286
|
+
# Creates an instance of the given class.
|
287
|
+
def create_instance_if_possible(klass_name, *args)
|
288
|
+
klass_name.to_s.camelize.constantize.new(*args)
|
289
|
+
rescue NameError
|
290
|
+
# Couldn't create instance.
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
# Exception thrown when the builder file for this view could not be found.
|
295
|
+
class BuilderFileNotFoundError < Exception
|
296
|
+
end
|
297
|
+
|
298
|
+
# Exception thrown when attempting to include a view which don't have a root
|
299
|
+
# set.
|
300
|
+
class RootWidgetNotSetForIncludedView < Exception
|
301
|
+
end
|
302
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module RuGUI
|
2
|
+
# A base class for view helpers.
|
3
|
+
class BaseViewHelper < BaseObject
|
4
|
+
include RuGUI::ObservablePropertySupport
|
5
|
+
include RuGUI::LogSupport
|
6
|
+
|
7
|
+
def initialize(observable_properties_values = {})
|
8
|
+
initialize_observable_property_support(observable_properties_values)
|
9
|
+
end
|
10
|
+
|
11
|
+
# This is included here so that the initialize method is properly updated.
|
12
|
+
include RuGUI::InitializeHooks
|
13
|
+
|
14
|
+
# Returns the framework_adapter for this class.
|
15
|
+
def framework_adapter
|
16
|
+
framework_adapter_for('BaseViewHelper')
|
17
|
+
end
|
18
|
+
|
19
|
+
# Called after the view helper is registered in a view.
|
20
|
+
def post_registration(view)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# Defines the environment to be used by the application.
|
2
|
+
RUGUI_ENV = (ENV['RUGUI_ENV'] || 'development').dup unless defined?(RUGUI_ENV)
|
3
|
+
|
4
|
+
module RuGUI
|
5
|
+
# Defines configurations for a RuGUI application.
|
6
|
+
class Configuration
|
7
|
+
# The application root path, defined by ::APPLICATION_ROOT
|
8
|
+
attr_reader :root_path
|
9
|
+
|
10
|
+
# The environment for this application.
|
11
|
+
attr_accessor :environment
|
12
|
+
|
13
|
+
# The framework adapter to be used for this application. Defaults to GTK.
|
14
|
+
# For now it can only be GTK.
|
15
|
+
attr_accessor :framework_adapter
|
16
|
+
|
17
|
+
# The specific logger to use. By default, a logger will be created and
|
18
|
+
# initialized using #log_path and #log_level, but a programmer may
|
19
|
+
# specifically set the logger to use via this accessor and it will be
|
20
|
+
# used directly.
|
21
|
+
attr_accessor :logger
|
22
|
+
|
23
|
+
# An array of paths for builder files.
|
24
|
+
attr_accessor :builder_files_paths
|
25
|
+
|
26
|
+
# An array of paths which should be automaticaly loaded.
|
27
|
+
attr_accessor :load_paths
|
28
|
+
|
29
|
+
# An array of paths which should be searched for gtkrc styles files.
|
30
|
+
#
|
31
|
+
# It searchs for files which have the '.rc' extension or files which have
|
32
|
+
# the string 'gtkrc' in its filename.
|
33
|
+
#
|
34
|
+
# The order in which the files are loaded is random, so do not rely on it.
|
35
|
+
#
|
36
|
+
# If you need to use absolute paths in a gtkrc file, such as set the pixmap
|
37
|
+
# path, you can use "{ROOT_PATH}", which will be substituted by the
|
38
|
+
# application root path when the file is read.
|
39
|
+
attr_accessor :styles_paths
|
40
|
+
|
41
|
+
# The timeout for queued calls. Useful when performing long tasks.
|
42
|
+
attr_accessor :queue_timeout
|
43
|
+
|
44
|
+
# A hash of application specific configurations.
|
45
|
+
attr_accessor :application
|
46
|
+
|
47
|
+
# An array of gems that this RuGUI application depends on. RuGUI will automatically load
|
48
|
+
# these gems during installation, and allow you to install any missing gems with:
|
49
|
+
#
|
50
|
+
# rake gems:install
|
51
|
+
#
|
52
|
+
# You can add gems with the #gem method.
|
53
|
+
attr_accessor :gems
|
54
|
+
|
55
|
+
def initialize
|
56
|
+
set_root_path!
|
57
|
+
|
58
|
+
self.environment = default_environment
|
59
|
+
self.framework_adapter = default_framework_adapter
|
60
|
+
self.load_paths = default_load_paths
|
61
|
+
self.builder_files_paths = default_builder_files_paths
|
62
|
+
self.styles_paths = default_styles_paths
|
63
|
+
self.queue_timeout = default_queue_timeout
|
64
|
+
self.gems = default_gems
|
65
|
+
self.logger = {}
|
66
|
+
self.application = {}
|
67
|
+
end
|
68
|
+
|
69
|
+
# The path to the current environment's file (<tt>development.rb</tt>, etc.). By
|
70
|
+
# default the file is at <tt>config/environments/#{environment}.rb</tt>.
|
71
|
+
def environment_path
|
72
|
+
root_path.join('config', 'environments', "#{environment}.rb")
|
73
|
+
end
|
74
|
+
|
75
|
+
def set_root_path!
|
76
|
+
raise 'APPLICATION_ROOT is not set' unless defined?(::APPLICATION_ROOT)
|
77
|
+
raise 'APPLICATION_ROOT is not a directory' unless File.directory?(::APPLICATION_ROOT)
|
78
|
+
|
79
|
+
@root_path = Pathname.new(File.expand_path(::APPLICATION_ROOT))
|
80
|
+
end
|
81
|
+
|
82
|
+
# Adds a single Gem dependency to the RuGUI application. By default, it will require
|
83
|
+
# the library with the same name as the gem. Use :lib to specify a different name.
|
84
|
+
#
|
85
|
+
# # gem 'aws-s3', '>= 0.4.0'
|
86
|
+
# # require 'aws/s3'
|
87
|
+
# config.gem 'aws-s3', :lib => 'aws/s3', :version => '>= 0.4.0', \
|
88
|
+
# :source => "http://code.whytheluckystiff.net"
|
89
|
+
#
|
90
|
+
# To require a library be installed, but not attempt to load it, pass :lib => false
|
91
|
+
#
|
92
|
+
# config.gem 'qrp', :version => '0.4.1', :lib => false
|
93
|
+
def gem(name, options = {})
|
94
|
+
@gems << RuGUI::GemDependency.new(name, options)
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
def default_environment
|
99
|
+
::RUGUI_ENV
|
100
|
+
end
|
101
|
+
|
102
|
+
def default_framework_adapter
|
103
|
+
'GTK'
|
104
|
+
end
|
105
|
+
|
106
|
+
def default_load_paths
|
107
|
+
paths = []
|
108
|
+
|
109
|
+
paths.concat %w(
|
110
|
+
app
|
111
|
+
app/models
|
112
|
+
app/controllers
|
113
|
+
app/views
|
114
|
+
app/views/helpers
|
115
|
+
config
|
116
|
+
lib
|
117
|
+
).map { |dir| root_path.join(dir) }.select { |dir| File.directory?(dir) }
|
118
|
+
end
|
119
|
+
|
120
|
+
def default_builder_files_paths
|
121
|
+
[]
|
122
|
+
end
|
123
|
+
|
124
|
+
def default_styles_paths
|
125
|
+
[root_path.join('app', 'resources', 'styles')]
|
126
|
+
end
|
127
|
+
|
128
|
+
def default_queue_timeout
|
129
|
+
50
|
130
|
+
end
|
131
|
+
|
132
|
+
def default_gems
|
133
|
+
[]
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,233 @@
|
|
1
|
+
require 'gtk2'
|
2
|
+
require 'libglade2'
|
3
|
+
|
4
|
+
# See the discussion here: http://eigenclass.org/hiki.rb?instance_exec
|
5
|
+
class Object
|
6
|
+
module InstanceExecHelper; end
|
7
|
+
include InstanceExecHelper
|
8
|
+
def instance_exec(*args, &block)
|
9
|
+
begin
|
10
|
+
old_critical, Thread.critical = Thread.critical, true
|
11
|
+
n = 0
|
12
|
+
n += 1 while respond_to?(mname="__instance_exec#{n}")
|
13
|
+
InstanceExecHelper.module_eval{ define_method(mname, &block) }
|
14
|
+
ensure
|
15
|
+
Thread.critical = old_critical
|
16
|
+
end
|
17
|
+
begin
|
18
|
+
ret = send(mname, *args)
|
19
|
+
ensure
|
20
|
+
InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil
|
21
|
+
end
|
22
|
+
ret
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
module Gtk
|
27
|
+
GTK_PENDING_BLOCKS = []
|
28
|
+
GTK_PENDING_BLOCKS_LOCK = Monitor.new
|
29
|
+
|
30
|
+
def Gtk.queue(&block)
|
31
|
+
if Thread.current == Thread.main
|
32
|
+
block.call
|
33
|
+
else
|
34
|
+
GTK_PENDING_BLOCKS_LOCK.synchronize do
|
35
|
+
GTK_PENDING_BLOCKS << block
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Adds a timeout to execute pending blocks in a queue.
|
41
|
+
def Gtk.queue_timeout(timeout)
|
42
|
+
Gtk.timeout_add timeout do
|
43
|
+
GTK_PENDING_BLOCKS_LOCK.synchronize do
|
44
|
+
GTK_PENDING_BLOCKS.each do |block|
|
45
|
+
block.call
|
46
|
+
end
|
47
|
+
GTK_PENDING_BLOCKS.clear
|
48
|
+
end
|
49
|
+
true
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def Gtk.load_style_paths
|
54
|
+
styles_paths = RuGUI.configuration.styles_paths.select { |path| File.directory?(path) }
|
55
|
+
styles_paths.each do |path|
|
56
|
+
Dir.new(path).each do |entry|
|
57
|
+
Gtk::RC.parse_string(get_style_file_contents(path, entry)) if is_style_file?(path, entry)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def Gtk.is_style_file?(path, filename)
|
63
|
+
File.extname(filename) == '.rc' or /gtkrc/.match(filename) if File.file?(File.join(path, filename))
|
64
|
+
end
|
65
|
+
|
66
|
+
def Gtk.get_style_file_contents(path, filename)
|
67
|
+
IO.read(File.join(path, filename)).sub('{ROOT_PATH}', RuGUI.configuration.root_path)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
Gtk.load_style_paths
|
72
|
+
|
73
|
+
module RuGUI
|
74
|
+
module FrameworkAdapters
|
75
|
+
module GTK
|
76
|
+
class BaseController < RuGUI::FrameworkAdapters::BaseFrameworkAdapter::BaseController
|
77
|
+
def queue(&block)
|
78
|
+
Gtk.queue(&block)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class BaseMainController < RuGUI::FrameworkAdapters::GTK::BaseController
|
83
|
+
def run
|
84
|
+
Gtk.queue_timeout(RuGUI.configuration.queue_timeout)
|
85
|
+
Gtk.main
|
86
|
+
end
|
87
|
+
|
88
|
+
def quit
|
89
|
+
Gtk.main_quit
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class BaseView < RuGUI::FrameworkAdapters::BaseFrameworkAdapter::BaseView
|
94
|
+
# Queues the block call, so that it is only gets executed in the main thread.
|
95
|
+
def queue(&block)
|
96
|
+
Gtk.queue(&block)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Adds a widget to the given container widget.
|
100
|
+
def add_widget_to_container(widget, container_widget)
|
101
|
+
container_widget.add(widget)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Removes a widget from the given container widget.
|
105
|
+
def remove_widget_from_container(widget, container_widget)
|
106
|
+
container_widget.remove(widget)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Removes all children from the given container widget.
|
110
|
+
def remove_all_children(container_widget)
|
111
|
+
container_widget.children.each do |child|
|
112
|
+
container_widget.remove(child)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Sets the widget name for the given widget if given.
|
117
|
+
def set_widget_name(widget, widget_name)
|
118
|
+
widget.name = widget_name.to_s unless widget_name.nil?
|
119
|
+
end
|
120
|
+
|
121
|
+
# Autoconnects signals handlers for the view. If +other_target+ is given
|
122
|
+
# it is used instead of the view itself.
|
123
|
+
def autoconnect_signals(other_target = nil)
|
124
|
+
if self.adapted_object.use_builder?
|
125
|
+
self.adapted_object.glade.signal_autoconnect_full do |source, target, signal_name, handler_name, signal_data, after|
|
126
|
+
target ||= other_target
|
127
|
+
self.adapted_object.glade.connect(source, target, signal_name, handler_name, signal_data) if target.respond_to?(handler_name)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Connects the signal from the widget to the given receiver block.
|
133
|
+
# The block is executed in the context of the receiver.
|
134
|
+
def connect_declared_signal_block(widget, signal, receiver, block)
|
135
|
+
widget.signal_connect(signal) do |*args|
|
136
|
+
receiver.instance_exec(*args, &block)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Connects the signal from the widget to the given receiver method.
|
141
|
+
def connect_declared_signal(widget, signal, receiver, method)
|
142
|
+
widget.signal_connect(signal) do |*args|
|
143
|
+
receiver.send(method, *args)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Builds widgets from the given filename, using the proper builder.
|
148
|
+
def build_widgets_from(filename)
|
149
|
+
self.adapted_object.glade = GladeXML.new(filename, self.adapted_object.root, nil, nil, GladeXML::FILE)
|
150
|
+
|
151
|
+
self.adapted_object.glade.widget_names.each do |widget_name|
|
152
|
+
self.adapted_object.send(:create_attribute_for_widget, widget_name) unless self.adapted_object.glade[widget_name].nil?
|
153
|
+
end
|
154
|
+
self.adapted_object.root_widget.show if self.adapted_object.display_root? and not self.adapted_object.root_widget.nil?
|
155
|
+
end
|
156
|
+
|
157
|
+
# Registers widgets as attributes of the view class.
|
158
|
+
def register_widgets
|
159
|
+
self.adapted_object.glade.widget_names.each do |widget_name|
|
160
|
+
unless self.adapted_object.glade[widget_name].nil?
|
161
|
+
self.adapted_object.send("#{widget_name}=".to_sym, self.adapted_object.glade[widget_name])
|
162
|
+
self.adapted_object.widgets[widget_name] = self.adapted_object.glade[widget_name]
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
class << self
|
168
|
+
# Returns the builder file extension to be used for this view class.
|
169
|
+
def builder_file_extension
|
170
|
+
'glade'
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
module RuGUI
|
179
|
+
class BaseView < BaseObject
|
180
|
+
class_inheritable_accessor :configured_glade_usage
|
181
|
+
|
182
|
+
attr_accessor :glade
|
183
|
+
|
184
|
+
# Adds a signal handler for all widgets of the given type.
|
185
|
+
def add_signal_handler_for_widget_type(widget_type, signal, &block)
|
186
|
+
widgets = []
|
187
|
+
widgets.concat(@widgets.values.select { |widget| widget.kind_of?(widget_type) }) unless @widgets.empty?
|
188
|
+
widgets.concat(@unnamed_widgets.select { |widget| widget.kind_of?(widget_type) }) unless @unnamed_widgets.empty?
|
189
|
+
|
190
|
+
widgets.each do |widget|
|
191
|
+
widget.signal_connect(signal, &block)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# Changes the widget style to use the given widget style.
|
196
|
+
#
|
197
|
+
# This widget style should be declared in a gtkrc file, by specifying a
|
198
|
+
# style using a widget path, such as:
|
199
|
+
#
|
200
|
+
# widget "main_window" style "main_window_style"
|
201
|
+
# widget "main_window_other" style "main_window_other_style"
|
202
|
+
#
|
203
|
+
# In this example, if you called this method like this:
|
204
|
+
#
|
205
|
+
# change_widget_style(:main_window, 'main_window_other')
|
206
|
+
# change_widget_style(self.main_window, 'main_window_other') # or, passing the widget instance directly
|
207
|
+
#
|
208
|
+
# The widget style would be set to "main_window_other_style".
|
209
|
+
#
|
210
|
+
# NOTE: Unfortunately, gtk doesn't offer an API to get declared styles, so
|
211
|
+
# you must set a style to a widget. Since the widget name set in the style
|
212
|
+
# definition doesn't need to point to an existing widget we can use this
|
213
|
+
# to simplify the widget styling here.
|
214
|
+
def change_widget_style(widget_or_name, widget_path_style)
|
215
|
+
if widget_or_name.is_a?(Gtk::Widget)
|
216
|
+
widget = widget_or_name
|
217
|
+
else
|
218
|
+
widget = @glade[widget_or_name.to_s]
|
219
|
+
end
|
220
|
+
style = Gtk::RC.get_style_by_paths(Gtk::Settings.default, widget_path_style.to_s, nil, nil)
|
221
|
+
widget.style = style
|
222
|
+
end
|
223
|
+
|
224
|
+
class << self
|
225
|
+
# Call this method at class level if the view should be built from a glade
|
226
|
+
# file.
|
227
|
+
def use_glade
|
228
|
+
self.logger.warn('DEPRECATED - Call use_builder class method instead in your view.')
|
229
|
+
use_builder
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|