whirled_peas 0.4.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +1 -0
- data/CHANGELOG.md +37 -0
- data/README.md +529 -156
- data/Rakefile +9 -3
- data/bin/reset_cursor +11 -0
- data/bin/screen_test +68 -0
- data/examples/intro.rb +52 -0
- data/examples/scrolling.rb +54 -0
- data/lib/whirled_peas.rb +6 -12
- data/lib/whirled_peas/animator.rb +5 -0
- data/lib/whirled_peas/animator/debug_consumer.rb +17 -0
- data/lib/whirled_peas/animator/easing.rb +72 -0
- data/lib/whirled_peas/animator/frame.rb +5 -0
- data/lib/whirled_peas/animator/frameset.rb +33 -0
- data/lib/whirled_peas/animator/producer.rb +35 -0
- data/lib/whirled_peas/animator/renderer_consumer.rb +31 -0
- data/lib/whirled_peas/command.rb +5 -0
- data/lib/whirled_peas/command/base.rb +86 -0
- data/lib/whirled_peas/command/config_command.rb +44 -0
- data/lib/whirled_peas/command/debug.rb +21 -0
- data/lib/whirled_peas/command/fonts.rb +22 -0
- data/lib/whirled_peas/command/frame_command.rb +34 -0
- data/lib/whirled_peas/command/frames.rb +24 -0
- data/lib/whirled_peas/command/help.rb +38 -0
- data/lib/whirled_peas/command/play.rb +108 -0
- data/lib/whirled_peas/command/record.rb +57 -0
- data/lib/whirled_peas/command/still.rb +29 -0
- data/lib/whirled_peas/command_line.rb +23 -228
- data/lib/whirled_peas/config.rb +56 -6
- data/lib/whirled_peas/device.rb +5 -0
- data/lib/whirled_peas/device/null_device.rb +8 -0
- data/lib/whirled_peas/device/output_file.rb +19 -0
- data/lib/whirled_peas/device/screen.rb +26 -0
- data/lib/whirled_peas/errors.rb +2 -0
- data/lib/whirled_peas/graphics.rb +19 -0
- data/lib/whirled_peas/graphics/box_painter.rb +101 -0
- data/lib/whirled_peas/graphics/canvas.rb +118 -0
- data/lib/whirled_peas/graphics/composer.rb +80 -0
- data/lib/whirled_peas/graphics/container_coords.rb +72 -0
- data/lib/whirled_peas/graphics/container_dimensions.rb +93 -0
- data/lib/whirled_peas/graphics/container_painter.rb +363 -0
- data/lib/whirled_peas/graphics/debugger.rb +52 -0
- data/lib/whirled_peas/graphics/grid_painter.rb +69 -0
- data/lib/whirled_peas/graphics/mock_screen.rb +26 -0
- data/lib/whirled_peas/graphics/painter.rb +33 -0
- data/lib/whirled_peas/graphics/renderer.rb +32 -0
- data/lib/whirled_peas/graphics/text_dimensions.rb +15 -0
- data/lib/whirled_peas/graphics/text_painter.rb +40 -0
- data/lib/whirled_peas/settings.rb +5 -0
- data/lib/whirled_peas/settings/alignment.rb +24 -0
- data/lib/whirled_peas/settings/bg_color.rb +24 -0
- data/lib/whirled_peas/settings/border.rb +101 -0
- data/lib/whirled_peas/settings/box_settings.rb +8 -0
- data/lib/whirled_peas/settings/color.rb +68 -0
- data/lib/whirled_peas/settings/container_settings.rb +223 -0
- data/lib/whirled_peas/settings/debugger.rb +96 -0
- data/lib/whirled_peas/settings/display_flow.rb +27 -0
- data/lib/whirled_peas/settings/element_settings.rb +61 -0
- data/lib/whirled_peas/settings/grid_settings.rb +19 -0
- data/lib/whirled_peas/settings/margin.rb +8 -0
- data/lib/whirled_peas/settings/padding.rb +8 -0
- data/lib/whirled_peas/settings/position.rb +15 -0
- data/lib/whirled_peas/settings/scrollbar.rb +15 -0
- data/lib/whirled_peas/settings/sizing.rb +19 -0
- data/lib/whirled_peas/settings/spacing.rb +58 -0
- data/lib/whirled_peas/settings/text_color.rb +21 -0
- data/lib/whirled_peas/settings/text_settings.rb +15 -0
- data/lib/whirled_peas/settings/vert_alignment.rb +24 -0
- data/lib/whirled_peas/utils/ansi.rb +19 -56
- data/lib/whirled_peas/utils/file_handler.rb +57 -0
- data/lib/whirled_peas/utils/formatted_string.rb +64 -0
- data/lib/whirled_peas/utils/title_font.rb +1 -1
- data/lib/whirled_peas/version.rb +1 -1
- data/screen_test/elements/box.frame +1 -0
- data/screen_test/elements/box.rb +20 -0
- data/screen_test/elements/grid.frame +1 -0
- data/screen_test/elements/grid.rb +13 -0
- data/screen_test/elements/screen_overflow_x.frame +1 -0
- data/screen_test/elements/screen_overflow_x.rb +9 -0
- data/screen_test/elements/screen_overflow_y.frame +1 -0
- data/screen_test/elements/screen_overflow_y.rb +9 -0
- data/screen_test/elements/text.frame +1 -0
- data/screen_test/elements/text.rb +9 -0
- data/screen_test/elements/text_multiline.frame +1 -0
- data/screen_test/elements/text_multiline.rb +9 -0
- data/screen_test/settings/align/box_around.frame +1 -0
- data/screen_test/settings/align/box_around.rb +16 -0
- data/screen_test/settings/align/box_between.frame +1 -0
- data/screen_test/settings/align/box_between.rb +16 -0
- data/screen_test/settings/align/box_center.frame +1 -0
- data/screen_test/settings/align/box_center.rb +21 -0
- data/screen_test/settings/align/box_default.frame +1 -0
- data/screen_test/settings/align/box_default.rb +20 -0
- data/screen_test/settings/align/box_evenly.frame +1 -0
- data/screen_test/settings/align/box_evenly.rb +16 -0
- data/screen_test/settings/align/box_left.frame +1 -0
- data/screen_test/settings/align/box_left.rb +21 -0
- data/screen_test/settings/align/box_right.frame +1 -0
- data/screen_test/settings/align/box_right.rb +21 -0
- data/screen_test/settings/align/children_center.frame +1 -0
- data/screen_test/settings/align/children_center.rb +15 -0
- data/screen_test/settings/align/children_left.frame +1 -0
- data/screen_test/settings/align/children_left.rb +15 -0
- data/screen_test/settings/align/children_right.frame +1 -0
- data/screen_test/settings/align/children_right.rb +15 -0
- data/screen_test/settings/align/grid_center.frame +1 -0
- data/screen_test/settings/align/grid_center.rb +18 -0
- data/screen_test/settings/align/grid_default.frame +1 -0
- data/screen_test/settings/align/grid_default.rb +17 -0
- data/screen_test/settings/align/grid_left.frame +1 -0
- data/screen_test/settings/align/grid_left.rb +18 -0
- data/screen_test/settings/align/grid_right.frame +1 -0
- data/screen_test/settings/align/grid_right.rb +18 -0
- data/screen_test/settings/ansi/bold.frame +1 -0
- data/screen_test/settings/ansi/bold.rb +14 -0
- data/screen_test/settings/ansi/color.frame +1 -0
- data/screen_test/settings/ansi/color.rb +37 -0
- data/screen_test/settings/ansi/underline.frame +1 -0
- data/screen_test/settings/ansi/underline.rb +14 -0
- data/screen_test/settings/border.frame +1 -0
- data/screen_test/settings/border.rb +13 -0
- data/screen_test/settings/flow/box_b2t.frame +1 -0
- data/screen_test/settings/flow/box_b2t.rb +26 -0
- data/screen_test/settings/flow/box_l2r.frame +1 -0
- data/screen_test/settings/flow/box_l2r.rb +26 -0
- data/screen_test/settings/flow/box_r2l.frame +1 -0
- data/screen_test/settings/flow/box_r2l.rb +26 -0
- data/screen_test/settings/flow/box_t2b.frame +1 -0
- data/screen_test/settings/flow/box_t2b.rb +26 -0
- data/screen_test/settings/flow/grid_b2t.frame +1 -0
- data/screen_test/settings/flow/grid_b2t.rb +14 -0
- data/screen_test/settings/flow/grid_l2r.frame +1 -0
- data/screen_test/settings/flow/grid_l2r.rb +14 -0
- data/screen_test/settings/flow/grid_r2l.frame +1 -0
- data/screen_test/settings/flow/grid_r2l.rb +14 -0
- data/screen_test/settings/flow/grid_t2b.frame +1 -0
- data/screen_test/settings/flow/grid_t2b.rb +14 -0
- data/screen_test/settings/height/box.frame +1 -0
- data/screen_test/settings/height/box.rb +13 -0
- data/screen_test/settings/height/box_border_sizing.frame +1 -0
- data/screen_test/settings/height/box_border_sizing.rb +15 -0
- data/screen_test/settings/height/grid.frame +1 -0
- data/screen_test/settings/height/grid.rb +14 -0
- data/screen_test/settings/height/overflow_box.frame +1 -0
- data/screen_test/settings/height/overflow_box.rb +13 -0
- data/screen_test/settings/height/overflow_box_l2r.frame +1 -0
- data/screen_test/settings/height/overflow_box_l2r.rb +17 -0
- data/screen_test/settings/height/overflow_box_t2b.frame +1 -0
- data/screen_test/settings/height/overflow_box_t2b.rb +16 -0
- data/screen_test/settings/height/overflow_grid.frame +1 -0
- data/screen_test/settings/height/overflow_grid.rb +16 -0
- data/screen_test/settings/margin.frame +1 -0
- data/screen_test/settings/margin.rb +16 -0
- data/screen_test/settings/padding.frame +1 -0
- data/screen_test/settings/padding.rb +13 -0
- data/screen_test/settings/position/box_left.frame +1 -0
- data/screen_test/settings/position/box_left.rb +17 -0
- data/screen_test/settings/position/box_left_negative.frame +1 -0
- data/screen_test/settings/position/box_left_negative.rb +17 -0
- data/screen_test/settings/position/box_top.frame +1 -0
- data/screen_test/settings/position/box_top.rb +17 -0
- data/screen_test/settings/position/box_top_negative.frame +1 -0
- data/screen_test/settings/position/box_top_negative.rb +17 -0
- data/screen_test/settings/position/grid_left.frame +1 -0
- data/screen_test/settings/position/grid_left.rb +18 -0
- data/screen_test/settings/position/grid_left_negative.frame +1 -0
- data/screen_test/settings/position/grid_left_negative.rb +18 -0
- data/screen_test/settings/position/grid_top.frame +1 -0
- data/screen_test/settings/position/grid_top.rb +18 -0
- data/screen_test/settings/position/grid_top_negative.frame +1 -0
- data/screen_test/settings/position/grid_top_negative.rb +18 -0
- data/screen_test/settings/scroll/horiz_box.frame +1 -0
- data/screen_test/settings/scroll/horiz_box.rb +17 -0
- data/screen_test/settings/scroll/horiz_box_align_center.rb +18 -0
- data/screen_test/settings/scroll/horiz_box_align_right.rb +18 -0
- data/screen_test/settings/scroll/vert_box.frame +1 -0
- data/screen_test/settings/scroll/vert_box.rb +20 -0
- data/screen_test/settings/title_font.frame +1 -0
- data/screen_test/settings/title_font.rb +12 -0
- data/screen_test/settings/valign/box_around.frame +1 -0
- data/screen_test/settings/valign/box_around.rb +17 -0
- data/screen_test/settings/valign/box_between.frame +1 -0
- data/screen_test/settings/valign/box_between.rb +17 -0
- data/screen_test/settings/valign/box_bottom.frame +1 -0
- data/screen_test/settings/valign/box_bottom.rb +17 -0
- data/screen_test/settings/valign/box_default.frame +1 -0
- data/screen_test/settings/valign/box_default.rb +16 -0
- data/screen_test/settings/valign/box_evenly.frame +1 -0
- data/screen_test/settings/valign/box_evenly.rb +17 -0
- data/screen_test/settings/valign/box_middle.frame +1 -0
- data/screen_test/settings/valign/box_middle.rb +17 -0
- data/screen_test/settings/valign/box_top.frame +1 -0
- data/screen_test/settings/valign/box_top.rb +17 -0
- data/screen_test/settings/valign/grid_bottom.frame +1 -0
- data/screen_test/settings/valign/grid_bottom.rb +15 -0
- data/screen_test/settings/valign/grid_default.frame +1 -0
- data/screen_test/settings/valign/grid_default.rb +14 -0
- data/screen_test/settings/valign/grid_middle.frame +1 -0
- data/screen_test/settings/valign/grid_middle.rb +15 -0
- data/screen_test/settings/valign/grid_top.frame +1 -0
- data/screen_test/settings/valign/grid_top.rb +15 -0
- data/screen_test/settings/width/box_border_sizing.frame +1 -0
- data/screen_test/settings/width/box_border_sizing.rb +15 -0
- data/screen_test/settings/width/box_content.frame +1 -0
- data/screen_test/settings/width/box_content.rb +15 -0
- data/screen_test/settings/width/box_default.frame +1 -0
- data/screen_test/settings/width/box_default.rb +14 -0
- data/screen_test/settings/width/grid.frame +1 -0
- data/screen_test/settings/width/grid.rb +14 -0
- data/screen_test/settings/width/overflow_align_center.frame +1 -0
- data/screen_test/settings/width/overflow_align_center.rb +14 -0
- data/screen_test/settings/width/overflow_align_right.frame +1 -0
- data/screen_test/settings/width/overflow_align_right.rb +14 -0
- data/screen_test/settings/width/overflow_box.frame +1 -0
- data/screen_test/settings/width/overflow_box.rb +13 -0
- data/screen_test/settings/width/overflow_box_l2r.frame +1 -0
- data/screen_test/settings/width/overflow_box_l2r.rb +16 -0
- data/screen_test/settings/width/overflow_box_t2b.frame +1 -0
- data/screen_test/settings/width/overflow_box_t2b.rb +17 -0
- data/screen_test/settings/width/overflow_grid.frame +1 -0
- data/screen_test/settings/width/overflow_grid.rb +14 -0
- data/tools/whirled_peas/tools/screen_tester.rb +285 -0
- metadata +213 -15
- data/bin/title_fonts +0 -6
- data/lib/whirled_peas/frame.rb +0 -7
- data/lib/whirled_peas/frame/event_loop.rb +0 -91
- data/lib/whirled_peas/frame/print_consumer.rb +0 -33
- data/lib/whirled_peas/frame/producer.rb +0 -61
- data/lib/whirled_peas/template.rb +0 -5
- data/lib/whirled_peas/template/element.rb +0 -230
- data/lib/whirled_peas/template/settings.rb +0 -530
- data/lib/whirled_peas/ui.rb +0 -5
- data/lib/whirled_peas/ui/canvas.rb +0 -68
- data/lib/whirled_peas/ui/painter.rb +0 -287
- data/lib/whirled_peas/ui/screen.rb +0 -63
- data/lib/whirled_peas/utils/color.rb +0 -101
| @@ -1,230 +0,0 @@ | |
| 1 | 
            -
            require_relative '../utils/title_font'
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            require_relative 'settings'
         | 
