Class: Ruber::Pane
- Inherits:
-
Qt::Widget
- Object
- Qt::Widget
- Ruber::Pane
- Includes:
- Enumerable
- Defined in:
- lib/ruber/pane.rb
Overview
Container used to organize multiple editor views in a tab
A pane can either contain a single non-Pane widget (usually an EditorView)
or multiple Panes in a Qt::Splitter
. In the first case, the Pane is said
to be in single view mode, while in the second it’s said to be in multiple view
mode.
A Pane is said to be a direct child of (or directly contained in) another Pane if it is one of the widgets contained in the second Pane’s splitter. A non-Pane widget is said to be a direct child of (or directly contained in) a Pane if the pane is in single view mode and contains that widget or if it is in multiple view mode and one of the widgets in the splitter is in single view mode and contains that widget.
The most important method provided by this class is #split, which allows a view to be split either horizontally or vertically.
Whenever a view is closed, it is automatically removed from the pane, and panes are automatically rearranged. The only situation you should care for this is when the last view of a top-level pane is closed. In this case, the pane emits the #closing_last_view signal.
Note: the pane containing a given view (or pane) can change without warning, so
never store them. To access the pane directly containing another pane, use #parent_pane
on the child pane; to access the pane directly containing another widget, call
the widget’s parent
method.
Note: this class allows to access the splitter widget. This is only meant to allow to resize it. It must not be used to add or remove widgets to or from it.
Instance Attribute Summary (collapse)
-
- (Qt::Splitter?) splitter
readonly
The splitter used by the pane or nil if the pane contains a single view.
Instance Method Summary (collapse)
-
- (Boolean) contain?(widget, mode = nil)
Whether the pane contains another pane or widget.
-
- (Pane, Enumerator) each_pane(mode = :flat, &blk)
Iterates on child panes.
-
- (Pane, Enumerator) each_view {|view| ... }
(also: #each)
Iterates on all views contained in the pane.
-
- (nil) hideEvent(e)
protected
Override of
Qt::Widget#hideEvent
. -
- (Integer?) index_of_contained_view(view)
private
Returns the index of the pane containing given view in the splitter.
-
- (Pane) initialize(*args)
constructor
A new instance of Pane.
-
- (Pane) insert_widget(idx, widget)
protected
Inserts a widget in the splitter.
- - (Object) keeping_focus(*widgets) private
-
- (String?) label
The text of label associated with the pane.
-
- (Object) label(text)
Changes the label of the view in the pane.
-
- (Pane) multiple_view_mode(orientation)
protected
Switches the pane to multiple view mode.
-
- (Integer?) orientation
The orientation in which the pane is split.
-
- (Array<Pane>) panes(mode = :flat)
The panes contained in this pane.
-
- (Pane) parent_pane
This pane’s containing pane.
-
- (nil) remove_pane(pane)
slot
private
Removes the given pane from the pane.
-
- (nil) remove_view(view)
slot
private
Slot called when the single view contained in the pane is closed.
-
- (Boolean) replace_view(old, replacement)
Replaces a view with another.
-
- (Boolean) set_view_label(view, text)
Changes the text of the label for the given view.
-
- (nil) showEvent(e)
protected
Override of
Qt::Widget#showEvent
. -
- (Boolean) single_view?
Whether the pane contains a single view or not.
-
- (Pane) single_view_mode(view, label = '')
protected
Switches the pane to single view mode.
-
- (Array(Pane, Pane)?) split(view, new_view, orientation, pos = :after)
Splits the pane in two in correspondence to the given view.
-
- (Array(Pane, Pane)?) split_recursive(view, new_view, orientation, pos)
private
Redirects the call to #split to the pane which actually contains the pane containing the given view.
-
- (nil) take_pane(pane)
protected
Prepares the pane for a contained pane to be moved elsewhere.
-
- (EditorView?) view
The view contained in the pane.
-
- (Array<Qt::Widget>) views
A list of all the views contained (directly or not) in the pane.
Methods included from Enumerable
Signal Summary
-
- closing_last_view(QWidget* pane)
Signal emitted whenever the single view associated with the paned is about to be closed.
-
- removing_view(QWidget* pane, QWidget* view)
Signal emitted whenever a view in the pane or one of its children has been removed.
-
- about_to_hide(QWidget* pane)
Signal emitted just before the pane is hidden.
-
- about_to_show(QWidget* pane)
Signal emitted just before the pane is shown.
-
- pane_split(QWidget* pane, QWidget* old_view, QWidget* new_view)
Signal emitted after the pane has been split.
-
- view_replaced(QWidget* pane, QWidget* old_view, QWidget* replacement)
Signal emitted after a view has been replaced.
Constructor Details
- (Pane) initialize(view, parent = nil) - (Pane) initialize(orientation, pane1, pane2, parent = nil)
A new instance of Pane
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/ruber/pane.rb', line 75 def initialize *args case args.size when 1..2 super args[1] @view = args[0] @view.parent = self self.layout = Qt::VBoxLayout.new self layout. @view @splitter = nil connect view, SIGNAL('closing(QWidget*)'), self, SLOT('remove_view(QWidget*)') when 3..4 super args[3] self.layout = Qt::VBoxLayout.new self orientation, pane1, pane2 = args[0..3] @splitter = Qt::Splitter.new orientation, self layout. @splitter 0, pane1 1, pane2 @view = nil end margins = layout.contents_margins margins.top = margins.left = margins.bottom = margins.right = 0 layout.contents_margins = margins @label = Qt::Label.new '', self # Use the following three lines when attempting to debug issue number 10, as it # helps understanding which pane each label belongs to # color = Qt::Color.new(rand(256), rand(256), rand(256)) # @label.style_sheet = "background-color: #{color.name};" @label. unless parent_pane layout. @label end |
Instance Attribute Details
- (Qt::Splitter?) splitter (readonly)
The splitter used by the pane or nil if the pane contains a single view
205 206 207 |
# File 'lib/ruber/pane.rb', line 205 def splitter @splitter end |
Instance Method Details
- (Boolean) contain?(widget) - (Boolean) contain?(widget, :directly)
Whether the pane contains another pane or widget
132 133 134 135 136 137 138 139 |
# File 'lib/ruber/pane.rb', line 132 def contain? , mode = nil if mode if @splitter then @splitter.any?{|w| w == } else @view == end else find_children(Pane).include? end end |
- (Pane, Enumerator) each_pane {|pane| ... } - (Pane, Enumerator) each_pane(:recursive) {|pane| ... }
Iterates on child panes
330 331 332 333 334 335 336 337 338 339 340 341 |
# File 'lib/ruber/pane.rb', line 330 def each_pane mode = :flat, &blk return to_enum(:each_pane, mode) unless block_given? return self unless @splitter if mode == :flat then @splitter.each{|w| yield w} else @splitter.each do |w| yield w w.each_pane :recursive, &blk end end self end |
- (Pane, Enumerator) each_view {|view| ... } Also known as: each
Iterates on all views contained in the pane
This method always acts recursively, meaning that views indirectly contained in the pane are returned.
If the pane is in single view mode, that only view is passed to the block. If the only view has been closed, the block is not called.
433 434 435 436 437 438 439 440 441 442 |
# File 'lib/ruber/pane.rb', line 433 def each_view &blk return to_enum(:each_view) unless block_given? if single_view? and @view.parent then yield @view elsif !single_view? each_pane(:recursive) do |pn| yield pn.view if pn.single_view? end end self end |
- (nil) hideEvent(e) (protected)
Override of Qt::Widget#hideEvent
It emits the #about_to_hide signal if the event is not spontaneous
562 563 564 |
# File 'lib/ruber/pane.rb', line 562 def e emit about_to_hide(self) unless e.spontaneous end |
- (Integer?) index_of_contained_view(view) (private)
Returns the index of the pane containing given view in the splitter
If the pane is in single view mode, it returns 0 if the single view is view and nil otherwise.
If the pane is in multiple view mode, it returns the index of the pane directly containing the view in the splitter or nil none of the panes in the splitter directly contain view.
Note: this method is not recursive. This means that if the splitter of self
contains the pane A
, whose splitter contains the pane B
which contains view,
this method returns nil.
608 609 610 611 612 613 |
# File 'lib/ruber/pane.rb', line 608 def index_of_contained_view view if @splitter then @splitter.find_index{|w| w.view == view} elsif @view == view then 0 else nil end end |
- (Pane) insert_widget(idx, widget) (protected)
Inserts a widget in the splitter
If the widget is not a pane, it’ll be enclosed in the pane (and become a child of it). The #closing_last_view signal of the pane will be connected with the #remove_pane slot of self.
This method assumes that the pane is not in single view mode
486 487 488 489 490 491 492 493 494 |
# File 'lib/ruber/pane.rb', line 486 def idx, = Pane.new , @splitter unless .is_a? Pane @splitter. idx, connect , SIGNAL('closing_last_view(QWidget*)'), self, SLOT('remove_pane(QWidget*)') connect , SIGNAL('pane_split(QWidget*,QWidget*,QWidget*)'), self, SIGNAL('pane_split(QWidget*,QWidget*,QWidget*)') connect , SIGNAL('removing_view(QWidget*, QWidget*)'), self, SIGNAL('removing_view(QWidget*, QWidget*)') connect , SIGNAL('view_replaced(QWidget*,QWidget*,QWidget*)'), self, SIGNAL('view_replaced(QWidget*,QWidget*,QWidget*)') end |
- (Object) keeping_focus(*widgets) (private)
579 580 581 582 583 584 585 586 587 588 |
# File 'lib/ruber/pane.rb', line 579 def keeping_focus * active_window = .find{|w| w.is_active_window} = Ruber::Application. if active_window begin yield ensure if and !.disposed? .set_focus end end end |
- (String?) label
The text of label associated with the pane
414 415 416 417 418 |
# File 'lib/ruber/pane.rb', line 414 def label # For some reason, Qt::Label#text returns nil if the text hasn't been set # or is set to '' @view ? (@label.text || '') : nil end |
- (Object) label=(text)
Changes the label of the view in the pane
If the pane is in multiple view mode, nothing is done.
If the text is empty the label is hidden. If the text is not empty, the label is made visible, unless the pane is top-level.
This method is similar to #set_view_label, except that it doesn’t allow to specify the view to change the label for and always acts on the view contained in this pane.
404 405 406 |
# File 'lib/ruber/pane.rb', line 404 def label= text set_view_label @view, text if @view end |
- (Pane) multiple_view_mode(orientation) (protected)
Switches the pane to multiple view mode
It disconnects the view’s #closing signal from self, creates a new splitter and inserts a new pane for the view in it.
It does nothing if the pane is already in multiple view mode
507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 |
# File 'lib/ruber/pane.rb', line 507 def multiple_view_mode orientation return if @splitter keeping_focus @view do layout. @view @label. @view.disconnect SIGNAL('closing(QWidget*)'), self, SLOT('remove_view(QWidget*)') @splitter = Qt::Splitter.new orientation, self layout. 0, @splitter pane = 0, @view # For some reason, Qt::Label#text returns nil if the text hasn't been set # or is set to '' pane.label = @label.text || '' @view = nil pane end end |
- (Integer?) orientation
The orientation in which the pane is split
231 232 233 |
# File 'lib/ruber/pane.rb', line 231 def orientation @splitter ? @splitter.orientation : nil end |
- (Array<Pane>) panes(:recursive) - (Array<Pane>) panes
The panes contained in this pane
355 356 357 |
# File 'lib/ruber/pane.rb', line 355 def panes mode = :flat single_view? ? [] : each_pane(mode).to_a end |
- (Pane) parent_pane
This pane’s containing pane
110 111 112 113 114 115 |
# File 'lib/ruber/pane.rb', line 110 def parent_pane if parent.is_a?(Qt::Splitter) pane = parent.parent pane.is_a?(Pane) ? pane : nil end end |
- (nil) remove_pane(pane) (private)
Removes the given pane from the pane
Depending on the situation, removing a pane causes different steps to be taken:
- the pane is always removed from the splitter
- if only one pane remains and it’s in single view mode, it’ll be deleted and the widget it contain will be displayed in this pane, which will be switched to single view mode
- if only one pane remains and it’s in multiple view mode, the pane it contains will be moved to this pane instead. The remaining one will be removed Note: this method is usually called in response to the #closing_last_view signal emitted by a chid pane, so it assumes the pane is in single view mode.
672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 |
# File 'lib/ruber/pane.rb', line 672 def remove_pane pane pane.parent = nil if @splitter.count == 1 remaining_pane = @splitter.(0) if remaining_pane.single_view? single_view_mode remaining_pane.view, remaining_pane.label else take_pane remaining_pane remaining_pane.splitter.to_a.each_with_index do |w, i| remaining_pane.take_pane w i, w end end remaining_pane.delete_later end nil end |
Slot Signature:
remove_pane(QWidget*)
- (nil) remove_view(view) (private)
Slot called when the single view contained in the pane is closed
It emis the #closing_last_view signal passing self as argument, makes the view parentless and schedules self for deletion.
After this method as been called (in particular, in slots connected to the #remove_view signal), calls to #view will return nil.
Note: this method assumes the Ruber::Pane is in single view mode
647 648 649 650 651 652 653 654 |
# File 'lib/ruber/pane.rb', line 647 def remove_view view emit closing_last_view(self) @view.parent = nil @view = nil emit removing_view self, view delete_later nil end |
Slot Signature:
remove_view(QWidget*)
- (Boolean) replace_view(old, replacement)
Replaces a view with another
If the pane is in single view mode and its view is the same as old, the view is replaced with replacement. If the view contained in the pane is different from old, nothing is done.
If the pane is in multiple view mode, the method call will be propagated to all child panes, until one is able to carry out the replacement.
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 |
# File 'lib/ruber/pane.rb', line 299 def replace_view old, replacement if @view return false unless @view == old @view = replacement replacement.parent = self layout. 0, replacement disconnect old, SIGNAL('closing(QWidget*)'), self, SLOT('remove_view(QWidget*)') connect replacement, SIGNAL('closing(QWidget*)'), self, SLOT('remove_view(QWidget*)') layout. old old.parent = nil emit view_replaced(self, old, replacement) true else each_pane.any?{|pn| pn.replace_view old, replacement} end end |
- (Boolean) set_view_label(view, text)
Changes the text of the label for the given view
If the pane is in single view mode and the view given as argument is the same contained in it, the text of the label will be changed, otherwise nothing will be done. If the pane is not a toplevel pane the label will be also made visible. If the pane is a toplevel pane, the label won’t be shown. The rationale for this behaviour is that the label can be used to distinguish different widgets in the same pane. If a top-level pane is in single view mode, however, it contains no other views, so there’s no need for a label to distinguish them.
If the text is an empty string, the label will be hidden.
If the pane is in multiple view mode, the method call will be propagated recursively to child panes, until a pane containing the given view is found.
381 382 383 384 385 386 387 388 389 |
# File 'lib/ruber/pane.rb', line 381 def set_view_label view, text if single_view? return false unless @view == view @label.text = text @label.visible = !text.empty? if parent_pane true else each_pane.any? {|pn| pn.set_view_label view, text} end end |
- (nil) showEvent(e) (protected)
Override of Qt::Widget#showEvent
It emits the #about_to_show signal if the event is not spontaneous
573 574 575 |
# File 'lib/ruber/pane.rb', line 573 def showEvent e emit about_to_show(self) unless e.spontaneous end |
- (Boolean) single_view?
Whether the pane contains a single view or not
213 214 215 |
# File 'lib/ruber/pane.rb', line 213 def single_view? @view.to_b end |
- (Pane) single_view_mode(view, label = '') (protected)
Switches the pane to single view mode
It disconnects the #closing_last_view signal of each pane in the splitter from self, deletes the splitter then sets the given view as single view for self.
It does nothing if the splitter is already in single view mode
535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 |
# File 'lib/ruber/pane.rb', line 535 def single_view_mode view, label = '' return unless @splitter keeping_focus view do @view = view @view.parent = self unless @view.parent == self @splitter.each{|w| take_pane w} # @splitter.each{|w| w.disconnect SIGNAL('closing_last_view(QWidget*)'), self, SLOT('remove_pane(QWidget*)')} layout. @splitter layout. 0, @view self.label = label @label.visible = false unless parent_pane connect @view, SIGNAL('closing(QWidget*)'), self, SLOT('remove_view(QWidget*)') @splitter.delete_later @splitter = nil self end end |
- (Array(Pane, Pane)?) split(view, new_view, orientation, pos = :after)
Splits the pane in two in correspondence to the given view
The place previously occupated by the view is divided bewtween it and another view, new_view. If needed, other panes are created to accomodate them.
The view to split must already be contained in the pane. It can be contained directly, as the only view of the pane or inserted in the splitter contained in the pane, or indirectly, contained in one of the panes contained by this pane.
If view is contained indirectly, the method call will be redirected to the correct pane (not the one associated with the view but the pane containing the latter).
If view is not contained in this pane, nothing is done.
new_view must not be associated with a pane. When this method returns, a new pane for it will have been created.
Note: after calling this method, the pane associated with view may be changed.
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 |
# File 'lib/ruber/pane.rb', line 263 def split view, new_view, orientation, pos = :after idx = index_of_contained_view view return split_recursive view, new_view, orientation, pos unless idx new_pane = Pane.new(new_view) multiple_view_mode orientation old_pane = @splitter. idx keeping_focus old_pane do if @splitter.orientation == orientation idx += 1 if pos == :after idx, new_pane else pane = old_pane.multiple_view_mode orientation new_idx = pos == :after ? 1 : 0 old_pane. new_idx, new_pane old_pane = pane end end emit pane_split self, view, new_view [old_pane, new_pane] end |
- (Array(Pane, Pane)?) split_recursive(view, new_view, orientation, pos) (private)
Redirects the call to #split to the pane which actually contains the pane containing the given view
It calls #split with the arguments passed to it to all panes contained in the splitter until one of them returns non-nil, then stops and with new_view. If view wasn’t contained in any of the children panes, nil is returned
626 627 628 629 630 631 632 633 |
# File 'lib/ruber/pane.rb', line 626 def split_recursive view, new_view, orientation, pos return nil unless @splitter @splitter.each do |w| res = w.split view, new_view, orientation, pos return res if res end nil end |
- (nil) take_pane(pane) (protected)
Prepares the pane for a contained pane to be moved elsewhere
In practice, this makes the pane to remove parentless and disconnects the #closing_last_view signal from self.
463 464 465 466 467 468 469 470 |
# File 'lib/ruber/pane.rb', line 463 def take_pane pane pane.parent = nil pane.disconnect SIGNAL('closing_last_view(QWidget*)'), self, SLOT('remove_pane(QWidget*)') pane.disconnect SIGNAL('pane_split(QWidget*, QWidget*, QWidget*)'), self, SIGNAL('pane_split(QWidget*, QWidget*, QWidget*)') pane.disconnect SIGNAL('removing_view(QWidget*, QWidget*)'), self, SIGNAL('removing_view(QWidget*, QWidget*)') pane.disconnect SIGNAL('view_replaced(QWidget*,QWidget*,QWidget*)'), self, SIGNAL('view_replaced(QWidget*,QWidget*,QWidget*)') nil end |
- (EditorView?) view
The view contained in the pane
222 223 224 |
# File 'lib/ruber/pane.rb', line 222 def view @view end |
- (Array<Qt::Widget>) views
A list of all the views contained (directly or not) in the pane
449 450 451 |
# File 'lib/ruber/pane.rb', line 449 def views to_a end |
Signal Details
- closing_last_view(QWidget* pane)
Signal emitted whenever the single view associated with the paned is about to be closed
- removing_view(QWidget* pane, QWidget* view)
Signal emitted whenever a view in the pane or one of its children has been removed
In slots connected to this signal, calls to #view will return nil.
- about_to_hide(QWidget* pane)
Signal emitted just before the pane is hidden
The signal is only emitted if the pane is hidden because of a reason internal to
Ruber and won’t be emitted, for example, when Ruber itself is minimized or otherwise
hidden. To be more specific, it’s only emitted if the underlying Qt::HideEvent
is not spontaneous
- about_to_show(QWidget* pane)
Signal emitted just before the pane is shown
The signal is only emitted if the pane is show because of a reason internal to
Ruber and won’t be emitted, for example, when Ruber itself becomes visible. To be more specific, it’s only emitted if the underlying Qt::ShowEvent
is not spontaneous
- pane_split(QWidget* pane, QWidget* old_view, QWidget* new_view)
Signal emitted after the pane has been split
- view_replaced(QWidget* pane, QWidget* old_view, QWidget* replacement)
Signal emitted after a view has been replaced