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
| @@ -0,0 +1,19 @@ | |
| 1 | 
            +
            module WhirledPeas
         | 
| 2 | 
            +
              module Settings
         | 
| 3 | 
            +
                class Sizing
         | 
| 4 | 
            +
                  CONTENT = :content
         | 
| 5 | 
            +
                  BORDER = :border
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                  DEFAULT = CONTENT
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  VALID = [CONTENT, BORDER]
         | 
| 10 | 
            +
                  private_constant :VALID
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  def self.validate!(sizing)
         | 
| 13 | 
            +
                    return if sizing.nil?
         | 
| 14 | 
            +
                    return sizing if VALID.include?(sizing)
         | 
| 15 | 
            +
                    raise ArgumentError, "Unsupported sizing model: #{sizing.inspect}"
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
            end
         | 
| @@ -0,0 +1,58 @@ | |
| 1 | 
            +
            module WhirledPeas
         | 
| 2 | 
            +
              module Settings
         | 
| 3 | 
            +
                class Spacing
         | 
| 4 | 
            +
                  attr_writer :left, :top, :right, :bottom
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  def left=(value)
         | 
| 7 | 
            +
                    @left = validate!(value)
         | 
| 8 | 
            +
                  end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  def left
         | 
| 11 | 
            +
                    @left || 0
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  def top=(value)
         | 
| 15 | 
            +
                    @top = validate!(value)
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  def top
         | 
| 19 | 
            +
                    @top || 0
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  def right=(value)
         | 
| 23 | 
            +
                    @right = validate!(value)
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  def right
         | 
| 27 | 
            +
                    @right || 0
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  def bottom=(value)
         | 
| 31 | 
            +
                    @bottom = validate!(value)
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  def bottom
         | 
| 35 | 
            +
                    @bottom || 0
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  def horiz=(value)
         | 
| 39 | 
            +
                    self.left = self.right = value
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  def vert=(value)
         | 
| 43 | 
            +
                    self.top = self.bottom = value
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                  private
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  def validate!(value)
         | 
| 49 | 
            +
                    if value && value < 0
         | 
| 50 | 
            +
                      raise ArgumentError, "Negative values for #{self.class.name.split('::').last.downcase} are not allowed"
         | 
| 51 | 
            +
                    else
         | 
| 52 | 
            +
                      value
         | 
| 53 | 
            +
                    end
         | 
| 54 | 
            +
                  end
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
                private_constant :Spacing
         | 
| 57 | 
            +
              end
         | 
| 58 | 
            +
            end
         | 
| @@ -0,0 +1,21 @@ | |
| 1 | 
            +
            require 'whirled_peas/utils/ansi'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require_relative 'color'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module WhirledPeas
         | 
| 6 | 
            +
              module Settings
         | 
| 7 | 
            +
                class TextColor < Color
         | 
| 8 | 
            +
                  BLACK = new(Utils::Ansi::BLACK)
         | 
| 9 | 
            +
                  RED = new(Utils::Ansi::RED)
         | 
| 10 | 
            +
                  GREEN = new(Utils::Ansi::GREEN)
         | 
| 11 | 
            +
                  YELLOW = new(Utils::Ansi::YELLOW)
         | 
| 12 | 
            +
                  BLUE = new(Utils::Ansi::BLUE)
         | 
| 13 | 
            +
                  MAGENTA = new(Utils::Ansi::MAGENTA)
         | 
| 14 | 
            +
                  CYAN = new(Utils::Ansi::CYAN)
         | 
| 15 | 
            +
                  WHITE = new(Utils::Ansi::WHITE)
         | 
| 16 | 
            +
                  GRAY = BLACK.bright
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  DEFAULT = nil  # Use system defined default
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
            end
         | 
| @@ -0,0 +1,15 @@ | |
| 1 | 
            +
            require 'whirled_peas/utils/title_font'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require_relative 'element_settings'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module WhirledPeas
         | 