| 4 | 
            -
             | 
| 5 | 
            -
            module WhirledPeas
         | 
| 6 | 
            -
              module UI
         | 
| 7 | 
            -
                STRINGALBE_CLASSES = [FalseClass, Float, Integer, NilClass, String, Symbol, TrueClass]
         | 
| 8 | 
            -
             | 
| 9 | 
            -
                class Element
         | 
| 10 | 
            -
                  attr_accessor :preferred_width, :preferred_height
         | 
| 11 | 
            -
                  attr_reader :name, :settings
         | 
| 12 | 
            -
             | 
| 13 | 
            -
                  def initialize(name, settings)
         | 
| 14 | 
            -
                    @name = name
         | 
| 15 | 
            -
                    @settings = settings
         | 
| 16 | 
            -
                  end
         | 
| 17 | 
            -
                end
         | 
| 18 | 
            -
                private_constant :Element
         | 
| 19 | 
            -
             | 
| 20 | 
            -
                class TextElement < Element
         | 
| 21 | 
            -
                  attr_reader :lines
         | 
| 22 | 
            -
             | 
| 23 | 
            -
                  def initialize(name, settings)
         | 
| 24 | 
            -
                    super(name, TextSettings.merge(settings))
         | 
| 25 | 
            -
                  end
         | 
