metaruby 1.0.0.rc2 → 1.0.0.rc3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,15 +1,16 @@
1
1
  <% prefix = if ressource_dir[0, 1] == "/" then "file://"
2
2
  end
3
3
  %>
4
- <html>
4
+ <head>
5
5
  <link rel="stylesheet" href="<%= prefix %><%= File.join(ressource_dir, 'page.css') %>" type="text/css" />
6
6
  <script type="text/javascript" src="<%= prefix %><%= File.join(ressource_dir, 'jquery.min.js') %>"></script>
7
7
  <script type="text/javascript" src="<%= prefix %><%= File.join(ressource_dir, 'jquery.selectfilter.js') %>"></script>
8
- <% javascript.each do |js| %>
9
- <script type="text/javascript" src="<%= prefix %><%= File.expand_path(js, ressource_dir) %>"></script>
10
- <% end %>
8
+ <%= head.join("\n").gsub('${RESOURCE_DIR}', "#{prefix}#{ressource_dir}") %>
9
+ <% if page_name %>
11
10
  <title><%= page_name %></title>
12
- </html>
11
+ <% end %>
12
+ </head>
13
+ <%= scripts.join("\n").gsub('${RESOURCE_DIR}', "#{prefix}#{ressource_dir}") %>
13
14
  <body>
14
15
  <%= html_body(ressource_dir: ressource_dir) %>
15
16
  </body>
@@ -54,9 +54,12 @@ module MetaRuby
54
54
  # the model view
55
55
  attr_reader :central_splitter
56
56
 
57
- # A Page object with a #link_to method that is suitable for the
58
- # model browser
57
+ # A Page object tunes to create URIs for objects that are suitable
58
+ # for {#model_selector}
59
59
  class Page < HTML::Page
60
+ # Overloaded from {HTML::Page} to resolve object paths (in the
61
+ # constant hierarchy, e.g. A::B::C) into the corresponding
62
+ # path expected by {#model_selector} (e.g. /A/B/C)
60
63
  def uri_for(object)
61
64
  if object.respond_to?(:name) && (obj_name = object.name) && (obj_name =~ /^[\w:]+$/)
62
65
  path = obj_name.split("::")
@@ -66,7 +69,6 @@ module MetaRuby
66
69
  end
67
70
  end
68
71
 
69
-
70
72
  def initialize(main = nil, exception_view: nil)
71
73
  super(main)
72
74
 
@@ -95,6 +97,10 @@ module MetaRuby
95
97
  update_exceptions
96
98
  end
97
99
 
100
+ # Restore the state of this widget from settings previously saved
101
+ # with {#save_to_settings}
102
+ #
103
+ # @param [Qt::Settings] settings
98
104
  def restore_from_settings(settings)
99
105
  %w{central_splitter vertical_splitter}.each do |object_name|
100
106
  sizes = settings.value(object_name)
@@ -107,6 +113,9 @@ module MetaRuby
107
113
  end
108
114
  end
109
115
 
116
+ # Save the current state of this widget in the given settings
117
+ #
118
+ # @param [Qt::Settings] settings
110
119
  def save_to_settings(settings)
111
120
  %w{central_splitter vertical_splitter}.each do |object_name|
112
121
  sizes = send(object_name).sizes
@@ -115,7 +124,7 @@ module MetaRuby
115
124
  end
116
125
  end
117
126
 
118
- # Update the model selector after {register_type} got called
127
+ # Update the model selector after {#register_type} got called
119
128
  def update_model_selector
120
129
  model_selector.update
121
130
  end
@@ -126,7 +135,7 @@ module MetaRuby
126
135
  # It registers the given type on the model browser so that it gets
127
136
  # displayed there.
128
137
  #
129
- # You must call {update_model_selector} after this call for the
138
+ # You must call {#update_model_selector} after this call for the
130
139
  # modification to have any effect (i.e. for the newly registered
131
140
  # models to appear on the selector)
132
141
  #
@@ -204,7 +213,7 @@ module MetaRuby
204
213
  # Call to render the given model
205
214
  #
206
215
  # @param [Model] mod the model that should be rendered
207
- # @raises [ArgumentError] if there is no view available for the
216
+ # @raise [ArgumentError] if there is no view available for the
208
217
  # given model
