class Descriptor

A descriptor is a rose tree with arbitrary properties at each node, used to define GUIs declaratively.

Descriptors can be created using a DSL. For example:

ex1 = Descriptor.build(:root, :name => 'parent') do
  child :name => 'foo'
  child :name => 'bar'
  merge_point
  child :name => 'hello' do
    grandchild :name => 'world'
  end
end

creates a tree which has a node with no name and three children with names ‘foo’, ‘bar’, and ‘hello’, and hello having a child of its own, called ‘world’. Note that descriptor tags (:root, :child and :grandchild in the example) are completely arbitrary, but they play a special role when merging, together with the :name property.

Merging consists of taking two descriptor trees, and matching their roots by tag and name. If they match, their children are recursively matched and merged, or simply concatenated when no match is found.

For example, if ex1 above is merged with the following descriptor:

ex2 = Descriptor.build(:root, :name => 'parent') do
  child :name => 'foo2'
  child :name => 'hello' do
    grandchild :name => 'world2'
  end
end

the resulting descriptor would be equivalent to the one created by:

ex1_merged_with_ex2 = Descriptor.build(:root, :name => 'parent') do
  child :name => 'foo'
  child :name => 'bar'
  child :name => 'foo2'
  child :name => 'hello' do
    grandchild :name => 'world'
    grandchild :name => 'world2'
  end
end

As can be seen in the example, merge points can be used to specify exactly where children of merged descriptors should be inserted.

Merge points can optionally have a count, which specifies the number of children to be inserted on that particular point. When the count is satisfied, additional children are added at the following merge point, or, if no more merge points exist, at the bottom.

Attributes

children[R]
opts[R]
tag[R]

Public Class Methods

build(tag, opts = { }, &blk) click to toggle source

Create a descriptor using the DSL. @param tag [Symbol] descriptor tag @param opts [Hash] arbitrary hash of properties @return [Descriptor]

# File lib/rui/descriptor.rb, line 73
def self.build(tag, opts = { }, &blk)
  root = new(tag, opts)
  builder = Builder.new(root)
  builder.instance_eval(&blk) if block_given?
  root
end
new(tag, opts = { }) click to toggle source

Create a descriptor with no children. @param tag [Symbol] descriptor tag @param opts [Hash] arbitrary hash of properties

# File lib/rui/descriptor.rb, line 85
def initialize(tag, opts = { })
  @tag = tag
  @opts = opts
  @children = []
end

Public Instance Methods

add_child(desc) click to toggle source

Add a child to this descriptor.

# File lib/rui/descriptor.rb, line 94
def add_child(desc)
  @children << desc
end
add_merge_point(position, count = -1) click to toggle source

Add a merge point to this descriptor. Newly added merge points will not affect existing children, even if they were added with merge_child @param position merge point position @param count maximum number of children that can be merged at this point.

If negative, no limit on the number of mergeable children is set.
# File lib/rui/descriptor.rb, line 118
def add_merge_point(position, count = -1)
  mp = MergePoint.new(position, count)
  @opts[:merge_points] ||= MergePoint::List.new
  @opts[:merge_points].add(mp)
  mp
end
merge!(other) click to toggle source

Destructively merge this descriptor with another.

Descriptors are merged if they match by tag and name, or if this descriptor has tag :group and the other one has a property :group set to the name of this descriptor.

@param other the descriptor to be merged @return [Boolean] whether the merge was successful

# File lib/rui/descriptor.rb, line 143
def merge!(other)
  if tag == other.tag and
      opts[:name] == other.opts[:name]
    # if roots match
    other.children.each do |child2|
      # merge each of the children of the second descriptor
      merged = false
      children.each do |child|
        # try to match with any of the children of the first descriptor
        if child.merge!(child2)
          merged = true
          break
        end
      end
      # if no match is found, just add it as a child of the root
      merge_child(child2.dup) unless merged
    end
    true
  elsif tag == :group and other.opts[:group] == opts[:name]
    # if the root is the group of the second descriptor, add it as a child
    merge_child(other)
  else
    false
  end
end
merge_child(desc) click to toggle source

Add a child to this descriptor, taking merge points into account.

# File lib/rui/descriptor.rb, line 101
def merge_child(desc)
  mp = @opts[:merge_points].first if @opts[:merge_points]
  if mp
    @children.insert(mp.position, desc)
    @opts[:merge_points].step!
  else
    add_child(desc)
  end
end
to_sexp() click to toggle source

Convert this descriptor to a human readable sexp representation. Descriptor properties are printed as ruby hashes.

# File lib/rui/descriptor.rb, line 129
def to_sexp
  "(#{@tag} #{@opts.inspect}#{@children.map{|c| ' ' + c.to_sexp}.join})"
end