| 26 | 
            -
             | 
| 27 | 
            -
                  def lines=(val)
         | 
| 28 | 
            -
                    unless STRINGALBE_CLASSES.include?(val.class)
         | 
| 29 | 
            -
                      raise ArgmentError, "Unsupported type for TextElement: #{val.class}"
         | 
| 30 | 
            -
                    end
         | 
| 31 | 
            -
                    if settings.title_font
         | 
| 32 | 
            -
                      @lines = Utils::TitleFont.to_s(val.to_s, settings.title_font).split("\n")
         | 
| 33 | 
            -
                    else
         | 
| 34 | 
            -
                      @lines = [val.to_s]
         | 
| 35 | 
            -
                    end
         | 
| 36 | 
            -
                    @preferred_width = settings.width || @lines.first.length
         | 
| 37 | 
            -
                    @preferred_height = @lines.length
         | 
| 38 | 
            -
                  end
         | 
| 39 | 
            -
             | 
| 40 | 
            -
                  def inspect(indent='')
         | 
| 41 | 
            -
                    dims = unless preferred_width.nil?
         | 
| 42 | 
            -
                      "#{indent + '  '}- Dimensions: #{preferred_width}x#{preferred_height}"
         | 
| 43 | 
            -
                    end
         | 
| 44 | 
            -
                    [
         | 
| 45 | 
            -
                      "#{indent}+ #{name} [#{self.class.name}]",
         | 
| 46 | 
            -
                      dims,
         | 
| 47 | 
            -
                      "#{indent + '  '}- Settings",
         | 
| 48 | 
            -
                      settings.inspect(indent + '    ')
         | 
| 49 | 
            -
                    ].compact.join("\n")
         | 
| 50 | 
            -
                  end
         | 
| 51 | 
            -
                end
         | 
| 52 | 
            -
             | 
| 53 | 
            -
                class ComposableElement < Element
         | 
| 54 | 
            -
                  class << self
         | 
| 55 | 
            -
                    def next_name
         | 
| 56 | 
            -
                      @counter ||= 0
         | 
| 57 | 
            -
                      @counter += 1
         | 
| 58 | 
            -
                      "Element-#{@counter}"
         | 
| 59 | 
            -
                    end
         | 
| 60 | 
            -
                  end
         | 
| 61 | 
            -
             | 
| 62 | 
            -
                  def initialize(name, settings)
         | 
| 63 | 
            -
                    super
         | 
| 64 | 
            -
                  end
         | 
| 65 | 
            -
             | 
| 66 | 
            -
                  def children
         | 
| 67 | 
            -
                    @children ||= []
         | 
| 68 | 
            -
                  end
         | 
| 69 | 
            -
             | 
| 70 | 
            -
                  def add_text(name=self.class.next_name, &block)
         | 
| 71 | 
            -
                    element = TextElement.new(name, settings)
         | 
| 72 | 
            -
                    element.lines = yield nil, element.settings
         | 
| 73 | 
            -
                    children << element
         | 
| 74 | 
            -
                  end
         | 
| 75 | 
            -
             | 
| 76 | 
            -
                  def add_box(name=self.class.next_name, &block)
         | 
| 77 | 
            -
                    element = BoxElement.new(name, settings)
         | 
| 78 | 
            -
                    value = yield element, element.settings
         | 
| 79 | 
            -
                    children << element
         | 
| 80 | 
            -
                    if element.children.empty? && STRINGALBE_CLASSES.include?(value.class)
         | 
| 81 | 
            -
                      element.add_text { value.to_s }
         | 
| 82 | 
            -
                    end
         | 
| 83 | 
            -
                  end
         | 
| 84 | 
            -
             | 
| 85 | 
            -
                  def add_grid(name=self.class.next_name, &block)
         | 
| 86 | 
            -
                    element = GridElement.new(name, settings)
         | 
| 87 | 
            -
                    values = yield element, element.settings
         | 
| 88 | 
            -
                    children << element
         | 
| 89 | 
            -
                    if element.children.empty? && values.is_a?(Array)
         | 
| 90 | 
            -
                      values.each { |v| element.add_text { v.to_s } }
         | 
| 91 | 
            -
                    end
         | 
| 92 | 
            -
                  end
         | 
| 93 | 
            -
             | 
| 94 | 
            -
                  def inspect(indent='')
         | 
| 95 | 
            -
                    kids = children.map { |c| c.inspect(indent + '    ') }.join("\n")
         | 
| 96 | 
            -
                    dims = unless preferred_width.nil?
         | 
| 97 | 
            -
                      "#{indent + '  '}- Dimensions: #{preferred_width}x#{preferred_height}"
         | 
| 98 | 
            -
                    end
         | 
| 99 | 
            -
                    [
         | 
| 100 | 
            -
                      "#{indent}+ #{name} [#{self.class.name}]",
         | 
| 101 | 
            -
                      dims,
         | 
| 102 | 
            -
                      "#{indent + '  '}- Settings",
         | 
| 103 | 
            -
                      settings.inspect(indent + '    '),
         | 
| 104 | 
            -
                      "#{indent + '  '}- Children",
         | 
| 105 | 
            -
                      kids
         | 
| 106 | 
            -
                    ].compact.join("\n")
         | 
| 107 | 
            -
                  end
         | 
| 108 | 
            -
             | 
| 109 | 
            -
                  private
         | 
| 110 | 
            -
             | 
| 111 | 
            -
                  def margin_width
         | 
| 112 | 
            -
                    settings.margin.left + settings.margin.right
         | 
| 113 | 
            -
                  end
         | 
| 114 | 
            -
             | 
| 115 | 
            -
                  def margin_height
         | 
| 116 | 
            -
                    settings.margin.top + settings.margin.bottom
         | 
