Class: TerminalLayout::RenderObject

Inherits:
Object
  • Object
show all
Includes:
EventEmitter
Defined in:
lib/terminal_layout.rb

Direct Known Subclasses

BlockRenderObject, InlineRenderObject, RenderTree

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from EventEmitter

#_callbacks, #emit, #on, #unsubscribe

Constructor Details

#initialize(box, parent:, content: nil, style: {x:nil, y:nil}, renderer: nil) ⇒ RenderObject

Returns a new instance of RenderObject.



16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/terminal_layout.rb', line 16

def initialize(box, parent:, content:nil, style:{x:nil, y:nil}, renderer:nil)
  @box = box
  @content = ANSIString.new(content)
  @children = []
  @parent = parent
  @renderer = renderer
  @style = style
  style[:x] || style[:x] = 0
  style[:y] || style[:y] = 0

  @box.update_computed(style)
end

Instance Attribute Details

#boxObject

Returns the value of attribute box.



14
15
16
# File 'lib/terminal_layout.rb', line 14

def box
  @box
end

#childrenObject

Returns the value of attribute children.



14
15
16
# File 'lib/terminal_layout.rb', line 14

def children
  @children
end

#contentObject

Returns the value of attribute content.



14
15
16
# File 'lib/terminal_layout.rb', line 14

def content
  @content
end

#parentObject

Returns the value of attribute parent.



14
15
16
# File 'lib/terminal_layout.rb', line 14

def parent
  @parent
end

#styleObject

Returns the value of attribute style.



14
15
16
# File 'lib/terminal_layout.rb', line 14

def style
  @style
end

Instance Method Details

#ending_x_for_current_yObject



51
52
53
54
55
56
57
58
# File 'lib/terminal_layout.rb', line 51

def ending_x_for_current_y
  children.map do |child|
    next unless child.float == :right
    next unless child.y && child.y <= @current_y && (child.y + child.height - 1) >= @current_y

    [child.x, width].min
  end.compact.min || self.width || @box.width
end

#heightObject



81
82
83
# File 'lib/terminal_layout.rb', line 81

def height
  style[:height]
end

#inspectObject



85
86
87
# File 'lib/terminal_layout.rb', line 85

def inspect
  to_s
end

#layoutObject



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/terminal_layout.rb', line 120

def layout
  self.children = []
  @current_x = 0
  @current_y = 0
  if @box.display == :block && @box.content.length > 0
    ending_x = ending_x_for_current_y
    available_width = ending_x - @current_x
    new_parent = Box.new(content: nil, style: @box.style.dup.merge(width: available_width))
    inline_box = Box.new(content: @box.content, style: {display: :inline})
    new_parent.children = [inline_box].concat @box.children
    children2crawl = [new_parent]
  else
    children2crawl = @box.children
  end

  children2crawl.each do |cbox|
    if cbox.display == :float
      next if cbox.width.to_i == 0

      render_object = layout_float cbox
      cbox.height = render_object.height

      next if cbox.height.to_i == 0

      self.children << render_object
    elsif cbox.display == :block
      if children.last && children.last.display == :inline && @current_x != 0
        @current_x = 0
        @current_y += 1
      end

      @current_x = starting_x_for_current_y
      available_width = ending_x_for_current_y - @current_x

      if cbox.width && cbox.width > available_width
        @current_y += 1
        @current_x = starting_x_for_current_y
        available_width = ending_x_for_current_y - @current_x
      end

      render_object = render_object_for(cbox, content:nil, style: {
        x: @current_x,
        y: @current_y,
        width: (cbox.width || available_width)
      })
      render_object.layout

      if cbox.height
        render_object.height = cbox.height
      end

      next if [nil, 0].include?(render_object.width) || [nil, 0].include?(render_object.height)

      @current_x = 0
      @current_y += [render_object.height, 1].max

      self.children << render_object
    elsif cbox.display == :inline
      @current_x = starting_x_for_current_y if @current_x == 0
      available_width = ending_x_for_current_y - @current_x

      content_i = 0
      content = ""

      loop do
        partial_content = cbox.content[content_i...(content_i + available_width)]
        chars_needed = partial_content.length
        self.children << render_object_for(
          cbox,
          content:partial_content,
          style: {
            display: :inline,
            x: @current_x,
            y: @current_y,
            width:chars_needed,
            height:1
          }
        )

        content_i += chars_needed

        if chars_needed >= available_width
          @current_y += 1
          @current_x = starting_x_for_current_y
          available_width = ending_x_for_current_y - @current_x
        elsif chars_needed == 0
          break
        else
          @current_x += chars_needed
        end

        break if content_i >= cbox.content.length
      end
    end
  end

  if !height
    if children.length >= 2
      last_child = children.max{ |child| child.y }
      self.height = last_child.y + last_child.height
    elsif children.length == 1
      self.height = self.children.first.height
    else
      self.height = @box.height || 0
    end
  end

  self.children.each do |child|
    child.box.computed[:x] += x
    child.box.computed[:y] += y
  end

  self.children
