Class: SilkLayout::Layout::BlockLayout

Inherits:
Object
  • Object
show all
Defined in:
lib/silk_layout/layout/block_layout.rb

Class Method Summary collapse

Class Method Details

.auto_content_width(box, available_width) ⇒ Object



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/silk_layout/layout/block_layout.rb', line 134

def self.auto_content_width(box, available_width)
  content_width =
    available_width -
    box.margin[:left] - box.margin[:right] -
    box.border[:left] - box.border[:right] -
    box.padding[:left] - box.padding[:right]

  constrained_width =
    if border_box_sizing?(box)
      constrain_css_width(box, content_width + horizontal_box_edges(box), available_width)
    else
      constrain_css_width(box, content_width, available_width)
    end

  content_width_from_css_width(box, constrained_width)
end

.border_box_sizing?(box) ⇒ Boolean

Returns:

  • (Boolean)


186
187
188
# File 'lib/silk_layout/layout/block_layout.rb', line 186

def self.border_box_sizing?(box)
  style_value(box, "box-sizing") == "border-box"
end

.constrain_css_width(box, width, available_width) ⇒ Object



151
152
153
154
155
156
157
158
# File 'lib/silk_layout/layout/block_layout.rb', line 151

def self.constrain_css_width(box, width, available_width)
  min_width = optional_width(box, "min-width", available_width)
  max_width = optional_width(box, "max-width", available_width)

  width = [width, min_width].max if min_width
  width = [width, max_width].min if max_width
  width
end

.content_height_from_css_height(box, height) ⇒ Object



180
181
182
183
184
# File 'lib/silk_layout/layout/block_layout.rb', line 180

def self.content_height_from_css_height(box, height)
  content_height = border_box_sizing?(box) ? height - vertical_box_edges(box) : height

  [content_height, 0].max
end

.content_width_from_css_width(box, width) ⇒ Object



167
168
169
170
171
# File 'lib/silk_layout/layout/block_layout.rb', line 167

def self.content_width_from_css_width(box, width)
  return width unless border_box_sizing?(box)

  width - horizontal_box_edges(box)
end

.declared_height(box) ⇒ Object



173
174
175
176
177
178
# File 'lib/silk_layout/layout/block_layout.rb', line 173

def self.declared_height(box)
  raw = style_value(box, "height")
  return box.height if raw.nil? || raw == "auto"

  CSS::Values.resolve_length(raw, default: box.height)
end

.declared_width(box, available_width) ⇒ Object



122
123
124
125
126
127
128
129
130
131
132
# File 'lib/silk_layout/layout/block_layout.rb', line 122

def self.declared_width(box, available_width)
  raw = style_value(box, "width")
  width =
    if raw && raw != "auto" && !preserve_assigned_width?(box, raw, available_width)
      CSS::Values.resolve_length(raw, reference: available_width, default: box.width)
    else
      box.width
    end

  constrain_css_width(box, width, available_width)
end

.horizontal_box_edges(box) ⇒ Object



190
191
192
# File 'lib/silk_layout/layout/block_layout.rb', line 190

def self.horizontal_box_edges(box)
  box.border[:left] + box.border[:right] + box.padding[:left] + box.padding[:right]
end

.layout(box, context, cursor_y = 0, parent_x = 0, containing_width = nil) ⇒ Object



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/silk_layout/layout/block_layout.rb', line 6