| 117 | 
            -
                  end
         | 
| 118 | 
            -
             | 
| 119 | 
            -
                  def outer_border_width
         | 
| 120 | 
            -
                    (settings.border.left? ? 1 : 0) + (settings.border.right? ? 1 : 0)
         | 
| 121 | 
            -
                  end
         | 
| 122 | 
            -
             | 
| 123 | 
            -
                  def outer_border_height
         | 
| 124 | 
            -
                    (settings.border.top? ? 1 : 0) + (settings.border.bottom? ? 1 : 0)
         | 
| 125 | 
            -
                  end
         | 
| 126 | 
            -
             | 
| 127 | 
            -
                  def inner_border_width
         | 
| 128 | 
            -
                    settings.border.inner_vert? ? 1 : 0
         | 
| 129 | 
            -
                  end
         | 
| 130 | 
            -
             | 
| 131 | 
            -
                  def inner_border_height
         | 
| 132 | 
            -
                    settings.border.inner_horiz? ? 1 : 0
         | 
| 133 | 
            -
                  end
         | 
| 134 | 
            -
             | 
| 135 | 
            -
                  def padding_width
         | 
| 136 | 
            -
                    settings.padding.left + settings.padding.right
         | 
| 137 | 
            -
                  end
         | 
| 138 | 
            -
             | 
| 139 | 
            -
                  def padding_height
         | 
| 140 | 
            -
                    settings.padding.top + settings.padding.bottom
         | 
| 141 | 
            -
                  end
         | 
| 142 | 
            -
                end
         | 
| 143 | 
            -
             | 
| 144 | 
            -
                class Template < ComposableElement
         | 
| 145 | 
            -
                  def initialize(settings=TemplateSettings.new)
         | 
| 146 | 
            -
                    super('TEMPLATE', settings)
         | 
| 147 | 
            -
                  end
         | 
| 148 | 
            -
                end
         | 
| 149 | 
            -
             | 
| 150 | 
            -
                class BoxElement < ComposableElement
         | 
| 151 | 
            -
                  attr_writer :content_width, :content_height
         | 
| 152 | 
            -
             | 
| 153 | 
            -
                  def initialize(name, settings)
         | 
| 154 | 
            -
                    super(name, BoxSettings.merge(settings))
         | 
| 155 | 
            -
                  end
         | 
| 156 | 
            -
             | 
| 157 | 
            -
                  def self.from_template(template, width, height)
         | 
| 158 | 
            -
                    box = new(template.name, template.settings)
         | 
| 159 | 
            -
                    template.children.each { |c| box.children << c }
         | 
| 160 | 
            -
                    box.content_width = box.preferred_width = width
         | 
| 161 | 
            -
                    box.content_height = box.preferred_height = height
         | 
| 162 | 
            -
                    box
         | 
| 163 | 
            -
                  end
         | 
| 164 | 
            -
             | 
| 165 | 
            -
                  def content_width
         | 
| 166 | 
            -
                    @content_width ||= begin
         | 
| 167 | 
            -
                      child_widths = children.map(&:preferred_width)
         | 
| 168 | 
            -
                      width = settings.horizontal_flow? ? child_widths.sum : (child_widths.max || 0)
         | 
| 169 | 
            -
                      [width, *settings.width].max
         | 
| 170 | 
            -
                    end
         | 
| 171 | 
            -
                  end
         | 
| 172 | 
            -
             | 
| 173 | 
            -
                  def preferred_width
         | 
| 174 | 
            -
                    @preferred_width ||=
         | 
| 175 | 
            -
                      margin_width + outer_border_width + padding_width + content_width
         | 
| 176 | 
            -
                  end
         | 
| 177 | 
            -
             | 
| 178 | 
            -
                  def content_height
         | 
| 179 | 
            -
                    @content_height ||= begin
         | 
| 180 | 
            -
                      child_heights = children.map(&:preferred_height)
         | 
| 181 | 
            -
                      settings.vertical_flow? ? child_heights.sum : (child_heights.max || 0)
         | 
| 182 | 
            -
                    end
         | 
| 183 | 
            -
                  end
         | 
| 184 | 
            -
             | 
| 185 | 
            -
                  def preferred_height
         | 
| 186 | 
            -
                    @preferred_height ||=
         | 
| 187 | 
            -
                      margin_height + outer_border_height + padding_height + content_height
         | 
| 188 | 
            -
                  end
         | 
| 189 | 
            -
                end
         | 
| 190 | 
            -
             | 
| 191 | 
            -
                class GridElement < ComposableElement
         | 
| 192 | 
            -
                  def initialize(name, settings)
         | 
| 193 | 
            -
                    super(name, GridSettings.merge(settings))
         | 
| 194 | 
            -
                  end
         | 
| 195 | 
            -
             | 
| 196 | 
            -
                  def col_width
         | 
| 197 | 
            -
                    return @col_width if @col_width
         | 
| 198 | 
            -
                    @col_width = 0
         | 
| 199 | 
            -
                    children.each do |child|
         | 
| 200 | 
            -
                      @col_width = child.preferred_width if child.preferred_width > @col_width
         | 
| 201 | 
            -
                    end
         | 
| 202 | 
            -
                    @col_width
         | 
| 203 | 
            -
                  end
         | 
| 204 | 
            -
             | 
| 205 | 
            -
                  def row_height
         | 
| 206 | 
            -
                    return @row_height if @row_height
         | 
| 207 | 
            -
                    @row_height = 0
         | 
| 208 | 
            -
                    children.each do |child|
         | 
| 209 | 
            -
                      @row_height = child.preferred_height if child.preferred_height > @row_height
         | 
| 210 | 
            -
                    end
         | 
| 211 | 
            -
                    @row_height
         | 
| 212 | 
            -
                  end
         | 
| 213 | 
            -
             | 
| 214 | 
            -
                  def preferred_width
         | 
| 215 | 
            -
                    margin_width +
         | 
| 216 | 
            -
                      outer_border_width +
         | 
| 217 | 
            -
                      settings.num_cols * (padding_width + col_width) +
         | 
| 218 | 
            -
                      (settings.num_cols - 1) * inner_border_width
         | 
| 219 | 
            -
                  end
         | 
| 220 | 
            -
             | 
| 221 | 
            -
                  def preferred_height
         | 
| 222 | 
            -
                    num_rows = (children.length / settings.num_cols).ceil
         | 
| 223 | 
            -
                    margin_height +
         | 
| 224 | 
            -
                      outer_border_height +
         | 
| 225 | 
            -
                      num_rows * (padding_height + row_height) +
         | 
| 226 | 
            -
                      (num_rows - 1) * inner_border_height
         | 
| 227 | 
            -
                  end
         | 
| 228 | 
            -
                end
         | 
| 229 | 
            -
              end
         | 
| 230 | 
            -
            end
         | 
| @@ -1,530 +0,0 @@ | |
| 1 | 
            -
            require 'json'
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            require_relative '../utils/color'
         | 
| 4 | 
            -
            require_relative '../utils/title_font'
         | 
| 5 | 
            -
             | 
| 6 | 
            -
            module WhirledPeas
         | 
| 7 | 
            -
              module UI
         | 
| 8 | 
            -
                module TextAlign
         | 
| 9 | 
            -
                  LEFT = :left
         | 
| 10 | 
            -
                  CENTER = :center
         | 
| 11 | 
            -
                  RIGHT = :right
         | 
| 12 | 
            -
             | 
| 13 | 
            -
                  def self.validate!(align)
         | 
| 14 | 
            -
                    return unless align
         | 