| 6 | 
            +
              module Settings
         | 
| 7 | 
            +
                class TextSettings < ElementSettings
         | 
| 8 | 
            +
                  attr_reader :title_font
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  def title_font=(font)
         | 
| 11 | 
            +
                    @title_font = Utils::TitleFont.validate!(font)
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
            end
         | 
| @@ -0,0 +1,24 @@ | |
| 1 | 
            +
            module WhirledPeas
         | 
| 2 | 
            +
              module Settings
         | 
| 3 | 
            +
                module VertAlignment
         | 
| 4 | 
            +
                  TOP = :top
         | 
| 5 | 
            +
                  MIDDLE = :middle
         | 
| 6 | 
            +
                  BOTTOM = :bottom
         | 
| 7 | 
            +
                  BETWEEN = :between
         | 
| 8 | 
            +
                  AROUND = :around
         | 
| 9 | 
            +
                  EVENLY = :evenly
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  DEFAULT = TOP
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  VALID = [TOP, MIDDLE, BOTTOM, BETWEEN, AROUND, EVENLY]
         | 
| 14 | 
            +
                  private_constant :VALID
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  def self.validate!(valign)
         | 
| 17 | 
            +
                    return if valign.nil?
         | 
| 18 | 
            +
                    return valign if VALID.include?(valign)
         | 
| 19 | 
            +
                    raise ArgumentError, "Unsupported vertical alignment: #{valign.inspect}"
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
                private_constant :VertAlignment
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
            end
         | 
| @@ -1,5 +1,5 @@ | |
| 1 1 | 
             
            module WhirledPeas
         | 
| 2 | 
            -
              module  | 
| 2 | 
            +
              module Utils
         | 
| 3 3 | 
             
                # Helper module for working with ANSI escape codes. The most useful ANSI escape codes
         | 
| 4 4 | 
             
                # relate to text formatting.
         | 
| 5 5 | 
             
                #
         | 
| @@ -7,6 +7,9 @@ module WhirledPeas | |
| 7 7 | 
             
                module Ansi
         | 
| 8 8 | 
             
                  ESC = "\033"
         | 
| 9 9 |  | 
| 10 | 
            +
                  END_FORMATTING = 0
         | 
| 11 | 
            +
                  private_constant :END_FORMATTING
         | 
| 12 | 
            +
             | 
| 10 13 | 
             
                  # Text formatting constants
         | 
| 11 14 | 
             
                  BOLD = 1
         | 
| 12 15 | 
             
                  UNDERLINE = 4
         | 
| @@ -21,10 +24,23 @@ module WhirledPeas | |
| 21 24 | 
             
                  CYAN = 36
         | 
| 22 25 | 
             
                  WHITE = 37
         | 
| 23 26 |  | 
| 24 | 
            -
                   | 
| 25 | 
            -
                   | 
| 27 | 
            +
                  # Bright colors are offset by this much from their standard versions
         | 
| 28 | 
            +
                  BRIGHT_OFFSET = 60
         | 
| 26 29 |  | 
| 27 30 | 
             
                  class << self
         | 
| 31 | 
            +
                    def with_screen(output, width: nil, height: nil, &block)
         | 
| 32 | 
            +
                      require 'highline'
         | 
| 33 | 
            +
                      unless width && height
         | 
| 34 | 
            +
                        width, height = HighLine.new.terminal.terminal_size
         | 
| 35 | 
            +
                      end
         | 
| 36 | 
            +
                      yield width, height
         | 
| 37 | 
            +
                    ensure
         | 
| 38 | 
            +
                      output.print clear
         | 
| 39 | 
            +
                      output.print cursor_pos(top: height - 1)
         | 
| 40 | 
            +
                      output.print cursor_visible(true)
         | 
| 41 | 
            +
                      output.flush
         | 
| 42 | 
            +
                    end
         | 
| 43 | 
            +
             | 
| 28 44 | 
             
                    def cursor_pos(top: 0, left: 0)
         | 
