Class: YogaLayout::Node

Inherits:
Wrapper show all
Defined in:
lib/yoga_layout/node.rb

Overview

YogaLayout::Node is the main object you will be interfacing with when using Yoga in Ruby. YogaLayout::Node is a thin FFI wrapper around the core Yoga library.

Use Nodes to build up a tree that describes your layout. Set layout and style properties to define your layout constraints.

Once you have set up a tree of nodes with styles you will want to get the result of a layout calculation. Call #calculate_layout to perform layout calculation. Once this function returns the results of the layout calculation is stored on each node. Traverse the tree and retrieve the values from each node.

Instance Attribute Summary

Attributes inherited from Wrapper

#pointer

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Wrapper

#==, auto_ptr, #inspect, map_method

Constructor Details

#initialize(auto_ptr = nil, config = nil) ⇒ Node

Returns a new instance of Node.



53
54
55
56
57
58
59
60
61
62
# File 'lib/yoga_layout/node.rb', line 53

def initialize(auto_ptr = nil, config = nil)
  super(auto_ptr)

  @children = []
  @parent = nil
  @data = nil
  @measure_func = nil
  @baseline_func = nil
  @config = config
end

Class Method Details

.[](opts = {}) ⇒ Node

Quickly create a bunch of nodes with this handy node literal syntax.

Parameters:

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

    save for the :children option, all of these are passed to #set_styles

Options Hash (opts):

  • :children (Array<Node>) — default: []

    Children, in order, to add to this node.

Returns:



24
25
26
27
28
29
30
31
# File 'lib/yoga_layout/node.rb', line 24

def self.[](opts = {})
  children = opts.delete(:children) || []
  node = new.set_styles(opts)
  children.each_with_index do |child, idx|
    node.insert_child(child, idx)
  end
  node
end

.layout_propsObject



113
114
115
# File 'lib/yoga_layout/node.rb', line 113

def self.layout_props
  @layout_props ||= {}
end

.new_with_config(config) ⇒ YogaLayout::Node

Create a new Node with a specific config.

Parameters:

Returns:



37
38
39
# File 'lib/yoga_layout/node.rb', line 37

def self.new_with_config(config)
  new(auto_ptr(YogaLayout::Bindings.YGNodeNewWithConfig(config.pointer)), config)
end

.style_propsObject



109
110
111
# File 'lib/yoga_layout/node.rb', line 109

def self.style_props
  @style_props ||= {}
end

.unsafe_free_pointer(pointer) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



49
50
51
# File 'lib/yoga_layout/node.rb', line 49

def self.unsafe_free_pointer(pointer)
  YogaLayout::Bindings.YGNodeFree(pointer)
end

.unsafe_new_pointerObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



43
44
45
# File 'lib/yoga_layout/node.rb', line 43

def self.unsafe_new_pointer
  YogaLayout::Bindings.YGNodeNew
end

Instance Method Details

#calculate_layout(par_width = nil, par_height = nil, par_dir = nil) ⇒ Object



288
289
290
291
292
293
294
295
296
297
# File 'lib/yoga_layout/node.rb', line 288

def calculate_layout(par_width = nil, par_height = nil, par_dir = nil)
  undefined = ::Float::NAN
  YogaLayout::Bindings.YGNodeCalculateLayout(
    pointer,
    par_width || undefined,
    par_height || undefined,
    par_dir || :inherit
  )
  self
end

#get_baseline_funcObject



262
263
264
# File 'lib/yoga_layout/node.rb', line 262

def get_baseline_func
  @baseline_func
end

#get_child(idx) ⇒ Object



211
212
213
214
215
216
217
218
219
# File 'lib/yoga_layout/node.rb', line 211

def get_child(idx)
  ruby_child = @children[idx]
  child_pointer = YogaLayout::Bindings.YGNodeGetChild(pointer, idx)
  return nil if ruby_child.nil? && child_pointer.nil?
  unless ruby_child && ruby_child.pointer.address == child_pointer.address
    raise Error, "Ruby child #{ruby_child.inspect} (index #{idx}) does not wrap native child #{child_pointer}"
  end
  ruby_child
end

#get_measure_funcObject



247
248
249
# File 'lib/yoga_layout/node.rb', line 247

def get_measure_func
  @measure_func
end

#get_parentObject



223
224
225
226
227
228
229
230
231
# File 'lib/yoga_layout/node.rb', line 223

def get_parent
  ruby_parent = parent
  parent_pointer = YogaLayout::Bindings.YGNodeGetParent(pointer)
  return nil if ruby_parent.nil? && parent_pointer.nil?
  unless ruby_parent && ruby_parent.pointer == parent_pointer
    raise Error, "Ruby parent #{ruby_parent.inspect} does not wrap native parent #{parent_pointer}"
  end
  ruby_parent