| 15 | 
            -
                    if [TextAlign::LEFT, TextAlign::CENTER, TextAlign::RIGHT].include?(align)
         | 
| 16 | 
            -
                      align
         | 
| 17 | 
            -
                    else
         | 
| 18 | 
            -
                      raise ArgumentError, "Invalid alignment: #{align}"
         | 
| 19 | 
            -
                    end
         | 
| 20 | 
            -
                  end
         | 
| 21 | 
            -
                end
         | 
| 22 | 
            -
             | 
| 23 | 
            -
                module DisplayFlow
         | 
| 24 | 
            -
                  LEFT_TO_RIGHT = :l2r
         | 
| 25 | 
            -
                  RIGHT_TO_LEFT = :r2l
         | 
| 26 | 
            -
                  TOP_TO_BOTTOM = :t2b
         | 
| 27 | 
            -
                  BOTTOM_TO_TOP = :b2t
         | 
| 28 | 
            -
             | 
| 29 | 
            -
                  def self.validate!(flow)
         | 
| 30 | 
            -
                    return unless flow
         | 
| 31 | 
            -
                    if [
         | 
| 32 | 
            -
                      DisplayFlow::LEFT_TO_RIGHT,
         | 
| 33 | 
            -
                      DisplayFlow::RIGHT_TO_LEFT,
         | 
| 34 | 
            -
                      DisplayFlow::TOP_TO_BOTTOM,
         | 
| 35 | 
            -
                      DisplayFlow::BOTTOM_TO_TOP
         | 
| 36 | 
            -
                    ].include?(flow)
         | 
| 37 | 
            -
                      flow
         | 
| 38 | 
            -
                    else
         | 
| 39 | 
            -
                      raise ArgumentError, "Invalid flow: #{flow}"
         | 
| 40 | 
            -
                    end
         | 
| 41 | 
            -
                  end
         | 
| 42 | 
            -
                end
         | 
| 43 | 
            -
             | 
| 44 | 
            -
                class Spacing
         | 
| 45 | 
            -
                  def left
         | 
| 46 | 
            -
                    @_left || 0
         | 
| 47 | 
            -
                  end
         | 
| 48 | 
            -
             | 
| 49 | 
            -
                  def left=(val)
         | 
| 50 | 
            -
                    @_left = val
         | 
| 51 | 
            -
                  end
         | 
| 52 | 
            -
             | 
| 53 | 
            -
                  def top
         | 
| 54 | 
            -
                    @_top || 0
         | 
| 55 | 
            -
                  end
         | 
| 56 | 
            -
             | 
| 57 | 
            -
                  def top=(val)
         | 
| 58 | 
            -
                    @_top = val
         | 
| 59 | 
            -
                  end
         | 
| 60 | 
            -
             | 
| 61 | 
            -
                  def right
         | 
| 62 | 
            -
                    @_right || 0
         | 
| 63 | 
            -
                  end
         | 
| 64 | 
            -
             | 
| 65 | 
            -
                  def right=(val)
         | 
| 66 | 
            -
                    @_right = val
         | 
| 67 | 
            -
                  end
         | 
| 68 | 
            -
             | 
| 69 | 
            -
                  def bottom
         | 
| 70 | 
            -
                    @_bottom || 0
         | 
| 71 | 
            -
                  end
         | 
| 72 | 
            -
             | 
| 73 | 
            -
                  def bottom=(val)
         | 
| 74 | 
            -
                    @_bottom = val
         | 
| 75 | 
            -
                  end
         | 
| 76 | 
            -
             | 
| 77 | 
            -
                  def merge(parent)
         | 
| 78 | 
            -
                    merged = Spacing.new
         | 
| 79 | 
            -
                    merged._left = @_left || parent._left
         | 
| 80 | 
            -
                    merged._top = @_top || parent._top
         | 
| 81 | 
            -
                    merged._right = @_right || parent._right
         | 
| 82 | 
            -
                    merged._bottom = @_bottom || parent._bottom
         | 
| 83 | 
            -
                    merged
         | 
| 84 | 
            -
                  end
         | 
| 85 | 
            -
             | 
| 86 | 
            -
                  def inspect
         | 
| 87 | 
            -
                    vals = {}
         | 
| 88 | 
            -
                    vals[:left] = @_left if @_left
         | 
| 89 | 
            -
                    vals[:top] = @_top if @_top
         | 
| 90 | 
            -
                    vals[:right] = @_right if @_right
         | 
| 91 | 
            -
                    vals[:bottom] = @_bottom if @_bottom
         | 
| 92 | 
            -
                    JSON.generate(vals) if vals.any?
         | 
| 93 | 
            -
                  end
         | 
| 94 | 
            -
             | 
| 95 | 
            -
                  protected
         | 
| 96 | 
            -
             | 
| 97 | 
            -
                  attr_accessor :_left, :_top, :_right, :_bottom
         | 
| 98 | 
            -
                end
         | 
| 99 | 
            -
                private_constant :Spacing
         | 
| 100 | 
            -
             | 
| 101 | 
            -
                class Padding < Spacing
         | 
| 102 | 
            -
                end
         | 
| 103 | 
            -
             | 
| 104 | 
            -
                class Margin < Spacing
         | 
| 105 | 
            -
                end
         | 
| 106 | 
            -
             | 
| 107 | 
            -
                BorderStyle = Struct.new(
         | 
| 108 | 
            -
                  :top_left, :top_horiz, :top_junc, :top_right,
         | 
| 109 | 
            -
                  :left_vert, :left_junc,
         | 
| 110 | 
            -
                  :middle_vert, :cross_junc, :middle_horiz,
         | 
| 111 | 
            -
                  :right_vert, :right_junc,
         | 
| 112 | 
            -
                  :bottom_left, :bottom_horiz, :bottom_junc, :bottom_right
         | 
| 113 | 
            -
                )
         | 
| 114 | 
            -
             | 
| 115 | 
            -
                module BorderStyles
         | 
| 116 | 
            -
                  BOLD = BorderStyle.new(
         | 
| 117 | 
            -
                    '┏', '━', '┳', '┓',
         | 
| 118 | 
            -
                    '┃', '┣',
         | 
| 119 | 
            -
                    '┃', '╋', '━',
         | 
| 120 | 
            -
                    '┃', '┫',
         | 
| 121 | 
            -
                    '┗', '━', '┻', '┛'
         | 
| 122 | 
            -
                  )
         | 
| 123 | 
            -
                  SOFT = BorderStyle.new(
         | 
| 124 | 
            -
                    '╭', '─', '┬', '╮',
         | 
| 125 | 
            -
                    '│', '├',
         | 
| 126 | 
            -
                    '│', '┼', '─',
         | 
| 127 | 
            -
                    '│', '┤',
         | 
| 128 | 
            -
                    '╰', '─', '┴', '╯'
         | 
| 129 | 
            -
                  )
         | 
| 130 | 
            -
                  DOUBLE = BorderStyle.new(
         | 
| 131 | 
            -
                    '╔', '═', '╦', '╗',
         | 
| 132 | 
            -
                    '║', '╠',
         | 
| 133 | 
            -
                    '║', '╬', '═',
         | 
| 134 | 
            -
                    '║', '╣',
         | 
| 135 | 
            -
                    '╚', '═', '╩', '╝'
         | 
| 136 | 
            -
                  )
         | 
| 137 | 
            -
             | 
| 138 | 
            -
                  def self.validate!(style)
         | 
| 139 | 
            -
                    return unless style
         | 
| 140 | 
            -
                    if style.is_a?(Symbol)
         | 