209
218
  def render_model(mod, options = Hash.new)
210
219
  page.clear
@@ -1,20 +1,42 @@
1
1
  module MetaRuby
2
2
  module GUI
3
- # A Qt widget that allows to browse the models registered in the Ruby
4
- # constanat hierarchy
3
+ # A Qt widget based on {RubyConstantsItemModel} to browse a set of
4
+ # models, and display them when the user selects one
5
5
  class ModelSelector < Qt::Widget
6
- attr_reader :btn_type_filter_menu
6
+ # A per-type matching of the type to the actio that allows to
7
+ # filter/unfilter on this type
8
+ #
9
+ # @return [Hash<Module,Qt::Action>]
7
10
  attr_reader :type_filters
11
+
12
+ # The view that shows the object hierarchy
13
+ #
14
+ # @return [Qt::TreeView]
8
15
  attr_reader :model_list
16
+
17
+ # Qt model filter
18
+ # @return [Qt::SortFilterProxyModel]
9
19
  attr_reader :model_filter
20
+
21
+ # (see {RubyConstantsItemModel#type_info)
22
+ attr_reader :type_info
23
+
24
+ # The Qt item model that represents the object hierarchy
25
+ # @return [RubyConstantsItemModel]
26
+ attr_reader :browser_model
27
+
28
+ # @return [Qt::PushButton] a button allowing to filter models by
29
+ # type
30
+ attr_reader :btn_type_filter_menu
10
31
  # @return [Qt::LineEdit] the line edit widget that allows to modify
11
32
  # the tree view filter
12
33
  attr_reader :filter_box
13
- # @return [Qt::Completer] auto-completion for {filter_box}
34
+ # @return [Qt::Completer] auto-completion for {#filter_box}
14
35
  attr_reader :filter_completer
15
- attr_reader :type_info
16
- attr_reader :browser_model
17
36
 
37
+ # Create a new widget with an optional parent
38
+ #
39
+ # @param [Qt::Widget,nil] parent
18
40
  def initialize(parent = nil)
19
41
  super
20
42
 
@@ -35,6 +57,14 @@ module MetaRuby
35
57
  setTabOrder(filter_button, model_list)
36
58
  end
37
59
 
60
+ # Register a new object type
61
+ #
62
+ # @param [Module] model_base a module or class whose all objects of
63
+ # this type have as superclass
64
+ # @param [String] name the string that should be used to represent
65
+ # objects of this type
66
+ # @param [Integer] priority if an object's ancestry matches multiple
67
+ # types, only the ones of the highest priority will be retained
38
68
  def register_type(model_base, name, priority = 0)
39
69
  type_info[model_base] = RubyConstantsItemModel::TypeInfo.new(name, priority)
40
70
  action = Qt::Action.new(name, self)
@@ -47,11 +77,15 @@ module MetaRuby
47
77
  end
48
78
  end
49
79
 
80
+ # Update the view, reloading the underlying model
50
81
  def update
51
82
  update_model_filter
52
83
  reload
53
84
  end
54
85
 
86
+ # @api private
87
+ #
88
+ # Update {#model_filter} to match the current filter setup
55
89
  def update_model_filter
56
90
  type_rx = type_filters.map do |model_base, act|
57
91
  if act.checked?
@@ -74,6 +108,10 @@ module MetaRuby
74
108
  auto_open
75
109
  end
76
110
 
111
+ # Auto-open in the current state
112
+ #
113
+ # @param [Integer] threshold the method opens items whose number of
114
+ # children is lower than this threshold
77
115
  def auto_open(threshold = 5)
78
116
  current_level = [Qt::ModelIndex.new]
79
117
  while !current_level.empty?
@@ -97,6 +135,7 @@ module MetaRuby
97
135
  end
98
136
  end
99
137
 
138
+ # Tests if an object if a model
100
139
  def model?(obj)
101
140
  type_info.any? do |model_base, _|
102
141
  obj.kind_of?(model_base) ||
@@ -113,6 +152,9 @@ module MetaRuby
113
152
  end
114
153
  end
115
154
 
