Class: Ruber::Pane

Inherits:
Qt::Widget
  • Object
show all
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

Instance Method Summary collapse

Methods included from Enumerable

#find!

Constructor Details

#initialize(view, parent = nil) ⇒ Pane #initialize(orientation, pane1, pane2, parent = nil) ⇒ Pane

Returns a new instance of Pane.

Overloads:

  • #initialize(view, parent = nil) ⇒ Pane

    Creates a Ruber::Pane containing a single view

    Parameters:

    • view (EditorView)

      the view to insert in the pane

    • parent (Qt::Widget, nil) (defaults to: nil)

      the parent widget

  • #initialize(orientation, pane1, pane2, parent = nil) ⇒ Pane

    Creates a Ruber::Pane containing two other panes in a splitter widget

    Parameters:

    • orientation (Integer)

      the orientation of the splitter. It can be @Qt::Horizontal@ or @Qt::Vertical@

    • pane1 (Pane)

      the pane to put in the first sector of the splitter

    • pane2 (Pane)

      the pane to put in the second sector of the splitter

    • parent (Qt::Widget, nil) (defaults to: nil)

      the parent widget



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.add_widget @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.add_widget @splitter
    insert_widget 0, pane1
    insert_widget 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.hide unless parent_pane
  layout.add_widget @label
end

Instance Attribute Details

#splitterQt::Splitter? (readonly)

Returns the splitter used by the pane or nil if the pane contains a single view.

Returns:

  • (Qt::Splitter, nil)

    the splitter used by the pane or nil if the pane contains a single view



185
186
187
# File 'lib/ruber/pane.rb', line 185

def splitter
  @splitter
end

Instance Method Details

#contain?(widget) ⇒ Boolean #contain?(widget, : directly) ⇒ Boolean

Whether the pane contains another pane or widget

Overloads:

  • #contain?(widget) ⇒ Boolean

    Whether the pane contains, directly or indirectly, another pane or widget

    Parameters:

    Returns:

    • (Boolean)

      true if widget is directly or indirectly contained in this pane and false if it isn’t contained in it

  • #contain?(widget, : directly) ⇒ Boolean

    Whether the pane directly contains another pane or widget

    Parameters:

    Returns:

    • (Boolean)

      true if widget is directly contained in this pane and false if it isn’t contained in it or it’s contained indirectly



132
133
134
135
136
137
138
139
# File 'lib/ruber/pane.rb', line 132

def contain? widget, mode = nil
  if mode
    if @splitter then @splitter.any?{|w| w == widget}
    else @view == widget
    end
  else find_children(Pane).include? widget
  end
end

#each_pane {|pane| ... } ⇒ Pane, Enumerator #each_pane(: recursive) {|pane| ... } ⇒ Pane, Enumerator

Iterates on child panes

Overloads:

  • #each_pane {|pane| ... } ⇒ Pane, Enumerator

    Iterates only on panes which are directly contained in this pane. Does nothing if the pane is in single view mode.

    Yield Parameters:

    • pane (Pane)

      a child pane

  • #each_pane(: recursive) {|pane| ... } ⇒ Pane, Enumerator

    Iterates on all contained panes (recursively). Does nothing if the pane is in single view mode.

    Yield Parameters:

    • pane (Pane)

      a child pane

Returns:

  • (Pane, Enumerator)

    if called with a block returns self, otherwise an Enumerator which iterates on the contained panes, acting recursively or not according to which of the two forms is called



310
311
312
313
314
315
316
317
318
319
320
321
# File 'lib/ruber/pane.rb', line 310

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

#each_view {|view| ... } ⇒ Pane, Enumerator 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.

Yield Parameters:

  • view (EditorView)

    a view contained (directly) in the pane

Returns:

  • (Pane, Enumerator)

    if a block is given then self, otherwise an Enumerator which iterates on all the views



413
414
415
416
417
418
419
420
421
422
# File 'lib/ruber/pane.rb', line 413

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

#labelString?

The text of label associated with the pane

Returns:

  • (String, nil)

    the text of the label associated with the pane or nil if the pane is in multiple view mode



394
395
396
397
398
# File 'lib/ruber/pane.rb', line 394

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

#label=(text) ⇒ Object

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.

Parameters:

  • text (String)

    the new label



384
385
386
# File 'lib/ruber/pane.rb', line 384

def label= text
  set_view_label @view, text if @view
end

#orientationInteger?

The orientation in which the pane is split

