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 ⇒ RubyConstantsItemModel
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 ⇒ Object
readonly
Returns the value of attribute type_info.
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.
-
#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?(obj) ⇒ Boolean
Tests if an object if a model.
- #object_paths ⇒ Object
-
#register_type(model_base, name, priority = 0) ⇒ 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_module(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
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/metaruby/gui/model_selector.rb', line 40 def initialize(parent = nil) super @type_info = Hash.new @browser_model = RubyConstantsItemModel.new(type_info) do |mod| model?(mod) end @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 ⇒ RubyConstantsItemModel (readonly)
The Qt item model that represents the object hierarchy
26 27 28 |
# File 'lib/metaruby/gui/model_selector.rb', line 26 def browser_model @browser_model end |
#btn_type_filter_menu ⇒ Qt::PushButton (readonly)
Returns a button allowing to filter models by type.
30 31 32 |
# File 'lib/metaruby/gui/model_selector.rb', line 30 def @btn_type_filter_menu end |
#filter_box ⇒ Qt::LineEdit (readonly)
Returns the line edit widget that allows to modify the tree view filter.
33 34 35 |
# File 'lib/metaruby/gui/model_selector.rb', line 33 def filter_box @filter_box end |
#filter_completer ⇒ Qt::Completer (readonly)
Returns auto-completion for #filter_box.
35 36 37 |
# File 'lib/metaruby/gui/model_selector.rb', line 35 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 ⇒ Object (readonly)
Returns the value of attribute type_info.
22 23 24 |
# File 'lib/metaruby/gui/model_selector.rb', line 22 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
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/metaruby/gui/model_selector.rb', line 158 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
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/metaruby/gui/model_selector.rb', line 115 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
299 300 301 302 303 304 305 |
# File 'lib/metaruby/gui/model_selector.rb', line 299 def current_selection index = model_list.selection_model.current_index if index.valid? index = model_filter.map_to_source(index) browser_model.info_from_index(index) end 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
256 257 258 259 260 261 262 263 264 265 266 267 268 |
# File 'lib/metaruby/gui/model_selector.rb', line 256 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 reset_filter model_filter.map_from_source(source_index) else index end end |
#model?(obj) ⇒ Boolean
Tests if an object if a model
139 140 141 142 143 144 |
# File 'lib/metaruby/gui/model_selector.rb', line 139 def model?(obj) type_info.any? do |model_base, _| obj.kind_of?(model_base) || (obj.kind_of?(Module) && obj <= model_base) end end |
#object_paths ⇒ Object
307 308 309 |
# File 'lib/metaruby/gui/model_selector.rb', line 307 def object_paths browser_model.object_paths end |
#register_type(model_base, name, priority = 0) ⇒ Object
Register a new object type
68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/metaruby/gui/model_selector.rb', line 68 def register_type(model_base, name, priority = 0) type_info[model_base] = RubyConstantsItemModel::TypeInfo.new(name, priority) action = Qt::Action.new(name, self) action.checkable = true action.checked = true type_filters[model_base] = 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
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 |
# File 'lib/metaruby/gui/model_selector.rb', line 219 def reload if current = current_selection current_module = current.this current_path = [] while current current_path.unshift current.name current = current.parent end end browser_model.reload if current_path && !select_by_path(*current_path) select_by_module(current_module) end end |
#reset_filter ⇒ Object
Resets the current filter
237 238 239 240 241 242 243 |
# File 'lib/metaruby/gui/model_selector.rb', line 237 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_module(model) ⇒ Boolean
Selects the given model if it registered in the model list This emits the model_selected signal
288 289 290 291 292 293 294 |
# File 'lib/metaruby/gui/model_selector.rb', line 288 def select_by_module(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
275 276 277 278 279 280 281 |
# File 'lib/metaruby/gui/model_selector.rb', line 275 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
177 178 179 180 181 |
# File 'lib/metaruby/gui/model_selector.rb', line 177 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
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/metaruby/gui/model_selector.rb', line 186 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.dynamic_sort_filter = true model_filter.filter_role = Qt::UserRole model_list.model = model_filter model_filter.source_model = browser_model @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) mod = browser_model.info_from_index(index) if model?(mod.this) emit model_selected(Qt::Variant.from_ruby(mod.this, mod.this)) end end end |
#update ⇒ Object
Update the view, reloading the underlying model
81 82 83 84 |
# File 'lib/metaruby/gui/model_selector.rb', line 81 def update update_model_filter reload 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
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/metaruby/gui/model_selector.rb', line 89 def update_model_filter type_rx = type_filters.map do |model_base, act| if act.checked? type_info[model_base].name 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(/:+/, "/") model_filter.filter_reg_exp = Qt::RegExp.new("(#{type_rx}).*;.*#{name_rx}") auto_open end |