def self.layout(box, context, cursor_y = 0, parent_x = 0, containing_width = nil)
  available_width = containing_width || context.width
  resolve_spacing(box, available_width)

  return FlexLayout.layout(box, context, cursor_y, parent_x, containing_width) if box.is_a?(FlexBox)

  box.x = parent_x + box.margin[:left]
  box.y = cursor_y + box.margin[:top]

  content_x =
    box.x + box.border[:left] + box.padding[:left]

  content_y =
    box.y + box.border[:top] + box.padding[:top]

  content_width = resolved_content_width(box, available_width)

  current_y = content_y
  new_children = []
  inline_buffer = []

  box.children.each do |child|
    if child.is_a?(InlineBox)
      inline_buffer << child
      next
    end

    if inline_buffer.any?
      lines = InlineFormatter.layout(inline_buffer, content_width, content_x, current_y)
      lines.each do |line|
        line.x = content_x
        line.y = current_y
        current_y += line.height
        new_children << line
      end
      inline_buffer.clear
    end

    layout(child, context, current_y, content_x, content_width)

    current_y +=
      child.height +
      child.margin[:top] +
      child.margin[:bottom]

    new_children << child
  end

  if inline_buffer.any?
    lines = InlineFormatter.layout(inline_buffer, content_width, content_x, current_y)
    lines.each do |line|
      line.x = content_x
      line.y = current_y
      current_y += line.height
      new_children << line
    end
  end

  box.children = new_children

  content_height = current_y - content_y
  if box.explicit_height
    content_height = [content_height, content_height_from_css_height(box, declared_height(box))].max
  end

  max_child_width =
    box.children.map(&:width).max || 0

  content_width = max_child_width if !box.explicit_width && content_width == 0

  box.width =
    content_width +
    box.padding[:left] + box.padding[:right] +
    box.border[:left] + box.border[:right]

  box.height =
    content_height +
    box.padding[:top] + box.padding[:bottom] +
    box.border[:top] + box.border[:bottom]
end

.optional_width(box, property, available_width) ⇒ Object



160
161
162
163
164
165
# File 'lib/silk_layout/layout/block_layout.rb', line 160

def self.optional_width(box, property, available_width)
  raw = style_value(box, property)
  return nil if raw.nil? || raw == "none" || raw == "auto"

  CSS::Values.resolve_length(raw, reference: available_width, default: 0)
end

.preserve_assigned_width?(box, raw_width, available_width) ⇒ Boolean

Returns:

  • (Boolean)


198
199
200
201
202
# File 'lib/silk_layout/layout/block_layout.rb', line 198

def self.preserve_assigned_width?(box, raw_width, available_width)
  return false if CSS::Values.reference_relative?(raw_width)

  available_width && box.width == available_width
end

.resolve_spacing(box, available_width) ⇒ Object



87
88
89
90
# File 'lib/silk_layout/layout/block_layout.rb', line 87

def self.resolve_spacing(box, available_width)
  box.margin = resolved_edges(box, "margin", available_width, box.margin)
  box.padding = resolved_edges(box, "padding", available_width, box.padding)
end

.resolved_content_width(box, available_width) ⇒ Object



111
112
113
114
115
116
117
118
119
120
# File 'lib/silk_layout/layout/block_layout.rb', line 111

def self.resolved_content_width(box, available_width)
  content_width =
    if box.explicit_width
      content_width_from_css_width(box, declared_width(box, available_width))
    else
      auto_content_width(box, available_width)
    end

  [content_width, 0].max
end

.resolved_edge(style, property, side, values, available_width, fallback) ⇒ Object



104
105
106
107
108
109
# File 'lib/silk_layout/layout/block_layout.rb', line 104

def self.resolved_edge(style, property, side, values, available_width, fallback)
  raw = style&.[]("#{property}-#{side}") || values[side]
  return fallback unless raw

  CSS::Values.resolve_length(raw, reference: available_width, default: fallback)
end

.resolved_edges(box, property, available_width, fallback) ⇒ Object



92
93
94
95
96
97
98
99
100
101
102
# File 'lib/silk_layout/layout/block_layout.rb', line 92

def self.resolved_edges(box, property, available_width, fallback)
  style = box.node&.computed_style
  values = CSS::Values.expanded_edges(style&.[](property))

  {
    top: resolved_edge(style, property, :top, values, available_width, fallback[:top]),
    right: resolved_edge(style, property, :right, values, available_width, fallback[:right]),
    bottom: resolved_edge(style, property, :bottom, values, available_width, fallback[:bottom]),
    left: resolved_edge(style, property, :left, values, available_width, fallback[:left])
  }
end

.style_value(box, property) ⇒ Object



204
205
206
207
208
209
# File 'lib/silk_layout/layout/block_layout.rb', line 204

def self.style_value(box, property)
  value = box.node&.computed_style&.[](property)
  return nil if value.to_s.strip.empty?

  value
end

.vertical_box_edges(box) ⇒ Object



194
195
196
# File 'lib/silk_layout/layout/block_layout.rb', line 194

def self.vertical_box_edges(box)
  box.border[:top] + box.border[:bottom] + box.padding[:top] + box.padding[:bottom]
end