155
+ # @api private
156
+ #
157
+ # Helper method for {#select_first_item}
116
158
  def all_leaves(model, limit = nil, item = Qt::ModelIndex.new, result = [])
117
159
  if !model.hasChildren(item)
118
160
  result << item
@@ -131,12 +173,16 @@ module MetaRuby
131
173
  return result
132
174
  end
133
175
 
176
+ # Select the first displayed item
134
177
  def select_first_item
135
178
  if item = all_leaves(model_filter, 1).first
136
179
  model_list.setCurrentIndex(item)
137
180
  end
138
181
  end
139
182
 
183
+ # @api private
184
+ #
185
+ # Create and setup {#model_list}
140
186
  def setup_tree_view(layout)
141
187
  @model_list = Qt::TreeView.new(self)
142
188
  @model_filter = Qt::SortFilterProxyModel.new
@@ -169,6 +215,7 @@ module MetaRuby
169
215
  end
170
216
  signals 'model_selected(QVariant)'
171
217
 
218
+ # Reload the object model, keeping the current selection if possible
172
219
  def reload
173
220
  if current = current_selection
174
221
  current_module = current.this
@@ -202,12 +249,11 @@ module MetaRuby
202
249
  # control whether the filter should be reset if the index given as
203
250
  # parameter is currently filtered out
204
251
  #
205
- # @param [Qt::ModelIndex] an index valid in {browser_model}
206
- # @option options [Boolean] :reset_filter (true) if true, the filter
252
+ # @param [Qt::ModelIndex] source_index an index valid in {#browser_model}
253
+ # @param [Boolean] reset_filter if true, the filter
207
254
  # is reset if the requested index is currently filtered out
208
- # @return [Qt::ModelIndex] an index filtered by {model_filter}
209
- def map_index_from_source(source_index, options = Hash.new)
210
- options = Kernel.validate_options options, :reset_filter => true
255
+ # @return [Qt::ModelIndex] an index filtered by {#model_filter}
256
+ def map_index_from_source(source_index, reset_filter: true)
211
257
  index = model_filter.map_from_source(source_index)
212
258
  if !index
213
259
  return
@@ -1,5 +1,10 @@
1
1
  module MetaRuby
2
2
  module GUI
3
+ # Management of HTML rendering of objects of different types
4
+ #
5
+ # Objects of this class allow to register a set of renderers, dedicated
6
+ # to rendering objects of a certain type (defined as a superclass) and
7
+ # automatically switch between the rendering objects.
3
8
  class RenderingManager < Qt::Object
4
9
  # @return [#push] the page object on which we render
5
10
  attr_reader :page
@@ -10,15 +15,19 @@ module MetaRuby
10
15
  # matches will be used.
11
16
  # Do not modify directly, use {#register_type} instead
12
17
  attr_reader :available_renderers
13
- # The last used rendering objects
18
+ # The rendering object used last in {#render}
14
19
  attr_reader :current_renderer
15
20
 
21
+ # Create a rendering manager that acts on a given page
16
22
  def initialize(page = nil)
17
23
  super()
18
24
  @page = page
19
25
  @available_renderers = Hash.new
20
26
  end
21
27
 
28
+ # A list of exceptions that happened during rendering
29
+ #
30
+ # @return [Array<Exception>]
22
31
  def registered_exceptions
23
32
  if current_renderer.respond_to?(:registered_exceptions)
24
33
  current_renderer.registered_exceptions
@@ -32,12 +41,15 @@ module MetaRuby
32
41
  # It registers the given type on the model browser so that it gets
33
42
  # displayed there.
34
43
  #
35
- # @param [Class] type the base model class for the models that are
36
- # considered here
44
+ # @param [Class] type objects whose class or ancestry include 'type'
45
+ # will be rendered using the provided rendering class. If more tha
46
+ # none matches, the first one is used.
37
47
  # @param [Class] rendering_class a class from which a relevant
38
48
  # rendering object can be created. The generated instances must
39
49
  # follow the rules described in the documentation of
40
50
  # {ModelBrowser}
51
+ # @param [Hash] render_options a set of options that must be passed
52
+ # to the renderer's #render method
41
53
  def register_type(type, rendering_class, render_options = Hash.new)