Returns:

  • (Integer, nil)

    @Qt::Horizontal@ or @Qt::Vertical@ according to the orientation of the splitter or nil if the pane contains a single view



211
212
213
# File 'lib/ruber/pane.rb', line 211

def orientation
  @splitter ? @splitter.orientation : nil
end

#panes(: recursive) ⇒ Array<Pane> #panesArray<Pane>

The panes contained in this pane

Overloads:

  • #panes(: recursive) ⇒ Array<Pane>

    Returns an array containing the panes contained in this pane, directly or indirectly. Returns an empty array if the pane is in single view mode.

    Returns:

    • (Array<Pane>)

      an array containing the panes contained in this pane, directly or indirectly. Returns an empty array if the pane is in single view mode

  • #panesArray<Pane>

    An array containing the panes directly contained in this pane. Returns an empty array if the pane is in single view mode

    Returns:

    • (Array<Pane>)

      an array containing the panes directly contained in this pane. Returns an empty array if the pane is in single view mode

Returns:



335
336
337
# File 'lib/ruber/pane.rb', line 335

def panes mode = :flat
  single_view? ? [] : each_pane(mode).to_a
end

#parent_panePane

Returns this pane’s containing pane.

Returns:

  • (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

#replace_view(old, replacement) ⇒ Boolean

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.

Parameters:

  • old (Qt::Widget)

    the view to replace

  • replacement (Qt::Widget)

    the view to insert in the pane in place of old

Returns:

  • (Boolean)

    true if the replacement was performed (that is, if the pane or one of its children contained old) and false otherwise



279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# File 'lib/ruber/pane.rb', line 279

def replace_view old, replacement
  if @view
    return false unless @view == old
    @view = replacement
    replacement.parent = self
    layout.insert_widget 0, replacement
    disconnect old, SIGNAL('closing(QWidget*)'), self, SLOT('remove_view(QWidget*)')
    connect replacement, SIGNAL('closing(QWidget*)'), self, SLOT('remove_view(QWidget*)')
    layout.remove_widget old
    old.parent = nil
    emit view_replaced(self, old, replacement)
    true
  else each_pane.any?{|pn| pn.replace_view old, replacement}
  end
end

#set_view_label(view, text) ⇒ Boolean

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.

Parameters:

  • view (Qt::Widget)

    the view to change the label for

  • text (String)

    the new label

Returns:

  • (Boolean)

    true if the label was changed either in this pane or in one of its children and false if neither this pane nor any of its children contain view



361
362
363
364
365
366
367
368
369
# File 'lib/ruber/pane.rb', line 361

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

#single_view?Boolean

Whether the pane contains a single view or not

Returns:

  • (Boolean)

    true if the pane contains a single view an false if it contains a splitter with multiple panes



193
194
195
# File 'lib/ruber/pane.rb', line 193

def single_view?
  @view.to_b
end

#split(view, new_view, orientation, pos = :after) ⇒ Array(Pane, Pane)?

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.

Parameters:

  • view (Ruber::EditorView)

    the view to split

  • new_view (Ruber::EditorView)

    the view to insert alongside view

  • orientation (Integer)

    whether the view should be split horizontally or vertically. It can be @Qt::Horizontal@ or @Qt::Vertical@

  • pos (Symbol) (defaults to: :after)

    whether new_view should be put after or before view. It can be @:after@ or @:before@

Returns:

  • (Array(Pane, Pane), nil)

    an array containing the panes associated with view and with new_view. If view wasn’t contained in the pane, nil is returned



243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
# File 'lib/ruber/pane.rb', line 243

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.widget idx
  keeping_focus old_pane do
    if @splitter.orientation == orientation
      idx += 1 if pos == :after
      insert_widget idx, new_pane
    else
      pane = old_pane.multiple_view_mode orientation
      new_idx = pos == :after ? 1 : 0
      old_pane.insert_widget new_idx, new_pane
      old_pane = pane
    end
  end
  emit pane_split self, view, new_view
  [old_pane, new_pane]
end

#viewEditorView?

The view contained in the pane

Returns:

  • (EditorView, nil)

    the view contained in the pane or nil if the pane contains multiple panes



202
203
204
# File 'lib/ruber/pane.rb', line 202

def view
  @view
end

#viewsArray<Qt::Widget>

Returns a list of all the views contained (directly or not) in the pane.

Returns:

  • (Array<Qt::Widget>)

    a list of all the views contained (directly or not) in the pane



429
430
431
# File 'lib/ruber/pane.rb', line 429

def views
  to_a
end