| 29 45 | 
             
                      "#{ESC}[#{top + 1};#{left + 1}H"
         | 
| 30 46 | 
             
                    end
         | 
| @@ -37,63 +53,10 @@ module WhirledPeas | |
| 37 53 | 
             
                      "#{ESC}[J"
         | 
| 38 54 | 
             
                    end
         | 
| 39 55 |  | 
| 40 | 
            -
                    # Format the string with the ANSI escapes codes for the given integer codes
         | 
| 41 | 
            -
                    #
         | 
| 42 | 
            -
                    # @param str [String] the string to format
         | 
| 43 | 
            -
                    # @param codes [Array<Integer>] the integer part of the ANSI escape code (see
         | 
| 44 | 
            -
                    #   constants in this module for codes and meanings)
         | 
| 45 | 
            -
                    def format(str, codes)
         | 
| 46 | 
            -
                      if str.empty? || codes.length == 0
         | 
| 47 | 
            -
                        str
         | 
| 48 | 
            -
                      else
         | 
| 49 | 
            -
                        start_formatting = codes.map(&method(:esc_seq)).join
         | 
| 50 | 
            -
                        "#{start_formatting}#{str}#{esc_seq(END_FORMATTING)}"
         | 
| 51 | 
            -
                      end
         | 
| 52 | 
            -
                    end
         | 
| 53 | 
            -
             | 
| 54 56 | 
             
                    def clear
         | 
| 55 57 | 
             
                      esc_seq(END_FORMATTING)
         | 
| 56 58 | 
             
                    end
         | 
| 57 59 |  | 
| 58 | 
            -
                    # If the string has unclosed formatting, add the end formatting characters to
         | 
| 59 | 
            -
                    # the end of the string
         | 
| 60 | 
            -
                    def close_formatting(str)
         | 