end

#has_children?Boolean

Returns:

  • (Boolean)


284
285
286
# File 'lib/yoga_layout/node.rb', line 284

def has_children?
  get_child_count != 0
end

#has_measure_func?Boolean

Returns:

  • (Boolean)


280
281
282
# File 'lib/yoga_layout/node.rb', line 280

def has_measure_func?
  get_measure_func != nil
end

#insert_child(node, idx) ⇒ Object



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/yoga_layout/node.rb', line 183

def insert_child(node, idx)
  unless node.is_a?(self.class)
    raise TypeError, "Child #{node.inspect} must be a YogaLayout::Node"
  end

  if node.parent
    raise Error, "Child #{node.inspect} already has a parent, it must be removed first."
  end

  if has_measure_func?
    raise Error, 'Cannot add child: Nodes with measure functions cannot have children.'
  end

  YogaLayout::Bindings.YGNodeInsertChild(pointer, node.pointer, idx)
  @children.insert(idx, node)
  node.parent = self
  self
end

#layoutHash

Retrieve all layout information as a hash

Returns:

  • (Hash)


94
95
96
97
98
# File 'lib/yoga_layout/node.rb', line 94

def layout
  Hash[
    self.class.layout_props.map { |method, prop| [prop, public_send(method)] }
  ]
end

#mark_dirtyObject



266
267
268
269
270
271
272
273
274
# File 'lib/yoga_layout/node.rb', line 266

def mark_dirty
  unless has_measure_func?
    raise Error, 'Only leaf nodes with custom measure functions should manually mark themselves as diry'
  end

  YogaLayout::Bindings.YGNodeMarkDirty(pointer)

  self
end

#remove_child(node) ⇒ Object



202
203
204
205
206
207
208
209
# File 'lib/yoga_layout/node.rb', line 202

def remove_child(node)
  # If asked to remove a child that isn't a child, Yoga just does nothing, so this is okay
  YogaLayout::Bindings.YGNodeRemoveChild(pointer, node.pointer)
  if @children.delete(node)
    node.parent = nil
  end
  self
end

#resetObject



168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/yoga_layout/node.rb', line 168

def reset
  if has_children?
    raise Error, 'Cannot reset a node which still has children attached'
  end

  unless parent.nil?
    raise Error, 'Cannot reset a node still attached to a parent'
  end

  YogaLayout::Bindings.YGNodeReset(pointer)
  @measure_func = nil
  @baseline_func = nil
  @data = nil
end

#set_baseline_func(callable = nil, &block) ⇒ Object



251
252
253
254
255
256
257
258
259
260
# File 'lib/yoga_layout/node.rb', line 251

def set_baseline_func(callable = nil, &block)
  @baseline_func = callable || block
  if @baseline_func
    YogaLayout::Bindings.YGNodeSetBaselineFunc(pointer, native_baseline_func)
  else
    YogaLayout::Bindings.YGNodeSetBaselineFunc(pointer, nil)
  end

  self
end

#set_measure_func(callable = nil, &block) ⇒ Object



233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/yoga_layout/node.rb', line 233

def set_measure_func(callable = nil, &block)
  if has_children?
    raise Error, 'Cannot set measure function: Nodes with measure functions cannot have children.'
  end
  @measure_func = callable || block
  if @measure_func
    YogaLayout::Bindings.YGNodeSetMeasureFunc(pointer, native_measure_func)
  else
    YogaLayout::Bindings.YGNodeSetMeasureFunc(pointer, nil)
  end

  self
end

#set_styles(styles) ⇒ Node

Set many styles at once.

Uses ruby magic to make it easier to set styles.

Parameters:

  • Map (Hash<#to_s, any>)

    from style property to value

Returns:



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/yoga_layout/node.rb', line 70

def set_styles(styles)
  styles.each do |prop, value|
    method_name = "style_set_#{prop}"

    if respond_to?(method_name)
      if method(method_name).arity == 1
        # Handle eg, flex_direction: :row
        public_send(method_name, value)
      else
        # Handle eg, padding: 25
        public_send(method_name, :all, value)
      end
    else
      # handle eg, margin_top: 50
      method_name, _, edge = method_name.rpartition('_')
      public_send(method_name, edge.to_sym, value)
    end
  end
  self
end

#stylesHash

Retrieve all style information as a hash

Returns:

  • (Hash)


103
104
105
106
107
# File 'lib/yoga_layout/node.rb', line 103

def styles
  Hash[
    self.class.style_props.map { |method, prop| [prop, public_send(method)] }
  ]
end