42
54
  render = if rendering_class.kind_of?(Class)
43
55
  rendering_class.new(page)
@@ -60,37 +72,54 @@ module MetaRuby
60
72
  end
61
73
  end
62
74
 
75
+ # @api private
76
+ #
77
+ # Find a rendering object for the given object
78
+ #
79
+ # @param [Object] mod the object we need to render
80
+ # @return [(Class,(#render,Hash)),nil] either the base class,
81
+ # rendering object and rendering options that should be used for
82
+ # the given object, or nil if there are no matching rendering
83
+ # objects
63
84
  def find_renderer(mod)
64
85
  available_renderers.find do |model, _|
65
86
  mod.kind_of?(model) || (mod.kind_of?(Module) && model.kind_of?(Module) && mod <= model)
66
87
  end
67
88
  end
68
89
 
90
+ # Disable the current renderer
69
91
  def disable
70
92
  if current_renderer
71
93
  current_renderer.disable
72
94
  end
73
95
  end
74
96
 
97
+ # Enable the current renderer
75
98
  def enable
76
99
  if current_renderer
77
100
  current_renderer.enable
78
101
  end
79
102
  end
80
103
 
104
+ # Clear the current renderer
81
105
  def clear
82
106
  if current_renderer
83
107
  current_renderer.clear
84
108
  end
85
109
  end
86
110
 
87
-
88
111
  # Call to render the given model
89
112
  #
90
- # @param [Model] mod the model that should be rendered
91
- # @raises [ArgumentError] if there is no view available for the
113
+ # The renderer that has been used is made active (enabled) and
114
+ # stored in {#current_renderer}. The previous one is disabled and
115
+ # cleared.
116
+ #
117
+ # @param [Model] object the model that should be rendered
118
+ # @param [Hash] push_options options that should be passed to the
119
+ # object's renderer {#render} method
120
+ # @raise [ArgumentError] if there is no view available for the
92
121
  # given model
93
- def render(object, push_options = Hash.new)
122
+ def render(object, **push_options)
94
123
  _, (renderer, render_options) = find_renderer(object)
95
124
  if renderer
96
125
  if current_renderer
@@ -1,21 +1,69 @@
1
1
  module MetaRuby
2
2
  module GUI
3
- # A Qt item model that lists Ruby modules that match a given predicate
4
- # (given at construction time)
3
+ # A Qt item model that enumerates models stored in the Ruby constant
4
+ # hierarchy
5
+ #
6
+ # The model exposes all registered constants for which {#predicate}
7
+ # returns true in a hierarchy, allowing the user to interact with it.
8
+ #
9
+ # Discovery starts at Object
5
10
  class RubyConstantsItemModel < Qt::AbstractItemModel
11
+ # Stored per-module information test
6
12
  ModuleInfo = Struct.new :id, :name, :this, :parent, :children, :row, :types, :full_name, :keyword_string, :path
13
+
14
+ # Information about different model types
7
15
  TypeInfo = Struct.new :name, :priority, :color
8
16
 
17
+ # Predicate that filters objects in addition to {#excludes}
18
+ #
19
+ # Only objects for which #call returns true and the constants that
20
+ # contain them are exposed by this model
21
+ #
22
+ # @return [#call]
9
23
  attr_reader :predicate
24
+
25
+ # Explicitely excluded objects
26
+ #
27
+ # Set of objects that should be excluded from discovery, regardless
28
+ # of what {#predicate} would return from them.
29
+ #
30
+ # Note that such objects are not discovered at all, meaning that if
31
+ # they contain objects that should have been discovered, they won't
32
+ # be.
33
+ #
34
+ # @return [Set]
35
+ attr_reader :excludes
36
+
37
+ # A list of expected object types
38
+ #
39
+ # This is used to decide where a given object should be "attached"
40
+ # in the hierarchy. Matching types are stored in {ModuleInfo#types}.
41
+ #
42
+ # @return [{Class=>TypeInfo}]
43
+ attr_reader :type_info
44
+
45
+ # Mapping from module ID to a module object
46
+ #
47
+ # @return [{Integer=>ModuleInfo}]
10
48
  attr_reader :id_to_module
