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
Public Class Methods
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
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 a child to this descriptor.
# File lib/rui/descriptor.rb, line 94 def add_child(desc) @children << desc end
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
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
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
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