| 141 | 
            -
                      error_message = "Unsupported border style: #{style}"
         | 
| 142 | 
            -
                      begin
         | 
| 143 | 
            -
                        style = self.const_get(style.upcase)
         | 
| 144 | 
            -
                        raise ArgumentError, error_message unless style.is_a?(BorderStyle)
         | 
| 145 | 
            -
                        style
         | 
| 146 | 
            -
                      rescue NameError
         | 
| 147 | 
            -
                        raise ArgumentError, error_message
         | 
| 148 | 
            -
                      end
         | 
| 149 | 
            -
                    else
         | 
| 150 | 
            -
                      style
         | 
| 151 | 
            -
                    end
         | 
| 152 | 
            -
                  end
         | 
| 153 | 
            -
                end
         | 
| 154 | 
            -
             | 
| 155 | 
            -
                class Border
         | 
| 156 | 
            -
                  def left?
         | 
| 157 | 
            -
                    @_left == true
         | 
| 158 | 
            -
                  end
         | 
| 159 | 
            -
             | 
| 160 | 
            -
                  def left=(val)
         | 
| 161 | 
            -
                    @_left = val
         | 
| 162 | 
            -
                  end
         | 
| 163 | 
            -
             | 
| 164 | 
            -
                  def top?
         | 
| 165 | 
            -
                    @_top == true
         | 
| 166 | 
            -
                  end
         | 
| 167 | 
            -
             | 
| 168 | 
            -
                  def top=(val)
         | 
| 169 | 
            -
                    @_top = val
         | 
| 170 | 
            -
                  end
         | 
| 171 | 
            -
             | 
| 172 | 
            -
                  def right?
         | 
| 173 | 
            -
                    @_right == true
         | 
| 174 | 
            -
                  end
         | 
| 175 | 
            -
             | 
| 176 | 
            -
                  def right=(val)
         | 
| 177 | 
            -
                    @_right = val
         | 
| 178 | 
            -
                  end
         | 
| 179 | 
            -
             | 
| 180 | 
            -
                  def bottom?
         | 
| 181 | 
            -
                    @_bottom == true
         | 
| 182 | 
            -
                  end
         | 
| 183 | 
            -
             | 
| 184 | 
            -
                  def bottom=(val)
         | 
| 185 | 
            -
                    @_bottom = val
         | 
| 186 | 
            -
                  end
         | 
| 187 | 
            -
             | 
| 188 | 
            -
                  def inner_horiz?
         | 
| 189 | 
            -
                    @_inner_horiz == true
         | 
| 190 | 
            -
                  end
         | 
| 191 | 
            -
             | 
| 192 | 
            -
                  def inner_horiz=(val)
         | 
| 193 | 
            -
                    @_inner_horiz = val
         | 
| 194 | 
            -
                  end
         | 
| 195 | 
            -
             | 
| 196 | 
            -
                  def inner_vert?
         | 
| 197 | 
            -
                    @_inner_vert == true
         | 
| 198 | 
            -
                  end
         | 
| 199 | 
            -
             | 
| 200 | 
            -
                  def inner_vert=(val)
         | 
| 201 | 
            -
                    @_inner_vert = val
         | 
| 202 | 
            -
                  end
         | 
| 203 | 
            -
             | 
| 204 | 
            -
                  def style
         | 
| 205 | 
            -
                    @_style || BorderStyles::BOLD
         | 
| 206 | 
            -
                  end
         | 
| 207 | 
            -
             | 
| 208 | 
            -
                  def style=(val)
         | 
| 209 | 
            -
                    @_style = BorderStyles.validate!(val)
         | 
| 210 | 
            -
                  end
         | 
| 211 | 
            -
             | 
| 212 | 
            -
                  def color
         | 
| 213 | 
            -
                    @_color || TextColor::WHITE
         | 
| 214 | 
            -
                  end
         | 
| 215 | 
            -
             | 
| 216 | 
            -
                  def color=(val)
         | 
| 217 | 
            -
                    @_color = TextColor.validate!(val)
         | 
| 218 | 
            -
                  end
         | 
| 219 | 
            -
             | 
| 220 | 
            -
                  def merge(parent)
         | 
| 221 | 
            -
                    merged = Border.new
         | 
| 222 | 
            -
                    merged._left = @_left.nil? ? parent._left : @_left
         | 
| 223 | 
            -
                    merged._top = @_top.nil? ? parent._top : @_top
         | 
| 224 | 
            -
                    merged._right = @_right.nil? ? parent._right : @_right
         | 
| 225 | 
            -
                    merged._bottom = @_bottom.nil? ? parent._bottom : @_bottom
         | 
| 226 | 
            -
                    merged._inner_horiz = @_inner_horiz.nil? ? parent._inner_horiz : @_inner_horiz
         | 
| 227 | 
            -
                    merged._inner_vert = @_inner_vert.nil? ? parent._inner_vert : @_inner_vert
         | 
| 228 | 
            -
                    merged._style = @_style || parent._style
         | 
| 229 | 
            -
                    merged._color = @_color || parent._color
         | 
| 230 | 
            -
                    merged
         | 
| 231 | 
            -
                  end
         | 
| 232 | 
            -
             | 
| 233 | 
            -
                  def inspect
         | 
| 234 | 
            -
                    vals = {}
         | 
| 235 | 
            -
                    vals[:left] = @_left unless @_left.nil?
         | 
| 236 | 
            -
                    vals[:top] = @_top unless @_top.nil?
         | 
| 237 | 
            -
                    vals[:right] = @_right unless @_right.nil?
         | 
| 238 | 
            -
                    vals[:bottom] = @_bottom unless @_bottom.nil?
         | 
| 239 | 
            -
                    vals[:inner_horiz] = @_inner_horiz unless @_inner_horiz.nil?
         | 
| 240 | 
            -
                    vals[:inner_vert] = @_inner_vert unless @_inner_vert.nil?
         | 
| 241 | 
            -
                    JSON.generate(vals) if vals.any?
         | 
| 242 | 
            -
                  end
         | 
| 243 | 
            -
             | 
| 244 | 
            -
                  protected
         | 
| 245 | 
            -
             | 
| 246 | 
            -
                  attr_accessor :_left, :_top, :_right, :_bottom, :_inner_horiz, :_inner_vert, :_style, :_color
         | 
| 247 | 
            -
                end
         | 
| 248 | 
            -
             | 
| 249 | 
            -
                class ElementSettings
         | 
| 250 | 
            -
                  def self.merge(parent)
         | 
| 251 | 
            -
                    self.new.merge(parent)
         | 
| 252 | 
            -
                  end
         | 
| 253 | 
            -
             | 
| 254 | 
            -
                  def color
         | 
| 255 | 
            -
                    @_color
         | 
| 256 | 
            -
                  end
         | 
| 257 | 
            -
             | 
| 258 | 
            -
                  def color=(color)
         | 
| 259 | 
            -
                    @_color = TextColor.validate!(color)
         | 
| 260 | 
            -
                  end
         | 
| 261 | 
            -
             | 
| 262 | 
            -
                  def bg_color
         | 
| 263 | 
            -
                    @_bg_color
         | 
| 264 | 
            -
                  end
         | 
| 265 | 
            -
             | 
| 266 | 
            -
                  def bg_color=(color)
         | 
| 267 | 
            -
                    @_bg_color = BgColor.validate!(color)
         | 
| 268 | 
            -
                  end
         | 
| 269 | 
            -
             | 
| 270 | 
            -
                  def bold?
         | 
| 271 | 
            -
                    @_bold || false
         | 
