Class: Descriptor

Inherits:
Object show all
Defined in:
lib/rui/descriptor.rb

Overview

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.

Defined Under Namespace

Classes: Builder, MergePoint

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(tag, opts = { }) ⇒ Descriptor

Create a descriptor with no children.

Parameters:

  • tag (Symbol)

    descriptor tag

  • opts (Hash) (defaults to: { })

    arbitrary hash of properties



85
86
87
88
89
# File 'lib/rui/descriptor.rb', line 85

def initialize(tag, opts = { })
  @tag = tag
  @opts = opts
  @children = []
end

Instance Attribute Details

#childrenArray (readonly)

Returns children of this descriptor.

Returns:

  • (Array)

    children of this descriptor



65
66
67
# File 'lib/rui/descriptor.rb', line 65

def children
  @children
end

#optsHash (readonly)

Returns properties for this descriptor.

Returns:

  • (Hash)

    properties for this descriptor



64
65
66
# File 'lib/rui/descriptor.rb', line 64

def opts
  @opts
end

#tagSymbol (readonly)

Returns the descriptor tag.

Returns:

  • (Symbol)

    the descriptor tag



63
64
65
# File 'lib/rui/descriptor.rb', line 63

def tag
  @tag
end

Class Method Details

.build(tag, opts = { }, &blk) ⇒ Descriptor

Create a descriptor using the DSL.

Parameters:

  • tag (Symbol)

    descriptor tag

  • opts (Hash) (defaults to: { })

    arbitrary hash of properties

Returns:



73
74
75
76
77
78
# 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

Instance Method Details

#add_child(desc) ⇒ Object

Add a child to this descriptor.



94
95
96
# File 'lib/rui/descriptor.rb', line 94

def add_child(desc)
  @children << desc
end

#add_merge_point(position, count = -1)) ⇒ Object

Add a merge point to this descriptor. Newly added merge points will not affect existing children, even if they were added with merge_child

Parameters:

  • position

    merge point position

  • count (defaults to: -1))

    maximum number of children that can be merged at this point. If negative, no limit on the number of mergeable children is set.



118
119
120
121
122
123
# 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) ⇒ Boolean

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.

Parameters:

  • other

    the descriptor to be merged

Returns:

  • (Boolean)

    whether the merge was successful



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# 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) ⇒ Object

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



101
102
103
104
105
106
107
108
109
# 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_sexpObject

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



129
130
131
# File 'lib/rui/descriptor.rb', line 129

def to_sexp
  "(#{@tag} #{@opts.inspect}#{@children.map{|c| ' ' + c.to_sexp}.join})"
end