end

#layout_float(fbox) ⇒ Object



235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
# File 'lib/terminal_layout.rb', line 235

def layout_float(fbox)
  # only allow the float to be as wide as its parent
  # - first check is the box itself, was it assigned a width?
  if @box.width && fbox.width > width
    fbox.width = width
  end

  if fbox.float == :left
    # if we cannot fit on this line, go to the next
    if @current_x + fbox.width > width
      @current_x = 0
      @current_y += 1
    end

    fbox.x = @current_x
    fbox.y = @current_y

    render_object = render_object_for(fbox, content: fbox.content, style: {height: fbox.height})
    render_object.layout

    @current_x += fbox.width
    return render_object
  elsif fbox.float == :right
    # loop in case there are left floats on the left as we move down rows
    loop do
      starting_x = starting_x_for_current_y
      available_width = ending_x_for_current_y - starting_x

      # if we cannot fit on this line, go to the next
      width_needed = fbox.width
      if width_needed > available_width
        @current_x = 0
        @current_y += 1
      else
        break
      end
    end

    @current_x = ending_x_for_current_y - fbox.width
    fbox.x = @current_x
    fbox.y = @current_y

    render_object = render_object_for(fbox, content: fbox.content, style: {height: fbox.height})
    render_object.layout

    # reset X back to what it should be
    @current_x = starting_x_for_current_y
    return render_object
  end
end

#offsetObject



29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/terminal_layout.rb', line 29

def offset
  offset_x = self.x
  offset_y = self.y
  _parent = @parent
  loop do
    break unless _parent
    offset_x += _parent.x
    offset_y += _parent.y
    _parent = _parent.parent
  end
  Position.new(offset_x, offset_y)
end

#positionObject



69
70
71
# File 'lib/terminal_layout.rb', line 69

def position
  Position.new(x, y)
end

#renderObject



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/terminal_layout.rb', line 97

def render
  # Rather than worry about a 2-dimensional space we're going to cheat
  # and convert everything to a single point.
  result = height.times.map { |n| (" " * width) }.join
  result = ANSIString.new(result)

  if content && content.length > 0
    result[0...content.length] = content.dup.to_s
  end

  children.each do |child|
    rendered_content = child.render

    # Find the single point where this child's content should be placed.
    #  (child.y * width): make sure we take into account the row we're on
    #  plus (child.y): make sure take into account the number of newlines
    x = child.x + (child.y * width)
    result[x...(x+rendered_content.length)] = rendered_content
  end

  result
end

#render_object_for(cbox, content: nil, style: {}) ⇒ Object



286
287
288
289
290
291
292
293
294
295
# File 'lib/terminal_layout.rb', line 286

def render_object_for(cbox, content:nil, style:{})
  case cbox.display
  when :block
    BlockRenderObject.new(cbox, parent: self, content: content, style: {width:@box.width}.merge(style), renderer:@renderer)
  when :inline
    InlineRenderObject.new(cbox, parent: self, content: content, style: style, renderer:@renderer)
  when :float
    FloatRenderObject.new(cbox, parent: self, content: content, style: {x: @current_x, y: @current_y, float: cbox.float}.merge(style), renderer:@renderer)
  end
end

#sizeObject



73
74
75
# File 'lib/terminal_layout.rb', line 73

def size
  Dimension.new(width, height)
end

#starting_x_for_current_yObject



42
43
44
45
46
47
48
49
# File 'lib/terminal_layout.rb', line 42

def starting_x_for_current_y
  children.map do |child|
    next unless child.float == :left || child.display == :inline
    next unless child.y && child.y <= @current_y && (child.y + child.height - 1) >= @current_y

    [child.x + child.width, x].max
  end.compact.max || 0
end

#to_sObject



93
94
95
# File 'lib/terminal_layout.rb', line 93

def to_s
  "<#{self.class.name} position=(#{x},#{y}) dimensions=#{width}x#{height} content=#{content} name=#{@box.name}/>"
end

#to_strObject



89
90
91
# File 'lib/terminal_layout.rb', line 89

def to_str
  to_s
end

#widthObject



77
78
79
# File 'lib/terminal_layout.rb', line 77

def width
  style[:width]
end