Class: MetaRuby::GUI::ModelSelector
- Inherits:
-
Qt::Widget
- Object
- Qt::Widget
- MetaRuby::GUI::ModelSelector
- Defined in:
- lib/metaruby/gui/model_selector.rb
Overview
A Qt widget based on RubyConstantsItemModel to browse a set of models, and display them when the user selects one
Defined Under Namespace
Classes: ModelPathCompleter
Instance Attribute Summary collapse
-
#browser_model ⇒ ModelHierarchy
readonly
The Qt item model that represents the object hierarchy.
-
#btn_type_filter_menu ⇒ Qt::PushButton
readonly
A button allowing to filter models by type.
-
#filter_box ⇒ Qt::LineEdit
readonly
The line edit widget that allows to modify the tree view filter.
-
#filter_completer ⇒ Qt::Completer
readonly
Auto-completion for #filter_box.
-
#model_filter ⇒ Qt::SortFilterProxyModel
readonly
Qt model filter.
-
#model_list ⇒ Qt::TreeView
readonly
The view that shows the object hierarchy.
-
#type_filters ⇒ Hash<Module,Qt::Action>
readonly
A per-type matching of the type to the actio that allows to filter/unfilter on this type.
-
#type_info ⇒ Hash<Object,String>
readonly
A mapping from a root model and the user-visible name for this root.
Instance Method Summary collapse
-
#all_leaves(model, limit = nil, item = Qt::ModelIndex.new, result = []) ⇒ Object
private
Helper method for #select_first_item.
-
#auto_open(threshold = 5) ⇒ Object
Auto-open in the current state.
-
#current_selection ⇒ RubyModuleModel::ModuleInfo?
Returns the currently selected item.
- #dump_filtered_item_model(parent = Qt::ModelIndex.new, indent = "") ⇒ Object
- #filter_row_count(parent = Qt::ModelIndex.new) ⇒ Object
-
#find_resolver_from_model(model) ⇒ Object?
Find the resolver object that has been responsible for a given object’s discovery.
-
#initialize(parent = nil) ⇒ ModelSelector
constructor
Create a new widget with an optional parent.
-
#map_index_from_source(source_index, reset_filter: true) ⇒ Qt::ModelIndex
Maps a model index from the source index to the filtered index, e.g.
- #model_item_from_filter_row(row, parent = Qt::ModelIndex.new) ⇒ Object
- #model_items_from_filter(parent = Qt::ModelIndex.new) ⇒ Object
- #object_paths ⇒ Object
-
#register_type(root_model, name, priority = 0, categories: [], resolver: ModelHierarchy::Resolver.new) ⇒ Object
Register a new object type.
-
#reload ⇒ Object
Reload the object model, keeping the current selection if possible.
-
#reset_filter ⇒ Object
Resets the current filter.
-
#select_by_model(model) ⇒ Boolean
Selects the given model if it registered in the model list This emits the model_selected signal.
-
#select_by_path(*path) ⇒ Boolean
Selects the current model given a path in the constant names This emits the model_selected signal.
-
#select_first_item ⇒ Object
Select the first displayed item.
-
#setup_tree_view(layout) ⇒ Object
private
Create and setup #model_list.
-
#update ⇒ Object
Update the view, reloading the underlying model.
-
#update_model_filter ⇒ Object
private
Update #model_filter to match the current filter setup.
Constructor Details
#initialize(parent = nil) ⇒ ModelSelector
Create a new widget with an optional parent
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/metaruby/gui/model_selector.rb', line 43 def initialize(parent = nil) super @type_info = Hash.new @browser_model = ModelHierarchy.new @type_filters = Hash.new layout = Qt::VBoxLayout.new(self) = Qt::PushButton.new('Filters', self) layout.() @btn_type_filter_menu = Qt::Menu.new . = setup_tree_view(layout) setTabOrder(filter_box, ) setTabOrder(, model_list) end |
Instance Attribute Details
#browser_model ⇒ ModelHierarchy (readonly)
The Qt item model that represents the object hierarchy
29 30 31 |
# File 'lib/metaruby/gui/model_selector.rb', line 29 def browser_model @browser_model end |
#btn_type_filter_menu ⇒ Qt::PushButton (readonly)
Returns a button allowing to filter models by type.
33 34 35 |
# File 'lib/metaruby/gui/model_selector.rb', line 33 def @btn_type_filter_menu end |
#filter_box ⇒ Qt::LineEdit (readonly)
Returns the line edit widget that allows to modify the tree view filter.
36 37 38 |
# File 'lib/metaruby/gui/model_selector.rb', line 36 def filter_box @filter_box end |
#filter_completer ⇒ Qt::Completer (readonly)
Returns auto-completion for #filter_box.
38 39 40 |
# File 'lib/metaruby/gui/model_selector.rb', line 38 def filter_completer @filter_completer end |
#model_filter ⇒ Qt::SortFilterProxyModel (readonly)
Qt model filter
19 20 21 |
# File 'lib/metaruby/gui/model_selector.rb', line 19 def model_filter @model_filter end |
#model_list ⇒ Qt::TreeView (readonly)
The view that shows the object hierarchy
15 16 17 |
# File 'lib/metaruby/gui/model_selector.rb', line 15 def model_list @model_list end |
#type_filters ⇒ Hash<Module,Qt::Action> (readonly)
A per-type matching of the type to the actio that allows to filter/unfilter on this type
10 11 12 |
# File 'lib/metaruby/gui/model_selector.rb', line 10 def type_filters @type_filters end |
#type_info ⇒ Hash<Object,String> (readonly)
A mapping from a root model and the user-visible name for this root
25 26 27 |
# File 'lib/metaruby/gui/model_selector.rb', line 25 def type_info @type_info end |
Instance Method Details
#all_leaves(model, limit = nil, item = Qt::ModelIndex.new, result = []) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Helper method for #select_first_item
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/metaruby/gui/model_selector.rb', line 185 def all_leaves(model, limit = nil, item = Qt::ModelIndex.new, result = []) if !model.hasChildren(item) result << item return result end row, child_item = 0, model.index(0, 0, item) while child_item.valid? all_leaves(model, limit, child_item, result) if limit && result.size == limit return result end row += 1 child_item = model.index(row, 0, item) end return result end |
#auto_open(threshold = 5) ⇒ Object
Auto-open in the current state
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/metaruby/gui/model_selector.rb', line 150 def auto_open(threshold = 5) current_level = [Qt::ModelIndex.new] while !current_level.empty? count = current_level.inject(0) do |total, index| total + model_filter.rowCount(index) end close_this_level = (count > threshold) current_level.each do |index| model_filter.rowCount(index).times.each do |row| model_list.setExpanded(model_filter.index(row, 0, index), !close_this_level) end end return if close_this_level last_level, current_level = current_level, [] last_level.each do |index| model_filter.rowCount(index).times.each do |row| current_level << model_filter.index(row, 0, index) end end end end |
#current_selection ⇒ RubyModuleModel::ModuleInfo?
Returns the currently selected item
322 323 324 325 326 327 328 |
# File 'lib/metaruby/gui/model_selector.rb', line 322 def current_selection index = model_list.selection_model.current_index if index.valid? index = model_filter.map_to_source(index) browser_model.find_model_from_index(index) end end |
#dump_filtered_item_model(parent = Qt::ModelIndex.new, indent = "") ⇒ Object
138 139 140 141 142 143 144 |
# File 'lib/metaruby/gui/model_selector.rb', line 138 def dump_filtered_item_model(parent = Qt::ModelIndex.new, indent = "") model_items_from_filter(parent).each_with_index do |(model_item, filter_index), i| data = model_item.data(Qt::UserRole).to_string puts "#{indent}[#{i}] #{model_item.text} #{data}" dump_filtered_item_model(filter_index, indent + " ") end end |
#filter_row_count(parent = Qt::ModelIndex.new) ⇒ Object
122 123 124 |
# File 'lib/metaruby/gui/model_selector.rb', line 122 def filter_row_count(parent = Qt::ModelIndex.new) model_filter.row_count(parent) end |
#find_resolver_from_model(model) ⇒ Object?
Find the resolver object that has been responsible for a given object’s discovery
89 90 91 |
# File 'lib/metaruby/gui/model_selector.rb', line 89 def find_resolver_from_model(model) @browser_model.find_resolver_from_model(model) end |
#map_index_from_source(source_index, reset_filter: true) ⇒ Qt::ModelIndex
Maps a model index from the source index to the filtered index, e.g. to select something in the view.
In addition to the normal map_from_source call, it allows to control whether the filter should be reset if the index given as parameter is currently filtered out
279 280 281 282 283 284 285 286 287 288 289 290 291 |
# File 'lib/metaruby/gui/model_selector.rb', line 279 def map_index_from_source(source_index, reset_filter: true) index = model_filter.map_from_source(source_index) if !index return elsif !index.valid? if !reset_filter return index end self.reset_filter model_filter.map_from_source(source_index) else index end end |
#model_item_from_filter_row(row, parent = Qt::ModelIndex.new) ⇒ Object
132 133 134 135 136 |
# File 'lib/metaruby/gui/model_selector.rb', line 132 def model_item_from_filter_row(row, parent = Qt::ModelIndex.new) filter_index = model_filter.index(row, 0, parent) model_index = model_filter.map_to_source(filter_index) return browser_model.item_from_index(model_index), filter_index end |
#model_items_from_filter(parent = Qt::ModelIndex.new) ⇒ Object
126 127 128 129 130 |
# File 'lib/metaruby/gui/model_selector.rb', line 126 def model_items_from_filter(parent = Qt::ModelIndex.new) (0...filter_row_count(parent)).map do |i| model_item_from_filter_row(i, parent) end end |
#object_paths ⇒ Object
330 331 332 |
# File 'lib/metaruby/gui/model_selector.rb', line 330 def object_paths browser_model.object_paths end |
#register_type(root_model, name, priority = 0, categories: [], resolver: ModelHierarchy::Resolver.new) ⇒ Object
Register a new object type
69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/metaruby/gui/model_selector.rb', line 69 def register_type(root_model, name, priority = 0, categories: [], resolver: ModelHierarchy::Resolver.new) @browser_model.add_root(root_model, priority, categories: categories, resolver: resolver) type_info[root_model] = name action = Qt::Action.new(name, self) action.checkable = true action.checked = true type_filters[root_model] = action .add_action(action) connect(action, SIGNAL('triggered()')) do update_model_filter end end |
#reload ⇒ Object
Reload the object model, keeping the current selection if possible
246 247 248 249 250 251 252 253 254 255 256 257 |
# File 'lib/metaruby/gui/model_selector.rb', line 246 def reload if current_model = current_selection current_path = @browser_model.find_path_from_model(current_model) end browser_model.reload if current_path select_by_path(*current_path) elsif current_model select_by_model(current_model) end end |
#reset_filter ⇒ Object
Resets the current filter
260 261 262 263 264 265 266 |
# File 'lib/metaruby/gui/model_selector.rb', line 260 def reset_filter # If there is a filter, reset it and try again if !filter_box.text.empty? filter_box.text = "" true end end |
#select_by_model(model) ⇒ Boolean
Selects the given model if it registered in the model list This emits the model_selected signal
311 312 313 314 315 316 317 |
# File 'lib/metaruby/gui/model_selector.rb', line 311 def select_by_model(model) if index = browser_model.find_index_by_model(model) index = map_index_from_source(index) model_list.current_index = index true end end |
#select_by_path(*path) ⇒ Boolean
Selects the current model given a path in the constant names This emits the model_selected signal
298 299 300 301 302 303 304 |
# File 'lib/metaruby/gui/model_selector.rb', line 298 def select_by_path(*path) if index = browser_model.find_index_by_path(*path) index = map_index_from_source(index) model_list.current_index = index true end end |
#select_first_item ⇒ Object
Select the first displayed item
204 205 206 207 208 |
# File 'lib/metaruby/gui/model_selector.rb', line 204 def select_first_item if item = all_leaves(model_filter, 1).first model_list.setCurrentIndex(item) end end |
#setup_tree_view(layout) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Create and setup #model_list
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
# File 'lib/metaruby/gui/model_selector.rb', line 213 def setup_tree_view(layout) @model_list = Qt::TreeView.new(self) @model_filter = Qt::SortFilterProxyModel.new model_filter.filter_case_sensitivity = Qt::CaseInsensitive model_filter.filter_role = Qt::UserRole model_filter.dynamic_sort_filter = true model_filter.source_model = browser_model model_filter.sort(0) model_list.model = model_filter @filter_box = Qt::LineEdit.new(self) filter_box.connect(SIGNAL('textChanged(QString)')) do |text| update_model_filter end filter_box.connect(SIGNAL('returnPressed()')) do |text| select_first_item end @filter_completer = ModelPathCompleter.new(browser_model, self) filter_completer.case_sensitivity = Qt::CaseInsensitive filter_box.completer = filter_completer layout.(filter_box) layout.(model_list) model_list.selection_model.connect(SIGNAL('currentChanged(const QModelIndex&, const QModelIndex&)')) do |index, _| index = model_filter.map_to_source(index) if model = browser_model.find_model_from_index(index) emit model_selected(Qt::Variant.from_ruby(model, model)) end end end |
#update ⇒ Object
Update the view, reloading the underlying model
83 84 85 86 |
# File 'lib/metaruby/gui/model_selector.rb', line 83 def update reload update_model_filter end |
#update_model_filter ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Update #model_filter to match the current filter setup
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/metaruby/gui/model_selector.rb', line 96 def update_model_filter type_rx = type_filters.map do |model_base, act| if act.checked? type_info[model_base] end end type_rx = type_rx.compact.join(",|,") model_filter.filter_role = Qt::UserRole # this contains the keywords (ancestry and/or name) # This workaround a problem that I did not have time to # investigate. Adding new characters to the filter updates the # browser just fine, while removing some characters does not # # This successfully resets the filter model_filter.filter_reg_exp = Qt::RegExp.new("") # The pattern has to match every element in the hierarchy. We # achieve this by making the suffix part optional name_rx = filter_box.text.downcase.gsub(/:+/, "/") name_rx = '[^;]*,[^,]*' + name_rx.split('/').join("[^,]*,[^;]*;[^;]*,") + '[^,]*,[^;]*' regexp = Qt::RegExp.new("(,#{type_rx},)[^;]*;([^;]*;)*#{name_rx}") regexp.case_sensitivity = Qt::CaseInsensitive model_filter.filter_reg_exp = regexp model_filter.invalidate auto_open end |