49
+
50
+ # Set of objects that have been filtered out by {#predicate}
11
51
  attr_reader :filtered_out_modules
12
- attr_reader :type_info
52
+
53
+ # Name of the root item
54
+ #
55
+ # @return [String]
13
56
  attr_accessor :title
57
+
58
+ # List of paths for each of the discovered objects
59
+ #
60
+ # @return [{Object=>String}]
14
61
  attr_reader :object_paths
15
- # @return [Set<Object>] a set of objects that should not be
16
- # discovered
17
- attr_reader :excludes
18
62
 
63
+ # Initialize this model for objects of the given type
64
+ #
65
+ # @param [Hash] type_info value for {#type_info}
66
+ # @param [#call] predicate the filter {#predicate}
19
67
  def initialize(type_info = Hash.new, &predicate)
20
68
  super()
21
69
  @predicate = predicate || proc { true }
@@ -28,6 +76,7 @@ module MetaRuby
28
76
  @object_paths = Hash.new
29
77
  end
30
78
 
79
+ # Discovers or rediscovers the objects
31
80
  def reload
32
81
  begin_reset_model
33
82
  @id_to_module = []
@@ -46,10 +95,19 @@ module MetaRuby
46
95
  end_reset_model
47
96
  end
48
97
 
98
+ # {ModuleInfo} for the root
49
99
  def root_info
50
100
  id_to_module.last
51
101
  end
52
102
 
103
+ # @api private
104
+ #
105
+ # Generate the path information, i.e. per-object path string
106
+ #
107
+ # @param [Hash] paths the generated paths (matches {#object_paths})
108
+ # @param [ModuleInfo] info the object information for the object to
109
+ # be discovered
110
+ # @param [String] current the path of 'info'
53
111
  def generate_paths(paths, info, current)
54
112
  info.children.each do |child|
55
113
  child_uri = current + '/' + child.name
@@ -58,6 +116,12 @@ module MetaRuby
58
116
  end
59
117
  end
60
118
 
119
+ # @api private
120
+ #
121
+ # Updates {ModuleInfo#types} so that it includes the type of its
122
+ # children
123
+ #
124
+ # @param [ModuleInfo] info the object info that should be updated
61
125
  def update_module_type_info(info)
62
126
  types = info.types.to_set
63
127
  info.children.each do |child_info|
@@ -68,6 +132,13 @@ module MetaRuby
68
132
  end.reverse
69
133
  end
70
134
 
135
+ # @api private
136
+ #
137
+ # Discovers an object and its children
138
+ #
139
+ # @param [Object] mod an object that should be discovered
140
+ # @param [Array] stack the current stack (to avoid infinite recursions)
141
+ # @return [ModuleInfo]
71
142
  def discover_module(mod, stack = Array.new)
72
143
  return if excludes.include?(mod)
73
144
  stack.push mod
@@ -143,13 +214,13 @@ module MetaRuby
143
214
  ensure stack.pop
144
215
  end
145
216
 
146
- def headerData(section, orientation, role)
147
- if role == Qt::DisplayRole && section == 0
148
- Qt::Variant.new(title)
149
- else Qt::Variant.new
150
- end
151
- end
152
-
217
+ # @api private
218
+ #
219
+ # Lazily computes the full name of a discovered object. It updates
220
+ # {ModuleInfo#full_name}
221
+ #
222
+ # @param [ModuleInfo] info
223
+ # @return [String]
153
224
  def compute_full_name(info)
154
225
  if name = info.full_name
155
226
  return name
@@ -164,6 +235,13 @@ module MetaRuby
164
235
  end
165
236
  end
166
237
 
238
+ # @api private
239
+ #
240
+ # Lazily compute the path of a discovered object. The result is
241
+ # stored in {ModuleInfo#path}
242
+ #
243
+ # @param [ModuleInfo] info
244
+ # @return [String]
167
245
  def compute_path(info)
168
246
  if path = info.path
169
247
  return path
@@ -173,6 +251,57 @@ module MetaRuby
173
251
  end
174
252
  end
175
253
 