| 272 | 
            -
                  end
         | 
| 273 | 
            -
             | 
| 274 | 
            -
                  def bold=(val)
         | 
| 275 | 
            -
                    @_bold = val
         | 
| 276 | 
            -
                  end
         | 
| 277 | 
            -
             | 
| 278 | 
            -
                  def underline?
         | 
| 279 | 
            -
                    @_underline || false
         | 
| 280 | 
            -
                  end
         | 
| 281 | 
            -
             | 
| 282 | 
            -
                  def underline=(val)
         | 
| 283 | 
            -
                    @_underline = val
         | 
| 284 | 
            -
                  end
         | 
| 285 | 
            -
             | 
| 286 | 
            -
                  def merge(parent)
         | 
| 287 | 
            -
                    merged = self.class.new
         | 
| 288 | 
            -
                    merged.color = @color || parent.color
         | 
| 289 | 
            -
                    merged.bg_color = @bg_color || parent.bg_color
         | 
| 290 | 
            -
                    merged._bold = @_bold.nil? ? parent._bold : @_bold
         | 
| 291 | 
            -
                    merged._underline = @_underline.nil? ? parent._underline : @_underline
         | 
| 292 | 
            -
                    merged
         | 
| 293 | 
            -
                  end
         | 
| 294 | 
            -
             | 
| 295 | 
            -
                  def inspect(indent='')
         | 
| 296 | 
            -
                    values = self.instance_variables.map do |v|
         | 
| 297 | 
            -
                      unless instance_variable_get(v).nil? || instance_variable_get(v).inspect.nil?
         | 
| 298 | 
            -
                        "#{indent}  #{v} = #{instance_variable_get(v).inspect}"
         | 
| 299 | 
            -
                      end
         | 
| 300 | 
            -
                    end.compact
         | 
| 301 | 
            -
                    details = values.length > 0 ? values.join("\n") : "#{indent}  <default>"
         | 
| 302 | 
            -
                    "#{indent}#{self.class.name}\n#{details}"
         | 
| 303 | 
            -
                  end
         | 
| 304 | 
            -
             | 
| 305 | 
            -
                  protected
         | 
| 306 | 
            -
             | 
| 307 | 
            -
                  attr_accessor :_bold, :_underline
         | 
| 308 | 
            -
                end
         | 
| 309 | 
            -
                private_constant :ElementSettings
         | 
| 310 | 
            -
             | 
| 311 | 
            -
                module WidthSettings
         | 
| 312 | 
            -
                  attr_accessor :width
         | 
| 313 | 
            -
                end
         | 
| 314 | 
            -
             | 
| 315 | 
            -
                module AlignSettings
         | 
| 316 | 
            -
                  def align
         | 
| 317 | 
            -
                    @_align || TextAlign::LEFT
         | 
| 318 | 
            -
                  end
         | 
| 319 | 
            -
             | 
| 320 | 
            -
                  def align=(align)
         | 
| 321 | 
            -
                    @_align = TextAlign.validate!(align)
         | 
| 322 | 
            -
                  end
         | 
| 323 | 
            -
             | 
| 324 | 
            -
                  def merge(parent)
         | 
| 325 | 
            -
                    merged = super
         | 
| 326 | 
            -
                    merged._align = if @_align
         | 
| 327 | 
            -
                      @_align
         | 
| 328 | 
            -
                    elsif parent.is_a?(AlignSettings)
         | 
| 329 | 
            -
                      parent._align
         | 
| 330 | 
            -
                    end
         | 
| 331 | 
            -
                    merged
         | 
| 332 | 
            -
                  end
         | 
| 333 | 
            -
             | 
| 334 | 
            -
                  protected
         | 
| 335 | 
            -
             | 
| 336 | 
            -
                  attr_accessor :_align
         | 
| 337 | 
            -
                end
         | 
| 338 | 
            -
             | 
| 339 | 
            -
                module MarginSettings
         | 
| 340 | 
            -
                  def set_margin(left: nil, top: nil, right: nil, bottom: nil)
         | 
| 341 | 
            -
                    @_margin = Margin.new unless @_margin
         | 
| 342 | 
            -
                    @_margin.left = left if left
         | 
| 343 | 
            -
                    @_margin.top = top if top
         | 
| 344 | 
            -
                    @_margin.right = right if right
         | 
| 345 | 
            -
                    @_margin.bottom = bottom if bottom
         | 
| 346 | 
            -
                  end
         | 
| 347 | 
            -
             | 
| 348 | 
            -
                  def clear_margin
         | 
| 349 | 
            -
                    set_margin(left: 0, top: 0, right: 0, bottom: 0)
         | 
| 350 | 
            -
                    @_auto_margin = nil
         | 
| 351 | 
            -
                  end
         | 
| 352 | 
            -
             | 
| 353 | 
            -
                  def margin
         | 
| 354 | 
            -
                    @_margin || Margin.new
         | 
| 355 | 
            -
                  end
         | 
| 356 | 
            -
             | 
| 357 | 
            -
                  def auto_margin=(val)
         | 
| 358 | 
            -
                    @_auto_margin = val
         | 
| 359 | 
            -
                  end
         | 
| 360 | 
            -
             | 
| 361 | 
            -
                  def auto_margin?
         | 
| 362 | 
            -
                    @_auto_margin || false
         | 
| 363 | 
            -
                  end
         | 
| 364 | 
            -
             | 
| 365 | 
            -
                  def merge(parent)
         | 
| 366 | 
            -
                    merged = super
         | 
| 367 | 
            -
                    if parent.is_a?(MarginSettings)
         | 
| 368 | 
            -
                      merged._margin = margin.merge(parent.margin)
         | 
| 369 | 
            -
                      merged._auto_margin = @_auto_margin.nil? ? parent._auto_margin : @_auto_margin
         | 
| 370 | 
            -
                    else
         | 
| 371 | 
            -
                      merged._margin = _margin
         | 
| 372 | 
            -
                      merged._auto_margin = @_auto_margin
         | 
| 373 | 
            -
                    end
         | 
| 374 | 
            -
                    merged
         | 
| 375 | 
            -
                  end
         | 
| 376 | 
            -
             | 
| 377 | 
            -
                  protected
         | 
| 378 | 
            -
             | 
| 379 | 
            -
                  attr_accessor :_margin, :_auto_margin
         | 
| 380 | 
            -
                end
         | 
| 381 | 
            -
                private_constant :MarginSettings
         | 
| 382 | 
            -
             | 
| 383 | 
            -
                module BorderSettings
         | 
| 384 | 
            -
                  def set_border(
         | 
| 385 | 
            -
                    left: nil, top: nil, right: nil, bottom: nil, inner_horiz: nil, inner_vert: nil, style: nil, color: nil
         | 
| 386 | 
            -
                  )
         | 
| 387 | 
            -
                    @_border = Border.new unless @_border
         | 
| 388 | 
            -
                    @_border.left = left unless left.nil?
         | 
| 389 | 
            -
                    @_border.top = top unless top.nil?
         | 
| 390 | 
            -
                    @_border.right = right unless right.nil?
         | 
| 391 | 
            -
                    @_border.bottom = bottom unless bottom.nil?
         | 
| 392 | 
            -
                    @_border.inner_horiz = inner_horiz unless inner_horiz.nil?
         | 
| 393 | 
            -
                    @_border.inner_vert = inner_vert unless inner_vert.nil?
         | 
| 394 | 
            -
                    @_border.style = style unless style.nil?
         | 
| 395 | 
            -
                    @_border.color = color unless color.nil?
         | 