| 61 | 
            -
                      codes = str.scan(/#{ESC}\[(\d+)m/)
         | 
| 62 | 
            -
                      if codes.length > 0 && codes.last[0] != END_FORMATTING.to_s
         | 
| 63 | 
            -
                        "#{str}#{esc_seq(END_FORMATTING)}"
         | 
| 64 | 
            -
                      else
         | 
| 65 | 
            -
                        str
         | 
| 66 | 
            -
                      end
         | 
| 67 | 
            -
                    end
         | 
| 68 | 
            -
             | 
| 69 | 
            -
                    # Return a substring of the input string that preservse the formatting
         | 
| 70 | 
            -
                    #
         | 
| 71 | 
            -
                    # @param str [String] the (possibly formatted) string
         | 
| 72 | 
            -
                    # @param first_visible_character [Integer] the index of the first character to
         | 
| 73 | 
            -
                    #   include in the substring (ignoring all hidden formatting characters)
         | 
| 74 | 
            -
                    # @param num_visible_chars [Integer] the maximum number of visible characters to
         | 
| 75 | 
            -
                    #   include in the substring (ignoring all hidden formatting characters)
         | 
| 76 | 
            -
                    def substring(str, first_visible_character, num_visible_chars)
         | 
| 77 | 
            -
                      substr = ''
         | 
| 78 | 
            -
                      is_visible = true
         | 
| 79 | 
            -
                      visible_index = 0
         | 
| 80 | 
            -
                      substr_visible_len = 0
         | 
| 81 | 
            -
                      str.chars.each do |char|
         | 
| 82 | 
            -
                        in_substring = (visible_index >= first_visible_character)
         | 
| 83 | 
            -
                        is_visible = false if is_visible && char == ESC
         | 
| 84 | 
            -
                        visible_index += 1 if is_visible
         | 
| 85 | 
            -
                        if !is_visible || in_substring
         | 
| 86 | 
            -
                          substr += char
         | 
| 87 | 
            -
                          substr_visible_len += 1 if is_visible
         | 
| 88 | 
            -
                        end
         | 
| 89 | 
            -
                        is_visible = true if !is_visible && char == 'm'
         | 
| 90 | 
            -
                        break if substr_visible_len == num_visible_chars
         | 
| 91 | 
            -
                      end
         | 
| 92 | 
            -
                      close_formatting(substr)
         | 
| 93 | 
            -
                    end
         | 
| 94 | 
            -
             | 
| 95 | 
            -
                    private
         | 
| 96 | 
            -
             | 
| 97 60 | 
             
                    def esc_seq(code)
         | 
| 98 61 | 
             
                      "#{ESC}[#{code}m"
         | 
| 99 62 | 
             
                    end
         | 
| @@ -0,0 +1,57 @@ | |
| 1 | 
            +
            require 'base64'
         | 
| 2 | 
            +
            require 'zlib'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module WhirledPeas
         | 
| 5 | 
            +
              module Utils
         | 
| 6 | 
            +
                module FileHandler
         | 
| 7 | 
            +
                  module FileWriter
         | 
| 8 | 
            +
                    VERSION = '1'
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                    def self.write(fp, renders)
         | 
| 11 | 
            +
                      fp.puts renders.count
         | 
| 12 | 
            +
                      renders.each do |strokes|
         | 
| 13 | 
            +
                        encoded = Base64.encode64(strokes)
         | 
| 14 | 
            +
                        fp.puts encoded.count("\n")
         | 
| 15 | 
            +
                        fp.puts encoded
         | 
| 16 | 
            +
                      end
         | 
| 17 | 
            +
                    end
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
                  private_constant :FileWriter
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  class FileReaderV1
         | 
| 22 | 
            +
                    def self.read(fp)
         | 
| 23 | 
            +
                      num_renders = Integer(fp.readline.chomp, 10)
         | 
| 24 | 
            +
                      num_renders.times.map do
         | 
| 25 | 
            +
                        num_strokes = Integer(fp.readline.chomp, 10)
         | 
| 26 | 
            +
                        Base64.decode64(num_strokes.times.map { fp.readline }.join)
         | 
| 27 | 
            +
                      end
         | 
| 28 | 
            +
                    end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                    private
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                    attr_reader :file
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
                  private_constant :FileReaderV1
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  READERS = {
         | 
| 37 | 
            +
                    '1' => FileReaderV1
         | 
| 38 | 
            +
                  }
         | 
| 39 | 
            +
                  private_constant :READERS
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                  def self.write(file, renders)
         | 
| 42 | 
            +
                    Zlib::GzipWriter.open(file, Zlib::BEST_COMPRESSION) do |gz|
         | 
| 43 | 
            +
                      gz.puts FileWriter::VERSION
         | 
| 44 | 
            +
                      FileWriter.write(gz, renders)
         | 
| 45 | 
            +
                    end
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  def self.read(file)
         | 
| 49 | 
            +
                    Zlib::GzipReader.open(file) do |gz|
         | 
| 50 | 
            +
                      version = gz.gets.chomp
         | 
| 51 | 
            +
                      raise ArgumentError, "Invalid file: #{file}" unless READERS.key?(version)
         | 
| 52 | 
            +
                      READERS[version].read(gz)
         | 
| 53 | 
            +
                    end
         | 
| 54 | 
            +
                  end
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
              end
         | 
| 57 | 
            +
            end
         | 
| @@ -0,0 +1,64 @@ | |
| 1 | 
            +
            require_relative 'ansi'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module WhirledPeas
         | 
| 4 | 
            +
              module Utils
         | 
| 5 | 
            +
                class FormattedString
         | 
| 6 | 
            +
                  def self.blank
         | 
| 7 | 
            +
                    new('')
         | 
| 8 | 
            +
                  end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  def initialize(raw, formatting=nil)
         | 
| 11 | 
            +
                    @raw = raw
         | 
| 12 | 
            +
                    @formatting = [*formatting]
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  def length
         | 
| 16 | 
            +
                    raw.length
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  def [](index_or_range)
         | 
| 20 | 
            +
                    self.class.new(raw[index_or_range], foramtting)
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  def blank?
         | 
| 24 | 
            +
                    raw.empty?
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  def hash
         | 
| 28 | 
            +
                    [raw, formatting].hash
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  def ==(other)
         | 
| 32 | 
            +
                    case other
         | 
| 33 | 
            +
                    when self.class
         | 
| 34 | 
            +
                      hash == other.hash
         | 
| 35 | 
            +
                    when String
         | 
| 36 | 
            +
                      formatting.empty? && raw == other
         | 
| 37 | 
            +
                    else
         | 
| 38 | 
            +
                      false
         | 
| 39 | 
            +
                    end
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  def to_s
         | 
| 43 | 
            +
                    if raw.empty? || formatting.length == 0
         | 
| 44 | 
            +
                      raw
         | 
| 45 | 
            +
                    else
         | 
| 46 | 
            +
                      start_formatting = formatting.map { |code| Ansi.esc_seq(code) }.join
         | 
| 47 | 
            +
                      "#{start_formatting}#{raw}#{Ansi.clear}"
         | 
| 48 | 
            +
                    end
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                  def inspect
         | 
| 52 | 
            +
                    if raw.empty? || formatting.length == 0
         | 
| 53 | 
            +
                      raw.inspect
         | 
| 54 | 
            +
                    else
         | 
| 55 | 
            +
                      "<#{formatting.join(', ')}>#{raw}<0>".inspect
         | 
| 56 | 
            +
                    end
         | 
| 57 | 
            +
                  end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                  private
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  attr_reader :raw, :formatting
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
              end
         | 
| 64 | 
            +
            end
         | 
| @@ -6,7 +6,7 @@ module WhirledPeas | |
| 6 6 |  | 
| 7 7 | 
             
                  class << self
         | 
| 8 8 | 
             
                    def validate!(font_key)
         | 
| 9 | 
            -
                      return  | 
| 9 | 
            +
                      return if font_key.nil?
         | 
| 10 10 | 
             
                      return font_key if fonts.key?(font_key)
         | 
| 11 11 | 
             
                      allowed = fonts.keys.map(&:inspect).sort
         | 
| 12 12 | 
             
                      message = "Unrecognized font: '#{font_key.inspect}', expecting one of #{allowed.join(', ')}"
         | 
    
        data/lib/whirled_peas/version.rb
    CHANGED
    
    
| @@ -0,0 +1 @@ | |
| 1 | 
            +
            [?25l[1;1H[J[1;1HTOP LEFT[2;1HBOTTOM LEFT[1;12H TOP RIGHT   [2;12H BOTTOM RIGHT[0m[24;1H[?25h
         | 
| @@ -0,0 +1,20 @@ | |
| 1 | 
            +
            require 'whirled_peas'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            class TemplateFactory
         | 
| 4 | 
            +
              def build(*)
         | 
| 5 | 
            +
                WhirledPeas.template do |composer|
         | 
| 6 | 
            +
                  composer.add_box('Container') do |composer, settings|
         | 
| 7 | 
            +
                    composer.add_box do |composer, settings|
         | 
| 8 | 
            +
                      settings.flow = :t2b
         | 
| 9 | 
            +
                      composer.add_box('TopLeft') { "TOP LEFT" }
         | 
| 10 | 
            +
                      composer.add_box('BottomLeft') { "BOTTOM LEFT" }
         | 
| 11 | 
            +
                    end
         | 
| 12 | 
            +
                    composer.add_box do |composer, settings|
         | 
| 13 | 
            +
                      settings.flow = :t2b
         | 
| 14 | 
            +
                      composer.add_box('TopRight') { " TOP RIGHT   " }
         | 
| 15 | 
            +
                      composer.add_box('BottomRight') { " BOTTOM RIGHT" }
         | 
| 16 | 
            +
                    end
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
            end
         |