Class: Layouter::Parent
Constant Summary collapse
- DIM =
{ rows: :height, cols: :width, }.freeze
Instance Attribute Summary
Attributes inherited from Element
#calculated_height, #calculated_width
Class Method Summary collapse
Instance Method Summary collapse
- #[](index) ⇒ Object
- #importance ⇒ Object
-
#initialize(orientation, children) ⇒ Parent
constructor
A new instance of Parent.
- #layout(width, height) ⇒ Object
- #render ⇒ Object
Methods inherited from Element
Constructor Details
#initialize(orientation, children) ⇒ Parent
Returns a new instance of Parent.
9 10 11 12 13 14 15 16 17 18 19 |
# File 'lib/layouter/parent.rb', line 9 def initialize(orientation, children) super() unless DIM.keys.include?(orientation) raise(ArgumentError.new("Invalid orientation")) end unless children.is_a?(Array) && children.all? { |c| c.is_a?(Element) } raise(ArgumentError.new("Invalid children")) end @orientation = orientation @children = children end |
Class Method Details
.distribute(value, importances, maxs) ⇒ Object
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/layouter/parent.rb', line 70 def self.distribute(value, importances, maxs) raise(AssertionError) unless value.is_a?(Integer) # Avoid endless loops. raise(AssertionError) unless value <= maxs.sum # Avoid endless loops. raise(AssertionError) unless importances.length == maxs.length unless maxs.all? { |v| v.is_a?(Integer) || v == Float::INFINITY } raise(AssertionError) end data = importances.zip(maxs).map.with_index do |(importance, max), i| { index: i, value: 0, importance: importance, max: max } end while true # Distribute based on the importance, adhering to the maxs. extra = value - data.map { |h| h[:value] }.sum break if extra <= Float::EPSILON * data.length candidates = data.select { |h| h[:value] < h[:max] } denominator = candidates.map { |h| h[:importance] }.sum.to_f candidates.each do |h| share = extra * h[:importance] / denominator h[:value] = h[:value] + share > h[:max] ? h[:max] : h[:value] + share end end data.each { |h| h[:value] = h[:value].round } while true # Fill up any extra or missing value from turning to integers. extra = value - data.map { |h| h[:value] }.sum break if extra == 0 delta = extra > 0 ? 1 : -1 data = data.sort_by { |h| h[:importance] } data = data.reverse if extra > 0 data.cycle do |h| if h[:max] > h[:value] && h[:value] > 0 h[:value] += delta extra -= delta break if extra == 0 end end end raise(AssertionError) unless data.map { |h| h[:value] }.sum == value raise(AssertionError) unless data.all? { |h| h[:value] <= h[:max] } data.sort_by { |h| h[:index] }.map { |h| h[:value] } end |
Instance Method Details
#[](index) ⇒ Object
21 22 23 |
# File 'lib/layouter/parent.rb', line 21 def [](index) @children[index] end |
#importance ⇒ Object
56 57 58 |
# File 'lib/layouter/parent.rb', line 56 def importance @children.map(&:importance).max end |
#layout(width, height) ⇒ Object
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/layouter/parent.rb', line 25 def layout(width, height) dim = DIM[@orientation] value = binding.local_variable_get(dim) min, max = send(:"min_#{dim}"), send(:"max_#{dim}") raise(AssertionError) if min > max raise(LayoutError.new(dim, :too_small)) unless value >= min raise(LayoutError.new(dim, :too_big)) unless value <= max importances = @children.map(&:importance) maxs = @children.map { |c| c.send(:"max_#{dim}") - c.send(:"min_#{dim}") } extras = self.class.distribute(value - min, importances, maxs) @children.zip(extras).each do |child, extra| child.layout( dim == :width ? child.min_width + extra : width, dim == :height ? child.min_height + extra : height, ) end @calculated_width, @calculated_height = width, height self end |
#render ⇒ Object
60 61 62 63 64 65 66 67 68 |
# File 'lib/layouter/parent.rb', line 60 def render layout! if @orientation == :rows @children.map(&:render).join("\n") elsif @orientation == :cols tmp = @children.map { |child| child.render.split("\n") } tmp[0].zip(*tmp[1..-1]).map(&:join).join("\n") end end |