| 396 | 
            -
                  end
         | 
| 397 | 
            -
             | 
| 398 | 
            -
                  def clear_border
         | 
| 399 | 
            -
                    set_border(
         | 
| 400 | 
            -
                      left: false, top: false, right: false, bottom: false, inner_horiz: false, inner_vert: false
         | 
| 401 | 
            -
                    )
         | 
| 402 | 
            -
                  end
         | 
| 403 | 
            -
             | 
| 404 | 
            -
                  def full_border(style: nil, color: nil)
         | 
| 405 | 
            -
                    set_border(
         | 
| 406 | 
            -
                      left: true, top: true, right: true, bottom: true, inner_horiz: true, inner_vert: true, style: style, color: color
         | 
| 407 | 
            -
                    )
         | 
| 408 | 
            -
                  end
         | 
| 409 | 
            -
             | 
| 410 | 
            -
                  def border
         | 
| 411 | 
            -
                    @_border || Border.new
         | 
| 412 | 
            -
                  end
         | 
| 413 | 
            -
             | 
| 414 | 
            -
                  def merge(parent)
         | 
| 415 | 
            -
                    merged = super
         | 
| 416 | 
            -
                    merged._border = parent.is_a?(BorderSettings) ? border.merge(parent.border) : _border
         | 
| 417 | 
            -
                    merged
         | 
| 418 | 
            -
                  end
         | 
| 419 | 
            -
             | 
| 420 | 
            -
                  protected
         | 
| 421 | 
            -
             | 
| 422 | 
            -
                  attr_accessor :_border
         | 
| 423 | 
            -
                end
         | 
| 424 | 
            -
                private_constant :BorderSettings
         | 
| 425 | 
            -
             | 
| 426 | 
            -
                module PaddingSettings
         | 
| 427 | 
            -
                  def set_padding(left: nil, top: nil, right: nil, bottom: nil)
         | 
| 428 | 
            -
                    @_padding = Padding.new unless @_padding
         | 
| 429 | 
            -
                    @_padding.left = left if left
         | 
| 430 | 
            -
                    @_padding.top = top if top
         | 
| 431 | 
            -
                    @_padding.right = right if right
         | 
| 432 | 
            -
                    @_padding.bottom = bottom if bottom
         | 
| 433 | 
            -
                  end
         | 
| 434 | 
            -
             | 
| 435 | 
            -
                  def clear_padding
         | 
| 436 | 
            -
                    set_padding(left: 0, top: 0, right: 0, bottom: 0)
         | 
| 437 | 
            -
                  end
         | 
| 438 | 
            -
             | 
| 439 | 
            -
                  def padding
         | 
| 440 | 
            -
                    @_padding || Padding.new
         | 
| 441 | 
            -
                  end
         | 
| 442 | 
            -
             | 
| 443 | 
            -
                  def merge(parent)
         | 
| 444 | 
            -
                    merged = super
         | 
| 445 | 
            -
                    merged._padding = parent.is_a?(PaddingSettings) ? padding.merge(parent.padding) : _padding
         | 
| 446 | 
            -
                    merged
         | 
| 447 | 
            -
                  end
         | 
| 448 | 
            -
             | 
| 449 | 
            -
                  protected
         | 
| 450 | 
            -
             | 
| 451 | 
            -
                  attr_accessor :_padding
         | 
| 452 | 
            -
                end
         | 
| 453 | 
            -
                private_constant :PaddingSettings
         | 
| 454 | 
            -
             | 
| 455 | 
            -
                class TemplateSettings < ElementSettings
         | 
| 456 | 
            -
                end
         | 
| 457 | 
            -
             | 
| 458 | 
            -
                class TextSettings < ElementSettings
         | 
| 459 | 
            -
                  include WidthSettings
         | 
| 460 | 
            -
                  include AlignSettings
         | 
| 461 | 
            -
             | 
| 462 | 
            -
                  attr_reader :title_font
         | 
| 463 | 
            -
             | 
| 464 | 
            -
                  def title_font=(font)
         | 
| 465 | 
            -
                    @title_font = Utils::TitleFont.validate!(font)
         | 
| 466 | 
            -
                  end
         | 
| 467 | 
            -
                end
         | 
| 468 | 
            -
             | 
| 469 | 
            -
                class ContainerSettings < ElementSettings
         | 
| 470 | 
            -
                  include MarginSettings
         | 
| 471 | 
            -
                  include BorderSettings
         | 
| 472 | 
            -
                  include PaddingSettings
         | 
| 473 | 
            -
                end
         | 
| 474 | 
            -
             | 
| 475 | 
            -
                class BoxSettings < ContainerSettings
         | 
| 476 | 
            -
                  include WidthSettings
         | 
| 477 | 
            -
                  include AlignSettings
         | 
| 478 | 
            -
             | 
| 479 | 
            -
                  def flow=(flow)
         | 
| 480 | 
            -
                    @_flow = DisplayFlow.validate!(flow)
         | 
| 481 | 
            -
                  end
         | 
| 482 | 
            -
             | 
| 483 | 
            -
                  def flow
         | 
| 484 | 
            -
                    @_flow || DisplayFlow::LEFT_TO_RIGHT
         | 
| 485 | 
            -
                  end
         | 
| 486 | 
            -
             | 
| 487 | 
            -
                  def horizontal_flow?
         | 
| 488 | 
            -
                    %i[l2r r2l].include?(flow)
         | 
| 489 | 
            -
                  end
         | 
| 490 | 
            -
             | 
| 491 | 
            -
                  def vertical_flow?
         | 
| 492 | 
            -
                    !horizontal_flow?
         | 
| 493 | 
            -
                  end
         | 
| 494 | 
            -
             | 
| 495 | 
            -
                  def forward_flow?
         | 
| 496 | 
            -
                    %i[l2r t2b].include?(flow)
         | 
| 497 | 
            -
                  end
         | 
| 498 | 
            -
             | 
| 499 | 
            -
                  def reverse_flow?
         | 
| 500 | 
            -
                    !forward_flow?
         | 
| 501 | 
            -
                  end
         | 
| 502 | 
            -
             | 
| 503 | 
            -
                  def merge(parent)
         | 
| 504 | 
            -
                    merged = super
         | 
| 505 | 
            -
                    merged._flow = if @_flow
         | 
| 506 | 
            -
                      @_flow
         | 
| 507 | 
            -
                    elsif parent.is_a?(BoxSettings)
         | 
| 508 | 
            -
                      parent._flow
         | 
| 509 | 
            -
                    end
         | 
| 510 | 
            -
                    merged
         | 
| 511 | 
            -
                  end
         | 
| 512 | 
            -
             | 
| 513 | 
            -
                  protected
         | 
| 514 | 
            -
             | 
| 515 | 
            -
                  attr_accessor :_flow
         | 
| 516 | 
            -
                end
         | 
| 517 | 
            -
             | 
| 518 | 
            -
                class GridSettings < ContainerSettings
         | 
| 519 | 
            -
                  include WidthSettings
         | 
| 520 | 
            -
                  include AlignSettings
         | 
| 521 | 
            -
             | 
| 522 | 
            -
                  attr_accessor :num_cols
         | 
| 523 | 
            -
                  attr_writer :transpose
         | 
| 524 | 
            -
             | 
| 525 | 
            -
                  def transpose?
         | 
| 526 | 
            -
                    @transpose || false
         | 
| 527 | 
            -
                  end
         | 
| 528 | 
            -
                end
         | 
| 529 | 
            -
              end
         | 
| 530 | 
            -
            end
         |