254
+
255
+ # Resolves a {ModuleInfo} from a Qt::ModelIndex
256
+ def info_from_index(index)
257
+ if !index.valid?
258
+ return id_to_module.last
259
+ else
260
+ id_to_module[index.internal_id >> 1]
261
+ end
262
+ end
263
+
264
+ # Return the Qt::ModelIndex that represents a given object
265
+ #
266
+ # @return [Qt::ModelIndex,nil] the index, or nil if the object is
267
+ # not included in this model
268
+ def find_index_by_model(model)
269
+ if info = id_to_module.find { |info| info.this == model }
270
+ return create_index(info.row, 0, info.id)
271
+ end
272
+ end
273
+
274
+ # Returns the Qt::ModelIndex that matches a given path
275
+ #
276
+ # @param [Array<String>] path path to the desired object
277
+ # @return [Qt::ModelIndex,nil] the index, or nil if the path does
278
+ # not resolve to an object included in this model
279
+ def find_index_by_path(*path)
280
+ current = id_to_module.last
281
+ if path.first == current.name
282
+ path.shift
283
+ end
284
+
285
+ path.each do |name|
286
+ current = id_to_module.find do |info|
287
+ info.name == name && info.parent == current
288
+ end
289
+ return if !current
290
+ end
291
+ create_index(current.row, 0, current.id)
292
+ end
293
+
294
+ # @api private
295
+ #
296
+ # Lazily compute a comma-separated string that can be used to search
297
+ # for the given node. The result is stored in
298
+ # {ModuleInfo#keyword_string}
299
+ #
300
+ # The returned string is of the form
301
+ # type0[,type1...]:name0[,name1...]
302
+ #
303
+ # @param [ModuleInfo] info
304
+ # @return [String]
176
305
  def compute_keyword_string(info)
177
306
  if keywords = info.keyword_string
178
307
  return keywords
@@ -186,6 +315,15 @@ module MetaRuby
186
315
  end
187
316
  end
188
317
 
318
+ # Reimplemented for Qt model interface
319
+ def headerData(section, orientation, role)
320
+ if role == Qt::DisplayRole && section == 0
321
+ Qt::Variant.new(title)
322
+ else Qt::Variant.new
323
+ end
324
+ end
325
+
326
+ # Reimplemented for Qt model interface
189
327
  def data(index, role)
190
328
  if info = info_from_index(index)
191
329
  if role == Qt::DisplayRole
@@ -199,6 +337,7 @@ module MetaRuby
199
337
  return Qt::Variant.new
200
338
  end
201
339
 
340
+ # Reimplemented for Qt model interface
202
341
  def index(row, column, parent)
203
342
  if info = info_from_index(parent)
204
343
  if child_info = info.children[row]
@@ -208,6 +347,7 @@ module MetaRuby
208
347
  Qt::ModelIndex.new
209
348
  end
210
349
 
350
+ # Reimplemented for Qt model interface
211
351
  def parent(child)
212
352
  if info = info_from_index(child)
213
353
  if info.parent && info.parent != root_info
@@ -217,6 +357,7 @@ module MetaRuby
217
357
  Qt::ModelIndex.new
218
358
  end
219
359
 
360
+ # Reimplemented for Qt model interface
220
361
  def rowCount(parent)
221
362
  if info = info_from_index(parent)
222
363
  info.children.size
@@ -224,38 +365,10 @@ module MetaRuby
224
365
  end
225
366
  end
226
367
 
368
+ # Reimplemented for Qt model interface
227
369
  def columnCount(parent)
228
370
  return 1
229
371
  end
230
-
231
- def info_from_index(index)
232
- if !index.valid?
233
- return id_to_module.last
234
- else
235
- id_to_module[index.internal_id >> 1]
236
- end
237
- end
238
-
239
- def find_index_by_model(model)
240
- if info = id_to_module.find { |info| info.this == model }
241
- return create_index(info.row, 0, info.id)
242
- end
243
- end
244
-
245
- def find_index_by_path(*path)
246
- current = id_to_module.last
247
- if path.first == current.name
248
- path.shift
249
- end
250
-
251
- path.each do |name|
252
- current = id_to_module.find do |info|
253
- info.name == name && info.parent == current
254
- end
255
- return if !current
256
- end
257
- create_index(current.row, 0, current.id)
258
- end
259
372
  end
260
373
  end
261
374
  end